/*--------------------------------*\
      Gen2 Shader
\*--------------------------------*/

static const int MAX_BONE_COUNT = 80;
static const float4 AMBIENT_LIGHT = float4(0.4, 0.4, 0.4, 1);

uniform int VS_Index;
uniform int PS_Index;

uniform extern float4x3 g_W;
uniform extern float4x4 g_WVP;
uniform extern float4x4 g_WVPT;
uniform extern float4x4 g_ProjWVPT;
uniform extern float4x3 g_SkinPallete[MAX_BONE_COUNT];

uniform extern float4 g_DepthDir;
uniform extern float g_DepthMaxRcp;
uniform extern float2 g_DepthRes;
uniform extern float2 g_DepthResRcp;
uniform extern float g_DepthBias = 0;

uniform extern float3 g_FactionColor = float3(0, 0, 0);

texture TexDiffuse0;
texture TexDiffuse1;
texture TexDiffuse2;
texture TexDiffuse3;
texture TexMask0;
texture TexDepth;
texture TexProj;

sampler SampDiffuse0 = sampler_state {
    Texture = <TexDiffuse0>;
    MinFilter = Linear;  
    MagFilter = Linear;
    MipFilter = Linear;
    AddressU  = Wrap;
    AddressV  = Wrap;
};

sampler SampDiffuse1 = sampler_state {
    Texture = <TexDiffuse1>;
    MinFilter = Linear;  
    MagFilter = Linear;
    MipFilter = Linear;
    AddressU  = Wrap;
    AddressV  = Wrap;
};

sampler SampDiffuse2 = sampler_state {
    Texture = <TexDiffuse2>;
    MinFilter = Linear;  
    MagFilter = Linear;
    MipFilter = Linear;
    AddressU  = Wrap;
    AddressV  = Wrap;
};

sampler SampDiffuse3 = sampler_state {
    Texture = <TexDiffuse3>;
    MinFilter = Linear;  
    MagFilter = Linear;
    MipFilter = Linear;
    AddressU  = Wrap;
    AddressV  = Wrap;
};

sampler SampMask0 = sampler_state {
    Texture = <TexMask0>;
    MinFilter = Linear;  
    MagFilter = Linear;
    MipFilter = Linear;
    AddressU  = Clamp;
    AddressV  = Clamp;
};

sampler SampDepth = sampler_state {
    Texture = <TexDepth>;
    MinFilter = Linear;  
    MagFilter = Linear;
    MipFilter = None;
    AddressU  = Border;
    AddressV  = Border;
    BorderColor = 0xffffffff;
};

sampler SampProj = sampler_state {
    Texture = <TexProj>;
    MinFilter = Linear;  
    MagFilter = Linear;
    MipFilter = None;
    AddressU  = Border;
    AddressV  = Border;
    BorderColor = 0;
};

float4 Lerp2(
  const in float4 v0, 
  const in float4 v1, 
  const float s1
) {
  return (v0 + (v1 - v0) * s1);
}

float PCF(
  float Depth,
  float2 TexCoord,
  sampler Samp
) {
  float2 TexCoordKernel[2][2];
  int DepthKernel[2][2];
  float2 tc = TexCoord;
  float2 tci = floor(tc * g_DepthRes) * g_DepthResRcp;
  TexCoordKernel[0][0] = tci;
  TexCoordKernel[1][0] = tci + float2(g_DepthResRcp.x, 0);
  TexCoordKernel[0][1] = tci + float2(0, g_DepthResRcp.y);
  TexCoordKernel[1][1] = tci + float2(g_DepthResRcp.x, g_DepthResRcp.y);
  for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 2; j++) {
      DepthKernel[i][j] = (tex2D(Samp, TexCoordKernel[i][j]).x - 0.0001 > Depth);
    }
  }
  float2 fc = frac(tc * g_DepthRes);
  float sx0 = Lerp2(DepthKernel[0][0], DepthKernel[1][0], fc.x);
  float sx1 = Lerp2(DepthKernel[0][1], DepthKernel[1][1], fc.x);
  return Lerp2(sx0, sx1, fc.y);
}

