/**
 * MaterialTemplate.usf:
 * Copyright 1998-2007 Epic Games, Inc. All Rights Reserved.
 */


/*
	Defined by the C++ code:
		MATERIALBLENDING_SOLID
		MATERIALBLENDING_MASKED
		MATERIALBLENDING_TRANSLUCENT
		MATERIALBLENDING_ADDITIVE
		MATERIALBLENDING_MODULATE

		MATERIAL_TWOSIDED
		MATERIAL_LIGHTINGMODEL_PHONG
		MATERIAL_LIGHTINGMODEL_NONDIRECTIONAL
		MATERIAL_LIGHTINGMODEL_UNLIT
		MATERIAL_LIGHTINGMODEL_CUSTOM

		WORLD_COORDS
		MATERIAL_USE_GAMMA_CORRECTION
*/

#define NUM_MATERIAL_TEXCOORDS %u

#if WORLD_COORDS
float3x3 LocalToWorldMatrix;
float3x3 WorldToViewMatrix;
#endif

/* transform from post-projection to world space */
float4x4 WorldProjectionInvMatrix;

/* world-space camera position */
float3 CameraWorldPos;

struct FMaterialParameters
{
#if NUM_MATERIAL_TEXCOORDS
	float2 TexCoords[NUM_MATERIAL_TEXCOORDS];
#endif
	float4	VertexColor;
	float3	TangentNormal,
			TangentReflectionVector,
			TangentCameraVector;
	half3	TangentLightVector;
	float4	ScreenPosition;
#if WORLD_COORDS
	float3x3	TangentBasisInverse;
#endif
#if USE_LENSFLARE
	float LensFlareIntensity;
	float LensFlareOcclusion;
	float LensFlareRadialDistance;
	float LensFlareSourceDistance;
	float LensFlareRayDistance;
#endif	//#if USE_LENSFLARE
};

// Uniform material expressions.
%s

float DepthBiasedAlpha( FMaterialParameters Parameters, float InAlpha, float InBias, float InBiasScale )
{
	float Result;
	half SceneDepth = PreviousDepth(Parameters.ScreenPosition);
	float DepthBias = (1.0 - InBias) * InBiasScale;
	float BlendAmt = saturate((SceneDepth - Parameters.ScreenPosition.w) / max(DepthBias,0.001));
	Result = InAlpha * BlendAmt;
	return Result;
}

float3 DepthBiasedBlend( FMaterialParameters Parameters, float3 InColor, float InBias, float InBiasScale )
{
	float3 Result;	
	float3 SceneColor = PreviousLighting(Parameters.ScreenPosition).rgb;
	half SceneDepth = PreviousDepth(Parameters.ScreenPosition);
	float DepthBias = (1.0 - InBias) * InBiasScale;
	
	float BlendAmt = saturate((SceneDepth - Parameters.ScreenPosition.w) / max(DepthBias,0.001));
	Result = lerp( SceneColor, InColor * 1.000001, BlendAmt );
	
	return Result;
}

/** Get the lens flare intensity */
float GetLensFlareIntensity(FMaterialParameters Parameters)
{
#if USE_LENSFLARE
	return Parameters.LensFlareIntensity;
#else	//#if USE_LENSFLARE
	return 1.0f;
#endif	//#if USE_LENSFLARE
}

/** Get the lens flare occlusion */
float GetLensFlareOcclusion(FMaterialParameters Parameters)
{
#if USE_LENSFLARE
	return Parameters.LensFlareOcclusion;
#else	//#if USE_LENSFLARE
	return 1.0f;
#endif	//#if USE_LENSFLARE
}

/** Get the lens flare radial distance */
float GetLensFlareRadialDistance(FMaterialParameters Parameters)
{
#if USE_LENSFLARE
	return Parameters.LensFlareRadialDistance;
#else	//#if USE_LENSFLARE
	return 0.0f;
#endif	//#if USE_LENSFLARE
}

/** Get the lens flare ray distance */
float GetLensFlareRayDistance(FMaterialParameters Parameters)
{
#if USE_LENSFLARE
	return Parameters.LensFlareRayDistance;
#else	//#if USE_LENSFLARE
	return 0.0f;
#endif	//#if USE_LENSFLARE
}

/** Get the lens flare source distance */
float GetLensFlareSourceDistance(FMaterialParameters Parameters)
{
#if USE_LENSFLARE
	return Parameters.LensFlareSourceDistance;
#else	//#if USE_LENSFLARE
	return 0.0f;
#endif	//#if USE_LENSFLARE
}

