float4x4    matViewProj : VIEWPROJ;
float4x4	matView : VIEW;
//float4x4	matWorld;
float3      LightDir : LIGHTDIR;
float4      LightColor : LIGHTCOLOR;
float4      AmbientColor : AMBIENT;

float g_LambertScale : LAMBERTSCALE = 1;

#include "ShadowMapping.fx"

//#define DEPTHWORLD //define DEPTHWORLD it the linear depth in eyespace should be written, othervise usual depth value is used
//#define WRITEWORLD //define WRITEWORLD if the worldposition should be written to color3

#define WPT_DEPTH 0
#define WPT_LINEARDEPTH 1
#define WPT_WORLDPOS 2

struct VS_INPUT
{
    float3 Pos : POSITION;
	//float Height : TEXCOORD0;
	//float4 Color : COLOR0;
	float2 Normal : NORMAL;
};

struct VS_OUTPUT
{
	float4 Pos : POSITION;
	float4 Color : COLOR0;
	float Diffuse : TEXCOORD0;
	float2 Depth : TEXCOORD1;
	float4 SMTexC : TEXCOORD2;
};

struct VS_OUTPUTDS
{
    float4 Pos : POSITION;
	float4 Color : COLOR0;
	float3 Normal : TEXCOORD0;
	
	float3 DepthWorld : TEXCOORD1;
};

VS_OUTPUT VSTerrain(VS_INPUT i)
{
	VS_OUTPUT o;

    //float3 p = float3(i.Pos, i.Height);    
	//float4 wpos = mul(float4(p, 1), matWorld);
	//o.Pos = mul(wpos, matViewProj);
	
	o.Pos = mul(float4(i.Pos, 1), matViewProj);

	float2 nxy = i.Normal;
	float3 Normal = float3(nxy, sqrt(1 - nxy.x*nxy.x - nxy.y*nxy.y));
	//float3 Normal = i.Normal;
	float dotLN = dot(Normal, -LightDir); 
    o.Diffuse = 1 + (saturate(dotLN) - 1) * g_LambertScale;

	//height coloring
	float4 colors[6] ={	
		float4(148/255.f,142/255.f,50/255.f,1),
		float4(148/255.f,142/255.f,50/255.f,1),
		float4(48/255.f,142/255.f,50/255.f,1),
		float4(127/255.f,177/255.f,112/255.f,1),
		float4(151/255.f,146/255.f,145/255.f,1),
		float4(230/255.f,238/255.f,254/255.f,1)};

	static const float step = 40;
	static const float baseoff = 40;

	float colind = (i.Pos.z - baseoff) / step;

	if(colind <= 0)
	{
		o.Color = colors[0];
	}
	else if(colind >= 5)
	{
		o.Color = colors[5];
	}
	else
	{
		float s = frac(colind);
		o.Color = lerp(colors[(int)colind], colors[((int)colind) + 1], s);
	}
	//o.Color = i.Color;

	float slope = (1-dotLN);
    float bias = slope * SM_SlopeBias + SM_DetphBias;
    float4 swpos = float4(i.Pos + bias * -LightDir, 1);
    o.SMTexC = CalcSMTexC(swpos);
    o.Depth = CalcDepth(swpos);

	//o.SMTexC = CalcSMTexC(wpos);
    //o.Depth = CalcDepth(wpos);
    
    return o;
}

VS_OUTPUTDS VSTerrainDS(VS_INPUT i, uniform int wpt)
{
    VS_OUTPUTDS o;

    //float3 p = float3(i.Pos, i.Height);    
	//float4 wpos = mul(float4(p, 1), matWorld);
	//o.Pos = mul(wpos, matViewProj);

	o.Pos = mul(float4(i.Pos, 1), matViewProj);

	float2 nxy = i.Normal;
	o.Normal = float3(nxy, sqrt(1 - nxy.x*nxy.x - nxy.y*nxy.y));

	//height coloring
	float4 colors[6] ={	
		float4(148/255.f,142/255.f,50/255.f,1),
		float4(148/255.f,142/255.f,50/255.f,1),
		float4(48/255.f,142/255.f,50/255.f,1),
		float4(127/255.f,177/255.f,112/255.f,1),
		float4(151/255.f,146/255.f,145/255.f,1),
		float4(230/255.f,238/255.f,254/255.f,1)};

	static const float step = 40;
	static const float baseoff = 40;

	float colind = (i.Pos.z - baseoff) / step;

	if(colind <= 0)
	{
		o.Color = colors[0];
	}
	else if(colind >= 5)
	{
		o.Color = colors[5];
	}
	else
	{
		float s = frac(colind);
		o.Color = lerp(colors[(int)colind], colors[((int)colind) + 1], s);
	}

	//o.Color = i.Color;

	if (wpt == WPT_LINEARDEPTH)
	{
		float4 wview = mul(float4(i.Pos, 1), matView);
		o.DepthWorld = wview.z;
	}
	else if (wpt == WPT_WORLDPOS)
	{
		o.DepthWorld = i.Pos;
	}
	else if (wpt == WPT_DEPTH)
	{
		o.DepthWorld = float3(o.Pos.zw, 0);
	}
	    
    return o;
}

