/*=============================================================================
	Common.hlsl: Common shader code.
	Copyright © 2003-2005 Epic Games, Inc. All Rights Reserved.
=============================================================================*/

#include "Definitions.usf"

#if XBOX
// changing this value requires a recompile of shaders. See XeD3DRenderTarget.cpp
// SCENE_COLOR_BIAS_FACTOR = 2^SCENE_COLOR_BIAS_FACTOR_EXP
#define SCENE_COLOR_BIAS_FACTOR 8.0f 
float4 BiasColor( float4 Color )
{
	return float4( Color.rgb * SCENE_COLOR_BIAS_FACTOR, 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

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 );
}

float4 GammaCorrect( float4 Color )
{
	return float4( pow( Color.xyz, 2.2 ), Color.w );
}

half3 PointLightPhong(half3 DiffuseColor,half3 SpecularColor,half SpecularPower, half3 L, float3 E, half3 N, float3 R)
{
	half3	DiffuseLighting = saturate(dot(N,L)),
			SpecularLighting = pow(saturate(dot(R,L)),max(SpecularPower,0.0001));

	return DiffuseColor * DiffuseLighting + SpecularLighting * SpecularColor;
}

half3 HemisphereLightPhong(half3 DiffuseColor, half3 S, half3 N)
{
	return DiffuseColor * Square(0.5 + 0.5 * dot(N,S));
}

sampler	SceneColorTexture;
float4	ScreenPositionScaleBias;

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
		);
}

sampler	LightAttenuationTexture;

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

sampler SceneDepthTexture;
float4 MinZ_MaxZRatio;

#if SUPPORTS_DEPTH_TEXTURES
#if PS3
    #define texDepth2D texDepth2D
#else
    #define texDepth2D tex2D
#endif

half ConvertToClipSpaceW( half DeviceZ )
{
	//return MinZ_MaxZRatio[0] / (1.0 - (DeviceZ / MinZ_MaxZRatio[1]));
	return 1.f / (DeviceZ * MinZ_MaxZRatio[2] - MinZ_MaxZRatio[3]);	
}
/** 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 ConvertToClipSpaceW(DeviceZ);
}
#else
#define texDepth2D tex2D
half ConvertToClipSpaceW( half DeviceZ );
/** return the depth value stored in lighting target's alpha */
half CalcSceneDepth( float2 ScreenUV )
{
	// depth stored in alpha
	return tex2D(SceneColorTexture,ScreenUV).a;
}
#endif

/** return the scene lighting texture */
half3 CalcSceneColor(float2 ScreenUV)
{
	half3 Color = tex2D(SceneColorTexture,ScreenUV).rgb;
#ifndef XBOX
	Color.r = min( 4, Color.r );
	Color.g = min( 4, Color.g );
	Color.b = min( 4, Color.b );
#endif
	return Color;
}

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

/** sample from previous lighting texture using distortion UV offsets */
float4 CalcDistortionColor(float4 ScreenPosition, float2 DistortionUVOffsets)
{
	return PreviousLighting( ScreenPosition + float4(DistortionUVOffsets * float2(1,-1),0,0) );
}

/** handle BLEND_Modulated mode */
float4 ModulateBlendBase(float4 ScreenPosition,float3 Color,float2 Distortion)
{
	return float4(CalcDistortionColor(ScreenPosition,Distortion) * Color,ScreenPosition.w);
}
float4 ModulateBlendAdd(float4 ScreenPosition,float3 Color)
{
	return float4(PreviousLighting(ScreenPosition) * ( Color, 1 ) );
}

/** handle BLEND_Additive mode */
float4 AdditiveBlendBase(float4 ScreenPosition,float3 Color,float2 Distortion,float Opacity)
{
	return float4(CalcDistortionColor(ScreenPosition,Distortion).rgb + Color * Opacity,ScreenPosition.w);
}
float4 AdditiveBlendAdd(float4 ScreenPosition,float3 Color,float Opacity)
{
	return float4(PreviousLighting(ScreenPosition).rgb + Color * Opacity,ScreenPosition.w);
}

