
float4 g_DiffuseColor;
float g_OpacityLevel;
float g_SpecularLevel;
float g_BumpLevel;
float3 g_EyePos;

float4x4 g_mWorld;
float4x4 g_mWorldView;
float4x4 g_mWorldViewProjection;
float4x4 g_mWorldViewProjectionNext;
float4x4 g_mLight[3];
float4x4 g_mLightTexture;

float4x4 g_mBones[32];

float3 g_MainLightDir;
float4 g_MainLightColor;
float3 g_SubLightDir;
float4 g_SubLightColor;
float3 g_AmbientLightColor;

float3 g_FogColor;
float g_FogStart;

float g_FogDensity;

float g_Z1;
float g_Z2;

float g_ShadowTextureSize = 1024.f;

texture g_DiffuseTexture;
sampler DiffuseSampler = 
sampler_state
{
    Texture = <g_DiffuseTexture>;
    MipFilter = LINEAR;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
};

texture g_OpacityTexture;
sampler OpacitySampler = 
sampler_state
{
    Texture = <g_OpacityTexture>;
    MipFilter = LINEAR;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
};

texture g_SpecularTexture;
sampler SpecularSampler = 
sampler_state
{
    Texture = <g_SpecularTexture>;
    MipFilter = LINEAR;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
};

texture g_BumpTexture;
sampler BumpSampler = 
sampler_state
{
    Texture = <g_BumpTexture>;
    MipFilter = LINEAR;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
};

texture g_ShadowTexture1;
sampler ShadowSampler1 = 
sampler_state
{
    Texture = <g_ShadowTexture1>;
    MipFilter = LINEAR;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
	AddressU = CLAMP;
    AddressV = CLAMP;
};

texture g_ShadowTexture2;
sampler ShadowSampler2 = 
sampler_state
{
    Texture = <g_ShadowTexture2>;
    MipFilter = LINEAR;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
	AddressU = CLAMP;
    AddressV = CLAMP;
};

texture g_ShadowTexture3;
sampler ShadowSampler3 = 
sampler_state
{
    Texture = <g_ShadowTexture3>;
    MipFilter = LINEAR;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
	AddressU = CLAMP;
    AddressV = CLAMP;
};

struct VS_OUTPUT
{
    float4 Position   : POSITION;
    float2 TextureUV  : TEXCOORD0;
	float Depth : TEXCOORD1;
	float3 ViewDir : TEXCOORD2;
	float3 Normal : TEXCOORD3;
	float4 ShadowUV : TEXCOORD4;
	float4 ShadowUV2 : TEXCOORD5;
	float4 ShadowUV3 : TEXCOORD6;
};

VS_OUTPUT RenderSceneVS( float4 vPos : POSITION, 
                         float3 vNormal : NORMAL,
                         float2 vTexUV : TEXCOORD0,
						 int4 vWeight : BLENDWEIGHT,
						 float4 vIndex : BLENDINDICES)
{
    VS_OUTPUT Output;
    
	//Skinning//
	float4 vVert = float4(0, 0, 0, 0);
	float3 vNorm = float3(0, 0, 0);
	for(int i = 0; i < 4; ++i)
	{
		if(vWeight[i] == 0) continue;
		vVert += mul(vPos, g_mBones[vIndex[i]]) * vWeight[i];
		vNorm += mul(vNormal, (float3x3)g_mBones[vIndex[i]]) * vWeight[i];
	}

    Output.Position = mul(vVert, g_mWorldViewProjection);
    
    float3 vWVPos = mul(vVert, g_mWorld).xyz;
    float3 vWVNormal = normalize(mul(vNorm, (float3x3)g_mWorld));

	Output.Normal = vWVNormal;
	Output.ViewDir = g_EyePos - vWVPos;
    Output.TextureUV = vTexUV;

	//Fog//
	Output.Depth = Output.Position.z;
	
	//Shadow Mapping//
	Output.ShadowUV = mul(vVert, g_mLight[0]);
	Output.ShadowUV2 = mul(vVert, g_mLight[1]);
	Output.ShadowUV3 = mul(vVert, g_mLight[2]);
    return Output;
}


struct PS_OUTPUT
{
    float4 RGBColor : COLOR0;
};

float GetShadowBlur3(sampler samp, float4 uv, float it)
{
	float s = 0;
	s += tex2Dproj(samp, uv).r;
	s += tex2Dproj(samp, uv + float4(it, 0, 0, 0)).r * .75;
	s += tex2Dproj(samp, uv + float4(0, it, 0, 0)).r * .75;
	s += tex2Dproj(samp, uv + float4(-it, 0, 0, 0)).r * .75;
	s += tex2Dproj(samp, uv + float4(0, -it, 0, 0)).r * .75;
	return s / 4;
}