half3 GetMaterialNormal(FMaterialParameters Parameters)
{
	return %s;
}

half3 GetMaterialEmissive(FMaterialParameters Parameters)
{
	return %s;
}

half3 GetMaterialDiffuseColor(FMaterialParameters Parameters)
{
	return %s;
}

half3 GetMaterialSpecularColor(FMaterialParameters Parameters)
{
	return %s;
}

half GetMaterialSpecularPower(FMaterialParameters Parameters)
{
	return %s;
}

half GetMaterialOpacity(FMaterialParameters Parameters)
{
	return %s;
}

#if MATERIALBLENDING_MASKED
float GetMaterialMask(FMaterialParameters Parameters)
{
	return %s - %s;
}
#endif

float2 GetMaterialDistortion(FMaterialParameters Parameters)
{
	return %s;
}

float3 GetMaterialTwoSidedLightingMask(FMaterialParameters Parameters)
{
	return %s;
}

#if MATERIAL_LIGHTINGMODEL_CUSTOM
float3 GetMaterialCustomLighting(FMaterialParameters Parameters)
{
	return %s;
}
#endif

float3 GetMaterialPointLightTransfer(FMaterialParameters Parameters,float3 WorldLightVector,half FalloffExponent)
{
	float3	TwoSidedLighting = 0;
	float3	TwoSidedLightingMask = 0;
	TwoSidedLightingMask = GetMaterialTwoSidedLightingMask(Parameters);
	TwoSidedLighting = TwoSidedLightingMask * GetMaterialDiffuseColor(Parameters) * RadialAttenuation(WorldLightVector,FalloffExponent);

	float3	Lighting = 0;
#if MATERIAL_LIGHTINGMODEL_NONDIRECTIONAL
	Lighting = GetMaterialDiffuseColor(Parameters) * RadialAttenuation(WorldLightVector,FalloffExponent);
#elif MATERIAL_LIGHTINGMODEL_PHONG
	Lighting = PointLightPhong(
		GetMaterialDiffuseColor(Parameters),
		GetMaterialSpecularColor(Parameters),
		GetMaterialSpecularPower(Parameters),
		Parameters.TangentLightVector,
		Parameters.TangentCameraVector,
		Parameters.TangentNormal,
		Parameters.TangentReflectionVector
		) *
		RadialAttenuation(WorldLightVector,FalloffExponent);
#elif MATERIAL_LIGHTINGMODEL_CUSTOM
	Lighting = GetMaterialCustomLighting(Parameters) * RadialAttenuation(WorldLightVector,FalloffExponent);
#endif

	return lerp(Lighting,TwoSidedLighting,TwoSidedLightingMask);
}

float3 GetMaterialHemisphereLightTransferFull(FMaterialParameters Parameters,float3 SkyVector, float3 UpperColor, float3 LowerColor)
{
	float3	TwoSidedLighting = 0;
	float3	TwoSidedLightingMask = 0;
	TwoSidedLightingMask = GetMaterialTwoSidedLightingMask(Parameters);
	TwoSidedLighting = TwoSidedLightingMask * GetMaterialDiffuseColor(Parameters);

	float3	UpperLighting = 0;
	float3  LowerLighting = 0;

#if MATERIAL_LIGHTINGMODEL_NONDIRECTIONAL
	UpperLighting = GetMaterialDiffuseColor(Parameters);
	LowerLighting = GetMaterialDiffuseColor(Parameters);	
#elif MATERIAL_LIGHTINGMODEL_PHONG
	//UpperLighting = HemisphereLightPhong(GetMaterialDiffuseColor(Parameters),SkyVector,Parameters.TangentNormal);
	//LowerLighting = HemisphereLightPhong(GetMaterialDiffuseColor(Parameters),-SkyVector,Parameters.TangentNormal);

	float  NormalContribution  = dot(SkyVector,Parameters.TangentNormal);
	float2 ContributionWeightsSqrt = float2(0.5, 0.5f) + float2(0.5f, -0.5f) * NormalContribution;
	float2 ContributionWeights = ContributionWeightsSqrt * ContributionWeightsSqrt;

	UpperLighting = GetMaterialDiffuseColor(Parameters) * ContributionWeights[0];
	LowerLighting = GetMaterialDiffuseColor(Parameters) * ContributionWeights[1];
#elif MATERIAL_LIGHTINGMODEL_CUSTOM
	UpperLighting = 0;//GetMaterialCustomLighting(Parameters);
	LowerLighting = 0;//GetMaterialCustomLighting(Parameters);
#endif

	return lerp(UpperLighting,TwoSidedLighting,TwoSidedLightingMask) * UpperColor +
	       lerp(LowerLighting,TwoSidedLighting,TwoSidedLightingMask) * LowerColor;
}