float VSM(
  float Depth,
  float2 DepthTex
) {
  float p = (Depth <= DepthTex.x);
  float Variance = DepthTex.y - DepthTex.x * DepthTex.x;
  Variance = max(Variance, 0.00001);
  float d = Depth + g_DepthBias - DepthTex.x;
  float p_max = Variance / (Variance + d * d);  
  return max(p, smoothstep(0, 1, p_max));
}

float DoShadow(
  float Depth,
  float DepthTex
) {
  float p = (Depth <= DepthTex);
  float s = saturate((Depth - DepthTex) / 0.0003);
  return max(p, lerp(1, p, s));
}

void VS_General_Geom (
  const in float3 InPosition0: Position0,
	const in float3 InTangent0: Tangent0,
	const in float3 InBinormal0: Binormal0,
	const in float3 InNormal0: Normal0,
	const in float2 InTexCoord0: TexCoord0,
  const in float4 InColor0: Color0, 
  out float4 OutPosition0: Position0,
  out float3 OutNormal0: TexCoord0,
	out float2 OutTexCoord0: TexCoord1,
  out float4 OutTexCoordDepth: TexCoord2,
  out float OutDepth: TexCoord3,
  out float4 OutTexCoordProj: TexCoord4,
  out float4 OutColor0: Color0
) {
	OutPosition0 = mul(float4(InPosition0, 1), g_WVP); 
  OutNormal0 = mul(InNormal0, (float3x3)g_W);
	OutTexCoord0 = InTexCoord0;
  OutTexCoordDepth = mul(float4(InPosition0, 1), g_WVPT);
  OutTexCoordProj = mul(float4(InPosition0, 1), g_ProjWVPT);
  OutDepth = saturate((dot(g_DepthDir.xyz, mul(float4(InPosition0, 1), (float4x3)g_W).xyz) - g_DepthDir.w) * g_DepthMaxRcp + g_DepthBias);
  OutColor0 = InColor0;
}

void PS_General_Geom_1 (
  const in float3 InNormal0: TexCoord0,
  const in float2 InTexCoord0: TexCoord1,
  const in float4 InTexCoordDepth: TexCoord2,
  const in float InDepth: TexCoord3,
  const in float4 InTexCoordProj: TexCoord4,
  const in float4 InColor0: Color0,
  out float4 OutColor0: Color0
) {
  //float Shadow = VSM(InDepth, tex2Dproj(SampDepth, InTexCoordDepth).xy) * saturate(dot(-g_DepthDir.xyz, normalize(InNormal0)) * 10) + AMBIENT_LIGHT;
  float Shadow = DoShadow(InDepth, tex2Dproj(SampDepth, InTexCoordDepth).x) * saturate(dot(-g_DepthDir.xyz, normalize(InNormal0)) * 10) + AMBIENT_LIGHT;
  float4 c = tex2D(SampDiffuse0, InTexCoord0);
  float4 Proj = tex2Dproj(SampProj, InTexCoordProj);
  c.xyz = lerp(c.xyz, Proj.xyz, Proj.w);
  OutColor0 = float4((float3)c * Shadow, c.w);
}

void PS_General_Geom_2 (
  const in float3 InNormal0: TexCoord0,
  const in float2 InTexCoord0: TexCoord1,  
  const in float4 InTexCoordDepth: TexCoord2,
  const in float InDepth: TexCoord3,
  const in float4 InTexCoordProj: TexCoord4,
  const in float4 InColor0: Color0,
  out float4 OutColor0: Color0
) {
  //float Shadow = VSM(InDepth, tex2Dproj(SampDepth, InTexCoordDepth).xy) * saturate(dot(-g_DepthDir.xyz, normalize(InNormal0)) * 10) + AMBIENT_LIGHT;
  float Shadow = DoShadow(InDepth, tex2Dproj(SampDepth, InTexCoordDepth).x) * saturate(dot(-g_DepthDir.xyz, normalize(InNormal0)) * 10) + AMBIENT_LIGHT;
	float4 t0 = tex2D(SampDiffuse0, InTexCoord0);
  float4 t1 = tex2D(SampDiffuse1, InTexCoord0);
  float4 c = (t0 + (t1 - t0) * InColor0.r);
  float4 Proj = tex2Dproj(SampProj, InTexCoordProj);
  c.xyz = lerp(c.xyz, Proj.xyz, Proj.w);
  OutColor0 = float4((float3)c * Shadow, c.w);
}    

