////////////////////////////////////////////////////////////////////////////////////////////////////
// Structures
////////////////////////////////////////////////////////////////////////////////////////////////////

// Input : ColorModel
struct PS_COLOR_INPUT
{
#if MIX_SM_HIGH
	float4 pos : SV_POSITION;
#endif //MIX_SM_HIGH

	float4 color : COLOR0;

	float4 worldPos : TEXCOORD0;
	float4 projPos  : TEXCOORD1;

#if ENABLE_LIGHTING

	#if ENABLE_BUMP
		float3 tangent     : TEXCOORD2;
		float3 binormal    : TEXCOORD3;
	#endif //ENABLE_BUMP

	float3 normal   : TEXCOORD4;

	#if ENABLE_SSAO
		float3 viewNormal : TEXCOORD5;
	#endif //ENABLE_SSAO

#endif //ENABLE_LIGHTING

#if ENABLE_TEXTURE
	float2 tex         : TEXCOORD6;
#endif //ENABLE_TEXTURE

#if ENABLE_SHADOW_RECEIVE
	float4 shadowDepth : TEXCOORD7;
	float4 shadowTex   : TEXCOORD8;
#endif //ENABLE_SHADOW_RECEIVE
};

// Output : ColorModel
struct PS_COLOR_OUTPUT
{
	float4 color  : MSV_TARGET0;
	float4 depth  : MSV_TARGET1;
	float4 data   : MSV_TARGET2;
#if ENABLE_SHADOW
	float4 shadow : MSV_TARGET3;
#endif //ENABLE_SHADOW
};

// Input : MaskModel
struct PS_MASK_MODEL_INPUT
{
#if MIX_SM_HIGH
	float4 pos     : SV_POSITION;
#endif //MIX_SM_HIGH
	float4 projPos : TEXCOORD0;
};

// Input : ShadowModel
struct PS_SHADOW_MODEL_INPUT
{
#if MIX_SM_HIGH
	float4 pos : SV_POSITION;
#endif //MIX_SM_HIGH

	float4 color : COLOR0;

#if ENABLE_TEXTURE
	float2 tex : TEXCOORD0;
#endif //ENABLE_TEXTURE

	float4 depth : TEXCOORD1;
};

// Misc : Light Info
#if ENABLE_LIGHTING

	struct LIGHT_INFO
	{
		float3 diffuseColor;

	#if ENABLE_SPECULAR
		float specularFactor;
	#endif //ENABLE_SPECULAR
	};

#endif //ENABLE_LIGHTING

////////////////////////////////////////////////////////////////////////////////////////////////////
// Global values
////////////////////////////////////////////////////////////////////////////////////////////////////

#if MIX_SM_HIGH

	/****************************************
		Reserved : b0
	****************************************/
	
	//

	/****************************************
		General : b1
	****************************************/

	//

	/****************************************
		LocalLight : b2
	****************************************/

	//

	/****************************************
		Material : b3
	****************************************/

	cbuffer cbMaterial : register( b3 )
	{
		float3 g_AmbientColor;
		float4 g_DiffuseColor;
		float4 g_DiffuseParam;
		float4 g_SpecularColor;
		float4 g_SpecularParam0;
		float4 g_SpecularParam1;
		float4 g_ReflectParam;
		float4 g_EmissiveColor;
		float4 g_EmissiveParam;
		float4 g_BumpParam0;
		float4 g_BumpParam1;
		float4 g_SoftParticleParam;
	};
	
#else //MIX_SM_HIGH

	/****************************************
		Reserved : c0 - c15
	****************************************/
	
	//

	/****************************************
		General : c16 - c47
	****************************************/
	
	//

	/****************************************
		LocalLight : c48 - c67
	****************************************/

	//

	/****************************************
		Material : c68 - c80
	****************************************/

	float3 g_AmbientColor      : register( c68 );
	float4 g_DiffuseColor      : register( c69 );
	float4 g_DiffuseParam      : register( c70 );
	float4 g_SpecularColor     : register( c71 );
	float4 g_SpecularParam0    : register( c72 );
	float4 g_SpecularParam1    : register( c73 );
	float4 g_ReflectParam      : register( c74 );
	float4 g_EmissiveColor     : register( c75 );
	float4 g_EmissiveParam     : register( c76 );
	float4 g_BumpParam0        : register( c77 );
	float4 g_BumpParam1        : register( c78 );
	float4 g_SoftParticleParam : register( c79 );

#endif //MIX_SM_HIGH

////////////////////////////////////////////////////////////////////////////////////////////////////
// Global textures and sampers
////////////////////////////////////////////////////////////////////////////////////////////////////