#if MATERIALBLENDING_TRANSLUCENT
	void GetMaterialClipping(FMaterialParameters Parameters) { clip(GetMaterialOpacity(Parameters) - 1.0 / 255.0); }
#elif MATERIALBLENDING_ADDITIVE
	void GetMaterialClipping(FMaterialParameters Parameters) { clip(GetMaterialOpacity(Parameters) - 1.0 / 255.0); }
#elif MATERIALBLENDING_MODULATE
	void GetMaterialClipping(FMaterialParameters Parameters) { clip(GetMaterialOpacity(Parameters) - 1.0 / 255.0); }
#else
	#if MATERIALBLENDING_MASKED
		void GetMaterialClipping(FMaterialParameters Parameters) { clip(GetMaterialMask(Parameters)); }
	#else
		void GetMaterialClipping(FMaterialParameters Parameters) { return; }
	#endif
#endif

#if SM2_PROFILE
	#define OPTIONAL_FacingSign
	static const float FacingSign = 1.0f;
#else
	#define OPTIONAL_FacingSign in float FacingSign : VFACE,
#endif

/** vectors needed for user material shaders */
void CalcMaterialParameters(
	in out FMaterialParameters Parameters,
	float FacingSign,
	float3 CameraVector,
	float4 ScreenPosition,
	half3 LightVector = half3(0,0,1),
	uniform bool bAllowTwoSidedFlip = true)
{
	Parameters.ScreenPosition = ScreenPosition;
	Parameters.TangentCameraVector = normalize(CameraVector);
	Parameters.TangentLightVector = normalize((half3)LightVector);
	Parameters.TangentNormal = normalize(GetMaterialNormal(Parameters));

#if MATERIAL_TWOSIDED
	// allow individual shaders to override the flip
	if (bAllowTwoSidedFlip)
	{
		// flip the normal for backfaces being rendered with a two-sided material
		Parameters.TangentNormal *= TwoSidedSign;
		// D3D requires that VFACE be used by a conditional instruction rather than used as a signed float directly.
		float FacingSignFloat = FacingSign >= 0 ? +1 : -1;
		#if PS3 || XBOX || SM4_PROFILE || SM2_PROFILE
			Parameters.TangentNormal *= FacingSignFloat;
		#else
			// in SM3 VFACE is actually negative for frontfaces since frontfacing polys in UE3 are CM_CW instead of CM_CCW
			Parameters.TangentNormal *= -FacingSignFloat;
		#endif
	}
#endif

	Parameters.TangentReflectionVector = -Parameters.TangentCameraVector + Parameters.TangentNormal * dot(Parameters.TangentNormal,Parameters.TangentCameraVector) * 2.0;
}

/** Create the inverse of the tangent basis */
float3x3 CalcInvTangentBasis( float4 Normal, float3 Tangent )
{
	// determinant of original tangent basis passed down in w component
	float Det = (Normal.w - 0.5) * 2;
	// scale bias from [0,1] to [-1,1] and normalize normal vector
	float3 NormalV = normalize((float3(Normal.xyz) - 0.5) * 2);
	// scale bias from [0,1] to [-1,1] and normalize tangent vector
	float3 TangentV = normalize((Tangent - 0.5) * 2);
	// flip binormal based on determinant of tangent basis
	float3 BinormalV = normalize(cross(NormalV,TangentV) * Det);
	// 3x3 tangent basis matrix	
	return transpose(float3x3(TangentV, BinormalV, NormalV));
}

#if MATERIAL_USE_GAMMA_CORRECTION
/** optional gamma correction to be applied to materials. Inverse gamma */
half		MatInverseGamma;
/** 
* Apply linear to gamma correction. This is needed when rendering materials to a render target with gamma != 1.0
* @param Color - color in linear space
* @return color in gamma space
*/
half4 MaterialGammaCorrect( half4 Color )
{
	return float4( pow( Color.xyz, MatInverseGamma ), Color.w );
}
#else
/** stub when not compiling with gamma correction */
#define MaterialGammaCorrect( Color ) ( Color )
#endif