/*=============================================================================
	Common.hlsl: Common shader code.
	Copyright 1998-2010 Epic Games, Inc. All Rights Reserved.
=============================================================================*/

#include "Definitions.usf"

#if PS3

	// Tangent space bias
	#define TangentBias(x)			( (x / 127.5) - 1 )
	#define TangentNorm(x)			(x / 255.0)
	
	// Register keyword in CG. Note no pixel constant registers on PS3!
	#define VERTEXREGISTER(cx)		: cx
	#define PIXELREGISTER(cx)

	// On PS3, remap Tangent and Binormal to slot 5 and 6, so they don't overlap texcoord 6 and 7
	#define TANGENT					ATTR5
	#define BINORMAL				ATTR6

	// Input semantic for pixel shaders to get the screenspace pixel position (window position)
	#define VPOS					WPOS
	#define VFACE					FACE

	// The PS3 reads our FColor values as ARGB; GBAR swizzles the components into the right order.
	#define FCOLOR_COMPONENT_SWIZZLE .gbar

#elif SM4_PROFILE

	// Tangent space bias
	#define TangentBias(x)			( (x * 2) - 1 )
	#define TangentNorm(x)			(x)

	// Register keyword in HLSL 4.0
	#define VERTEXREGISTER(cx)
	#define PIXELREGISTER(cx)

	// D3D10 vertex declarations read our FColor values as BGRA, so they need to be reversed.
	#define FCOLOR_COMPONENT_SWIZZLE .bgra

#else

	// Tangent space bias
	#define TangentBias(x)			( (x / 127.5) - 1 )
	#define TangentNorm(x)			(x / 255.0)

	// Register keyword in HLSL
	#define VERTEXREGISTER(cx)		: register(cx)
	#define PIXELREGISTER(cx)		: register(cx)

	#define FCOLOR_COMPONENT_SWIZZLE .rgba
#endif

// Note that in SM2 and PS3 COLOR0 and COLOR1 are fixed point and clamped to [0,1]
// We use PackColorN() and UnpackColorN() helper functions to pass vectors through a COLOR interpolator.
#define TANGENTTOWORLD0			COLOR0
#define TANGENTTOWORLD2			COLOR1

#if PS3
	// Potentially normalizes and remaps a vector to [0,1] range, to fit in a COLOR interpolator.
	float3	PackColor3( float3 UnpackedVector )
	{
		return normalize(UnpackedVector)*0.5f + 0.5f;
	}
	// Potentially normalizes and remaps a vector to [0,1] range, to fit in a COLOR interpolator.
	float4	PackColor4( float4 UnpackedVector )
	{
		float4 PackedVector;
		PackedVector.xyz = normalize(UnpackedVector.xyz);
		PackedVector.w = UnpackedVector.w;
		return PackedVector*0.5f + 0.5f;
	}
	// Potentially remaps a vector from [0,1] to [-1,+1] range, to unpack a vector from a COLOR interpolator.
	half3	UnpackColor3( half3 PackedVector )
	{
		return PackedVector*2.0f - 1.0f;
	}
	// Potentially remaps a vector from [0,1] to [-1,+1] range, to unpack a vector from a COLOR interpolator.
	half4	UnpackColor4( half4 PackedVector )
	{
		return PackedVector*2.0f - 1.0f;
	}
#elif SM2_PROFILE
	// Potentially normalizes and remaps a vector to [0,1] range, to fit in a COLOR interpolator.
	float3	PackColor3( float3 UnpackedVector )
	{
		return UnpackedVector*0.5f + 0.5f;
	}
	// Potentially normalizes and remaps a vector to [0,1] range, to fit in a COLOR interpolator.
	float4	PackColor4( float4 UnpackedVector )
	{
		return UnpackedVector*0.5f + 0.5f;
	}
	// Potentially remaps a vector from [0,1] to [-1,+1] range, to unpack a vector from a COLOR interpolator.
	half3	UnpackColor3( half3 PackedVector )
	{
		return PackedVector*2.0f - 1.0f;
	}
	// Potentially remaps a vector from [0,1] to [-1,+1] range, to unpack a vector from a COLOR interpolator.
	half4	UnpackColor4( half4 PackedVector )
	{
		return PackedVector*2.0f - 1.0f;
	}
