////////////////////////////////////////////////////////////////////////////////////////////////////
// Includes
////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////
// Macros
////////////////////////////////////////////////////////////////////////////////////////////////////

//MIX_SM_3 : Shader model 3
//MIX_SM_4 : Shader model 4
//MIX_SM_5 : Shader model 5

#define MIX_SM_LOW ( MIX_SM_3 )
#define MIX_SM_HIGH ( MIX_SM_4 || MIX_SM_5 )

//Output target
#if MIX_SM_3

	#define MSV_POSITION POSITION

	#define MSV_TARGET0 COLOR0
	#define MSV_TARGET1 COLOR1
	#define MSV_TARGET2 COLOR2
	#define MSV_TARGET3 COLOR3

#else //MIX_SM_3

	#define MSV_POSITION SV_POSITION

	#define MSV_TARGET0 SV_TARGET0
	#define MSV_TARGET1 SV_TARGET1
	#define MSV_TARGET2 SV_TARGET2
	#define MSV_TARGET3 SV_TARGET3

#endif //MIX_SM_3

////////////////////////////////////////////////////////////////////////////////////////////////////
// Constants
////////////////////////////////////////////////////////////////////////////////////////////////////

static const float3 X_AXIS = float3( 1.0, 0.0, 0.0 );
static const float3 Y_AXIS = float3( 0.0, 1.0, 0.0 );
static const float3 Z_AXIS = float3( 0.0, 0.0, 1.0 );

////////////////////////////////////////////////////////////////////////////////////////////////////
// Constant values
////////////////////////////////////////////////////////////////////////////////////////////////////

#define MAX_ACTIVE_LIGHT 4
#define MAX_SHADOW_PCF_SIZE 7

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

#if MIX_SM_HIGH

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

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

	cbuffer cbGeneral : register( b1 )
	{
		// Camera
		float3 g_EyePos             : packoffset( c0 );
		float g_FarZInv             : packoffset( c0.w );

		// Grobal ambient
		float4 g_GlobalAmbientColor : packoffset( c1 );

		// Hemisphere light
		float4 g_HSSkyAxis          : packoffset( c2 );
		float4 g_HSGroundColor      : packoffset( c3 );
		float4 g_HSSkyColor         : packoffset( c4 );

		// Sun(Directional) light
		float4 g_SunDir             : packoffset( c5 );
		float4 g_SunColor           : packoffset( c6 );

		// Fog
		float4 g_FogParam           : packoffset( c7 );
		float3 g_FogColor           : packoffset( c8 );

		// Shadow
		float4 g_ShadowParam0       : packoffset( c9 );
		float4 g_ShadowParam1       : packoffset( c10 );

		// Atmosphere scattering
		float4 g_AS_Color           : packoffset( c11 );
		float3 g_AS_SunDir          : packoffset( c12 );
		float4 g_AS_Multipliers     : packoffset( c13 );
		float3 g_AS_HG              : packoffset( c14 );
		float3 g_AS_BetaDashR       : packoffset( c15 );
		float3 g_AS_BetaDashM       : packoffset( c16 );
		float3 g_AS_BetaRM          : packoffset( c17 );
		float3 g_AS_OneOverBetaRM   : packoffset( c18 );
	};

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

	cbuffer cbLocalLight : register( b2 )
	{
		float4 g_LightPos[MAX_ACTIVE_LIGHT];
		float4 g_LightDir[MAX_ACTIVE_LIGHT];
		float4 g_LightAtten[MAX_ACTIVE_LIGHT];
		float4 g_LightColor[MAX_ACTIVE_LIGHT];
		float4 g_LightParam[MAX_ACTIVE_LIGHT];
	};

	/****************************************
		Material : b3
	****************************************/
	
	//
	