#if MIX_SM_HIGH

	// Textures ( t0 - t7 )
	Texture2D    g_DiffuseTexture  : register( t0 );
	Texture2D    g_SpecularTexture : register( t1 );
	Texture2D    g_EmissiveTexture : register( t2 );
	Texture2D    g_BumpTexture     : register( t3 );
	Texture2D    g_DepthTexture    : register( t4 );
	Texture2D    g_ShadowTexture   : register( t5 );
	Texture2D    g_RefractTexture  : register( t6 );
	TextureCube  g_ReflectTexture  : register( t7 );

	// Samplers ( s0 - s7 )
	SamplerState g_DiffuseSampler  : register( s0 );
	SamplerState g_SpecularSampler : register( s1 );
	SamplerState g_EmissiveSampler : register( s2 );
	SamplerState g_BumpSampler     : register( s3 );
	SamplerState g_DepthSampler    : register( s4 );
	SamplerState g_ShadowSampler   : register( s5 );
	SamplerState g_RefractSampler  : register( s6 );
	SamplerState g_ReflectSampler  : register( s7 );

#else //MIX_SM_HIGH

	// Samplers ( s0 - s7 )
	sampler g_DiffuseSampler  : register( s0 );
	sampler g_SpecularSampler : register( s1 );
	sampler g_EmissiveSampler : register( s2 );
	sampler g_BumpSampler     : register( s3 );
	sampler g_DepthSampler    : register( s4 );
	sampler g_ShadowSampler   : register( s5 );
	sampler g_RefractSampler  : register( s6 );
	sampler g_ReflectSampler  : register( s7 );

#endif //MIX_SM_HIGH

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mascros : Texture
////////////////////////////////////////////////////////////////////////////////////////////////////

#if MIX_SM_HIGH

	#define TEX_DIFFUSE_2D( uv )                g_DiffuseTexture.Sample( g_DiffuseSampler, uv )
	#define TEX_DIFFUSE_2D_GRAD( loc, dx, dy )  g_DiffuseTexture.SampleGrad( g_DiffuseSampler, loc, dx, dy )

	#define TEX_SPECULAR_2D( uv )               g_SpecularTexture.Sample( g_SpecularSampler, uv )
	#define TEX_SPECULAR_2D_GRAD( loc, dx, dy ) g_SpecularTexture.SampleGrad( g_SpecularSampler, loc, dx, dy )

	#define TEX_EMISSIVE_2D( uv )               g_EmissiveTexture.Sample( g_EmissiveSampler, uv )
	#define TEX_EMISSIVE_2D_GRAD( loc, dx, dy ) g_EmissiveTexture.SampleGrad( g_EmissiveSampler, loc, dx, dy )

	#define TEX_BUMP_2D( uv )                   g_BumpTexture.Sample( g_BumpSampler, uv )
	#define TEX_BUMP_2D_GRAD( loc, dx, dy )     g_BumpTexture.SampleGrad( g_BumpSampler, loc, dx, dy )

	#define TEX_DEPTH_2D( uv )                  g_DepthTexture.Sample( g_DepthSampler, uv )

	#define TEX_SHADOW_2D( uv )                 g_ShadowTexture.Sample( g_ShadowSampler, uv )

	#define TEX_REFRACT_2D( uv )                g_RefractTexture.Sample( g_RefractSampler, uv )

	#define TEX_REFLECT_CUBE( uv )              g_ReflectTexture.Sample( g_ReflectSampler, uv )

#else //MIX_SM_HIGH

	#define TEX_DIFFUSE_2D( uv )                tex2D( g_DiffuseSampler, uv )
	#define TEX_DIFFUSE_2D_GRAD( loc, dx, dy )  tex2Dgrad( g_DiffuseSampler, loc, dx, dy )

	#define TEX_SPECULAR_2D( uv )               tex2D( g_SpecularSampler, uv )
	#define TEX_SPECULAR_2D_GRAD( loc, dx, dy ) tex2Dgrad( g_SpecularSampler, loc, dx, dy )

	#define TEX_EMISSIVE_2D( uv )               tex2D( g_EmissiveSampler, uv )
	#define TEX_EMISSIVE_2D_GRAD( loc, dx, dy ) tex2Dgrad( g_EmissiveSampler, loc, dx, dy )

	#define TEX_BUMP_2D( uv )                   tex2D( g_BumpSampler, uv )
	#define TEX_BUMP_2D_GRAD( loc, dx, dy )     tex2Dgrad( g_BumpSampler, loc, dx, dy )

	#define TEX_DEPTH_2D( uv )                  tex2D( g_DepthSampler, uv )

	#define TEX_SHADOW_2D( uv )                 tex2D( g_ShadowSampler, uv )
	#define TEX_SHADOW_2D_PROJ( uv )            tex2Dproj( g_ShadowSampler, uv )

	#define TEX_REFRACT_2D( uv )                tex2D( g_RefractSampler, uv )
	#define TEX_REFRACT_2D_PROJ( uv )           tex2Dproj( g_RefractSampler, uv )

	#define TEX_REFLECT_CUBE( uv )              texCUBE( g_ReflectSampler, uv )

#endif //MIX_SM_HIGH

////////////////////////////////////////////////////////////////////////////////////////////////////
// Macros : Global values
////////////////////////////////////////////////////////////////////////////////////////////////////