float GetShadowBlur9(sampler samp, float4 uv, float it)
{
	float s = 0;
	s += tex2Dproj(samp, uv).r;
	s += tex2Dproj(samp, uv + float4(it, 0, 0, 0)).r;
	s += tex2Dproj(samp, uv + float4(0, it, 0, 0)).r;
	s += tex2Dproj(samp, uv + float4(-it, 0, 0, 0)).r;
	s += tex2Dproj(samp, uv + float4(0, -it, 0, 0)).r;
	s += tex2Dproj(samp, uv + float4(it, -it, 0, 0)).r * .5;
	s += tex2Dproj(samp, uv + float4(it, it, 0, 0)).r * .5;
	s += tex2Dproj(samp, uv + float4(-it, it, 0, 0)).r * .5;
	s += tex2Dproj(samp, uv + float4(-it, -it, 0, 0)).r * .5;
	return s / 7;
}

PS_OUTPUT RenderScenePS( VS_OUTPUT In ) 
{ 
    PS_OUTPUT Output;

	float3 normal = normalize(In.Normal);
	float3 viewDir = normalize(In.ViewDir);
	float fMLight = max(0, dot(normal, g_MainLightDir));
	float fSLight = max(0, dot(normal, g_SubLightDir));
	float3 hM = normalize(g_MainLightDir + viewDir);
	float3 hS = normalize(g_SubLightDir + viewDir);
	float sMLight = pow(max(0, dot(normal, hM)), 32);
	float sSLight = pow(max(0, dot(normal, hS)), 32);
	
	float3 diffuse = lerp(g_DiffuseColor.rgb, tex2D(DiffuseSampler, In.TextureUV).rgb, tex2D(DiffuseSampler, In.TextureUV).a);
	float3 specular = (g_SpecularLevel) * tex2D(SpecularSampler, In.TextureUV).rgb;
	
	//Shadow Mapping//
	float4 shadow = float4(1, 1, 1, 1);
	float it = 1.f / g_ShadowTextureSize;
	if(In.Depth < g_Z1)
	{
		In.ShadowUV /= In.ShadowUV.w;
		shadow = GetShadowBlur9(ShadowSampler1, In.ShadowUV, it);
	}
	else if(In.Depth < g_Z2)
	{
		In.ShadowUV2 /= In.ShadowUV2.w;
		shadow = GetShadowBlur3(ShadowSampler2, In.ShadowUV2, it);
	}
	else
	{
		In.ShadowUV3 /= In.ShadowUV3.w;
		shadow = tex2Dproj(ShadowSampler3, In.ShadowUV3);
	}

	float3 mcolor = (shadow * ceil(2*fMLight + 0.2)/2 * g_MainLightColor + ceil(2*fSLight + 0.2)/2 * g_SubLightColor + g_AmbientLightColor) * diffuse
		+ (floor(sMLight + 0.5) + floor(sSLight + 0.5))* specular;
	Output.RGBColor.rgb = lerp(mcolor, g_FogColor, 1 - exp(-g_FogDensity * max(In.Depth - g_FogStart, 0)));
	Output.RGBColor.a = tex2D(OpacitySampler, In.TextureUV).a * g_OpacityLevel;
    return Output;
}

technique RenderScene
{
    pass P0
    {      
		AlphaTestEnable = TRUE;
		AlphaRef = 8;
		AlphaFunc = GREATER;
		AlphaBlendEnable = TRUE;
		BlendOp = ADD;
		DestBlend = INVSRCALPHA;
		SrcBlend = SRCALPHA;
		ZFunc = LESSEQUAL;
        VertexShader = compile vs_3_0 RenderSceneVS();
        PixelShader  = compile ps_3_0 RenderScenePS(); 
    }
}

struct VS_OUTPUT_SKYBOX
{
    float4 Position   : POSITION;
    float2 TextureUV  : TEXCOORD0;
};

VS_OUTPUT_SKYBOX RenderSkyboxVS( float4 vPos : POSITION, 
                         float2 vTexUV : TEXCOORD0,
						 int4 vWeight : BLENDWEIGHT,
						 float4 vIndex : BLENDINDICES)
{
    VS_OUTPUT_SKYBOX Output;
    
	//Skinning//
	float4 vVert = float4(0, 0, 0, 0);
	for(int i = 0; i < 4; ++i)
	{
		if(vWeight[i] == 0) continue;
		vVert += mul(vPos, g_mBones[vIndex[i]]) * vWeight[i];
	}

    Output.Position = mul(vVert, g_mWorldViewProjection);
    Output.TextureUV = vTexUV;
    return Output;
}