#else
	// Potentially normalizes and remaps a vector to [0,1] range, to fit in a COLOR interpolator.
	float3	PackColor3( float3 UnpackedVector )
	{
		return UnpackedVector;
	}
	// Potentially normalizes and remaps a vector to [0,1] range, to fit in a COLOR interpolator.
	float4	PackColor4( float4 UnpackedVector )
	{
		return UnpackedVector;
	}
	// Potentially remaps a vector from [0,1] to [-1,+1] range, to unpack a vector from a COLOR interpolator.
	half3	UnpackColor3( half3 PackedVector )
	{
		return PackedVector;
	}
	// Potentially remaps a vector from [0,1] to [-1,+1] range, to unpack a vector from a COLOR interpolator.
	half4	UnpackColor4( half4 PackedVector )
	{
		return PackedVector;
	}
#endif


#if PS3
	// When multiplying with a matrix that is passed in externally through a matrix parameter, you MUST use MulMatrix.
	// When multiplying by a generated matrix (float3x3(VecA, VecB, VecC)), you MUST use mul
	// Note that MulMatrix also works for multiplying vector by matrix (transforming by transposed matrix), e.g. MulMatrix( Vect, Mtx ).
	#define MulMatrix(Mtx, Vect)	mul(Vect, Mtx)
	#define MulBone(Mtx, Vect)		mul(Mtx, Vect)
	
	// Clamp the base, so it's never <= 0.0f on PS3 (INF/NaN).
	#define pow(x,y)				pow( max(abs(x), 0.000001f), (y) )
#else
	#define MulMatrix(Mtx, Vect)	mul(Mtx, Vect)
	#define MulBone(Mtx, Vect)		mul(Vect, Mtx)

	// Clamp the base, so it's never <= 0.0f (INF/NaN).
	float ClampedPow(float X,float Y)
	{
		// Workaround for a bug in the March 2009 DX SDK that compiles a scalar pow(max(abs(X), 0.0001f), Y) as:
		// max( pow( abs(X), 80 ), pow( 0.0001f, 80 ) ); 
		// The pow( 0.0001f, 80 ) generates a denorm. 
		#if PC_D3D
			return abs(X) < 0.000001f ? 0 : pow(abs(X), Y);
		#else
			return pow(max(abs(X),0.000001f),Y);
		#endif
	}
	float2 ClampedPow(float2 X,float2 Y)
	{
		return pow(max(abs(X),float2(0.000001f,0.000001f)),Y);
	}
	float3 ClampedPow(float3 X,float3 Y)
	{
		return pow(max(abs(X),float3(0.000001f,0.000001f,0.000001f)),Y);
	}
	float4 ClampedPow(float4 X,float4 Y)
	{
		return pow(max(abs(X),float4(0.000001f,0.000001f,0.000001f,0.000001f)),Y);
	}
	#define pow(x,y)				ClampedPow(x,y)

	// It's necessary to use these whenever you're reading a depth value from a depth texture
    #define texDepth2D				tex2D
	#define texDepth2Dlod			tex2Dlod
	#define texDepth2Dproj			tex2Dproj
#endif

#if !SM4_PROFILE
	// SM4 is the only platform that natively supports uints in shaders; we just use floats on other platforms.
	#define uint4	float4
#endif

#if !VERTEXSHADER
	#undef VERTEXREGISTER
	#define VERTEXREGISTER(cx)
#endif

#if !PIXELSHADER
	#undef PIXELREGISTER
	#define PIXELREGISTER(cx)
#endif

// Pixel and vertex shader constant registers that are reserved by the Engine.
// ----------------------------------------------------------------------------------------
// These #defines must match the enums EPixelShaderRegisters and EVertexShaderRegister
// as they are defined in RHI.h.
#define PSR_ColorBiasFactor			c0	// Factor applied to the color output from the pixelshader
#define PSR_ScreenPositionScaleBias	c1	// Converts projection-space XY coordinates to texture-space UV coordinates
#define PSR_MinZ_MaxZ_Ratio			c2	// Converts device Z values to clip-space W values
#define VSR_ViewProjMatrix			c0	// View-projection matrix, transforming from World space to Projection space
#define VSR_ViewOrigin				c4	// World space position of the camera
#define VSR_PreViewTranslation		c5	// Offset applied to LocalToWorld to reduce precision problems far from the world space origin