// Material //

#define M_AMBIENT_COLOR             g_AmbientColor

#define M_DIFFUSE_COLOR             g_DiffuseColor
#define M_DIFFUSE_FRESNEL_RATIO     g_DiffuseParam.x
#define M_DIFFUSE_RIM_HARDNESS      g_DiffuseParam.y
#define M_DIFFUSE_RIM_SCALE         g_DiffuseParam.z

#define M_SPECULAR_COLOR            g_SpecularColor
#define M_SPECULAR_HARDNESS         g_SpecularParam0.x
#define M_SPECULAR_SCALE            g_SpecularParam0.y
#define M_SPECULAR_CT_FRESNEL       g_SpecularParam1.x
#define M_SPECULAR_CT_ROUGHNESS_INV g_SpecularParam1.y
#define M_SPECULAR_CT_ROUGHNESS2    g_SpecularParam1.z

#define M_REFLECT_SCALE             g_ReflectParam.x
#define M_REFLECT_INTENSITY         g_ReflectParam.y
#define M_REFLECT_BIAS              g_ReflectParam.z
#define M_REFLECT_EXPROSURE         g_ReflectParam.w

#define M_EMISSIVE_COLOR            g_EmissiveColor
#define M_EMISSIVE_SCALE            g_EmissiveParam.x

#define M_BUMP_HEIGHT_SCALE         g_BumpParam0.x
#define M_BUMP_HEIGHT_SAMPLE        g_BumpParam0.y
#define M_BUMP_REFLECT_RATIO        g_BumpParam0.z
#define M_BUMP_WAVE_OFFSET          g_BumpParam0.w
#define M_BUMP_WAVE_UV              g_BumpParam1.xy
#define M_BUMP_WAVE_AVEL            g_BumpParam1.z
#define M_BUMP_REFRACT_RATIO        g_BumpParam1.w

#define M_SOFTPARTICLE_COIFFE       g_SoftParticleParam.x

////////////////////////////////////////////////////////////////////////////////////////////////////
// Function : General
////////////////////////////////////////////////////////////////////////////////////////////////////

#if ENABLE_SOFTPARTICLE

	float ComputeSoftEdge( float depth, float2 tex )
	{
		float ret;

		ret = max( 0.0, TEX_DEPTH_2D( tex ).r - depth );
		ret *= rcp( M_SOFTPARTICLE_COIFFE * INV_FAR_Z );

		return clamp( ret, 0.0, 1.0 );
	}

#endif //ENABLE_SOFTPARTICLE

////////////////////////////////////////////////////////////////////////////////////////////////////
// Function : Bump
////////////////////////////////////////////////////////////////////////////////////////////////////