/** handle BLEND_Translucent mode */
float4 TranslucentBlendBase(float4 ScreenPosition,float3 Color,float2 Distortion,float Opacity)
{
	return float4(
		lerp(CalcDistortionColor(ScreenPosition,Distortion).rgb,Color,Opacity),
		ScreenPosition.w
		);
}
float4 TranslucentBlendAdd(float4 ScreenPosition,float3 Color,float Opacity)
{
	return float4(
			PreviousLighting(ScreenPosition).rgb + Color * Opacity,
			ScreenPosition.w
			);
}

float4 OpaqueBlendBase(float4 ScreenPosition,float3 Color)
{
	return float4(Color,ScreenPosition.w);
}

//nse vbunny--
#if SUPPORTS_FP_BLENDING
	float4 OpaqueBlendAdd(float4 ScreenPosition,float3 Color)
	{
		return float4(Color,0);
	}
#else
	float4 OpaqueBlendAdd(float4 ScreenPosition,float3 Color)
	{
		return float4(PreviousLighting(ScreenPosition) + Color,ScreenPosition.w);
		//return float4( ScreenPosition.xy / ScreenPosition.w * ScreenPositionScaleBias.xy + ScreenPositionScaleBias.wz, 0, 0 );
		
	}
#endif

#if SUPPORTS_FP_BLENDING
#else
	float4 OpaqueBlendModulate(float4 ScreenPosition,float4 Color)
	{
		return PreviousLighting(ScreenPosition) *  Color;
	}
#endif

//--nse vbunny

#define FLOAT16_EPSILON	4.8875809e-4f

float GetDepthCompareEpsilon(float Depth)
{
#if 1
	// This is an approximate, much faster way to calculate the epsilon.
	return Depth * FLOAT16_EPSILON * 2.0;
#else
	// This is the slower way to calculate the epsilon.
	return exp2(ceil(log2(Depth))) * FLOAT16_EPSILON;
#endif
}

half	TwoSidedSign;	// +1 if rendering the front side of a face, -1 if rendering the back side.

/** 
 * 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);
}

/** clips the fragment if it is closer than the previous depth */
void ClipIfNearer( float4 ScreenPosition )
{
// Only draw pixels which are farther than PreviousDepth.		
#if 0 //@todo xenon
	clip( ScreenPosition.z/ScreenPosition.w - tex2D(SceneDepthTexture,ScreenPosition.xy / ScreenPosition.w * ScreenPositionScaleBias.xy + ScreenPositionScaleBias.wz).r - GetDepthCompareEpsilon(ScreenPosition.z/ScreenPosition.w) );
#else
	clip(ScreenPosition.w - PreviousDepth(ScreenPosition) - GetDepthCompareEpsilon(ScreenPosition.w));
#endif
}

/** clips the fragment if it is farther away than the previous depth */
void ClipIfFarther( float4 ScreenPosition )
{
// Only draw pixels which are nearer than PreviousDepth.
#if 0 //@todo xenon
	clip( tex2D(SceneDepthTexture,ScreenPosition.xy / ScreenPosition.w * ScreenPositionScaleBias.xy + ScreenPositionScaleBias.wz).r - ScreenPosition.z/ScreenPosition.w - GetDepthCompareEpsilon(ScreenPosition.z/ScreenPosition.w) );
#else
	clip(PreviousDepth(ScreenPosition) - ScreenPosition.w - GetDepthCompareEpsilon(ScreenPosition.w));
#endif
}


// 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
#if PS3
#define MulMatrix(Mtx, Vect) mul(Vect, Mtx)
#define MulBone(Mtx, Vect)   mul(Mtx, Vect)
#else
#define MulMatrix(Mtx, Vect) mul(Mtx, Vect)
#define MulBone(Mtx, Vect)   mul(Vect, Mtx)
#endif


// PS3 needs to specify some constants in certain registers for proper compiling for some shaders
#if PS3
#define REGISTER(x) :c##x
#else
#define REGISTER(x)
#endif