#if SM4_PROFILE
	cbuffer VSOffsetConstants : register(b1)
	{
		float4x4	ViewProjectionMatrix	VERTEXREGISTER(VSR_ViewProjMatrix);
		float4		CameraPosition			VERTEXREGISTER(VSR_ViewOrigin);
		float4		PreViewTranslation		VERTEXREGISTER(VSR_PreViewTranslation);
	};
	cbuffer PSOffsetConstants : register(b2)
	{
		// Converts projection-space XY coordinates to texture-space UV coordinates
		float4		ScreenPositionScaleBias	PIXELREGISTER(PSR_ScreenPositionScaleBias);
		float4	    MinZ_MaxZRatio			PIXELREGISTER(PSR_MinZ_MaxZ_Ratio);
	};
#else
	float4x4	ViewProjectionMatrix		VERTEXREGISTER(VSR_ViewProjMatrix);
	float4		CameraPosition				VERTEXREGISTER(VSR_ViewOrigin);
	float4		PreViewTranslation			VERTEXREGISTER(VSR_PreViewTranslation);

	// Converts projection-space XY coordinates to texture-space UV coordinates
	float4		ScreenPositionScaleBias		PIXELREGISTER(PSR_ScreenPositionScaleBias);
	float4	    MinZ_MaxZRatio				PIXELREGISTER(PSR_MinZ_MaxZ_Ratio);
#endif

#if XBOX
	// The x component is set to 1 when rendering to an LDR buffer, otherwise 2^SCENE_COLOR_BIAS_FACTOR_EXP (See XeD3DRenderTarget.cpp)
	float4	SCENE_COLOR_BIAS_FACTOR			PIXELREGISTER(PSR_ColorBiasFactor);
#else
	#define SCENE_COLOR_BIAS_FACTOR			1.0f
#endif

#if MATERIAL_TWOSIDED
	// For two sided surfaces, this is the sign that should be applied to the normal.
	// In SM2, the surfaces are drawn twice with opposite signs.
	// In all other profiles, the surfaces are only drawn once without backface culling, and VFACE
	// is used to determine which side is being drawn.  TwoSidedSign still contains useful information
	// about whether the model is flipped in that case.
	half TwoSidedSign;
#endif

sampler2D 	SceneDepthTexture;
sampler2D	SceneColorTexture;
sampler2D	LightAttenuationTexture;



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

#if XBOX
	float4 BiasColor( float4 Color )
	{
		return float4( Color.rgb * SCENE_COLOR_BIAS_FACTOR.x, Color.a );
	}
	// RETURN_COLOR should only be used when rendering to the SceneColor surface
	#define RETURN_COLOR( Color ) BiasColor( Color );
#else
	// We don't use an inline function so we can avoid type promotion/ coercion.
	#define RETURN_COLOR( Color ) ( Color )
#endif

//the largest value any color component is allowed to have, scene color is clamped to this in DOFAndBloomGatherPixelShader.usf
//also used to pack color into the fixed point filter buffer, which requires a range of [0-1]
#define MAX_SCENE_COLOR 4.0f

float Square(float A)
{
	return A * A;
}

float4 ExpandRGBE( float4 RGBE )
{
	return float4( ldexp( RGBE.xyz, 255.0 * RGBE.w - 128.0 ), 1.0 );
}

float4 ExpandCompressedRGBE( float4 RGBE )
{
	return float4( ldexp( RGBE.xyz, 255.0 / 16.0 * RGBE.w - 8.0 ), 1.0 );
}

half3 PointLightPhong(half3 DiffuseColor,half DiffusePower,half3 TransmissionMask,half3 SpecularColor,half SpecularPower, half3 L, float3 E, half3 N, float3 R)
{
	half3	DiffuseLighting = pow(saturate(dot(N,L)),DiffusePower);
#ifndef DISABLE_DYNAMIC_SPECULAR
	half3	SpecularLighting = pow(saturate(dot(R,L)),SpecularPower);
#else
	half3	SpecularLighting = half3(0.0f, 0.0f, 0.0f);
#endif

	// Only apply transmission to diffuse, not specular
	return DiffuseColor * lerp(DiffuseLighting, TransmissionMask, TransmissionMask) + SpecularLighting * SpecularColor;
}