void PS_General_Geom_3 (
  const in float3 InNormal0: TexCoord0,
  const in float2 InTexCoord0: TexCoord1,
  const in float4 InTexCoordDepth: TexCoord2,
  const in float InDepth: TexCoord3,
  const in float4 InTexCoordProj: TexCoord4,
  const in float4 InColor0: Color0,
  out float4 OutColor0: Color0
) {
  //float Shadow = VSM(InDepth, tex2Dproj(SampDepth, InTexCoordDepth).xy) * saturate(dot(-g_DepthDir.xyz, normalize(InNormal0)) * 10) + AMBIENT_LIGHT;
  float Shadow = DoShadow(InDepth, tex2Dproj(SampDepth, InTexCoordDepth).x) * saturate(dot(-g_DepthDir.xyz, normalize(InNormal0)) * 10) + AMBIENT_LIGHT;
	float4 t0 = tex2D(SampDiffuse0, InTexCoord0);
  float4 t1 = tex2D(SampDiffuse1, InTexCoord0);
  float4 t2 = tex2D(SampDiffuse2, InTexCoord0);
  float4 ct = (t0 + (t1 - t0) * InColor0.r);
  float4 c = (ct + (t2 - ct) * InColor0.g);
  float4 Proj = tex2Dproj(SampProj, InTexCoordProj);
  c.xyz = lerp(c.xyz, Proj.xyz, Proj.w);
  OutColor0 = float4((float3)c * Shadow, c.w);
}

void PS_General_Geom_4 (
  const in float3 InNormal0: TexCoord0,
  const in float2 InTexCoord0: TexCoord1,   
  const in float4 InTexCoordDepth: TexCoord2,
  const in float InDepth: TexCoord3,
  const in float4 InTexCoordProj: TexCoord4,
  const in float4 InColor0: Color0,
  out float4 OutColor0: Color0
) {
  //float Shadow = VSM(InDepth, tex2Dproj(SampDepth, InTexCoordDepth).xy) * saturate(dot(-g_DepthDir.xyz, normalize(InNormal0)) * 10) + AMBIENT_LIGHT;
  float Shadow = DoShadow(InDepth, tex2Dproj(SampDepth, InTexCoordDepth).x) * saturate(dot(-g_DepthDir.xyz, normalize(InNormal0)) * 10) + AMBIENT_LIGHT;
	float4 t0 = tex2D(SampDiffuse0, InTexCoord0);
  float4 t1 = tex2D(SampDiffuse1, InTexCoord0);
  float4 t2 = tex2D(SampDiffuse2, InTexCoord0);
  float4 t3 = tex2D(SampDiffuse3, InTexCoord0);
  float4 ct = (t0 + (t1 - t0) * InColor0.r);
  ct = (ct + (t2 - ct) * InColor0.g);
  float4 c = (ct + (t3 - ct) * InColor0.b);
  float4 Proj = tex2Dproj(SampProj, InTexCoordProj);
  c.xyz = lerp(c.xyz, Proj.xyz, Proj.w);
  OutColor0 = float4((float3)c * Shadow, c.w);
}

void VS_General_Char_0 (
  const in float3 InPosition0: Position0,
	const in float3 InTangent0: Tangent0,
	const in float3 InBinormal0: Binormal0,
	const in float3 InNormal0: Normal0,
	const in float2 InTexCoord0: TexCoord0,
  out float4 OutPosition0: Position0,
	out float2 OutTexCoord: TexCoord0,
	out float4 OutTexCoordDepth: TexCoord1,
  out float OutDepth: TexCoord2,
	out float3 OutNormalW: TexCoord3
) {
	OutPosition0 = mul(float4(InPosition0, 1), g_WVP);
	OutNormalW = mul(InNormal0, (float3x3)g_W);
	OutTexCoord = InTexCoord0;
  OutTexCoordDepth = mul(float4(InPosition0, 1), g_WVPT);
  OutDepth = saturate((dot(g_DepthDir.xyz, mul(float4(InPosition0, 1), (float4x3)g_W).xyz) - g_DepthDir.w) * g_DepthMaxRcp + g_DepthBias);
}