PS_OUTPUT RenderSkyboxPS( VS_OUTPUT_SKYBOX In ) 
{ 
    PS_OUTPUT Output;
	
	float3 diffuse = lerp(tex2D(DiffuseSampler, In.TextureUV).rgb, tex2D(OpacitySampler, In.TextureUV), g_OpacityLevel);
	Output.RGBColor.rgb = diffuse;
	Output.RGBColor.a = 1;
    return Output;
}

technique RenderSkybox
{
    pass P0
    {      
		AlphaTestEnable = TRUE;
		AlphaRef = 8;
		AlphaFunc = GREATER;
		AlphaBlendEnable = TRUE;
		BlendOp = ADD;
		DestBlend = INVSRCALPHA;
		SrcBlend = SRCALPHA;
		ZFunc = LESSEQUAL;
        VertexShader = compile vs_3_0 RenderSkyboxVS();
        PixelShader  = compile ps_3_0 RenderSkyboxPS(); 
    }
}

struct VS_OUTPUT_NOSHADOW
{
    float4 Position   : POSITION;
    float2 TextureUV  : TEXCOORD0;
	float Depth : TEXCOORD1;
	float3 ViewDir : TEXCOORD2;
	float3 Normal : TEXCOORD3;
};

VS_OUTPUT_NOSHADOW NoShadowVS( float4 vPos : POSITION, 
                         float3 vNormal : NORMAL,
                         float2 vTexUV : TEXCOORD0,
						 int4 vWeight : BLENDWEIGHT,
						 float4 vIndex : BLENDINDICES)
{
    VS_OUTPUT_NOSHADOW Output;
    
	//Skinning//
	float4 vVert = float4(0, 0, 0, 0);
	float3 vNorm = float3(0, 0, 0);
	for(int i = 0; i < 4; ++i)
	{
		if(vWeight[i] == 0) continue;
		vVert += mul(vPos, g_mBones[vIndex[i]]) * vWeight[i];
		vNorm += mul(vNormal, (float3x3)g_mBones[vIndex[i]]) * vWeight[i];
	}

    Output.Position = mul(vVert, g_mWorldViewProjection);
    
    float3 vWVPos = mul(vVert, g_mWorld).xyz;
    float3 vWVNormal = normalize(mul(vNorm, (float3x3)g_mWorld));

	Output.Normal = vWVNormal;
	Output.ViewDir = g_EyePos - vWVPos;
    Output.TextureUV = vTexUV;

	//Fog//
	Output.Depth = Output.Position.z;
    return Output;
}

PS_OUTPUT NoShadowPS( VS_OUTPUT_NOSHADOW In ) 
{ 
    PS_OUTPUT Output;

	float3 normal = normalize(In.Normal);
	float3 viewDir = normalize(In.ViewDir);
	float fMLight = max(0, dot(normal, g_MainLightDir));
	float fSLight = max(0, dot(normal, g_SubLightDir));
	float3 hM = normalize(g_MainLightDir + viewDir);
	float3 hS = normalize(g_SubLightDir + viewDir);
	float sMLight = pow(max(0, dot(normal, hM)), 32);
	float sSLight = pow(max(0, dot(normal, hS)), 32);
	
	float3 diffuse = lerp(g_DiffuseColor.rgb, tex2D(DiffuseSampler, In.TextureUV).rgb, tex2D(DiffuseSampler, In.TextureUV).a);
	float3 specular = (g_SpecularLevel) * tex2D(SpecularSampler, In.TextureUV).rgb;

	float3 mcolor = (ceil(2*fMLight + 0.2)/2 * g_MainLightColor + ceil(2*fSLight + 0.2)/2 * g_SubLightColor + g_AmbientLightColor) * diffuse
		+ (floor(sMLight + 0.5) + floor(sSLight + 0.5))* specular;
	Output.RGBColor.rgb = lerp(mcolor, g_FogColor, 1 - exp(-g_FogDensity * max(In.Depth - g_FogStart, 0)));
	Output.RGBColor.a = tex2D(OpacitySampler, In.TextureUV).a;
    return Output;
}