#else //MIX_SM_HIGH

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

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

	// Camera
	float g_FarZInv             : register( c16 );
	float3 g_EyePos             : register( c17 );

	// Grobal ambient
	float4 g_GlobalAmbientColor : register( c18 );

	// Hemisphere light
	float4 g_HSSkyAxis          : register( c19 );
	float4 g_HSGroundColor      : register( c20 );
	float4 g_HSSkyColor         : register( c21 );

	// Directional light
	float4 g_SunDir             : register( c22 );
	float4 g_SunColor           : register( c23 );

	// Fog
	float4 g_FogParam           : register( c24 );
	float3 g_FogColor           : register( c25 );

	// Shadow
	float4 g_ShadowParam0       : register( c26 );
	float4 g_ShadowParam1       : register( c27 );

	// Atmosphere scattering
	float4 g_AS_Color           : register( c28 );
	float3 g_AS_SunDir          : register( c29 );
	float4 g_AS_Multipliers     : register( c30 );
	float3 g_AS_HG              : register( c31 );
	float3 g_AS_BetaDashR       : register( c32 );
	float3 g_AS_BetaDashM       : register( c33 );
	float3 g_AS_BetaRM          : register( c34 );
	float3 g_AS_OneOverBetaRM   : register( c35 );

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

	float4 g_LightPos[MAX_ACTIVE_LIGHT]   : register( c48 );
	float4 g_LightDir[MAX_ACTIVE_LIGHT]   : register( c52 );
	float4 g_LightAtten[MAX_ACTIVE_LIGHT] : register( c56 );
	float4 g_LightColor[MAX_ACTIVE_LIGHT] : register( c60 );
	float4 g_LightParam[MAX_ACTIVE_LIGHT] : register( c64 );

	/****************************************
		Material : c68 - ?
	****************************************/
	
	//

#endif //MIX_SM_HIGH

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

// General //

#define INV_FAR_Z g_FarZInv // Inverse far z ( float )
#define EYE_POS   g_EyePos  // Eye position( float3 )

// Global ambient //

#define GLOBAL_AMBIENT_COLOR g_GlobalAmbientColor.rgb // Color ( float3 )
#define GLOBAL_SHADE_FACTOR g_GlobalAmbientColor.a // Shade factor ( float )

// Hemisphere light //

#define HEMISPHERE_ENABLED      ( g_HSSkyAxis.w > 0.5 ) // Enabled ( bool )
#define HEMISPHERE_AXIS         g_HSSkyAxis.xyz         // Axis ( float3 )
#define HEMISPHERE_GROUND_COLOR g_HSGroundColor.rgb     // Ground color ( float3 )
#define HEMISPHERE_GROUND_SCALE g_HSGroundColor.a       // Ground scaler ( float )
#define HEMISPHERE_SKY_COLOR    g_HSSkyColor.rgb        // Sky color ( float3 )
#define HEMISPHERE_SKY_SCALE    g_HSSkyColor.a          // Sky scaler ( float )

// Sun //

#define SUN_ENABLED ( g_SunDir.w > 0.5 ) // Enabled ( bool )
#define SUN_DIR     g_SunDir.xyz         // Direction ( float3 )
#define SUN_COLOR   g_SunColor.rgb       // Color ( float3 )
#define SUN_SCALE   g_SunColor.a         // Scaler ( float )

// Fog //

#define FOG_ENABLED ( g_FogParam.w > 0.5f ) // Enabled ( bool )
#define FOG_P0      g_FogParam.x            // Paramter0 ( float )
#define FOG_P1      g_FogParam.y            // Paramter1 ( float )
#define FOG_COLOR   g_FogColor.rgb          // Color ( float3 )

// Shadow //

#define SHADOW_ENABLED            ( g_ShadowParam0.w > 0.5 ) // Enabled ( bool )
#define SHADOW_DEPTH_BIAS         g_ShadowParam0.x           // Depth Bias ( float )
#define SHADOW_DEPTH_SLOPE_SCALE  g_ShadowParam0.y           // Depth SlopeScale ( float )
#define SHADOW_DEPTH_BIAS_CLAMP   g_ShadowParam0.z           // Depth Bias Clamp ( float )
#define SHADOW_PCF_RANGE          g_ShadowParam1.x           // PCF ( Size - 1 ) * 0.5 ( float )
#define SHADOW_PCF_MUL            g_ShadowParam1.y           // PCF 1.0 / ( Size * Size ) ( float )
#define SHADOW_TEXEL_SIZE         g_ShadowParam1.zw          // Texel Size ( float2 )

// Atmosphere scattering //

