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

#ifndef COMMON_FXC
#define COMMON_FXC

// Includes ---------------------------------------------------------------------------------------------------------------------------------------------------
#include "system.fxc"
// !!! Don't include other files here. The error shader includes this for now!

// These are static descriptor sets, they must be defined here only
// Slots are the first available slot (First 3 sets are reserved for rendersystem)
ExternalDescriptorSet g_globalLateBoundBindlessSet Slot 1;

#ifndef PER_VIEW_CBUFFER_DESCRIPTOR_SET
#define PER_VIEW_CBUFFER_DESCRIPTOR_SET
#endif

cbuffer PerViewConstantBuffer_t PER_VIEW_CBUFFER_DESCRIPTOR_SET
{
	float4x4 g_matWorldToProjection;
	float4x4 g_matProjectionToWorld;
	float4x4 g_matWorldToView;
	float4x4 g_matViewToProjection;
	float4 g_vInvProjRow3;

	float4 g_vClipPlane0; // Deprecated, use oblique projection matrix

	float g_flToneMapScalarLinear;
	float g_flLightMapScalar;
	float g_flEnvMapScalar;
	float g_flToneMapScalarGamma;

	float3 g_vCameraPositionWs;
	float g_flViewportMinZ;

	float3 g_vCameraDirWs;
	float g_flViewportMaxZ;

	float3 g_vCameraUpDirWs;
	float g_flTime;

	float3 g_vDepthPsToVsConversion;	// Used for converting projective-space depth buffer depth to view-space (linear) depth.
	float g_flNearPlane;

	float g_flFarPlane;
	float g_flCameraFOV;				// Camera FOV in radians
	float2 g_vInvViewportSize;			// 1/viewport width, 1/viewport height, 

	float2 g_vViewportToGBufferRatio;	// viewport width/gbuffer width, viewport height/gbuffer height
	float2 g_vMorphTextureAtlasSize;
	
	float4 g_vInvGBufferSize; // 1/gbuffer width, 1/gbuffer height, 1/transform buf width, 1/transform buf height
	#define g_flOOTransformTextureWidth ( g_vInvGBufferSize.z )
	#define g_flOOTransformTextureHeight ( g_vInvGBufferSize.w )

	float2 g_vViewportOffset;
	float2 g_vViewportSize;
	float2 g_vRenderTargetSize;

	float g_flFogBlendToBackground;
	float g_flHenyeyGreensteinCoeff;
	float3 g_vFogColor;
	float g_flNegFogStartOverFogRange;		// -fog_start / ( fog_end - fog_start )

	float g_flInvFogRange;					// 1 / ( fog_end - fog_start )
	float g_flFogMaxDensity;
	float g_flFogExponent;

	float g_flMod2xIdentity;		// For cards that blend in linear space vs gamme space

	//bool4 g_bRoughnessParams;
	float2 g_bRoughnessParams;
	#define g_bUseRoughnessCone ( bool( g_bRoughnessParams.x ) )
	#define g_bUseRoughnessEllipse ( bool( g_bRoughnessParams.y ) )
	
    int g_nMSAASampleCount;

	// Stereo and VR constants
	float g_flStereoCameraIndex;
	float3 g_vMiddleEyePositionWs;
	float g_flPad2;
	float4x4 g_matUnusedMultiview1[ 2 ];
	float4 g_vUnusedMultiview2[ 2 ];

	float4 g_vFrameBufferCopyInvSizeAndUvScale;
	float4 g_vCameraAngles;
	float4 g_vWorldToCameraOffset;
	float4 g_vUnusedMultiview3[ 2 ];
	float4 g_vPerViewConstantExtraData0;
	float4 g_vPerViewConstantExtraData1;
	float4 g_vPerViewConstantExtraData2;
	float4 g_vPerViewConstantExtraData3;

	// Sbox

	float4x4 g_matPrevProjectionToWorld;

	float4x4 g_matViewToScreen;		// proj matrix going to pixel coords
	float4x4 g_matProjectionToView;	// inv proj matrix
	float4x4 g_matCurrFrameViewToPrevFrameProj;

    float4 g_vRandomFloats;

};

//
// Let's get all this, toolsvis, and other shit into a cbuffer
//
bool g_bWireframeMode < Attribute( "g_bWireframeMode" ); Default( 0 ); >;
float4 g_vWireframeColor < Attribute("wireframeColor"); Default4(1,1,1,1); >;

bool g_bNonDirectionalDiffuseLighting < Attribute( "g_bNonDirectionalDiffuseLighting" ); Default( 0 ); >;

