// Copyright (c) Valve Corporation, All rights reserved. ===================================================================
//
//=========================================================================================================================

#ifndef VR_COMMON_FXC
#define VR_COMMON_FXC

//-----------------------------------------------------------------------------------------------------------------

#if defined(INSTANCING_FXC)
# error "Missed setting format before instancing.fxc is included"
#endif

#include "system.fxc" // This should always be the first include in COMMON
#include "common.fxc" 
#include "math_general.fxc" 

//-----------------------------------------------------------------------------------------------------------------
//
// VR Specfic Per-View Constant Buffer
//
//-----------------------------------------------------------------------------------------------------------------

cbuffer PerViewConstantBufferVR_t
{
	bool4		g_bFogTypeEnabled;
	bool4		g_bOtherFxEnabled;

	float4		g_vAoProxyDownres; // Unused

	float4		g_vXenAnimation; // Unused

	// wind
	float4		g_vWindDirection;
	float4		g_vWindStrengthFreqMulHighStrength;

	// environmental interaction
	float4		g_vInteractionProjectionOrigin;
	float4		g_vInteractionVolumeInvExtents;
	float4		g_vInteractionTriggerVolumeInvMins;
	float4		g_vInteractionTriggerVolumeWorldToVolumeScale;

	// gradient fog
	float4 g_vGradientFogBiasAndScale;
	float4 m_vGradientFogExponents;
	float4 g_vGradientFogColor_Opacity;
	float4 g_vGradientFogCullingParams;		// [dist^2, min z, _, _]

	// cubemap fog
	float4 g_vCubeFog_Offset_Scale_Bias_Exponent;
	#define g_flCubeFogOffset g_vCubeFog_Offset_Scale_Bias_Exponent.x
	#define g_flCubeFogScale g_vCubeFog_Offset_Scale_Bias_Exponent.y
	#define g_flCubeFogBias	g_vCubeFog_Offset_Scale_Bias_Exponent.z
	#define g_flCubeFogExponent g_vCubeFog_Offset_Scale_Bias_Exponent.w

	float4 g_vCubeFog_Height_Offset_Scale_Exponent_Log2Mip;
	#define g_flCubeFogHeightOffset g_vCubeFog_Height_Offset_Scale_Exponent_Log2Mip.x
	#define g_flCubeFogHeightScale g_vCubeFog_Height_Offset_Scale_Exponent_Log2Mip.y
	#define g_flCubeFogHeightExponent g_vCubeFog_Height_Offset_Scale_Exponent_Log2Mip.z
	#define g_flCubeMapSizeLog2 g_vCubeFog_Height_Offset_Scale_Exponent_Log2Mip.w

	float4x4 g_matvCubeFogSkyWsToOs;
	float4 g_vCubeFogCullingParams;		// [dist^2, min z, _, _]

	// spherical vignete
	float4 g_vSphericalVignetteBiasAndScale;
	float4 g_vSphericalVignetteOrigin_Exponent;
	float4 g_vSphericalVignetteColor_Opacity;

	// volumetric fog
	float4 g_vVolFog_VolumeScale_Shift_Near_Range;
	float4 g_vVolFogDitherScaleBias;
	float4 g_vVolFogPostWorldToFrustumScale;
	float4 g_vVolFogPostWorldToFrustumBias;
	float4x4 g_mVolFogFromWorld[ 2 ];

	// high precision lighting
	float4 g_vHighPrecisionLightingOffsetWs;
};

#define g_bVolumetricFogEnabled				g_bFogTypeEnabled.x
#define g_bGradientFogEnabled				g_bFogTypeEnabled.y
#define g_bCubemapFogEnabled				g_bFogTypeEnabled.z
#define g_bSphericalVignetteEnabled			g_bFogTypeEnabled.w
#define g_bAmbientOcclusionProxiesEnabled	g_bOtherFxEnabled.x

#define g_flVolFogNearClipPlane				g_vVolFog_VolumeScale_Shift_Near_Range.z
#define g_flVolFogClipPlaneRange			g_vVolFog_VolumeScale_Shift_Near_Range.w

//-----------------------------------------------------------------------------------------------------------------
//
// Common Defines
//
//-----------------------------------------------------------------------------------------------------------------