#define AS_ENABLED          ( g_AS_Color.w > 0.5 ) // Enabled ( bool )
#define AS_COLOR            ( g_AS_Color.rgb )     // Color ( float3 )
#define AS_SUN_DIR          g_AS_SunDir.xyz        // Sun direction ( float3 )
#define AS_LIN_SCALE        g_AS_Multipliers.x     // Lin scaler ( float )
#define AS_FEX_SCALE        g_AS_Multipliers.yzw   // Fex scaler ( float3 )
#define AS_HG_M             g_AS_HG.x              // HG 1.0 - hg * hg ( float )
#define AS_HG_P             g_AS_HG.y              // HG 1.0 + hg * hg ( float )
#define AS_HG_T             g_AS_HG.z              // HG 2.0 * hg ( float )
#define AS_BETA_DASH_R      g_AS_BetaDashR         // Beta dash R ( float3 )
#define AS_BETA_DASH_M      g_AS_BetaDashM         // Beta dash M ( float3 )
#define AS_BETA_RM          g_AS_BetaRM            // Beta RM ( float3 )
#define AS_ONE_OVER_BETA_RM g_AS_OneOverBetaRM     // One over beta RM ( float3 )

// LocalLight //

#define LO_EXIST( index )     ( g_LightPos[index].w > 0.5 ) // Common : Is enabled ( bool )
#define LO_SPOT( index )      ( g_LightDir[index].w > 0.5 ) // Common : Is spot ( bool )
#define LO_POS( index )       g_LightPos[index].xyz         // Common : Position ( float3 )
#define LO_RANGE( index )     g_LightParam[index].x         // Common : Range ( float )
#define LO_COLOR( index )     g_LightColor[index]           // Common : Color ( float4 )
#define LO_ATTEN( index )     g_LightAtten[index].xyz       // Common : Atten ( float3 )
#define LO_DIR( index )       g_LightDir[index].xyz         // Spot   : Direction ( float3 )
#define LO_INNER_COS( index ) g_LightParam[index].y         // Spot   : Inner cos angle ( float )
#define LO_OUTER_COS( index ) g_LightParam[index].z         // Spot   : Outer cos angle ( float )
#define LO_EXP( index )       g_LightParam[index].w         // Spot   : Exprosure ( float )
#define LO_DIST_DIFF( index ) g_LightParam[index].y         // Point  : Range diff ( float )

////////////////////////////////////////////////////////////////////////////////////////////////////
// Functions
////////////////////////////////////////////////////////////////////////////////////////////////////

// Fog ////

#if ENABLE_FOG

	float ComputeFogRatio( float projPosW )
	{
		return saturate( FOG_P0 + projPosW * FOG_P1 );
	}

	#if ENABLE_ATMOSPHERE

		float3 ApplyFog( float3 srcColor, float3 dir, float dist, float ratio )
		{
			float3 output;
			
			if( FOG_ENABLED )
			{
				if( AS_ENABLED )
				{
					float theta;
					float phase1;
					float phase2;
					float3 extinction;
					float3 fex;
					float3 lin;
				
					////////////////////////////////////////////////////////////////////////////////////////////////////

					theta = dot( -AS_SUN_DIR, dir.xyz );

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

					phase1 = 1.0 + theta * theta;
					phase2 = rsqrt( AS_HG_P - AS_HG_T * theta ) * AS_HG_M;

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

					extinction = exp( -AS_BETA_RM * dist );

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

					fex = extinction;
					fex *= AS_FEX_SCALE;
					fex *= AS_COLOR;

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

					float3 betaRay = AS_BETA_DASH_R * phase1;
					float3 betaMie = AS_BETA_DASH_M * phase2;

					lin = ( betaRay + betaMie ) * AS_ONE_OVER_BETA_RM * ( 1.0 - extinction );
					lin *= AS_LIN_SCALE;
					lin *= AS_COLOR;

					output = srcColor;
					output *= lerp( fex, float3( 1.0, 1.0f, 1.0 ), ratio );
					output += ( lin * FOG_COLOR ) * ( 1.0 - ratio );
				}
				else
				{
					output = lerp( FOG_COLOR, srcColor, ratio );
				}
			}
			else
			{
				output = srcColor;
			}
			
			return output;
		}

	#else //ENABLE_ATMOSPHERE
	
		float3 ApplyFog( float3 srcColor, float ratio )
		{
			float3 output;
			
			if( FOG_ENABLED )
			{
				output = lerp( FOG_COLOR, srcColor, ratio );
			}
			else
			{
				output = srcColor;
			}
			
			return output;
		}

	#endif //ENABLE_ATMOSPHERE