#if ( ENABLE_LIGHTING && ENABLE_BUMP )

	#if ENABLE_WATER
		float2 ComputePairBumpTex( float2 tex )
		{
			float2 waveAmount = M_BUMP_WAVE_UV * M_BUMP_WAVE_AVEL;
			float2 output = tex + float2( M_BUMP_WAVE_OFFSET, M_BUMP_WAVE_OFFSET );

		#if ENABLE_WAVE_INVERT
			output -= waveAmount;
		#else //ENABLE_WAVE_INVERT
			output += waveAmount;
		#endif //ENABLE_WAVE_INVERT

//			output = tex + M_BUMP_WAVE_UV * M_BUMP_WAVE_AVEL + float2( M_BUMP_WAVE_OFFSET, M_BUMP_WAVE_OFFSET );

			return output;
		}
	#endif //ENABLE_WATER

	#if ENABLE_PARALLAX_MAPPING

		// Parallax :  offset
		float2 ComputeParallaxOffset( float3 tsEyeVec )
		{
			float len = length( tsEyeVec.xy ) / ( length( tsEyeVec.z ) + 0.5 );
		    float2 dir = normalize( tsEyeVec.xy );

		    return dir * M_BUMP_HEIGHT_SCALE * min( len, 20.0 );
		}

		// Parallax :  tex cooord
		float2 ComputeParallaxTex( float3 eyeDir, float2 texUV, float3 normal, float2 parallaxOffsetTS, float2 dx, float2 dy )
		{
			float2 texSample;

			// Find min of change in u and v across quad: compute du and dv magnitude across quad
			float2 texCoords = ( dx * dx ) + ( dy * dy );

			// Standard mipmapping uses max here
			float minTexCoordDelta = max( texCoords.x, texCoords.y );

			// Compute the current mip level  (* 0.5 is effectively computing a square root before )
			float mipLevel = max( 0.5 * log2( minTexCoordDelta ), 0.0 );

			// Multiplier for visualizing the level of detail (see notes for 'nLODThreshold' variable
			// for how that is done visually)

			if( mipLevel <= 0.0 )
			{
				float stepCount = lerp( M_BUMP_HEIGHT_SAMPLE * 2.0, M_BUMP_HEIGHT_SAMPLE * 0.5, dot( eyeDir, normal ) );
				float stepIndex = 0.0;
				float stepSize = rcp( ( float )stepCount );//1.0 / ( float )stepCount;

				float curHeight = 0.0;
				float preHeight = 1.0;

				float2 texOffsetPerStep = stepSize * parallaxOffsetTS;
				float2 texCurOffset = texUV.xy;

				float  curBound = 1.0;

				float2 p0 = 0.0;
				float2 p1 = 0.0;

				while( stepIndex < stepCount ) 
				{
					curBound -= stepSize;
					texCurOffset -= texOffsetPerStep;

					curHeight = TEX_BUMP_2D_GRAD( texCurOffset, dx, dy ).a;

					if( curHeight > curBound ) 
					{
						p0 = float2( curBound, curHeight );
						p1 = float2( curBound + stepSize, preHeight );

						stepIndex = stepCount + 1;
					}
					else
					{
						preHeight = curHeight;
						stepIndex += 1.0;
					}
				}

				float d0 = p0.x - p0.y;
				float d1 = p1.x - p1.y;
				float denom = d1 - d0;

				if( denom > 0.0 )
				{
					float parallaxAmount = ( ( p0.x * d1 ) - ( p1.x * d0 ) ) / denom;
					texSample = parallaxOffsetTS * ( 1.0 - parallaxAmount );
				}
				else
				{
					// The computed texture offset for the displaced point on the pseudo-extruded surface:
					texSample = parallaxOffsetTS;
				}
			}

			return texSample;
		}

		float3 ComputeLocalBumpNormal( float2 tex, float3 tangent, float3 binormal, float3 normal, float3 eyeVec, float3 eyeDir )
		{
			float2 parallax = ComputeParallaxOffset( mul( float3x3( tangent, binormal, normal ), eyeVec ) );

			float2 dx;
			float2 dy;
			float2 parallaxOffset;
			float2 gradTex;

			float3 output;
	
		#if ENABLE_WATER

			gradTex = tex + M_BUMP_WAVE_UV;
			dx = ddx( gradTex );
			dy = ddy( gradTex );
			parallaxOffset = ComputeParallaxTex( eyeDir, gradTex, normal, parallax, dx, dy );
			gradTex -= parallaxOffset;
			output = TEX_BUMP_2D_GRAD( gradTex, dx, dy ).xyz * 2.0 - 1.0;

//			gradTex = tex + M_BUMP_WAVE_UV * M_BUMP_WAVE_AVEL + float2( M_BUMP_WAVE_OFFSET, M_BUMP_WAVE_OFFSET );
			gradTex = ComputePairBumpTex( tex );
			dx = ddx( gradTex );
			dy = ddy( gradTex );
			parallaxOffset = ComputeParallaxTex( eyeDir, gradTex, normal, parallax, dx, dy );
			gradTex -= parallaxOffset;
			output += TEX_BUMP_2D_GRAD( gradTex, dx, dy ).xyz * 2.0 - 1.0;

			output = normalize( output * 0.5 );

		#else //ENABLE_WATER

			dx = ddx( tex );
			dy = ddy( tex );
			parallaxOffset = ComputeParallaxTex( eyeDir, tex, normal, parallax, dx, dy );
			gradTex = tex - parallaxOffset;

			output = TEX_BUMP_2D_GRAD( gradTex, dx, dy ).xyz * 2.0 - 1.0;

		#endif //ENABLE_WATER

			return output;
		}

	#else //ENABLE_PARALLAX_MAPPING

		// Normal : Extract
		float3 ComputeLocalBumpNormal( float2 tex )
		{
			float3 output;

			#if ENABLE_WATER

				float2 modTex;

				modTex = tex + M_BUMP_WAVE_UV;
				output = TEX_BUMP_2D( modTex ).xyz * 2.0 - 1.0;

//				modTex = tex + M_BUMP_WAVE_UV * M_BUMP_WAVE_AVEL + float2( M_BUMP_WAVE_OFFSET, M_BUMP_WAVE_OFFSET );
				modTex = ComputePairBumpTex( tex );
				
				output += TEX_BUMP_2D( modTex ).xyz * 2.0 - 1.0;

				output = normalize( output * 0.5 );

			#else //ENABLE_WATER

				output = TEX_BUMP_2D( tex ).xyz * 2.0 - 1.0;

			#endif //ENABLE_WATER

			return output;
		}

	#endif //ENABLE_PARALLAX_MAPPING

	// Normal : World transform
	float3 TransformBumpNormal( float3 localNormal, float3 tangent, float3 binormal, float3 normal )
	{
		return normalize( mul( localNormal, float3x3( normalize( tangent ), normalize( binormal ), normalize( normal ) ) ) );
	}

#endif //( ENABLE_LIGHTING && ENABLE_BUMP )

////////////////////////////////////////////////////////////////////////////////////////////////////
// Function : Fresnel(Diffuse)
////////////////////////////////////////////////////////////////////////////////////////////////////