#ifndef VS_INPUT_HAS_TANGENT_BASIS
	#define VS_INPUT_HAS_TANGENT_BASIS 1
#endif

#ifndef PS_INPUT_HAS_TANGENT_BASIS
	#define PS_INPUT_HAS_TANGENT_BASIS 1
#endif

#define SUBSURFACE_SCATTERING_PREINTEGRATED 1
#define SUBSURFACE_SCATTERING_WRAP 2
#define SUBSURFACE_SCATTERING_DEFAULT_WRAP_PARAMETERS float4( 0.0, 1.0, 1.0, 1.0 )

#define DEPTH_MASK_NONE float4( 0.0, 0.0, 0.0, 0.0 )
#define DEPTH_MASK_TRANSMISSIVE float4( 0.0, 0.0, 0.0, 1.0 )

//
// UV2 Controls
//
#if ( S_UV2_CONTROLS )
	#define GetUVDefault1( bUseUv2, vTexCoord ) ( ( bUseUv2 ) ? ( vTexCoord ).zw : ( vTexCoord ).xy )
	#define GetUVDefault2( bUseUv2, vTexCoord ) ( ( bUseUv2 ) ? ( vTexCoord ).zw : ( vTexCoord ).xy )
#elif ( S_UV2 )
	#define GetUVDefault1( bUseUv2, vTexCoord ) ( vTexCoord ).xy
	#define GetUVDefault2( bUseUv2, vTexCoord ) ( vTexCoord ).zw
#else
	#define GetUVDefault1( bUseUv2, vTexCoord ) ( vTexCoord ).xy
	#define GetUVDefault2( bUseUv2, vTexCoord ) ( vTexCoord ).xy
#endif

//=========================================================================================================================
//
// Final Combiner Input Structure
//
//=========================================================================================================================
//
// This is an attempt to standardize the lighting models across HLVR scene shaders, with the goal of accelerating shader
// authoring and homogenizing lighting calculations and making code extension easier.
//
// Fill this structure in and the common shader FinalCombiner function will perform lighting, tonemapping etc. 
// before writing to framebuffer
//
//=========================================================================================================================

struct FinalCombinerInput_t
{
	//=========================================================================================================================
	//
	// Public Parameters
	//
	// Can be set/overriden by your custom shaders prior to calling PS_FinalCombiner( )
	//
	//=========================================================================================================================

	float3		vDiffuseColor;
	float3		vSpecularColor;
	float		flOpacity;
	float2		vRoughness;
	float3		vEmissive;
	float3		vNormalWs;
	float		flAmbientOcclusion;
	float		flFresnelExponent;
	float		flRetroReflectivity;

	// S_AMBIENT_OCCLUSION_COLOR_BLEED
	float3		vAmbientOcclusionColorBleed;
	float		flAmbientOcclusionDirectDiffuse;
	float		flAmbientOcclusionDirectSpecular;

	// S_TWO_LOBE_SPECULAR
	float		flTwoLobeSpecularRatio;

	// S_SUBSURFACE_SCATTERING
	float3		vSSSNormalWs;
	float		flSSSCurvature;
	float3		vSSSWrapColor;
	float4		vSSSWrapParameters;
	float		flSSSMask;

	// S_TRANSMISSIVE
	float3		vTransmissiveMask;
	float3		vTransmissiveFalloff;

	// Only uses the following for indirect lighting if ( bUseBentNormalsForIndirect == true )
	bool		bUseBentNormalsForIndirect;
	float3		vBentNormalWs;
	float3		vBentDiffuseNormalWs;

	//=========================================================================================================================
	//
	// Public Parameters that come with a health warning
	//
	// If you are not calling PS_CommonProcessing and instead calling PS_InitFinalCombiner, you may need to set these up
	// manually or you will not get correct lighting. Ideally you should use a standard PS_INPUT and use the former
	// function in most cases.
	//
	//=========================================================================================================================

	float3		vPositionWs;
	float3		vPositionWithOffsetWs;