// Global Constants -------------------------------------------------------------------------------------------------------------------------------------------
static const float g_flPixelCenter = 0.5; // DX11 pixel centers are in between pixels

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
// Used to blink shader code to see before/after during development. Meant to be used like this: if ( Blink( 1.0 ) )
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
float Blink( float flNumSeconds )
{
	return step( 0.5, frac( g_flTime * 0.5 / flNumSeconds ) );
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
// World -> View
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
float4 Position4WsToVs( float4 vPositionWs )
{
	return mul( g_matWorldToView, vPositionWs.xyzw );
}

float4 Position3WsToVs( float3 vPositionWs )
{
	return Position4WsToVs( float4( vPositionWs.xyz, 1.0 ) );
}

float3 Vector3WsToVs( float3 vVecWs )
{
	return Position4WsToVs( float4( vVecWs.xyz, 0.0 ) ).xyz;
}

float3 Vector3VsToWs( float3 vVecVs )
{
	return mul( float4( vVecVs.xyz, 0.0f ), g_matWorldToView ).xyz;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
// View -> Proj
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
float4 Position4VsToPs( float4 vPositionVs )
{
	return mul( g_matViewToProjection, vPositionVs.xyzw );
}

float4 Position3VsToPs( float3 vPositionVs )
{
	return Position4VsToPs( float4( vPositionVs.xyz, 1.0 ) );
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
// World -> View -> Proj
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
float4 Position4WsToPs( float4 vPositionWs )
{
	vPositionWs += ( g_vWorldToCameraOffset * vPositionWs.w );
	return mul( g_matWorldToProjection, vPositionWs.xyzw );
}

float4 Position3WsToPs( float3 vPositionWs )
{
	return Position4WsToPs( float4( vPositionWs.xyz, 1.0 ) );
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
float3 ToneMapLinear( float3 vColor )
{
	return vColor.rgb * g_flToneMapScalarLinear;
}

float3 ToneMapGamma( float3 vColor )
{
	return vColor.rgb * g_flToneMapScalarGamma;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
float4 ClipSpacePositionForLightBinner( float3 vPositionWs, float4 vPositionPs )
{
	float3 vCameraToWorldWs = vPositionWs.xyz - g_vCameraPositionWs.xyz;
	float flCameraProjection = dot( vCameraToWorldWs.xyz, g_vCameraDirWs.xyz );
	float flZPos = ( flCameraProjection - g_flNearPlane ) / ( g_flFarPlane - g_flNearPlane );
	return float4( vPositionPs.xy, flZPos, vPositionPs.w );
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
// Tangent transform helper functions
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
float3 Vec3WsToTs( float3 vVectorWs, float3 vNormalWs, float3 vTangentUWs, float3 vTangentVWs )
{
	float3 vVectorTs;
	vVectorTs.x = dot( vVectorWs.xyz, vTangentUWs.xyz );
	vVectorTs.y = dot( vVectorWs.xyz, vTangentVWs.xyz );
	vVectorTs.z = dot( vVectorWs.xyz, vNormalWs.xyz );
	return vVectorTs.xyz; // Return without normalizing
}

float3 Vec3WsToTsNormalized( float3 vVectorWs, float3 vNormalWs, float3 vTangentUWs, float3 vTangentVWs )
{
	return normalize( Vec3WsToTs( vVectorWs.xyz, vNormalWs.xyz, vTangentUWs.xyz, vTangentVWs.xyz ) );
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
float3 Vec3TsToWs( float3 vVectorTs, float3 vNormalWs, float3 vTangentUWs, float3 vTangentVWs )
{
	float3 vVectorWs;
	vVectorWs.xyz = vVectorTs.x * vTangentUWs.xyz;
	vVectorWs.xyz += vVectorTs.y * vTangentVWs.xyz;
	vVectorWs.xyz += vVectorTs.z * vNormalWs.xyz;
	return vVectorWs.xyz; // Return without normalizing
}

float3 Vec3TsToWsNormalized( float3 vVectorTs, float3 vNormalWs, float3 vTangentUWs, float3 vTangentVWs )
{
	return normalize( Vec3TsToWs( vVectorTs.xyz, vNormalWs.xyz, vTangentUWs.xyz, vTangentVWs.xyz ) );
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
float3 CalculateCameraToPositionRayWs( float3 vPositionWs )
{
	return ( vPositionWs.xyz - g_vCameraPositionWs.xyz );
}

float3 CalculateCameraToPositionDirWs( float3 vPositionWs )
{
	return normalize( CalculateCameraToPositionRayWs( vPositionWs.xyz ) );
}

float3 CalculateCameraToPositionRayTs( float3 vPositionWs, float3 vTangentUWs, float3 vTangentVWs, float3 vNormalWs )
{
	float3 vViewVectorWs = CalculateCameraToPositionRayWs( vPositionWs.xyz ); // Not normalized
	return Vec3WsToTs( vViewVectorWs.xyz, vTangentUWs.xyz, vTangentVWs.xyz, vNormalWs.xyz ); // Not Normalized
}

float3 CalculateCameraToPositionDirTs( float3 vPositionWs, float3 vTangentUWs, float3 vTangentVWs, float3 vNormalWs )
{
	return normalize( CalculateCameraToPositionRayTs( vPositionWs.xyz, vTangentUWs.xyz, vTangentVWs.xyz, vNormalWs.xyz ) );
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
// This helps the compiler reuse the output of the reverse functions above instead of duplicating the above code with camera - position
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
float3 CalculatePositionToCameraRayWs( float3 vPositionWs )
{
	return -CalculateCameraToPositionRayWs( vPositionWs.xyz );
}

float3 CalculatePositionToCameraDirWs( float3 vPositionWs )
{
	return -CalculateCameraToPositionDirWs( vPositionWs.xyz );
}

float3 CalculatePositionToCameraRayTs( float3 vPositionWs, float3 vTangentUWs, float3 vTangentVWs, float3 vNormalWs )
{
	return -CalculateCameraToPositionRayTs( vPositionWs.xyz, vTangentUWs.xyz, vTangentVWs.xyz, vNormalWs.xyz );
}

float3 CalculatePositionToCameraDirTs( float3 vPositionWs, float3 vTangentUWs, float3 vTangentVWs, float3 vNormalWs )
{
	return -CalculateCameraToPositionDirTs( vPositionWs.xyz, vTangentUWs.xyz, vTangentVWs.xyz, vNormalWs.xyz );
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
float3 CalculateCameraReflectionDirWs( float3 vPositionWs, float3 vNormalWs )
{
	float3 vViewVectorWs = CalculateCameraToPositionDirWs( vPositionWs.xyz );
	float3 vReflectionVectorWs = reflect( vViewVectorWs.xyz, vNormalWs.xyz );
	return vReflectionVectorWs.xyz;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
float CalculateDistanceToCamera( float3 vPositionWs )
{
	return length( g_vCameraPositionWs.xyz - vPositionWs.xyz );
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
// Convert projective depth stored in standard HW depth buffer to view space.
// WARNING: Because we're using right-handed coordinates the view depth returned will be negative. Use abs() or negate if you want positive view depth.
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
float ConvertDepthPsToVs( float flDepthPs )
{
	return g_vDepthPsToVsConversion.x / ( g_vDepthPsToVsConversion.y * flDepthPs + g_vDepthPsToVsConversion.z );
}

float ConvertDepthVsToPs( float flDepthVs )
{
	return ( g_vDepthPsToVsConversion.y * flDepthVs + g_vDepthPsToVsConversion.z ) / g_vDepthPsToVsConversion.x;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
// Generalized Schlick approximation for Fresnel term.  Results in a factor between flFrenselReflectance and 1.
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
float CalculateFresnel( float flFrenselReflectance, float flFresnelExponent, float3 vPositionWs, float3 vNormalWs )
{
	float flVDotNk = pow( abs( 1.0 - saturate( dot( CalculatePositionToCameraDirWs( vPositionWs.xyz ), vNormalWs.xyz ) ) ), flFresnelExponent );
	return flFrenselReflectance + ( 1.0 - flFrenselReflectance ) * flVDotNk;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
// Generalized Schlick approximation for Fresnel term.  Rescaled to so that it integrates to 1 over the hemisphere.
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
float CalculateNormalizedFresnel( float flFresnelReflectance, float flFresnelExponent, float3 vPositionWs, float3 vNormalWs )
{
	float flUnNormalizedFresnel = CalculateFresnel( flFresnelReflectance, flFresnelExponent, vPositionWs, vNormalWs );

	float flExponentPlusOne = flFresnelExponent + 1.0;
	float flDenominator = ( 1.0 - flFresnelReflectance ) + flFresnelReflectance * flExponentPlusOne;
	
	return ( flExponentPlusOne / flDenominator ) * flUnNormalizedFresnel;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
// 8 instructions, but should be 6 when we preprocess MinMaxExp to be ScaleBiasExp
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
float CalculateFresnel( float3 vFresnelMinMaxExp, float3 vPositionWs, float3 vNormalWs )
{
	// NOTE: The abs is there because the compiler complains that the first arg to pow needs abs().
	//       The abs below doesn't seem to change the instruction count. I think the compiler is
	//       smart enough to see a 1.0-saturate and remove the abs. If we need to get rid of the abs later,
	//       this pragma will make fxc stop complaining about the lack of abs.
	//#pragma warning( disable: 3571 ) // Disable warning about pow needing abs for first argument

	// NOTE: vFresnelMinMaxExp needs to be preprocessed to be ScaleBiasExp so it is a mad and a pow
	float flVDotNk = pow( abs( 1.0 - saturate( dot( CalculatePositionToCameraDirWs( vPositionWs.xyz ), vNormalWs.xyz ) ) ), vFresnelMinMaxExp.z );
	float flFresnel = ( flVDotNk * ( vFresnelMinMaxExp.y - vFresnelMinMaxExp.x ) ) + vFresnelMinMaxExp.x;

	return flFresnel;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
// Inverse fresnel used for effects like self illum fresnel
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
float CalculateInvFresnel( float3 vFresnelMinMaxExp, float3 vPositionWs, float3 vNormalWs )
{
	// NOTE: The abs is there because the compiler complains that the first arg to pow needs abs().
	//       The abs below doesn't seem to change the instruction count. I think the compiler is
	//       smart enough to see a 1-saturate and remove the abs. If we need to get rid of the abs later,
	//       this pragma will make fxc stop complaining about the lack of abs.
	//#pragma warning( disable: 3571 ) // Disable warning about pow needing abs for first argument

	// NOTE: vFresnelMinMaxExp needs to be preprocessed to be ScaleBiasExp so it is a mad and a pow
	float flVDotNk = pow( abs( saturate( dot( CalculatePositionToCameraDirWs( vPositionWs.xyz ), vNormalWs.xyz ) ) ), vFresnelMinMaxExp.z );
	float flFresnel = ( flVDotNk * ( vFresnelMinMaxExp.y - vFresnelMinMaxExp.x ) ) + vFresnelMinMaxExp.x;

	return flFresnel;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
// Packs a specular exponent into [0,1] range.
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
float PackSpecularExponent( float flSpecularExponent )
{
	return ( flSpecularExponent - 1.0 ) * ( 1.0 / 149.0 );
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
// Unpack a specular exponent from [0,1] range.
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
float UnpackSpecularExponent( float flSpecularExponentPacked )
{
	return 1.0 + ( 149.0 * flSpecularExponentPacked );
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
// Viewport uv helper functions
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
// This can take a custom inv size argument, usually created with Source( InvTextureDim )
float2 CalculateViewportUvFromInvSize( float2 vPositionSs, float2 vInvViewportSize )
{
	float2 vScreenUv;
	vScreenUv.xy = vPositionSs.xy * vInvViewportSize.xy;
	return vScreenUv.xy;
}

// This uses the current viewport size
float2 CalculateViewportUv( float2 vPositionSs )
{
	vPositionSs.xy -= g_vViewportOffset.xy;
	return CalculateViewportUvFromInvSize( vPositionSs.xy, g_vInvViewportSize.xy ) * 1.0f;
}

// Currently we don't upscale depth and it's very minimal when this is used
float2 CalculateViewportDepthUv( float2 vPositionSs )
{
	vPositionSs.xy -= g_vViewportOffset.xy;
	float flResolutionScale = 1.0f;
	if( flResolutionScale == 1.0f ) flResolutionScale = 1.0f;
	return CalculateViewportUvFromInvSize( vPositionSs.xy, g_vInvViewportSize.xy ) * flResolutionScale;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
float GetEnvMapScalar( void )
{
	return g_flEnvMapScalar;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
float GetLightMapScalar( void )
{
	return g_flLightMapScalar;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
// Recover world position given a projection space depth and a ray through the position to the far plane
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
float3 RecoverWorldPosFromProjectedDepthAndRay( float flProjectedDepth, float3 vCameraToPositionRayWs )
{
	float flZScale = g_vInvProjRow3.z;
	float flZTran = g_vInvProjRow3.w;

	//float flViewspaceZ = -1.0 / ( flProjectedDepth * flZScale + flZTran );
	//float flDepthRelativeToRayLength = -flViewspaceZ / dot( g_vCameraDirWs.xyz, vCameraToPositionRayWs.xyz );

	// TODO: Iterate dot( g_vCameraDirWs.xyz, vCameraToPositionRayWs.xyz )
	float flDepthRelativeToRayLength = 1.0 / ( ( flProjectedDepth * flZScale + flZTran ) * dot( g_vCameraDirWs.xyz, vCameraToPositionRayWs.xyz ) );

	// Use the adjusted depth value to recover the world position along the input ray
	float3 vPositionWs = g_vCameraPositionWs.xyz + ( vCameraToPositionRayWs.xyz * flDepthRelativeToRayLength );
	return vPositionWs.xyz;
}

float3 ReprojectFromLastFrameSs( float3 vPositionWs, bool angleOnly = false )
{
	// Reprojection
	float3 vPositionVs = Position4WsToVs( float4( vPositionWs, 1.0 ) ).xyz;

	float4 vPrevFramePosPs = mul( g_matCurrFrameViewToPrevFrameProj, float4( vPositionVs.xyz, angleOnly ? 0.0 : 1.0 ) );
	vPrevFramePosPs.xyz /= vPrevFramePosPs.w;

	return vPrevFramePosPs.xyz;
}

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

#endif // COMMON_FXC