#endif //ENABLE_FOG


////////////////////////////////////////////////////////////////////////////////////////////////////
// Definitions
////////////////////////////////////////////////////////////////////////////////////////////////////

#define TRANSPARENCY_THRESHOLD 0.001 // Ɣʂŏl

////////////////////////////////////////////////////////////////////////////////////////////////////
// Macros
////////////////////////////////////////////////////////////////////////////////////////////////////

#define ENABLE_PROJ_TEX ( ENABLE_REFRACT_CLIP || ENABLE_REFRACT_MAPPING || ENABLE_SOFTPARTICLE ) // Projection UV
#define ENABLE_FRESNEL ( ENABLE_DIFFUSE_FRESNEL || ENABLE_REFLECT_FRESNEL ) // tlʂ̗L

#define ENABLE_N_DOT_L ( ENABLE_LIGHTING || ( ENABLE_SHADOW && ENABLE_SHADOW_RECEIVE ) ) // BumpNormal . LightDirection
#define ENABLE_N_DOT_E ( ENABLE_RIM_LIGHTING || ENABLE_COOK_TORRANCE ) // BumpNormal . EyeDirection
#define ENABLE_N_DOT_E_PLANE ( ENABLE_WATER || ENABLE_FRESNEL ) // FaceNormal . EyeDirection

#define ENABLE_SPECULAR ( ENABLE_PHONG || ENABLE_BLINN_PHONG || ENABLE_COOK_TORRANCE ) // XyL̗L
#define ENABLE_TRANSPARENCY_CLIP ( ENABLE_WATER == 0 ) // Ȃ̂Nbv邩ǂ

////////////////////////////////////////////////////////////////////////////////////////////////////
// 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


////////////////////////////////////////////////////////////////////////////////////////////////////
// Main Function : ColorModel
////////////////////////////////////////////////////////////////////////////////////////////////////