	float3		vNormalTs;		// Only necessary for baked lighting
	float3		vTangentUWs;	// Only necessary for baked lighting and/or anisotropic lighting
	float3		vTangentVWs;	// Only necessary for baked lighting and/or anisotropic lighting

	//=========================================================================================================================
	//
	// Private "system" parameters
	//
	// Don't modify values after this point in custom shader code; ( you can read them in custom PS code )
	//
	//=========================================================================================================================
	float2		vAnisotropy;
	float3		vPerPixelTangentUWs;
	float3		vPerPixelTangentVWs;
	float		flWrinkle;

	float4		vPositionSs;

	// Lightmap UV ( If LightmappedLight::UsesLightmaps() )
	float2	vLightmapUV;

	// Texcoords (for tools vis only)
	float2	vTextureCoords;
	float3	vAlbedo;
	//=========================================================================================================================
};


//-----------------------------------------------------------------------------------------------------------------
// Typically used for debugging two shaders side by side - returns true if the pixel is on the right half of the screen
// Use: if ( IsRightSplit( i.vPositionSs.x, 0.5f ) ) { ... }
//-----------------------------------------------------------------------------------------------------------------
bool IsRightSplit( float flScreenSpaceXPos, float flRatio )
{
	return ( flScreenSpaceXPos > g_vViewportSize.x * flRatio );
}

//-----------------------------------------------------------------------------------------------------------------
float MyRand( float2 v )
{
	return frac( sin( dot( v.xy, float2( 12.9898, 78.233 ) ) ) * 43758.5453 );
}

//-----------------------------------------------------------------------------------------------------------------
float3 DecodeHemiOctahedronNormal( float2 vHemiOct )
{
	// Rotate and scale the unit square back to the center diamond
	// Using 1.00392163 instead of 1.0 causes the normal map color (128, 128, 255) to decode as (0.0, 0.0, 1.0)
	float2 temp = float2( vHemiOct.x + vHemiOct.y - 1.00392163, vHemiOct.x - vHemiOct.y );
	float3 v = float3( temp.xy, 1.0 - abs( temp.x ) - abs( temp.y ) );
	return normalize( v );
}

//-----------------------------------------------------------------------------------------------------------------
float CalculateMipLevel( float2 vTextureCoordinates, float2 vTextureSize )
{
	// FIXME: hlsl has texture.CalculateLevelOfDetail( sampler, texcoord )
	// Fewer instructions?
	float2 dx = ddx( vTextureCoordinates.xy * vTextureSize.x );
	float2 dy = ddy( vTextureCoordinates.xy * vTextureSize.y );
	float d = max( dot( dx, dx ), dot( dy, dy ) );
	return 0.5 * log2( d );
}

//-----------------------------------------------------------------------------------------------------------------
float RelativeLuminance( float3 vRGB )
{
	return dot( vRGB.rgb, float3( 0.2126, 0.7152, 0.0722 ) );
}