float3 PointLightAnisotropic(half3 DiffuseColor,half3 TransmissionMask,half3 SpecularColor,half SpecularPower, half3 L, float3 E, half3 N, float3 R, half3 T)
{
	// from http://ati.amd.com/developer/shaderx/ShaderX_PerPixelAniso.pdf
	half3	DiffuseLighting = sqrt(1.0-Square(dot(L,T)));
	half3	VdotR = sqrt(1.0-Square(dot(L,T))) * sqrt(1.0-Square(dot(E,T))) - dot(L,T) * dot(E,T);
#ifndef DISABLE_DYNAMIC_SPECULAR
	half3	SpecularLighting = pow(saturate(VdotR),SpecularPower);
#else
	half3	SpecularLighting = half3(0.0f, 0.0f, 0.0f);
#endif
			
	// We attenuate the diffuse and specular on the side facing away from the light
	// because otherwise we get horrible hard edges at the shadow map edge.
	half	DiffuseAttenuation = saturate(dot(N,L));
	half	SpecularAttenuation = pow(saturate(dot(N,L)), 0.3);

	// Only apply transmission to diffuse, not specular
	return DiffuseColor * lerp(DiffuseAttenuation * DiffuseLighting, TransmissionMask, TransmissionMask) + SpecularAttenuation * SpecularLighting * SpecularColor;
}

float4 PreviousLighting(float4 ScreenPosition)
{
	return tex2D(SceneColorTexture,ScreenPosition.xy / ScreenPosition.w * ScreenPositionScaleBias.xy + ScreenPositionScaleBias.wz);
}

float RadialAttenuation(float3 WorldLightVector,half FalloffExponent)
{
	return pow(
		saturate(1.0f - dot(WorldLightVector,WorldLightVector)),
		FalloffExponent
		);
}

half4 GetLightAttenuation(float4 ScreenPosition)
{
	return tex2D(LightAttenuationTexture, ScreenPosition.xy / ScreenPosition.w * ScreenPositionScaleBias.xy + ScreenPositionScaleBias.wz);
}

/** return the scene lighting texture */
half3 CalcSceneColor(float2 ScreenUV)
{
	return tex2D(SceneColorTexture,ScreenUV).rgb;
}

/** return all channels of the scene lighting texture */
half4 CalcFullSceneColor(float2 ScreenUV)
{
      return tex2D(SceneColorTexture,ScreenUV);
}

half ConvertFromDeviceZ(half DeviceZ)
{
	//return MinZ_MaxZRatio[0] / (1.0 - (DeviceZ / MinZ_MaxZRatio[1]));
	return 1.f / (DeviceZ * MinZ_MaxZRatio[2] - MinZ_MaxZRatio[3]);	
}

/** Encodes the W coordinate of a pixel for storage in floating point. */
half EncodeFloatW(float W)
{
	#if SUPPORTS_DEPTH_TEXTURES
		return 0;
	#else
		float DepthAdd = MinZ_MaxZRatio[0];
		float DepthMul = MinZ_MaxZRatio[1];
		return DepthMul + DepthAdd / W;
	#endif
}

#if SUPPORTS_DEPTH_TEXTURES

	/** return the depth value stored in the depth buffer - but convert it to w first */
	half CalcSceneDepth( float2 ScreenUV )
	{
		// get depth buffer z value
		half DeviceZ = texDepth2D(SceneDepthTexture,ScreenUV).r;

		// convert it to clip space w
		return ConvertFromDeviceZ(DeviceZ);
	}

#elif SM2_PROFILE

	/** return the depth value stored in lighting target's alpha */
	half CalcSceneDepth( float2 ScreenUV )
	{
		// depth stored in alpha
		return ConvertFromDeviceZ(tex2D(SceneDepthTexture,ScreenUV).a);
	}