PS_COLOR_OUTPUT mainColorModel( PS_COLOR_INPUT input )
{
	PS_COLOR_OUTPUT output = ( PS_COLOR_OUTPUT )0;

	////////////////////////////////////////////////////////////
	// Initialize
	////////////////////////////////////////////////////////////

#if ENABLE_PROJ_TEX
	float2 projTex = 0.5 + ( input.projPos.xy / input.projPos.w ) * float2( 0.5, -0.5 );
#endif //ENABLE_PROJ_TEX

	// Refract clip ( RGB=SurfaceZ A=Mask ) Only transparency
#if ENABLE_REFRACT_CLIP
	clip( ( input.projPos.z / input.projPos.w ) - TEX_REFRACT_2D( projTex ).r );
#endif //ENABLE_REFRACT_CLIP

	float depth = saturate( input.projPos.z * INV_FAR_Z );

	// Eye parameater
#if ( ENABLE_LIGHTING || ( ENABLE_FOG && ENABLE_ATMOSPHERE ) )
	float3 eyeVec = EYE_POS - input.worldPos.xyz;
	float3 eyeDir = normalize( eyeVec );
#endif //( ENABLE_LIGHTING || ( ENABLE_FOG && ENABLE_ATMOSPHERE ) )

#if ENABLE_LIGHTING
	#if ENABLE_BUMP
		#if ENABLE_PARALLAX_MAPPING
			float3 localBumpNormal = ComputeLocalBumpNormal( input.tex, input.tangent, input.binormal, input.normal, eyeVec, eyeDir );
			float3 bumpNormal = TransformBumpNormal( localBumpNormal, input.tangent, input.binormal, input.normal );
		#else //ENABLE_PARALLAX_MAPPING
			float3 localBumpNormal = ComputeLocalBumpNormal( input.tex );
			float3 bumpNormal = TransformBumpNormal( localBumpNormal, input.tangent, input.binormal, input.normal );
		#endif //ENABLE_PARALLAX_MAPPING
	#else //ENABLE_BUMP
		float3 bumpNormal = input.normal;
	#endif //ENABLE_BUMP
#endif //ENABLE_LIGHTING

#if ENABLE_N_DOT_L
	float NdotL = dot( bumpNormal, -SUN_DIR );
#endif //ENABLE_N_DOT_L

#if ENABLE_N_DOT_E
	float NdotE = dot( bumpNormal, eyeDir );
#endif //ENABLE_N_DOT_E

#if ENABLE_N_DOT_E_PLANE
	float planeNdotE = dot( input.normal, eyeDir );
#endif //ENABLE_N_DOT_E_PLANE

#if ENABLE_FRESNEL
	float fresnelFacing = 1.0 - abs( planeNdotE );
//	float fresnelFacing = 1.0 - abs( NdotE );
#endif //ENABLE_FRESNEL

#if ENABLE_LIGHTING
	float3 ambientColor;
#endif //ENABLE_LIGHTING

	float4 diffuseColor;

#if ENABLE_SPECULAR
	float4 specularColor;
#endif //ENABLE_SPECULAR

#if ENABLE_REFLECT_MAPPING
	float reflectGloss;
	float reflectRatio;
#endif //ENABLE_REFLECT_MAPPING

	float4 emissiveColor;

#if ENABLE_LIGHTING
	LIGHT_INFO lightInfo = ( LIGHT_INFO )0;
#endif //ENABLE_LIGHTING

#if ENABLE_FOG
	float fogRatio = ComputeFogRatio( input.projPos.w );
	#if ENABLE_ATMOSPHERE
		float eyeDist = length( eyeVec );
	#endif //ENABLE_ATMOSPHERE
#endif //ENABLE_FOG

	////////////////////////////////////////////////////////////
	// Base
	////////////////////////////////////////////////////////////

	// Ambient
#if ENABLE_LIGHTING
	ambientColor = GLOBAL_AMBIENT_COLOR * M_AMBIENT_COLOR;
#endif //ENABLE_LIGHTING
	
#if ENABLE_WATER

	// Diffuse
	if( planeNdotE >= 0.0 )
	{
		diffuseColor = input.color * M_DIFFUSE_COLOR;
	}
	else
	{
		diffuseColor = float4( 0.0, 0.0, 0.0, 0.0 );
	}

	// Specular
	#if ENABLE_SPECULAR
		specularColor = M_SPECULAR_COLOR;
	#endif //ENABLE_SPECULAR

	// Reflect
	#if ENABLE_REFLECT_MAPPING
		reflectGloss = 1.0;
	#endif //ENABLE_REFLECT_MAPPING

	// Emissive
	emissiveColor = M_EMISSIVE_COLOR;

#else //ENABLE_WATER

	// Diffuse
	#if ENABLE_DIFFUSE_MAPPING
		diffuseColor = input.color * M_DIFFUSE_COLOR;
		#if ENABLE_PARALLAX_MAPPING
			diffuseColor *= TEX_DIFFUSE_2D_GRAD( gradTex, dx, dy );
		#else //ENABLE_PARALLAX_MAPPING
			diffuseColor *= TEX_DIFFUSE_2D( input.tex );
		#endif //ENABLE_PARALLAX_MAPPING
	#else //ENABLE_DIFFUSE_MAPPING
		diffuseColor = input.color * M_DIFFUSE_COLOR;
	#endif //ENABLE_DIFFUSE_MAPPING
		
	// Specular
	#if ENABLE_SPECULAR
		#if ENABLE_SPECULAR_MAPPING
			
			#if ENABLE_PARALLAX_MAPPING
				float4 texSpecularColor = TEX_SPECULAR_2D_GRAD( gradTex, dx, dy );
			#else //ENABLE_PARALLAX_MAPPING
				float4 texSpecularColor = TEX_SPECULAR_2D( input.tex );
			#endif //ENABLE_PARALLAX_MAPPING

			specularColor = M_SPECULAR_COLOR;
			specularColor.rgb *= texSpecularColor.rgb;
			specularColor *= texSpecularColor.a;

			#if ENABLE_REFLECT_MAPPING
				reflectGloss = texSpecularColor.a;
			#endif //ENABLE_REFLECT_MAPPING

		#else //ENABLE_SPECULAR_MAPPING

			specularColor = M_SPECULAR_COLOR;

			#if ENABLE_REFLECT_MAPPING
				reflectGloss = 1.0;
			#endif //ENABLE_REFLECT_MAPPING

		#endif //ENABLE_SPECULAR_MAPPING
	#endif //ENABLE_SPECULAR

	// Emissive
	#if ENABLE_EMISSIVE_MAPPING
		#if ENABLE_PARALLAX_MAPPING
			float4 texEmissiveColor = TEX_EMISSIVE_2D_GRAD( gradTex, dx, dy );
		#else //ENABLE_PARALLAX_MAPPING
			float4 texEmissiveColor = TEX_EMISSIVE_2D( input.tex );
		#endif //ENABLE_PARALLAX_MAPPING
		emissiveColor = M_EMISSIVE_COLOR * texEmissiveColor;
	#else //ENABLE_EMISSIVE_MAPPING
		emissiveColor = M_EMISSIVE_COLOR;
	#endif //ENABLE_EMISSIVE_MAPPING

#endif //ENABLE_WATER

	// Diffuse ( Fresnel )
#if ENABLE_DIFFUSE_FRESNEL
	diffuseColor = ComputeDiffuseFresnel( diffuseColor, fresnelFacing );
#endif //ENABLE_DIFFUSE_FRESNEL

	// Diffuse ( Reflect )
#if ENABLE_REFLECT_MAPPING
	#if ENABLE_REFLECT_FRESNEL
		reflectRatio = ComputeReflectRatio( reflectGloss, fresnelFacing );
	#else //ENABLE_REFLECT_FRESNEL
		reflectRatio = ComputeReflectRatio( reflectGloss );
	#endif //ENABLE_REFLECT_FRESNEL
	diffuseColor = ComputeReflect( diffuseColor, eyeDir, input.normal, bumpNormal, reflectRatio );
#endif //ENABLE_REFLECT_MAPPING

	////////////////////////////////////////////////////////////
	// Lighting
	////////////////////////////////////////////////////////////

#if ENABLE_LIGHTING

	// Hemisphere lighting
	lightInfo = ComputeHemisphereLight( lightInfo, bumpNormal );

	// Directional lighting
#if ENABLE_N_DOT_E
	lightInfo = ComputeDirectionalLight( lightInfo, NdotL, NdotE, eyeDir, bumpNormal );
#else //ENABLE_N_DOT_E
	lightInfo = ComputeDirectionalLight( lightInfo, NdotL, eyeDir, bumpNormal );
#endif //ENABLE_N_DOT_E

	// Local lighting
#if ENABLE_N_DOT_E
	lightInfo = ComputeLocalLight( lightInfo, NdotE, eyeDir, input.worldPos.xyz, bumpNormal );
#else //ENABLE_N_DOT_E
	lightInfo = ComputeLocalLight( lightInfo, eyeDir, input.worldPos.xyz, bumpNormal );
#endif //ENABLE_N_DOT_E

	// Apply ambient
	ambientColor *= max( 0.0, 1.0 - lightInfo.diffuseColor );

	// Apply diffuse
	diffuseColor.rgb *= lightInfo.diffuseColor;

	// Apply specular
#if ENABLE_SPECULAR
	specularColor *= lightInfo.specularFactor;
#endif //ENABLE_SPECULAR

#endif //ENABLE_LIGHTING

	////////////////////////////////////////////////////////////
	// Out : Color
	////////////////////////////////////////////////////////////

	// Ambient
#if ENABLE_LIGHTING
	output.color.rgb += ambientColor;
#endif //ENABLE_LIGHTING

	// Diffuse
	output.color += diffuseColor;

	// Specular
#if ENABLE_SPECULAR
	output.color += specularColor * M_SPECULAR_SCALE;
#endif //ENABLE_SPECULAR

	// Emissive
	output.color += emissiveColor * M_EMISSIVE_SCALE;

	// Fog
#if ENABLE_FOG
	#if ENABLE_ATMOSPHERE
		output.color.rgb = ApplyFog( output.color.rgb, -eyeDir, eyeDist, fogRatio );
	#else //ENABLE_ATMOSPHERE
		output.color.rgb = ApplyFog( output.color.rgb, fogRatio );
	#endif //ENABLE_ATMOSPHERE
#endif //ENABLE_FOG

	// Only Transparency ( SoftEdge or Refraction )
#if ENABLE_REFRACT_MAPPING
	//RGB=BackgroundColor A=Mask
	#if ENABLE_SOFTPARTICLE
		output.color = ComputeRefract( output.color, localBumpNormal, input.projPos, projTex, depth );
	#else //ENABLE_SOFTPARTICLE
		output.color = ComputeRefract( output.color, localBumpNormal, input.projPos, projTex );
	#endif //ENABLE_SOFTPARTICLE
#else //ENABLE_REFRACT_MAPPING
	#if ENABLE_SOFTPARTICLE
		output.color.a *= ComputeSoftEdge( depth, projTex );
	#endif //ENABLE_SOFTPARTICLE
#endif //ENABLE_REFRACT_MAPPING

	// Clip transparency
#if ENABLE_TRANSPARENCY_CLIP
	clip( output.color.a - TRANSPARENCY_THRESHOLD );
#endif //ENABLE_TRANSPARENCY_CLIP

	////////////////////////////////////////////////////////////
	// Out : Depth
	////////////////////////////////////////////////////////////

	output.depth.rgb = depth;
	output.depth.a = 1.0;

	////////////////////////////////////////////////////////////
	// Out : Data
	////////////////////////////////////////////////////////////
	
#if ( ENABLE_SSAO || ENABLE_SHADOW )
	#if ENABLE_LIGHTING
		#if ENABLE_REFRACT_MAPPING
			output.data = 0.0;
		#else //ENABLE_REFRACT_MAPPING
			output.data.xyz = input.viewNormal.xyz * 0.5 + 0.5;
			output.data.a = output.color.a;
		#endif //ENABLE_REFRACT_MAPPING
	#else //ENABLE_LIGHTING
		output.data = float4( 0.0, 0.0, 0.0, output.color.a );
	#endif //ENABLE_LIGHTING
#else //( ENABLE_SSAO || ENABLE_SHADOW )
	output.data = float4( 0.0, 0.0, 0.0, output.color.a );
#endif //( ENABLE_SSAO || ENABLE_SHADOW )

	////////////////////////////////////////////////////////////
	// Out : Shadow
	////////////////////////////////////////////////////////////

#if ENABLE_SHADOW
	#if ENABLE_SHADOW_RECEIVE
		output.shadow = ComputeShadow( input.shadowTex, input.shadowDepth, NdotL );
	#else //ENABLE_SHADOW_RECEIVE
		#if ENABLE_REFRACT_MAPPING
			output.shadow = float4( 1.0, 0.0, 0.0, 1.0 );
		#else //ENABLE_REFRACT_MAPPING
			output.shadow.rgb = float3( 1.0, 0.0, 0.0 );
			output.shadow.a = ( ( output.color.a - TRANSPARENCY_THRESHOLD ) > 0.0 )? output.color.a : 0.0;
		#endif //ENABLE_REFRACT_MAPPING
	#endif //ENABLE_SHADOW_RECEIVE
#endif //ENABLE_SHADOW

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

	return output;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Main Function : MaskModel
////////////////////////////////////////////////////////////////////////////////////////////////////

float4 mainMaskModel( PS_MASK_MODEL_INPUT input ) : MSV_TARGET0
{
	float4 output;

	output.rgb = input.projPos.z / input.projPos.w;
	output.a = 1.0;

	return output;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Main Function : ShadowModel
////////////////////////////////////////////////////////////////////////////////////////////////////

float4 mainShadowModel( PS_SHADOW_MODEL_INPUT input ) : MSV_TARGET0
{
	float4 color = input.color * M_DIFFUSE_COLOR;
	float4 output = 0.0;

#if ENABLE_DIFFUSE_MAPPING
	color *= TEX_DIFFUSE_2D( input.tex );
#endif // ENABLE_DIFFUSE_MAPPING

#if 1

	output.r = input.depth.z / input.depth.w;
	output.g = 0.0;
	output.b = 0.0;
	output.a = color.a;

#else

	float depth = input.depth.z / input.depth.w;
	float dx = ddx( depth );
	float dy = ddy( depth );

	output.r = depth;
	output.g = depth * depth + 0.25 * ( dx * dx + dy * dy );
	output.b = 0.0;
	output.a = color.a;

#endif

	return output;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Main Function : SelectModel
////////////////////////////////////////////////////////////////////////////////////////////////////

float4 mainSelectModel( float4 pos : SV_POSITION ) : MSV_TARGET0
{
	return M_DIFFUSE_COLOR;
}