//-----------------------------------------------------------------------------------------------------------------
// Source: http://www.iryoku.com/stare-into-the-future "Next-Generation Character Rendering" by Jorge Jimenez and Javier von der Pahlen
//-----------------------------------------------------------------------------------------------------------------
#ifdef S_POWER_SPECULAR_AMBIENT_OCCLUSION
float PowerSpecularOcclusion( float3 vPositionWs, float3 vNormalWs, float flAmbientOcclusion )
{
	// Using the middle eye allows me to stereo fuse the result
	float3 vPositionToMiddleEyeDirWs = normalize( g_vMiddleEyePositionWs.xyz - vPositionWs.xyz );

	float flSpecularAO = ClampToPositive( dot( vPositionToMiddleEyeDirWs.xyz, vNormalWs.xyz ) );
	flSpecularAO = saturate( flSpecularAO * flSpecularAO - 0.3 );
	flSpecularAO = lerp( pow( flAmbientOcclusion, 8.0 ), 1.0, flSpecularAO );
	return flSpecularAO;
}
#else
float PowerSpecularOcclusion( float3 vPositionWs, float3 vNormalWs, float flAmbientOcclusion )
{
	return flAmbientOcclusion;
}
#endif

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
float AdjustOpacityForAlphaToCoverage( float flOpacity, float flAlphaTestReference, float flAntiAliasedEdgeStrength, float2 vTextureCoords )
{
	// Shader derivative magic for sharp anti-aliased edges in alpha-to-coverage
	float flAntiAliasedOpacityEdge = saturate( 0.5 + ( flOpacity - flAlphaTestReference ) / max( fwidth( flOpacity ), 0.000001 ) );

	// Fall back to ordinary alpha-to-coverage at a sufficient distance/angle
	float flAlphaToCoverageAmount = saturate( 4.0 * length( fwidth( vTextureCoords.xy ) ) );

	// Fall back to ordinary alpha-to-coverage with user parameter
	flAlphaToCoverageAmount = lerp( 1.0, flAlphaToCoverageAmount, flAntiAliasedEdgeStrength );

	return lerp( flAntiAliasedOpacityEdge, flOpacity, flAlphaToCoverageAmount );
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
// Infer non-uniform scale from transformation matrix
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
float3 ComputeMatrixScale( float3x4 m )
{
	float3 vScale;
	vScale.x = sqrt( m[0][0] * m[0][0] + m[1][0] * m[1][0] + m[2][0] * m[2][0] );
	vScale.y = sqrt( m[0][1] * m[0][1] + m[1][1] * m[1][1] + m[2][1] * m[2][1] );
	vScale.z = sqrt( m[0][2] * m[0][2] + m[1][2] * m[1][2] + m[2][2] * m[2][2] );
	return vScale;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
// Quaternions
//-------------------------------------------------------------------------------------------------------------------------------------------------------------

float4 QuaternionMultiplication( float4 q1, float4 q2 )
{
	return float4(
		q2.xyz * q1.w + q1.xyz * q2.w + cross( q1.xyz, q2.xyz ),
		q1.w * q2.w - dot( q1.xyz, q2.xyz )
	);
}

float3 QuaternionRotation( float3 v, float4 q )
{
	float4 q_c = q.xyzw * float4( -1, -1, -1, 1 );
	return QuaternionMultiplication( q, QuaternionMultiplication( float4( v.xyz, 0 ), q_c.xyzw ) ).xyz;
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
// Compute inverse transpose of transformation matrix
//-------------------------------------------------------------------------------------------------------------------------------------------------------------

float3x3 ComputeInverseTranspose( float3x4 m )
{

	float flDet = m[0][0] * ( m[1][1] * m[2][2] - m[2][1] * m[1][2] ) -
			      m[0][1] * ( m[1][0] * m[2][2] - m[1][2] * m[2][0] ) +
				  m[0][2] * ( m[1][0] * m[2][1] - m[1][1] * m[2][0] );


	float3x3 InvTranspose;

	InvTranspose[0][0] = ( m[1][1] * m[2][2] - m[2][1] * m[1][2] ) / flDet;
	InvTranspose[0][1] = ( m[1][2] * m[2][0] - m[1][0] * m[2][2] ) / flDet;
	InvTranspose[0][2] = ( m[1][0] * m[2][1] - m[2][0] * m[1][1] ) / flDet;
	InvTranspose[1][0] = ( m[0][2] * m[2][1] - m[0][1] * m[2][2] ) / flDet;
	InvTranspose[1][1] = ( m[0][0] * m[2][2] - m[0][2] * m[2][0] ) / flDet;
	InvTranspose[1][2] = ( m[2][0] * m[0][1] - m[0][0] * m[2][1] ) / flDet;
	InvTranspose[2][0] = ( m[0][1] * m[1][2] - m[0][2] * m[1][1] ) / flDet;
	InvTranspose[2][1] = ( m[1][0] * m[0][2] - m[0][0] * m[1][2] ) / flDet;
	InvTranspose[2][2] = ( m[0][0] * m[1][1] - m[1][0] * m[0][1] ) / flDet;

	return InvTranspose;
}

//-----------------------------------------------------------------------------	
// Convert pixel space coordinates to screen space coordinates
float2 PsToSs( float4 vPositionSs )
{
    const float fZoom = 0.5f;
    float2 vPositionPs = fZoom * ( vPositionSs.xy ) - float2( 0.5f, 0.5f );
    vPositionPs *= -1.0;
    return vPositionPs;
}



#endif