
float4 g_DiffuseColor;
float g_OpacityLevel;
float g_SpecularLevel;
float3 g_EyePosView;

float3 g_LightDirView;
float3 g_LightPosView;
float3 g_LightColor;
float g_LightDecayStart;
float g_LightRange;
float g_LightCosTheta;
float g_LightCosPhi;
float3 g_AmbientColor;

float4x4 g_mWorld;
float4x4 g_mWorldView;
float4x4 g_mWorldViewIT;
float4x4 g_mWorldViewProjection;
float4x4 g_mWorldViewProjectionNext;
float4x4 g_mProjectionI;

float g_FarPlane;

float4x4 g_mBones[32];


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 MRTDiffuse;
sampler MRTDiffuseSampler =
sampler_state 
{
    texture = <MRTDiffuse>;
    AddressU  = CLAMP;        
    AddressV  = CLAMP;
    MINFILTER = POINT;
    MAGFILTER = POINT;
    MIPFILTER = NONE;
};

texture MRTNormal;
sampler MRTNormalSampler =
sampler_state 
{
    texture = <MRTNormal>;
    AddressU  = CLAMP;        
    AddressV  = CLAMP;
    MINFILTER = POINT;
    MAGFILTER = POINT;
    MIPFILTER = NONE;
};

#if PP >= 2

texture MRTVelocity;
sampler MRTVelocitySampler =
sampler_state 
{
    texture = <MRTVelocity>;
    AddressU  = CLAMP;        
    AddressV  = CLAMP;
    MINFILTER = POINT;
    MAGFILTER = POINT;
    MIPFILTER = NONE;
};

#endif

texture MRTDepth;
sampler MRTDepthSampler =
sampler_state 
{
    texture = <MRTDepth>;
    AddressU  = CLAMP;        
    AddressV  = CLAMP;
    MINFILTER = POINT;
    MAGFILTER = POINT;
    MIPFILTER = NONE;
};

texture MRTLight;
sampler MRTLightSampler =
sampler_state 
{
    texture = <MRTLight>;
    AddressU  = CLAMP;        
    AddressV  = CLAMP;
    MINFILTER = POINT;
    MAGFILTER = POINT;
    MIPFILTER = NONE;
};

#if SHADOW > 0

float4x4 g_mShadowMat;

texture ShadowMap;
sampler ShadowSampler =
sampler_state 
{
    texture = <ShadowMap>;
    AddressU  = CLAMP;        
    AddressV  = CLAMP;
    MINFILTER = LINEAR;
    MAGFILTER = LINEAR;
    MIPFILTER = NONE;
};

#endif

float g_Width, g_Height;

texture MorphMap;
sampler MorphSampler =
sampler_state 
{
    texture = <MorphMap>;
    AddressU  = WRAP;        
    AddressV  = WRAP;
    MINFILTER = POINT;
    MAGFILTER = POINT;
    MIPFILTER = NONE;
};

texture MorphNormalMap;
sampler MorphNormalSampler =
sampler_state 
{
    texture = <MorphNormalMap>;
    AddressU  = WRAP;        
    AddressV  = WRAP;
    MINFILTER = POINT;
    MAGFILTER = POINT;
    MIPFILTER = NONE;
};

const float g_MorphMapSize = 4096.f;
float g_Offset;

texture TileMap;
sampler TileMapSampler =
sampler_state
{
	texture = <TileMap>;
	MINFILTER = POINT;
    MAGFILTER = POINT;
    MIPFILTER = NONE;
};

struct VS_OUTPUT0 {
    float4 Position : POSITION; //in projection space
	float3 Normal : NORMAL;
    float2 TexCoord0 : TEXCOORD0;
	float2 Velocity : TEXCOORD1;
    float4 Position2 : TEXCOORD2;
};

struct VS_OUTPUT_TAN {
    float4 Position : POSITION; //in projection space
	float3 Normal : NORMAL;
	float3 Tangent : TANGENT;
    float2 TexCoord0 : TEXCOORD0;
	float2 Velocity : TEXCOORD1;
    float4 Position2 : TEXCOORD2;
};

struct PS_MRT_OUTPUT {
	float4 Color0 : COLOR0;
	float4 Color1 : COLOR1;
	float4 Color2 : COLOR2;
#if PP >= 2
	float4 Color3 : COLOR3;
#endif
};

struct VS_OUTPUT1 {
    float4 Position   : POSITION;
    float4 TexCoord   : TEXCOORD0;
    float4 Position2  : TEXCOORD1;
};