float4 PSTerrain(VS_OUTPUT i, uniform bool id, uniform bool filter) : COLOR0
{
	//float shadow = CalcShadow_4xPCF(i.SMTexC.xy/i.SMTexC.w, i.Depth.x/i.Depth.y);
	float shadow;

	if (filter)
	{
		shadow = CalcShadow_9xGaussPCF(i.SMTexC.xy/ i.SMTexC.w, i.Depth.x/i.Depth.y);
	}
	else
	{
		shadow = CalcShadow_Simple(i.SMTexC, i.Depth.x/i.Depth.y, id ? SM_ID : 0);
	}
	
	// + float4(0.5f * 1/SM_ShadowMapSize, 0.5f * 1/SM_ShadowMapSize, 0, 0)
	//float shadow = CalcShadow_Simple(i.SMTexC, i.Depth.y);
	//float shadow = 0;

	float4 Color = i.Color;
	    
	float s = min(saturate(i.Diffuse), 1-shadow);

	Color.rgb = (s * LightColor.rgb + AmbientColor) * Color.rgb;

	return Color;
	//return float4(0,1,1,1);
}

struct PS_OUT
{
	float4 Color : COLOR0; //32-bit argb
	float4 NXY : COLOR1; //2x16bit float
	float4 DepthWorld : COLOR2;
};

struct PS_OUT_ID
{
	float4 Color : COLOR0; //32-bit argb
	float4 NXY : COLOR1; //2x16bit float
	float4 DepthWorld : COLOR2;
	float4 ID : COLOR3;
};

PS_OUT PSTerrainDS(VS_OUTPUTDS i, uniform int wpt)
{
	PS_OUT o;

	if (wpt == WPT_WORLDPOS || wpt == WPT_LINEARDEPTH)
	{
		o.DepthWorld = float4(i.DepthWorld, 0);
	}
	else if (wpt == WPT_DEPTH)
	{
		o.DepthWorld = i.DepthWorld.x / i.DepthWorld.y;
	}
	
	o.NXY = float4(normalize(i.Normal).xy, 0, 0);
	o.Color = i.Color;
	o.Color.a = sign(i.Normal.z) * 0.5 + 0.5;

	return o;
}

PS_OUT_ID PSTerrainDS_ID(VS_OUTPUTDS i, uniform int wpt)
{
	PS_OUT_ID o;

	if (wpt == WPT_WORLDPOS || wpt == WPT_LINEARDEPTH)
	{
		o.DepthWorld = float4(i.DepthWorld, 0);
	}
	else if (wpt == WPT_DEPTH)
	{
		o.DepthWorld = i.DepthWorld.x / i.DepthWorld.y;
	}

	o.ID = SM_ID;
	
	o.NXY = float4(normalize(i.Normal).xy, 0, 0);
	o.Color = i.Color;
	o.Color.a = sign(i.Normal.z) * 0.5 + 0.5;

	return o;
}

SHADOW_VERTEX_OUT VSShadow(VS_INPUT i)
{
	SHADOW_VERTEX_OUT o;

	//float3 p = float3(i.Pos, i.Height);    
	//float4 wpos = mul(float4(p, 1), SM_matWorld);
	//o.Pos = mul(wpos, SM_matSM);
	o.Pos = mul(float4(i.Pos, 1), SM_matSM);
	o.Depth = o.Pos.zw;

	return o;
}

struct SHADOW_PRIMID_VERTEX_OUT
{
	float4 Pos : POSITION;
	float PrimID : COLOR;
};

SHADOW_PRIMID_VERTEX_OUT DepthVSPrimID(float3 Pos : POSITION, float2 Normal : NORMAL)
{
	SHADOW_PRIMID_VERTEX_OUT o;
			
	o.Pos = mul(float4(Pos, 1), SM_matSM);

	o.PrimID = (Normal.x + Normal.y) * 0.1f + 0.2f + Pos.x / 100 + Pos.y / 256 + Pos.z / 1000; //abs(Pos.x / 500 + Pos.z / 50 + Pos.y / 500 + abs(Normal.y)*300 + abs(Normal.y)*400);
	//o.PrimID = Pos.x / 100;
	
	return o;
}

float4 DepthPSPrimID(float PrimID : COLOR) : COLOR
{
	return (SM_ID + PrimID) / 2;
}

SHADOW_PRIMID_VERTEX_OUT VSMaterialDS_PrimID(float3 Pos : POSITION, float2 Normal : NORMAL)
{
    SHADOW_PRIMID_VERTEX_OUT o;

	o.Pos = mul(float4(Pos, 1), matViewProj);
	
	o.PrimID = (Normal.x + Normal.y) * 0.1f + 0.2f + Pos.x / 100 + Pos.y / 256 + Pos.z / 1000;//abs(Pos.x / 500 + Pos.z / 50 + Pos.y / 500 + abs(Normal.y)*300 + abs(Normal.y)*400);
	//o.PrimID = Pos.x / 100;
	
    return o;
}