void VS_General_Char_1 (
  const in float3 InPosition0: Position0,
	const in float3 InTangent0: Tangent0,
	const in float3 InBinormal0: Binormal0,
	const in float3 InNormal0: Normal0,
	const in float2 InTexCoord0: TexCoord0,
  const in int InBIndices0: BlendIndices0,
  out float4 OutPosition0: Position0,
	out float2 OutTexCoord: TexCoord0,
	out float4 OutTexCoordDepth: TexCoord1,
  out float OutDepth: TexCoord2,
	out float3 OutNormalW: TexCoord3
) {
	float4x3 S = g_SkinPallete[InBIndices0];
	float4x3 SW = mul(S, g_W);
	float3 PositionS = mul(float4(InPosition0, 1), S);
	OutPosition0 = mul(float4(PositionS, 1), g_WVP);
	OutNormalW = mul(InNormal0, (float3x3)SW);
	OutTexCoord = InTexCoord0;
  OutTexCoordDepth = mul(float4(PositionS, 1), g_WVPT);
  OutDepth = saturate((dot(g_DepthDir.xyz, mul(float4(PositionS, 1), (float4x3)g_W).xyz) - g_DepthDir.w) * g_DepthMaxRcp + g_DepthBias);
}

void VS_General_Char_2 (
  const in float3 InPosition0: Position0,
	const in float3 InTangent0: Tangent0,
	const in float3 InBinormal0: Binormal0,
	const in float3 InNormal0: Normal0,
	const in float2 InTexCoord0: TexCoord0,
  const in int2 InBIndices0: BlendIndices0,
  const in float2 InBWeights0: BlendWeight0,
  out float4 OutPosition0: Position0,
	out float2 OutTexCoord: TexCoord0,
	out float4 OutTexCoordDepth: TexCoord1,
  out float OutDepth: TexCoord2,
	out float3 OutNormalW: TexCoord3
) {
	float4x3 S = (
		g_SkinPallete[InBIndices0[0]] * InBWeights0[0] +
		g_SkinPallete[InBIndices0[1]] * InBWeights0[1]
	);
	float4x3 SW = mul(S, g_W);
	float3 PositionS = mul(float4(InPosition0, 1), S);
	OutPosition0 = mul(float4(PositionS, 1), g_WVP);
	OutNormalW = mul(InNormal0, (float3x3)SW);
	OutTexCoord = InTexCoord0;
  OutTexCoordDepth = mul(float4(PositionS, 1), g_WVPT);
  OutDepth = saturate((dot(g_DepthDir.xyz, mul(float4(PositionS, 1), (float4x3)g_W).xyz) - g_DepthDir.w) * g_DepthMaxRcp + g_DepthBias);
}

void VS_General_Char_3 (
  const in float3 InPosition0: Position0,
	const in float3 InTangent0: Tangent0,
	const in float3 InBinormal0: Binormal0,
	const in float3 InNormal0: Normal0,
	const in float2 InTexCoord0: TexCoord0,
  const in int3 InBIndices0: BlendIndices0,
  const in float3 InBWeights0: BlendWeight0,
  out float4 OutPosition0: Position0,
	out float2 OutTexCoord: TexCoord0,
	out float4 OutTexCoordDepth: TexCoord1,
  out float OutDepth: TexCoord2,
	out float3 OutNormalW: TexCoord3
) {
	float4x3 S = (
		g_SkinPallete[InBIndices0[0]] * InBWeights0[0] +
		g_SkinPallete[InBIndices0[1]] * InBWeights0[1] +
		g_SkinPallete[InBIndices0[2]] * InBWeights0[2]
	);
	float4x3 SW = mul(S, g_W);
	float3 PositionS = mul(float4(InPosition0, 1), S);
	OutPosition0 = mul(float4(PositionS, 1), g_WVP);
	OutNormalW = mul(InNormal0, (float3x3)SW);
	OutTexCoord = InTexCoord0;
  OutTexCoordDepth = mul(float4(PositionS, 1), g_WVPT);
  OutDepth = saturate((dot(g_DepthDir.xyz, mul(float4(PositionS, 1), (float4x3)g_W).xyz) - g_DepthDir.w) * g_DepthMaxRcp + g_DepthBias);
}