half4 PackNormal(half3 n)
{
    /*half scale = 1.7777;
    half2 enc = n.xy / (n.z+1);
    enc /= scale;
    enc = enc*0.5+0.5;
    return half4(enc,0,0);*/
	/*return half4(n.xy, 0, 0);*/
	return half4(n*0.5 + 0.5, 0);
}

half3 UnpackNormal(half4 enc)
{
    /*half scale = 1.7777;
    half3 nn =
        enc.xyz*half3(2*scale,2*scale,0) +
        half3(-scale,-scale,1);
    half g = 2.0 / dot(nn.xyz,nn.xyz);
    half3 n;
    n.xy = g*nn.xy;
    n.z = g-1;
    return n;*/
	/*half z = sqrt(1 - dot(enc.xy, enc.xy));
	return half3(enc.xy, z);*/
	return normalize(enc.xyz*2 - 1);
}

float GetBlur2(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 GetBlur3(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;
}

float GetBlur4(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 * .75;
	s += tex2Dproj(samp, uv + float4(it, it, 0, 0)).r * .75;
	s += tex2Dproj(samp, uv + float4(-it, it, 0, 0)).r * .75;
	s += tex2Dproj(samp, uv + float4(-it, -it, 0, 0)).r * .75;
	s += tex2Dproj(samp, uv + float4(2*it, 0, 0, 0)).r * 0.5;
	s += tex2Dproj(samp, uv + float4(0, 2*it, 0, 0)).r * 0.5;
	s += tex2Dproj(samp, uv + float4(-2*it, 0, 0, 0)).r * 0.5;
	s += tex2Dproj(samp, uv + float4(0, -2*it, 0, 0)).r * 0.5;
	return s / 10;
}

VS_OUTPUT0 SimpleVS(
		float4 vPos : POSITION, //in object space
		float3 vNormal   : NORMAL, //in object space
		float2 vTexUV : TEXCOORD0,
		int4 vWeight : BLENDWEIGHT,
		float4 vIndex : BLENDINDICES)
{
    VS_OUTPUT0 Out;

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

    Out.Normal = normalize(mul(vNorm, (float3x3)g_mWorldViewIT));
    Out.Position = now;
	//Out.Position2 = now;
	Out.Position2.z = mul(now, g_mProjectionI).z / g_FarPlane;
	Out.Position2 = Out.Position2.zzzz;
	Out.TexCoord0 = vTexUV;

	now /= now.w;
	next /= next.w;
	Out.Velocity.xy = next - now;
	Out.Velocity.xy /= 2;

	return Out;
}

PS_MRT_OUTPUT SimplePS(VS_OUTPUT0 In)
{
	PS_MRT_OUTPUT Out;
	clip(tex2D(OpacitySampler, In.TexCoord0).a * g_OpacityLevel - 0.5);
	
	float4 diffuseTex = tex2D(DiffuseSampler, In.TexCoord0);

	Out.Color0 = float4(lerp(g_DiffuseColor.rgb, diffuseTex.rgb, diffuseTex.a), g_SpecularLevel);
	Out.Color1 = PackNormal(normalize(In.Normal));
	Out.Color2 = float4(In.Position2.z, 0, 0, 1);
#if PP >= 2
	Out.Color3 = float4(In.Velocity.xy, 1, 1);
#endif
	return Out;
}

technique CreateMRT
{
	pass P0
	{		
		AlphaBlendEnable	= false;
		AlphaTestEnable		= false;
		CullMode			= CW;

		VertexShader = compile vs_3_0 SimpleVS();
		PixelShader = compile ps_3_0 SimplePS();
	}
}

VS_OUTPUT0 InstancingVS(
		float4 vPos : POSITION, //in object space
		float3 vNormal   : NORMAL, //in object space
		float2 vTexUV : TEXCOORD0,
		int4 vWeight : BLENDWEIGHT,
		float4 vIndex : BLENDINDICES,
		float4 m1 : TEXCOORD4,
		float4 m2 : TEXCOORD5,
		float4 m3 : TEXCOORD6,
		float4 m4 : TEXCOORD7)
{
    VS_OUTPUT0 Out;

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

	float4x4 world = {m1, m2, m3, m4};
	vVert = mul(vVert, world);
	vNorm = mul(vNorm, (float3x3)world);

	float4 now = mul(vVert, g_mWorldViewProjection);
    float4 next = mul(vVert, g_mWorldViewProjectionNext);

    Out.Normal = normalize(mul(vNorm, (float3x3)g_mWorldViewIT));
    Out.Position = now;
	//Out.Position2 = now;
	Out.Position2.z = mul(now, g_mProjectionI).z / g_FarPlane;
	Out.Position2 = Out.Position2.zzzz;
	Out.TexCoord0 = vTexUV;

	now /= now.w;
	next /= next.w;
	Out.Velocity.xy = next - now;
	Out.Velocity.xy /= 2;

	return Out;
}

technique CreateMRTInstancing
{
	pass P0
	{		
		AlphaBlendEnable	= false;
		AlphaTestEnable		= false;
		CullMode			= CW;

		VertexShader = compile vs_3_0 InstancingVS();
		PixelShader = compile ps_3_0 SimplePS();
	}
}

VS_OUTPUT0 MorphVS(
		float4 vPos : POSITION, //in object space
		float3 vNormal   : NORMAL, //in object space
		float2 vTexUV : TEXCOORD0,
		int4 vWeight : BLENDWEIGHT,
		float4 vIndex : BLENDINDICES,
		float2 vMorphIdx : TEXCOORD1)
{
    VS_OUTPUT0 Out;

	if(vMorphIdx[0] >= 0)
	{
		vPos += tex1Dlod(MorphSampler, float4((vMorphIdx[0] + g_Offset) / g_MorphMapSize, 0, 0, 0));
		vNormal += tex1Dlod(MorphNormalSampler, float4((vMorphIdx[0] + g_Offset) / g_MorphMapSize, 0, 0, 0));
		vNormal = normalize(vNormal);
	}

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

    Out.Normal = normalize(mul(vNorm, (float3x3)g_mWorldViewIT));
    Out.Position = now;
	//Out.Position2 = now;
	Out.Position2.z = mul(now, g_mProjectionI).z / g_FarPlane;
	Out.Position2 = Out.Position2.zzzz;
	Out.TexCoord0 = vTexUV;

	now /= now.w;
	next /= next.w;
	Out.Velocity.xy = next - now;
	Out.Velocity.xy /= 2;

	return Out;
}


technique CreateMRTWithMorph
{
	pass P0
	{		
		AlphaBlendEnable	= false;
		AlphaTestEnable		= false;
		CullMode			= CW;

		VertexShader = compile vs_3_0 MorphVS();
		PixelShader = compile ps_3_0 SimplePS();
	}
}

VS_OUTPUT_TAN TileVS(
		float4 vPos : POSITION, //in object space
		float3 vNormal   : NORMAL, //in object space
		float3 vTangent    : TANGENT,
		float2 vTexUV : TEXCOORD0)
{
    VS_OUTPUT_TAN Out;

	float4 now = mul(vPos, g_mWorldViewProjection);
    float4 next = mul(vPos, g_mWorldViewProjectionNext);

    Out.Normal = normalize(mul(vNormal, (float3x3)g_mWorldViewIT));
	Out.Tangent = normalize(mul(vTangent, (float3x3)g_mWorldViewIT));
    Out.Position = now;
	Out.Position2.z = mul(now, g_mProjectionI).z / g_FarPlane;
	Out.Position2 = Out.Position2.zzzz;
	Out.TexCoord0 = vTexUV;

	now /= now.w;
	next /= next.w;
	Out.Velocity.xy = next - now;
	Out.Velocity.xy /= 2;

	return Out;
}

PS_MRT_OUTPUT TilePS(VS_OUTPUT_TAN In)
{
	PS_MRT_OUTPUT Out;
	
	int tileIdx = tex2D(TileMapSampler, float2(In.TexCoord0.x / g_Width, In.TexCoord0.y / g_Height)).a * 255;
	//float2 tileUV = (frac(In.TexCoord0.xy) + float2(int(tileIdx % 8), int(tileIdx / 8))) / 8.f;
	float2 tileUV = (frac(In.TexCoord0.xy)*0.94f + float2(0.03f, 0.03f) + float2(int(tileIdx % 8), int(tileIdx / 8))) / 8.f;

	clip(tex2D(OpacitySampler, tileUV).a * g_OpacityLevel - 0.5);
	float3 normalT = tex2D(OpacitySampler, tileUV).rgb * 2.f - 1.f;
	float3 binormal = cross(In.Normal, In.Tangent);

	float4 diffuseTex = tex2D(DiffuseSampler, tileUV);
	Out.Color0 = float4(lerp(g_DiffuseColor.rgb, diffuseTex.rgb, diffuseTex.a), g_SpecularLevel);
	Out.Color1 = PackNormal(normalize(In.Tangent*normalT.x + binormal*normalT.y + In.Normal*normalT.z));
	Out.Color2 = float4(In.Position2.z, 0, 0, 1);
#if PP >= 2
	Out.Color3 = float4(In.Velocity.xy, 1, 1);
#endif
	return Out;
}

technique CreateMRTTile
{
	pass P0
	{		
		AlphaBlendEnable	= false;
		AlphaTestEnable		= false;
		CullMode			= CW;

		VertexShader = compile vs_3_0 TileVS();
		PixelShader = compile ps_3_0 TilePS();
	}
}

VS_OUTPUT1 MRT_VS(
		float4 Position : POSITION //in object space
		)
{
    VS_OUTPUT1 Out;

    // transform position to projection space
    Out.Position = mul(Position, g_mWorldViewProjection);
    
	// projection position to far plane and transform back into view space
	Out.Position2 = mul(float4(Out.Position.xy / Out.Position.w, 1.f, 1.f) * g_FarPlane, g_mProjectionI);
    Out.Position2 *= Out.Position.w;

    // calc screen space texel address
	// (x, y) -> (x/2 + 0.5, -y/2 + 0.5) to unapply projection correction
	float2 xy = float2(Out.Position.x, -Out.Position.y);
	xy = (xy + Out.Position.ww)/2;
	Out.TexCoord = float4(xy, 0, Out.Position.w);
	return Out;
}

half cartoonsmooth(half a, half x)
{
	return smoothstep(a, 1 - a, x);
}

half cartoonsmooth2(half a, half x)
{
	return (smoothstep(a, 0.45 - a, x) 
		+ smoothstep(0.45 + a, 0.9 - a, x)) * 0.5;
}

half cartoonstep(int n, half bias, half x)
{
	return ceil(x*n + bias) / n;
}

half cartoonA(half x)
{
	return cartoonstep(3, 0.16, x);
}

half cartoonB(half x)
{
#if PP >= 2
	return cartoonsmooth2(0.2, x + 0.2);
#else
	return cartoonstep(3, 0.2, x);
#endif
}

half cartoonHL(half x)
{
	//return floor(x + 0.0125);
	return cartoonsmooth(0.4, pow(x, 64));
}

float4 ReconstructPosView(float3 screenPos, float depth)
{
	return float4(screenPos * depth, 1.f);
}

half4 MainDirLightingPS(VS_OUTPUT1 In) : COLOR
{
	half4 diffuseTex  = tex2Dproj(MRTDiffuseSampler, In.TexCoord);
	half4 normalTex   = tex2Dproj(MRTNormalSampler, In.TexCoord);
	float z = tex2Dproj(MRTDepthSampler, In.TexCoord);

	half3 normal = UnpackNormal(normalTex);

	float4 posView = ReconstructPosView(In.Position2.xyz / In.Position2.w, z);

#if SHADOW > 0
	float4 posLight = mul(float4(posView.xyz, 1), g_mShadowMat);
	posLight /= posLight.w;
	half shadow = GetBlur4(ShadowSampler, posLight, float2(1 / g_Width, 1 / g_Height));
#else
	const half shadow = 1;
#endif

	float3 viewDir = normalize(g_EyePosView - posView);
	float3 halfVec = normalize(normal + viewDir);

	half NdotL = max(0, dot(normal, g_LightDirView));
	half3 diffuse = (cartoonB(NdotL) * g_LightColor * shadow + g_AmbientColor) * diffuseTex.rgb;
#if PP >= 1
	half3 spec =  cartoonHL(max(0, dot(normal, halfVec))) * shadow * diffuseTex.a;
#else
	half3 spec = half3(0, 0, 0);
#endif
	return half4(diffuse + spec, 1.0f);
}

technique MainDirLighting
{
	pass P0
	{
		VertexShader = compile vs_3_0 MRT_VS();
		PixelShader = compile ps_3_0 MainDirLightingPS();
		
		AlphaTestEnable = false;
		ZEnable = false;
		CullMode = None;
		ZWriteEnable = false;
		FillMode = Solid;
	}
}

half4 DirLightingPS(VS_OUTPUT1 In) : COLOR
{
	half4 diffuseTex  = tex2Dproj(MRTDiffuseSampler, In.TexCoord);
	half4 normalTex   = tex2Dproj(MRTNormalSampler, In.TexCoord);
	float z = tex2Dproj(MRTDepthSampler, In.TexCoord);

	half3 normal = UnpackNormal(normalTex);

	float4 posView = ReconstructPosView(In.Position2.xyz / In.Position2.w, z);

	float3 viewDir = normalize(g_EyePosView - posView);
	float3 halfVec = normalize(normal + viewDir);

	half NdotL = max(0, dot(normal, g_LightDirView));
	half3 diffuse = cartoonB(NdotL) * g_LightColor * diffuseTex.rgb;
#if PP >= 1
	half3 spec =  cartoonHL(max(0, dot(normal, halfVec))) * diffuseTex.a;
#else
	half3 spec = half3(0, 0, 0);
#endif
	return half4(diffuse + spec, 1.0f);
}

technique DirLighting
{
	pass P0
	{
		VertexShader = compile vs_3_0 MRT_VS();
		PixelShader = compile ps_3_0 DirLightingPS();
		
		AlphaTestEnable = false;
		ZEnable = false;
		CullMode = None;
		ZWriteEnable = false;
		FillMode = Solid;
	}
}

half4 PointLightingPS(VS_OUTPUT1 In) : COLOR
{
	half4 diffuseTex  = tex2Dproj(MRTDiffuseSampler, In.TexCoord);
	half4 normalTex   = tex2Dproj(MRTNormalSampler, In.TexCoord);
	float z = tex2Dproj(MRTDepthSampler, In.TexCoord);

	half3 normal = UnpackNormal(normalTex);

	float4 posView = ReconstructPosView(In.Position2.xyz / In.Position2.w, z);
	
	float3 lightVec = (g_LightPosView - posView.xyz);
	float l = length(lightVec);
	lightVec /= l;

	half atten = min(1, g_LightDecayStart / l)
		* (1 - saturate((l - g_LightDecayStart) / (g_LightRange - g_LightDecayStart)));
	
	float3 viewDir = normalize(g_EyePosView - posView);
	float3 halfVec = normalize(normal + viewDir);
	
	half NdotL = max(0, dot(normal, lightVec));
	half3 diffuse = cartoonB(NdotL)  * atten * g_LightColor * diffuseTex.rgb;
#if PP >= 1
	half3 spec =  cartoonHL(max(0, dot(normal, halfVec))) * atten * diffuseTex.a;
#else
	half3 spec = half3(0, 0, 0);
#endif
	return half4(diffuse + spec, 1.0f);
}

technique PointLighting
{
	pass P0
	{
		VertexShader = compile vs_3_0 MRT_VS();
		PixelShader = compile ps_3_0 PointLightingPS();
		
		AlphaTestEnable = false;
		CullMode = CCW;
		ZEnable = false;
		ZFunc = Always;
		ZWriteEnable = false;
		FillMode = Solid;
	}
}

float4 SpotLightingPS(VS_OUTPUT1 In) : COLOR
{
	half4 diffuseTex  = tex2Dproj(MRTDiffuseSampler, In.TexCoord);
	half4 normalTex   = tex2Dproj(MRTNormalSampler, In.TexCoord);
	float z = tex2Dproj(MRTDepthSampler, In.TexCoord);

	half3 normal = UnpackNormal(normalTex);

	float4 posView = ReconstructPosView(In.Position2.xyz / In.Position2.w, z);
	
	float3 lightVec = (g_LightPosView - posView.xyz);
	float l = length(lightVec);
	lightVec /= l;

	half atten = 1 - saturate((l - g_LightDecayStart) / (g_LightRange - g_LightDecayStart));
	
	half angle = dot(g_LightDirView, -lightVec);

	half atten2 = min(1, (g_LightCosPhi - angle) / (g_LightCosPhi - g_LightCosTheta));

	float3 viewDir = normalize(g_EyePosView - posView);
	float3 halfVec = normalize(normal + viewDir);
	
	half NdotL = max(0, dot(normal, lightVec));
	half3 diffuse = cartoonB(NdotL)  * atten * atten2 * g_LightColor * diffuseTex.rgb;
#if PP >= 1
	half3 spec =  cartoonHL(max(0, dot(normal, halfVec))) * atten * atten2 * diffuseTex.a;
#else
	half3 spec = half3(0, 0, 0);
#endif
	return float4(diffuse + spec, 1.0f);
}

technique SpotLighting
{
	pass P0
	{
		VertexShader = compile vs_3_0 MRT_VS();
		PixelShader = compile ps_3_0 SpotLightingPS();
		
		AlphaTestEnable = false;
		CullMode = CCW;
		ZEnable = false;
		ZFunc = Always;
		ZWriteEnable = false;
		FillMode = Solid;
	}
}

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

VS_OUTPUT_SKINNED RenderSkinnedVS( float4 vPos : POSITION, 
                         float2 vTexUV : TEXCOORD0,
						 int4 vWeight : BLENDWEIGHT,
						 float4 vIndex : BLENDINDICES)
{
    VS_OUTPUT_SKINNED 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;
}

VS_OUTPUT_SKINNED RenderSkyboxVS( float4 vPos : POSITION, 
                         float2 vTexUV : TEXCOORD0)
{
    VS_OUTPUT_SKINNED Output;
    Output.Position = mul(vPos, g_mWorldViewProjection);
	Output.Position.z = Output.Position.w;
    Output.TextureUV = vTexUV;
    return Output;
}


float4 RenderSkyboxPS( VS_OUTPUT_SKINNED In ) : COLOR0
{ 
    float4 RGBColor;
	
	float3 diffuse = lerp(tex2D(DiffuseSampler, In.TextureUV).rgb, tex2D(OpacitySampler, In.TextureUV), g_OpacityLevel);
	RGBColor.rgb = diffuse;
	RGBColor.a = 1;
    return RGBColor;
}

technique RenderSkybox
{
    pass P0
    {      
		CullMode = CW;
		DestBlend = Zero;
		ZWriteEnable = true;
		ZEnable = true;
		ZFunc = LESSEQUAL;
        VertexShader = compile vs_3_0 RenderSkyboxVS();
        PixelShader  = compile ps_3_0 RenderSkyboxPS(); 
    }
}

float4 ShadowOutputPS( VS_OUTPUT_SKINNED In ) : COLOR
{ 
	clip(tex2D(OpacitySampler, In.TextureUV).a * g_OpacityLevel - 0.5);
	return float4(0, 0, 0, 0);
}

technique ShadowOutput
{
    pass P0
    {
		AlphaTestEnable = false;
		ZWriteEnable = true;
		ZEnable = true;
		ZFunc = LESSEQUAL;
        VertexShader = compile vs_3_0 RenderSkinnedVS();
        PixelShader  = compile ps_3_0 ShadowOutputPS(); 
    }
}

float4 FinalPS(VS_OUTPUT1 In) : COLOR
{
	half3 lightTex   = tex2D(MRTLightSampler, In.TexCoord).rgb;

	return float4(lightTex, 1.0f);
}

technique Final
{
	pass P0
	{
		ZWriteEnable = false;
		PixelShader = compile ps_3_0 FinalPS();
	}
}

struct VS_OUTPUT_ALPHA {
    float4 Position : POSITION; //in projection space
    float2 TexUV : TEXCOORD0;
    float3 Normal : TEXCOORD1;
};

VS_OUTPUT_ALPHA AlphaVS(
		float4 vPos : POSITION, //in object space
		float3 vNormal   : NORMAL, //in object space
		float2 vTexUV : TEXCOORD0,
		int4 vWeight : BLENDWEIGHT,
		float4 vIndex : BLENDINDICES)
{
    VS_OUTPUT_ALPHA Out;

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

    Out.Normal = normalize(mul(vNorm, (float3x3)g_mWorldViewIT));
    Out.Position = mul(vVert, g_mWorldViewProjection);
	Out.TexUV = vTexUV;
	return Out;
}

float4 AlphaPS(VS_OUTPUT_ALPHA In) : COLOR
{
	float4 Out;
	
	float4 diffuseTex = tex2D(DiffuseSampler, In.TexUV);
	float fMLight = max(0, dot(normalize(In.Normal), g_LightDirView));
	Out.rgb = (ceil(fMLight * 3 + 0.16) / 3 * g_LightColor + g_AmbientColor) * diffuseTex * g_DiffuseColor.rgb;
	Out.a = tex2D(OpacitySampler, In.TexUV).a * g_OpacityLevel;
	return Out;
}

technique Alpha
{
    pass P0
    {      
		CullMode = CW;
		AlphaBlendEnable = true;
		DestBlend = InvSrcAlpha;
		SrcBlend = SrcAlpha;
		ZWriteEnable = false;
		ZEnable = true;
		ZFunc = LESSEQUAL;
        VertexShader = compile vs_3_0 AlphaVS();
        PixelShader  = compile ps_3_0 AlphaPS(); 
    }
}