technique NoShadowScene
{
    pass P0
    {      
		AlphaTestEnable = TRUE;
		AlphaRef = 8;
		AlphaFunc = GREATER;
		AlphaBlendEnable = TRUE;
		BlendOp = ADD;
		DestBlend = INVSRCALPHA;
		SrcBlend = SRCALPHA;
        VertexShader = compile vs_3_0 NoShadowVS();
        PixelShader  = compile ps_3_0 NoShadowPS(); 
    }
}

struct VS_OUTPUT_SHADOW
{
    float4 Position : POSITION;
	float2 TextureUV  : TEXCOORD0;
	float2 Depth : TEXCOORD1;
};

VS_OUTPUT_SHADOW ShadowOutputVS( float4 vPos : POSITION, 
                         float2 vTexUV : TEXCOORD0,
						 int4 vWeight : BLENDWEIGHT,
						 float4 vIndex : BLENDINDICES)
{
    VS_OUTPUT_SHADOW Output;
    
	float4 vVert = float4(0, 0, 0, 0);
	for(int i = 0; i < 4; ++i)
	{
		if(vWeight[i] == 0) continue;
		vVert += mul(vPos, g_mBones[vIndex[i]]) * vWeight[i];
	}

    Output.Position = mul(vVert, g_mLight[0]);
    Output.Depth = Output.Position.zw;
	Output.TextureUV = vTexUV;

    return Output;
}

PS_OUTPUT ShadowOutputPS( VS_OUTPUT_SHADOW In ) 
{ 
    PS_OUTPUT Output;
	Output.RGBColor.rgb = In.Depth.x / In.Depth.y;
	Output.RGBColor.a = tex2D(OpacitySampler, In.TextureUV).a;
    return Output;
}

technique ShadowOutput
{
    pass P0
    {
		AlphaTestEnable = TRUE;
		AlphaRef = 8;
		AlphaFunc = GREATER;
		AlphaBlendEnable = FALSE;
        VertexShader = compile vs_3_0 ShadowOutputVS();
        PixelShader  = compile ps_3_0 ShadowOutputPS(); 
    }
}

struct VS_OUTPUT_NORMAL
{
    float4 Position : POSITION;
	float2 TextureUV  : TEXCOORD0;
	float Z : TEXCOORD1;
	float3 Normal : TEXCOORD3;
};

VS_OUTPUT_NORMAL NormalOutputVS( float4 vPos : POSITION, 
                         float3 vNormal : NORMAL,
                         float2 vTexUV : TEXCOORD0,
						 int4 vWeight : BLENDWEIGHT,
						 float4 vIndex : BLENDINDICES)
{
    VS_OUTPUT_NORMAL Output;
    
	float4 vVert = float4(0, 0, 0, 0);
	float3 vNorm = float3(0, 0, 0);
	for(int i = 0; i < 4; ++i)
	{
		if(vWeight[i] == 0) continue;
		vVert += mul(vPos, g_mBones[vIndex[i]]) * vWeight[i];
		vNorm += mul(vNormal, (float3x3)g_mBones[vIndex[i]]) * vWeight[i];
	}

    Output.Position = mul(vVert, g_mWorldViewProjection);
    
    float3 vWVPos = mul(vVert, g_mWorldView).xyz;
    float3 vWVNormal = normalize(mul(vNorm, (float3x3)g_mWorldView));

	Output.Normal = vWVNormal;
	Output.TextureUV = vTexUV;
	Output.Z = Output.Position.z / Output.Position.w;
    
    return Output;
}

PS_OUTPUT NormalOutputPS( VS_OUTPUT_NORMAL In ) 
{ 
    PS_OUTPUT Output;
	float3 normal = normalize(In.Normal);
	Output.RGBColor.rgb = normal;
	Output.RGBColor.a = tex2D(OpacitySampler, In.TextureUV).a;
	if(Output.RGBColor.a > 8/255.f)
	{
		Output.RGBColor.a = In.Z;
	}
    return Output;
}

technique NormalOutput
{
    pass P0
    {
		AlphaTestEnable = TRUE;
		AlphaRef = 8;
		AlphaFunc = GREATER;
		AlphaBlendEnable = FALSE;
        VertexShader = compile vs_3_0 NormalOutputVS();
        PixelShader  = compile ps_3_0 NormalOutputPS(); 
    }
}

struct VS_OUTPUT_VEL
{
    float4 Position : POSITION;
	float2 TextureUV  : TEXCOORD0;
	float3 Vel  : TEXCOORD1;
};