void VS_General_Char_4 (
  const in float3 InPosition0: Position0,
	const in float3 InTangent0: Tangent0,
	const in float3 InBinormal0: Binormal0,
	const in float3 InNormal0: Normal0,
	const in float2 InTexCoord0: TexCoord0,
  const in int4 InBIndices0: BlendIndices0,
  const in float4 InBWeights0: BlendWeight0,
  out float4 OutPosition0: Position0,
	out float2 OutTexCoord: TexCoord0,
	out float4 OutTexCoordDepth: TexCoord1,
  out float OutDepth: TexCoord2,
	out float3 OutNormalW: TexCoord3
) {
	float4x3 S = (
		g_SkinPallete[InBIndices0[0]] * InBWeights0[0] +
		g_SkinPallete[InBIndices0[1]] * InBWeights0[1] +
		g_SkinPallete[InBIndices0[2]] * InBWeights0[2] +
		g_SkinPallete[InBIndices0[3]] * InBWeights0[3]
	);
	float4x3 SW = mul(S, g_W);
	float3 PositionS = mul(float4(InPosition0, 1), S);
	OutPosition0 = mul(float4(PositionS, 1), g_WVP);
	OutNormalW = mul(InNormal0, (float3x3)SW);
	OutTexCoord = InTexCoord0;
  OutTexCoordDepth = mul(float4(PositionS, 1), g_WVPT);
  OutDepth = saturate((dot(g_DepthDir.xyz, mul(float4(PositionS, 1), (float4x3)g_W).xyz) - g_DepthDir.w) * g_DepthMaxRcp + g_DepthBias);
}

void PS_General_Char (
	const in float2 InTexCoord: TexCoord0,
	const in float4 InTexCoordDepth: TexCoord1,
  const in float InDepth: TexCoord2,
	const in float3 InNormalW: TexCoord3,
  out float4 OutColor0: Color0
) {
	//float Shadow = VSM(InDepth, tex2Dproj(SampDepth, InTexCoordDepth).xy) * saturate(dot(-g_DepthDir.xyz, normalize(InNormalW)) * 10) + AMBIENT_LIGHT;
  float Shadow = DoShadow(InDepth, tex2Dproj(SampDepth, InTexCoordDepth).x) * saturate(dot(-g_DepthDir.xyz, normalize(InNormalW)) * 10) + AMBIENT_LIGHT;
  float4 c = tex2D(SampDiffuse0, InTexCoord);
  float m = tex2D(SampMask0, InTexCoord).x;
  c.xyz = lerp(c.xyz, g_FactionColor, m);
  OutColor0 = float4((float3)c * Shadow, c.w);
}

VertexShader VS_General_Arr[6] = {
	compile vs_2_0 VS_General_Geom(),
  compile vs_2_0 VS_General_Char_0(),
  compile vs_2_0 VS_General_Char_1(),
  compile vs_2_0 VS_General_Char_2(),
  compile vs_2_0 VS_General_Char_3(),
  compile vs_2_0 VS_General_Char_4()
};

PixelShader PS_General_Arr[5] = {
	compile ps_2_0 PS_General_Geom_1(),
  compile ps_2_0 PS_General_Geom_2(),
  compile ps_2_0 PS_General_Geom_3(),
  compile ps_2_0 PS_General_Geom_4(),
  compile ps_2_0 PS_General_Char()
};

technique General {
  pass P0 {
    VertexShader = VS_General_Arr[VS_Index];
    PixelShader = PS_General_Arr[PS_Index];
  }
}