stateblock RSTerrain = stateblock_state
{
	CullMode = CCW;
    ZFunc = LESS;
    ZEnable = true;
    ZWriteEnable = true;
    AlphaBlendEnable = false;
    AlphaTestEnable = false;
};

technique RenderCommon
{
    pass P0
    {      
		StateBlock = (RSTerrain);

        VertexShader = compile vs_2_0 VSTerrain();
        PixelShader  = compile ps_2_0 PSTerrain(false, false);
    }
}

technique RenderCommon_ID
{
    pass P0
    {      
		StateBlock = (RSTerrain);

        VertexShader = compile vs_2_0 VSTerrain();
        PixelShader  = compile ps_2_0 PSTerrain(true, false);
    }
}

technique RenderCommon_Filter
{
    pass P0
    {      
		StateBlock = (RSTerrain);

        VertexShader = compile vs_2_0 VSTerrain();
        PixelShader  = compile ps_3_0 PSTerrain(false, true);
    }
}

technique RenderDS_Depth
{
    pass P0
    {      
		StateBlock = (RSTerrain);

        VertexShader = compile vs_2_0 VSTerrainDS(WPT_DEPTH);
        PixelShader  = compile ps_2_0 PSTerrainDS(WPT_DEPTH);
    }
}

technique RenderDS_LinearDepth
{
    pass P0
    {      
		StateBlock = (RSTerrain);

        VertexShader = compile vs_2_0 VSTerrainDS(WPT_LINEARDEPTH);
        PixelShader  = compile ps_2_0 PSTerrainDS(WPT_LINEARDEPTH);
    }
}

technique RenderDS_World
{
    pass P0
    {      
		StateBlock = (RSTerrain);

        VertexShader = compile vs_2_0 VSTerrainDS(WPT_WORLDPOS);
        PixelShader  = compile ps_2_0 PSTerrainDS(WPT_WORLDPOS);
    }
}

technique RenderShadowDepth
{
    pass P0
    {      
		StateBlock = (RSTerrain);
		//need to disable culling because terrain meshes are not closed
		CullMode = NONE;

        VertexShader = compile vs_2_0 VSShadow();
        PixelShader  = compile ps_2_0 DepthPS();
    }
}

// with ID

technique RenderDS_Depth_ID
{
    pass P0
    {      
		StateBlock = (RSTerrain);

        VertexShader = compile vs_2_0 VSTerrainDS(WPT_DEPTH);
        PixelShader  = compile ps_2_0 PSTerrainDS_ID(WPT_DEPTH);
    }
}

technique RenderDS_LinearDepth_ID
{
    pass P0
    {      
		StateBlock = (RSTerrain);

        VertexShader = compile vs_2_0 VSTerrainDS(WPT_LINEARDEPTH);
        PixelShader  = compile ps_2_0 PSTerrainDS_ID(WPT_LINEARDEPTH);
    }
}

technique RenderDS_World_ID
{
    pass P0
    {      
		StateBlock = (RSTerrain);

        VertexShader = compile vs_2_0 VSTerrainDS(WPT_WORLDPOS);
        PixelShader  = compile ps_2_0 PSTerrainDS_ID(WPT_WORLDPOS);
    }
}

technique RenderDS_PrimID
{
	pass P0
    {      
		CullMode = NONE;
		ZFunc = EQUAL;
		ZEnable = true;
		ZWriteEnable = false;
		AlphaBlendEnable = false;
		AlphaTestEnable = false;
		ShadeMode = FLAT;

        VertexShader = compile vs_2_0 VSMaterialDS_PrimID();
        PixelShader  = compile ps_2_0 DepthPSPrimID();
    }
}

technique RenderShadowDepth_ID
{
    pass P0
    {      
		StateBlock = (RSTerrain);
		//need to disable culling because terrain meshes are not closed
		CullMode = NONE;

        VertexShader = compile vs_2_0 VSShadow();
        PixelShader  = compile ps_2_0 DepthIDPS();
    }
}

technique RenderShadowDepth_PrimID
{
	pass P0
    { 
		CullMode = NONE;
		ZFunc = LESS;
		ZEnable = true;
		ZWriteEnable = true;
		AlphaBlendEnable = false;
		AlphaTestEnable = false;
		ColorWriteEnable = RED;
		ShadeMode = GOURAUD;

		VertexShader = compile vs_2_0 VSShadow();
        PixelShader  = compile ps_2_0 DepthPS();
	}

	pass P1
	{
		CullMode = NONE;
		ZFunc = EQUAL;
		ZEnable = true;
		ZWriteEnable = false;
		AlphaBlendEnable = false;
		AlphaTestEnable = false;
		ColorWriteEnable = GREEN;
		ShadeMode = FLAT;

		VertexShader = compile vs_2_0 DepthVSPrimID();
        PixelShader  = compile ps_2_0 DepthPSPrimID();
	}
}