#if ( ENABLE_LIGHTING && ENABLE_DIFFUSE_FRESNEL )

	float4 ComputeDiffuseFresnel( float4 srcColor, float facing )
	{
		float4 dstColor = float4( srcColor.rgb * srcColor.a, 1.0 );
		float ratio = facing * M_DIFFUSE_FRESNEL_RATIO;

		return lerp( srcColor, dstColor, ratio );
	}

#endif //( ENABLE_LIGHTING && ENABLE_DIFFUSE_FRESNEL )

////////////////////////////////////////////////////////////////////////////////////////////////////
// Function : Diffuse
////////////////////////////////////////////////////////////////////////////////////////////////////

#if ENABLE_LIGHTING

	// Lighting
#if ENABLE_RIM_LIGHTING
	float ComputeDiffuseLight( float NdotL, float NdotE, float3 lightVec, float3 eyeVec )
	{
#else //ENABLE_RIM_LIGHTING
	float ComputeDiffuseLight( float NdotL, float3 lightVec, float3 eyeVec )
	{
#endif //ENABLE_RIM_LIGHTING
		float output = 0.0;

		#if ENABLE_BOTH_LIGHTING
			output = abs( NdotL );
		#else //ENABLE_BOTH_LIGHTING
			output = max( 0.0, NdotL );
		#endif //ENABLE_BOTH_LIGHTING

		#if ENABLE_RIM_LIGHTING
			float rim = abs( 1.0 - NdotE ) * max( dot( -lightVec, eyeVec ), 0.0 );
			output += pow( rim, M_DIFFUSE_RIM_HARDNESS ) * M_DIFFUSE_RIM_SCALE;
		#endif //ENABLE_RIM_LIGHTING

		#if ENABLE_HALF_LAMBERT
			output = output * 0.5 + 0.5;
			output = output * output;
		#endif //ENABLE_HALF_LAMBERT

		output = lerp( 1.0, output, GLOBAL_SHADE_FACTOR );

		return output;
	}

#endif //ENABLE_LIGHTING

////////////////////////////////////////////////////////////////////////////////////////////////////
// Function : Specular
////////////////////////////////////////////////////////////////////////////////////////////////////

#if ENABLE_SPECULAR

	#if ENABLE_COOK_TORRANCE

		// Coock torrance : Fresnel
		float ComputeFresnelCT( float EdotH )
		{
			return pow( abs( 1.0 + EdotH ), M_SPECULAR_CT_FRESNEL );
		}

		// Coock torrance : Distribution - Beckmann
		float ComputeDistributionCT( float NdotH )
		{
			// Gaus
//			return exp( -1.0 * pow( abs( tan( acos( NdotH ) ) * M_SPECULAR_CT_ROUGHNESS_INV ), 2.0 ) );
			
			// Beckmann
			float a = exp( -1.0 * pow( tan( acos( NdotH ) ) * M_SPECULAR_CT_ROUGHNESS_INV, 2.0 ) );
			float b = 4.0 * M_SPECULAR_CT_ROUGHNESS2 * pow( NdotH, 4.0 );

			return a / b;
		}

		// Coock torrance : Geometric
		float ComputeGeometricCT( float NdotH, float NdotE, float EdotH, float NdotL )
		{
			float p0 = 2.0 * NdotH;
			float p1 = rcp( EdotH );

			float a = ( p0 * NdotE ) * p1;
			float b = ( p0 * NdotL ) * p1;

			return min( 1.0, min( a, b ) );
		}

	#endif //ENABLE_COOK_TORRANCE

	// Lighting
#if ENABLE_COOK_TORRANCE
	float ComputeSpecularLight( float NdotL, float NdotE, float3 lightDir, float3 eyeDir, float3 normal )
	{
#else //ENABLE_COOK_TORRANCE
	float ComputeSpecularLight( float NdotL, float3 lightDir, float3 eyeDir, float3 normal )
	{
#endif //ENABLE_COOK_TORRANCE
		float intensity = 0.0;

		#if ( ENABLE_BLINN_PHONG || ENABLE_COOK_TORRANCE )
			float3 halfVec = normalize( lightDir + eyeDir );
			float NdotH = dot( normal, halfVec );
		#endif //( ENABLE_BLINN_PHONG || ENABLE_COOK_TORRANCE )

		#if ENABLE_PHONG

			float3 reflectVec = reflect( eyeDir, normal );
			float LdotR = dot( -lightDir, reflectVec );

			#if ENABLE_BOTH_LIGHTING
				intensity = abs( LdotR );
			#else //ENABLE_BOTH_LIGHTING
				intensity = max( 0.0, LdotR );
			#endif //ENABLE_BOTH_LIGHTING

		#elif ENABLE_BLINN_PHONG

			#if ENABLE_BOTH_LIGHTING
				intensity = abs( NdotH );
			#else //ENABLE_BOTH_LIGHTING
				intensity = max( 0.0, NdotH );
			#endif //ENABLE_BOTH_LIGHTING

		#elif ENABLE_COOK_TORRANCE

			float EdotH = dot( eyeDir, halfVec );

			float D = ComputeDistributionCT( NdotH );
			float F = ComputeFresnelCT( NdotE );
			float G = ComputeGeometricCT( NdotH, NdotE, EdotH, NdotL );

			intensity = ( D * F * G ) / NdotE;

			#if ENABLE_BOTH_LIGHTING
				intensity = abs( intensity );
			#else //ENABLE_BOTH_LIGHTING
				intensity = max( 0.0, intensity );
			#endif //ENABLE_BOTH_LIGHTING

		#endif //ENABLE_COOK_TORRANCE

		return pow( intensity, M_SPECULAR_HARDNESS );
	}

#endif //ENABLE_SPECULAR

////////////////////////////////////////////////////////////////////////////////////////////////////
// Function  : Hemisphere lighting
////////////////////////////////////////////////////////////////////////////////////////////////////

#if ENABLE_LIGHTING

	LIGHT_INFO ComputeHemisphereLight( LIGHT_INFO input, float3 normal )
	{
		LIGHT_INFO output = input;

		if( HEMISPHERE_ENABLED )
		{
			float amount = ( dot( HEMISPHERE_AXIS, normal.xyz ) + 1.0 ) * 0.5;

			// Diffuse Color
			output.diffuseColor += lerp( HEMISPHERE_GROUND_COLOR * HEMISPHERE_GROUND_SCALE, HEMISPHERE_SKY_COLOR * HEMISPHERE_SKY_SCALE, amount );
		}

		return output;
	}

#endif //ENABLE_LIGHTING

////////////////////////////////////////////////////////////////////////////////////////////////////
// Function : Directional lighting
////////////////////////////////////////////////////////////////////////////////////////////////////

#if ENABLE_LIGHTING

#if ENABLE_N_DOT_E
	LIGHT_INFO ComputeDirectionalLight( LIGHT_INFO input, float NdotL, float NdotE, float3 eyeVec, float3 normal )
	{
#else //ENABLE_N_DOT_E
	LIGHT_INFO ComputeDirectionalLight( LIGHT_INFO input, float NdotL, float3 eyeVec, float3 normal )
	{
#endif //ENABLE_N_DOT_E
		LIGHT_INFO output = input;

		if( SUN_ENABLED )
		{
			// Diffuse Color
		#if ENABLE_RIM_LIGHTING
			output.diffuseColor += SUN_COLOR * ComputeDiffuseLight( NdotL, NdotE, -SUN_DIR, eyeVec ) * SUN_SCALE;
		#else //ENABLE_RIM_LIGHTING
			output.diffuseColor += SUN_COLOR * ComputeDiffuseLight( NdotL, -SUN_DIR, eyeVec ) * SUN_SCALE;
		#endif //ENABLE_RIM_LIGHTING

			// Specular Factor
		#if ENABLE_SPECULAR
			#if ENABLE_COOK_TORRANCE
				output.specularFactor += ComputeSpecularLight( NdotL, NdotE, -SUN_DIR, eyeVec, normal ) * SUN_SCALE;
			#else //ENABLE_COOK_TORRANCE
				output.specularFactor += ComputeSpecularLight( NdotL, -SUN_DIR, eyeVec, normal ) * SUN_SCALE;
			#endif //ENABLE_COOK_TORRANCE
		#endif //ENABLE_SPECULAR
		}

		return output;
	}

#endif //ENABLE_LIGHTING

////////////////////////////////////////////////////////////////////////////////////////////////////
// Function : Local lighting
////////////////////////////////////////////////////////////////////////////////////////////////////

#if ENABLE_LIGHTING

#if ENABLE_N_DOT_E
	LIGHT_INFO ComputeLocalLight( LIGHT_INFO input, float NdotE, float3 eyeVec, float3 worldPos, float3 worldNormal )
	{
#else //ENABLE_N_DOT_E
	LIGHT_INFO ComputeLocalLight( LIGHT_INFO input, float3 eyeVec, float3 worldPos, float3 worldNormal )
	{
#endif //ENABLE_N_DOT_E
		LIGHT_INFO output = input;

		[unroll(MAX_ACTIVE_LIGHT)]
		for( int i = 0; ( i < MAX_ACTIVE_LIGHT ) && ( LO_EXIST( i ) ); i++ )
		{
			float3 vec = ( LO_POS( i ) - worldPos.xyz );
			float dist = length( vec );
			float range = LO_RANGE( i );

			if( range > dist )
			{
				float3 dir = normalize( vec );
				float4 color = LO_COLOR( i );
				float3 baseColor = color.rgb * color.a;
				float3 atten = LO_ATTEN( i );
				float NdotL = dot( worldNormal, dir );
				float ratio = 0.0;

				// Falloff
				if( LO_SPOT( i ) )
				{
					// Spot
					float cosAlpha = dot( -LO_DIR( i ), dir );
					float innerCos = LO_INNER_COS( i );
					float outerCos = LO_OUTER_COS( i );

					if( cosAlpha > innerCos )
					{
						ratio = lerp( 1.0f, 0.0f, dist / range );
					}
					else if( cosAlpha > outerCos )
					{
						ratio = pow( abs( ( cosAlpha - outerCos ) / ( innerCos - outerCos ) ), LO_EXP( i ) ) * lerp( 1.0f, 0.0f, dist / range );
					}
				}
				else
				{
					// Point
					ratio = clamp( ( range - dist ) * LO_DIST_DIFF( i ), 0.0, 1.0 );
				}
				
				// Ratio - Apply Intensity
				ratio *= color.a;

				// Ratio - Apply Atten
				ratio *= rcp( atten.x + ( atten.y * dist ) + ( atten.z * dist * dist ) );

				// Diffuse Color
			#if ENABLE_RIM_LIGHTING
				output.diffuseColor += color.rgb * ComputeDiffuseLight( NdotL, NdotE, dir, eyeVec ) * ratio;
			#else //ENABLE_RIM_LIGHTING
				output.diffuseColor += color.rgb * ComputeDiffuseLight( NdotL, dir, eyeVec ) * ratio;
			#endif //ENABLE_RIM_LIGHTING

				// Specular Factor
			#if ENABLE_SPECULAR
				#if ENABLE_COOK_TORRANCE
					output.specularFactor += ComputeSpecularLight( NdotL, NdotE, dir, eyeVec, worldNormal ) * ratio;
				#else //ENABLE_COOK_TORRANCE
					output.specularFactor += ComputeSpecularLight( NdotL, dir, eyeVec, worldNormal ) * ratio;
				#endif //ENABLE_COOK_TORRANCE
			#endif //ENABLE_SPECULAR
			}
		}

		return output;
	}

#endif //ENABLE_LIGHTING

////////////////////////////////////////////////////////////////////////////////////////////////////
// Function : Reflection
////////////////////////////////////////////////////////////////////////////////////////////////////

#if ENABLE_REFLECT_MAPPING

	#if ENABLE_REFLECT_FRESNEL

		// Ratio(
		float ComputeReflectRatio( float gloss, float facing )
		{
			return M_REFLECT_INTENSITY * gloss * ( M_REFLECT_BIAS + ( 1.0 - M_REFLECT_BIAS ) * pow( facing, M_REFLECT_EXPROSURE ) );
		}

	#else //ENABLE_REFLECT_FRESNEL
	
		float ComputeReflectRatio( float gloss )
		{
			return M_REFLECT_INTENSITY * gloss;
		}

	#endif //ENABLE_REFLECT_FRESNEL

	float4 ComputeReflect( float4 srcColor, float3 eyeVec, float3 normal0, float3 normal1, float ratio )
	{
	#if ENABLE_BUMP
		float3 tex = reflect( -eyeVec, lerp( normal0, normal1, M_BUMP_REFLECT_RATIO ) );
	#else //ENABLE_BUMP
		float3 tex = reflect( -eyeVec, normal1 );
	#endif //ENABLE_BUMP

		float4 dstColor = TEX_REFLECT_CUBE( tex );

		dstColor.rgb *= ( 1.0 + dstColor.a ) * M_REFLECT_SCALE;
		dstColor.a = 1.0;

		return lerp( srcColor, dstColor, ratio );
	}

#endif// ENABLE_REFLECT_MAPPING

////////////////////////////////////////////////////////////////////////////////////////////////////
// Function : Refraction
////////////////////////////////////////////////////////////////////////////////////////////////////

#if ENABLE_REFRACT_MAPPING

#if ENABLE_SOFTPARTICLE
	float4 ComputeRefract( float4 color, float3 normal, float4 projPos, float2 projTex, float depth )
	{
#else //ENABLE_SOFTPARTICLE
	float4 ComputeRefract( float4 color, float3 normal, float4 projPos, float2 projTex )
	{
#endif //ENABLE_SOFTPARTICLE
		float2 tex;
		float4 output;

		tex = ( projPos.xy + normal.xy * M_BUMP_REFRACT_RATIO ) / projPos.w;
		tex = 0.5 + tex * float2( 0.5, -0.5 );

		output = TEX_REFRACT_2D( tex );

		if( output.a < 0.5 )
		{
			// Outside
			output = TEX_REFRACT_2D( projTex );
		#if ENABLE_SOFTPARTICLE
			tex = projTex;
		#endif //ENABLE_SOFTPARTICLE
		}

	#if ENABLE_SOFTPARTICLE
		output.rgb = lerp( output.rgb, color.rgb, color.a * ComputeSoftEdge( depth, tex ) );
	#else //ENABLE_SOFTPARTICLE
		output.rgb = lerp( output.rgb, color.rgb, color.a );
	#endif //ENABLE_SOFTPARTICLE

		output.a = 1.0;

		return output;
	}
/*
	float4 ComputeRefract( float4 srcColor, float4 projPos, float2 projTex, float3 localNormal, float softRatio )
	{
		float ratio = M_BUMP_REFRACT_RATIO * softRatio;
		float4 tex = ( projPos + float4( localNormal.xyz * ratio, 0.0f ) ) / projPos.w;
		float4 output;

		#if MIX_SM_HIGH
			tex.xy = 0.5 + ( tex.xy / tex.w * float2( 0.5, -0.5 ) );
			output = TEX_REFRACT_2D( tex.xy );
		#else //MIX_SM_HIGH
			tex.xyz = 0.5 + tex.xyz * float3( 0.5, -0.5, 0.5 );
			output = TEX_REFRACT_2D_PROJ( tex );
		#endif //MIX_SM_HIGH

		if( output.a < 0.5 )
		{
			// Outside
			output = TEX_REFRACT_2D( projTex.xy );
		}

		output.rgb = lerp( output.rgb, srcColor.rgb, srcColor.a );
		output.a = 1.0;

		return output;
	}
*/
#endif //ENABLE_REFRACT_MAPPING

////////////////////////////////////////////////////////////////////////////////////////////////////
// Function : Shadow mapping
////////////////////////////////////////////////////////////////////////////////////////////////////

#if ENABLE_SHADOW_RECEIVE

	float4 ComputeShadow( float4 tex, float4 depth, float NdotL )
	{
		float4 output;

		if( SHADOW_ENABLED )
		{
			float maxDepthSlope;
			float depthBias;
			float compDepth;

			float shade;
		#if ENABLE_HALF_LAMBERT
			float neut;
		#endif //ENABLE_HALF_LAMBERT
			float4 data;

			////////////////////////////////////////////////////////////////////////////////////////////////////

			tex.xy /= tex.w;
			depth.z /= depth.w;

			////////////////////////////////////////////////////////////////////////////////////////////////////

			maxDepthSlope = max( abs( ddx( depth.z ) ), abs( ddy( depth.z ) ) );
			depthBias = SHADOW_DEPTH_BIAS + SHADOW_DEPTH_SLOPE_SCALE * maxDepthSlope;
			depthBias = min( depthBias, SHADOW_DEPTH_BIAS_CLAMP );

//			float theta = clamp( NdotL, 0.0, 1.0 );
//			depthBias = tan( acos( theta ) ) * SHADOW_DEPTH_BIAS;

			compDepth = depth.z - depthBias;

			////////////////////////////////////////////////////////////////////////////////////////////////////

			shade = 0.0;

			[unroll(MAX_SHADOW_PCF_SIZE)]
			for( float x = -SHADOW_PCF_RANGE; x <= +SHADOW_PCF_RANGE; x += 1.0 )
			{
				[unroll(MAX_SHADOW_PCF_SIZE)]
				for( float y = -SHADOW_PCF_RANGE; y <= +SHADOW_PCF_RANGE; y += 1.0 )
				{
					shade += step( compDepth, TEX_SHADOW_2D( tex.xy + SHADOW_TEXEL_SIZE * float2( x, y ) ).r );
//					shade += step( compDepth, TEX_SHADOW_2D( tex.xy + SHADOW_TEXEL_SIZE * ( 1.0 - depth.z ) * SHADOW_PCF_RANGE * float2( x, y ) ).r );
				}
			}

			shade = clamp( shade * SHADOW_PCF_MUL, 0.0, 1.0 );

			////////////////////////////////////////////////////////////////////////////////////////////////////

			// Red ( Shade ) //

			data = TEX_SHADOW_2D( tex.xy );

		#if ENABLE_HALF_LAMBERT
			neut = 1.0 - max( 0.0, NdotL );
			neut = neut * 0.5 + 0.5;
			neut = neut * neut;
		#endif //ENABLE_HALF_LAMBERT

			output.r = step( compDepth, shade );
		#if ENABLE_HALF_LAMBERT
			output.r = lerp( 0.75, output.r, data.a );
			output.r = clamp( output.r + neut, 0.0, 1.0 );
		#else //ENABLE_HALF_LAMBERT
			output.r = lerp( 1.0, output.r, data.a );
		#endif //ENABLE_HALF_LAMBERT
			output.r = lerp( 1.0, output.r, GLOBAL_SHADE_FACTOR );

		#if ENABLE_HALF_LAMBERT
			output.r = output.r * 0.5 + 0.5;
			output.r = output.r * output.r;
		#endif //ENABLE_HALF_LAMBERT

			// Green ( Edge ) //
			
			output.g = ( ( shade - 1.0 ) * shade * max( 0.0, NdotL ) != 0.0 )? 1.0 : 0.0;
			
			// Blue ( Unused ) //
			
			output.b = 0.0;
			
			// Alpha ( Blend )
			
			output.a = 1.0;
		}
		else
		{
			output = float4( 1.0, 0.0, 0.0, 0.0 );
		}

		return output;
	}

#endif //ENABLE_SHADOW_RECEIVE