VS_OUTPUT_VEL VelocityOutputVS( float4 vPos : POSITION,
							float2 vTexUV : TEXCOORD0,
						 int4 vWeight : BLENDWEIGHT,
						 float4 vIndex : BLENDINDICES)
{
    VS_OUTPUT_VEL Output;
    
	float4 vVert = float4(0, 0, 0, 0);
	for(int i = 0; i < 4; ++i)
	{
		if(vWeight[i] == 0) continue;
		vVert += mul(vPos, g_mBones[vIndex[i]]) * vWeight[i];
	}

    float4 now = mul(vVert, g_mWorldViewProjection);
	float4 next = mul(vVert, g_mWorldViewProjectionNext);
	Output.Position = now;
	Output.TextureUV = vTexUV;
	
	now /= now.w;
	next /= next.w;
	Output.Vel.xy = next - now;
	Output.Vel.xy /= 2;
	Output.Vel.z = now.z;
    return Output;
}

PS_OUTPUT VelocityOutputPS( VS_OUTPUT_VEL In ) 
{ 
    PS_OUTPUT Output;
	Output.RGBColor.rgb = In.Vel;
	Output.RGBColor.a = tex2D(OpacitySampler, In.TextureUV).a;
	if(Output.RGBColor.a > 8/255.f)
	{
		Output.RGBColor.a = In.Vel.z;
	}
    return Output;
}

technique VelocityOutput
{
    pass P0
    {
		AlphaTestEnable = TRUE;
		AlphaRef = 8;
		AlphaFunc = GREATER;
		AlphaBlendEnable = TRUE;
        VertexShader = compile vs_3_0 VelocityOutputVS();
        PixelShader  = compile ps_3_0 VelocityOutputPS(); 
    }
}

struct VS_OUTPUT_NORMALVEL
{
    float4 Position : POSITION;
	float2 TextureUV  : TEXCOORD0;
	float3 Vel  : TEXCOORD1;
	float Z : TEXCOORD2;
	float3 Normal : TEXCOORD3;
};

VS_OUTPUT_NORMALVEL NormalVelOutputVS( float4 vPos : POSITION, 
                         float3 vNormal : NORMAL,
                         float2 vTexUV : TEXCOORD0,
						 int4 vWeight : BLENDWEIGHT,
						 float4 vIndex : BLENDINDICES)
{
    VS_OUTPUT_NORMALVEL Output;
    
	float4 vVert = float4(0, 0, 0, 0);
	float3 vNorm = float3(0, 0, 0);
	for(int i = 0; i < 4; ++i)
	{
		if(vWeight[i] == 0) continue;
		vVert += mul(vPos, g_mBones[vIndex[i]]) * vWeight[i];
		vNorm += mul(vNormal, (float3x3)g_mBones[vIndex[i]]) * vWeight[i];
	}

    float4 now = mul(vVert, g_mWorldViewProjection);
	float4 next = mul(vVert, g_mWorldViewProjectionNext);
	Output.Position = now;
	Output.TextureUV = vTexUV;
	
	now /= now.w;
	next /= next.w;
	Output.Vel.xy = next - now;
	Output.Vel.xy /= 2;
	Output.Vel.z = now.z;

    float3 vWVPos = mul(vVert, g_mWorldView).xyz;
    float3 vWVNormal = normalize(mul(vNorm, (float3x3)g_mWorldView));

	Output.Normal = vWVNormal;
	Output.TextureUV = vTexUV;
	Output.Z = Output.Position.z / Output.Position.w;
    
    return Output;
}

struct PS_OUTPUT_NORMALVEL
{
    float4 Normal : COLOR0;
	float4 Velocity : COLOR1;
};

PS_OUTPUT_NORMALVEL NormalVelOutputPS( VS_OUTPUT_NORMALVEL In ) 
{ 
    PS_OUTPUT_NORMALVEL Output;
	float3 normal = normalize(In.Normal);
	Output.Normal.rgb = normal;
	float alpha = tex2D(OpacitySampler, In.TextureUV).a;
	if(alpha > 8/255.f)
	{
		Output.Normal.a = In.Z;
		Output.Velocity.a = In.Z;
	}
	else
	{
		Output.Normal.a = 0;
		Output.Velocity.a = 0;
	}

	Output.Velocity.rgb = In.Vel;
    return Output;
}

technique NormalVelOutput
{
    pass P0
    {
		AlphaTestEnable = TRUE;
		AlphaRef = 8;
		AlphaFunc = GREATER;
		AlphaBlendEnable = TRUE;
        VertexShader = compile vs_3_0 NormalVelOutputVS();
        PixelShader  = compile ps_3_0 NormalVelOutputPS(); 
    }
}