#else

	/** return the depth value stored in lighting target's alpha */
	half CalcSceneDepth( float2 ScreenUV )
	{
		// depth stored in alpha
		// clamp to fp 16 max to avoid INF on Geforce 6, 7 and RSX
		return ConvertFromDeviceZ(min(tex2D(SceneColorTexture,ScreenUV).a, 65503.0f));
	}

#endif

/**
* Returns scene color in rgb, depth in a
*/
half4 CalcSceneColorAndDepth( float2 ScreenUV )
{
	return half4(CalcSceneColor(ScreenUV), CalcSceneDepth(ScreenUV));
}


#if SM2_PROFILE
	/**
	* Used to output a scale value based on scene color luminance when additively blending.
	*/
	half4 AccumulateSceneColor(half4 InSceneColor)
	{
		// Tweakable luminance scale
		const half SceneColorAccumulationFactor = 0.1f;
		const half MaxLuminanceScale = 0.1f;
		const half3 LuminanceWeights = half3(.3f, .59f, .11f) * SceneColorAccumulationFactor;
		half ScaledLuminance = clamp(dot(InSceneColor.rgb, LuminanceWeights), 0.0f, MaxLuminanceScale);
		return half4(InSceneColor.rgb, ScaledLuminance * ScaledLuminance);
	}
#else
	half4 AccumulateSceneColor(half4 InSceneColor)
	{
		return InSceneColor;
	}
#endif

half PreviousDepth(float4 ScreenPosition)
{
	return CalcSceneDepth(ScreenPosition.xy / ScreenPosition.w * ScreenPositionScaleBias.xy + ScreenPositionScaleBias.wz);
}

#if PS3
	half PreviousDepthForShadows(float4 ScreenPosition)
	{
		float2 ScreenUV = ScreenPosition.xy / ScreenPosition.w * ScreenPositionScaleBias.xy + ScreenPositionScaleBias.wz;

		// get depth buffer z value
		half DeviceZ = texDepth2D(SceneDepthTexture,ScreenUV).r;

		// convert it to clip space w
		return ConvertFromDeviceZ(DeviceZ);
	}
#else
	half PreviousDepthForShadows(float4 ScreenPosition)
	{
		return PreviousDepth(ScreenPosition);
	}
#endif

/** 
 * aligns the clip space position so that it can be used as a texture coordinate
 * to properly align in screen space
 */
float4 ScreenAlignedPosition( float4 ScreenPosition )
{
	return float4(ScreenPosition.xy / ScreenPosition.w * ScreenPositionScaleBias.xy + ScreenPositionScaleBias.wz, ScreenPosition.z/ScreenPosition.w,1);
}

/** 
 * Aligns the [0,1] UV to match the view within the backbuffer
 */
half2 ScreenAlignedUV( half2 UV )
{
	return (UV*half2(2,-2) + half2(-1,1))*ScreenPositionScaleBias.xy + ScreenPositionScaleBias.wz;
}

/**
 * Calculate the normal Z component from X and Y
 */
float4 NormalCalcZ( float4 Normal )
{
	return float4( Normal.xy, sqrt(1.0 - dot(Normal.xy,Normal.xy)), 1 );
}
 
/*
 * Bias the normal sampled from a texture to the range of (-1..1, -1..1, -1..1, 0..1)
 */
float4 BiasNormalMap( float4 Normal )
{
	return Normal * float4(2.f,2.f,2.f,1.f) - float4(1.f,1.f,1.f,0.f);
}

#if PS3

float4 BiasNormalizeNormalMap_DXT1( float4 Normal ) { return Normal; }
float4 BiasNormalizeNormalMap_DXT5( float4 Normal ) { return BiasNormalMap(Normal); }
float4 BiasNormalizeNormalMap_V8U8( float4 Normal ) { return NormalCalcZ(Normal); }

#elif XBOX

// these cause problems with the Xbox shader compiler so we use a #define instead.
/*
float4 BiasNormalizeNormalMap_DXT1( float4 Normal ) { return BiasNormalMap(Normal); }
float4 BiasNormalizeNormalMap_DXT5( float4 Normal ) { return BiasNormalMap(Normal); }
float4 BiasNormalizeNormalMap_V8U8( float4 Normal ) { return NormalCalcZ(Normal); }
float4 BiasNormalizeNormalMap_BC5( float4 Normal ) { return NormalCalcZ(BiasNormalMap(Normal)); }
*/

#define NORMALCALCZ(Normal) (float4((Normal).xy, sqrt(1.f - dot((Normal).xy,(Normal).xy)), 1.f))
#define BIASNORMALMAP(Normal) ((Normal) * float4(2.f,2.f,2.f,1.f) - float4(1.f,1.f,1.f,0.f))

#define BiasNormalizeNormalMap_DXT1(Normal) BIASNORMALMAP(Normal)
#define BiasNormalizeNormalMap_DXT5(Normal) BIASNORMALMAP(Normal)
#define BiasNormalizeNormalMap_V8U8(Normal) NORMALCALCZ(Normal)
#define BiasNormalizeNormalMap_BC5(Normal) NORMALCALCZ(BIASNORMALMAP(Normal))

#else

float4 BiasNormalizeNormalMap_DXT1( float4 Normal ) { return BiasNormalMap(Normal); }
float4 BiasNormalizeNormalMap_DXT5( float4 Normal ) { return BiasNormalMap(Normal); }
float4 BiasNormalizeNormalMap_V8U8( float4 Normal ) { return NormalCalcZ(Normal); }
float4 BiasNormalizeNormalMap_BC5( float4 Normal ) { return NormalCalcZ(BiasNormalMap(Normal)); }

#endif

// Antialiased version of a binary comparison between ThresholdConst and a texture channel.
float AntialiasedTextureMask( sampler2D Tex, float2 UV, float ThresholdConst, int Channel )
{
	// By setting MaskConst to 0001, 0010, 0100 or 1000 individual channels can be chosen (the compiler should be able to optimize that).
	half4 MaskConst = half4(Channel == 0, Channel == 1, Channel == 2, Channel == 3);

#if SM2_PROFILE
	// ShaderModel 2.0 doesn't support ddx() ,ddy()
	return dot(MaskConst, tex2D(Tex, UV)) > ThresholdConst;
#else
	// border width in pixels, for antialiasing 1 .. 1.5 is good but 1.0 is good for optimizations 
	const float WidthConst = 1.0f;			
	float InvWidthConst = 1 / WidthConst;

	// Problem:

	// A simple texture lookup with a comparison against some thresold value allows to get a mask useful
	// for many purposes (e.g. text rendering, signs, oil/water/paint). Antialiased masks look much better
	// and mip mapping provides that but only for minification. So when the texture resolution is lower than
	// the rendering size results get blurry.

	// Idea:

	// We compute the distance to the threshold line in pixels (with subpixel precision). We can visualize
	// the problem as a heightmap that intersects a axis aligned plane at the threshold height. Only surface
	// above the threshold plane contributes to the mask. Looking at one pixel the heightmap can be approximated
	// by a plane. We can easily get the plane center value form a texture lookup and get the plane equation from
	// ddx and ddy of that value (only one value per 2x2 block) or some other more precise method. We can reduce the
	// 3d problem to 2d (looking at the steepest angle only) and the resulting value tells us how much the texture value
	// changes for one pixel. This allows us to scale and bias (threshold) the texture value the so it maps to the
	// distance function. We rescaling the distance to 0.5 coverage at the line, >1 half a pixel inside and <0 half
	// a pixel outside. Clamping this value in the range from 0..1 gives us a good approximation of the pixel coverage.

	// We tried multiple possible implementations - this is the cheapest and looks ok is most cases.
	// If quality improvements are needed we can add an option to the node later on.
	float Result;
	{
		// optimized, ddx/ddy only for every 2x2 block (bad for distant stuff)
		float Sample1 = dot(MaskConst, tex2D(Tex, UV));

		// compute the derivatives of the texture content
		float2 TexDD = float2(ddx(Sample1), ddy(Sample1));

		float TexDDLength = max(abs(TexDD.x), abs(TexDD.y)); 
		float Top = InvWidthConst * (Sample1 - ThresholdConst);
		Result = Top / TexDDLength + ThresholdConst;
	}

	Result = saturate(Result);	// no always needed (e.g. DX9 framebuffer blending)

	return Result;
#endif
}