//global scene parameters, they will be automatically set by semantic
float4x4    matViewProj : VIEWPROJ;
float4x4    matView : VIEW;

float3      LightDir : LIGHTDIR;
float4      LightColor : LIGHTCOLOR;
float4      AmbientColor : AMBIENT;

float g_LambertScale : LAMBERTSCALE = 1;

//object dependend parameters
float4x4	matWorld;
float4		Diffuse;

float4		g_Color;

#include "ShadowMapping.fx"

#define USEIDBUFFER

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

int TexFilter = 3; //2=LINEAR, 3=ANI
int TexAnisotropy = 8;

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

texture Texture;
sampler STexture = sampler_state
{
    Texture = <Texture>;
    AddressU = Wrap;
    AddressV = Wrap;
    MipFilter = LINEAR;
    MagFilter = LINEAR;
    MinFilter = (TexFilter);
    MaxAnisotropy = (TexAnisotropy);
};

struct VS_INPUT
{
    float3 Pos : POSITION;
	float3 Normal : NORMAL;
	float2 TexCoord : TEXCOORD0;
};

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

struct VS_OUT_DS
{
    float4 Pos : POSITION;
	float3 Normal : TEXCOORD0;
	float2 TexCoord : TEXCOORD1;

	//can be linear depth, depthzw, or direct worldpos
	float3 DepthWorld : TEXCOORD2;
};

VS_OUTPUT VSMaterial(VS_INPUT i, uniform bool tex)
{
    VS_OUTPUT o;

	float4 wpos = mul(float4(i.Pos, 1), matWorld);
	o.Pos = mul(wpos, matViewProj);
	float3 Normal = normalize(mul(i.Normal, matWorld));
	float dotLN = dot(Normal, -LightDir);
	o.Diffuse = 1 + (saturate(dotLN) - 1) * g_LambertScale;

	float slope = (1-dotLN);
    float bias = slope * SM_SlopeBias + SM_DetphBias;
    float4 swpos = float4(wpos + bias * -LightDir, 1);
    o.SMTexC = CalcSMTexC(swpos); //wpos and swpos will generate the same texture coordinate, baucause bias is only in z direction
    o.Depth = CalcDepth(swpos);

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

	if(tex)
		o.TexCoord = i.TexCoord;
	else
		o.TexCoord = float2(0,0);
	
    return o;
}

VS_OUT_DS VSMaterialDS(VS_INPUT i, uniform bool tex, uniform int wpt)
{
    VS_OUT_DS o;

	float4 wpos = mul(float4(i.Pos, 1), matWorld);
	o.Pos = mul(wpos, matViewProj);
	o.Normal = normalize(mul(i.Normal, matWorld));

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

	if(tex)
		o.TexCoord = i.TexCoord;
	else
		o.TexCoord = float2(0,0);
	
    return o;
}

float4 PSMaterial(VS_OUTPUT i, uniform bool tex, uniform bool id, uniform bool filter) : COLOR
{
	//float shadow = CalcShadow_4xPCF(i.SMTexC.xy/i.SMTexC.w, i.Depth.x/i.Depth.y);
    //float shadow = CalcShadow_Simple(i.SMTexC, i.Depth.x/i.Depth.y);
	//float shadow = CalcShadow_Simple(i.SMTexC, i.Depth.x/i.Depth.y, id ? SM_ID : 0);
    //float shadow = 0;

	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 Color = Diffuse;

	if(tex)
		Color *=  tex2D(STexture, i.TexCoord);
        
    float s = min(saturate(i.Diffuse), 1-shadow);

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

	//return i.Diffuse;
}

struct PS_OUT_DS
{
	float4 Color : COLOR0;
	float4 NXY : COLOR1;
	float4 DepthWorld : COLOR2;
};

struct PS_OUT_DS_ID
{
	float4 Color : COLOR0;
	float4 NXY : COLOR1;
	float4 DepthWorld : COLOR2;
	float4 ID : COLOR3;
};

PS_OUT_DS PSMaterialDS(VS_OUT_DS i, uniform bool tex, uniform int wpt)
{
	PS_OUT_DS 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 = Diffuse;
	if(tex)
		o.Color *= tex2D(STexture, i.TexCoord);

	o.Color.a = sign(i.Normal.z) * 0.5 + 0.5;

	return o;
}

PS_OUT_DS_ID PSMaterialDS_ID(VS_OUT_DS i, uniform bool tex, uniform int wpt)
{
	PS_OUT_DS_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 = Diffuse;
	if(tex)
		o.Color *= tex2D(STexture, i.TexCoord);

	o.Color.a = sign(i.Normal.z) * 0.5 + 0.5;

	return o;
}

float4 PSColor(void) : COLOR
{
	return g_Color;
}

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

SHADOW_PRIMID_VERTEX_OUT DepthVSPrimID(float3 Pos : POSITION, float3 Normal : NORMAL)
{
	SHADOW_PRIMID_VERTEX_OUT o;
			
	float4 wpos = mul(float4(Pos, 1), SM_matWorld);
	float3 n = normalize(mul(Normal, SM_matWorld)); 

	o.Pos = mul(wpos, SM_matSM);

	o.PrimID = abs(wpos.x / 673 + wpos.z / 123 + wpos.y / 757  + n.x + n.y);
	
	return o;
}

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

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

	float4 wpos = mul(float4(Pos, 1), matWorld);
	float3 n = normalize(mul(Normal, matWorld)); 

	o.Pos = mul(wpos, matViewProj);
	
	o.PrimID = abs(wpos.x / 673 + wpos.z / 123 + wpos.y / 757 + n.x + n.y);
	
    return o;
}

stateblock RSMaterial = stateblock_state
{
	CullMode = CCW;
	//CullMode = NONE;
    ZFunc = LESS;
    ZEnable = true;
    ZWriteEnable = true;
    AlphaBlendEnable = false;
    AlphaTestEnable = false;
	ColorWriteEnable = RED | GREEN | BLUE | ALPHA;
	ShadeMode = GOURAUD;
};

technique ClearColor
{
	pass P0
    {      
		CullMode = NONE;
		ZEnable = false;
		AlphaBlendEnable = false;
		AlphaTestEnable = false;

        VertexShader = null;
        PixelShader  = compile ps_2_0 PSColor();
    }
}

technique Render
{
	pass P0
    {      
		StateBlock = (RSMaterial);

        VertexShader = compile vs_2_0 VSMaterial(false);
        PixelShader  = compile ps_2_b PSMaterial(false, false, false);
    }
}

technique RenderTextured
{
    pass P0
    {      
		StateBlock = (RSMaterial);

        VertexShader = compile vs_2_0 VSMaterial(true);
        PixelShader  = compile ps_2_b PSMaterial(true, false, false);
    }
}

technique Render_ID
{
	pass P0
    {      
		StateBlock = (RSMaterial);

        VertexShader = compile vs_2_0 VSMaterial(false);
        PixelShader  = compile ps_2_b PSMaterial(false, true, false);
    }
}

technique RenderTextured_ID
{
    pass P0
    {      
		StateBlock = (RSMaterial);

        VertexShader = compile vs_2_0 VSMaterial(true);
        PixelShader  = compile ps_2_b PSMaterial(true, true, false);
    }
}

technique Render_Filter
{
	pass P0
    {      
		StateBlock = (RSMaterial);

        VertexShader = compile vs_2_0 VSMaterial(false);
        PixelShader  = compile ps_3_0 PSMaterial(false, false, true);
    }
}

technique RenderTextured_Filter
{
    pass P0
    {      
		StateBlock = (RSMaterial);

        VertexShader = compile vs_2_0 VSMaterial(true);
        PixelShader  = compile ps_3_0 PSMaterial(true, false, true);
    }
}

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

        VertexShader = compile vs_2_0 VSMaterialDS(false, WPT_DEPTH);
        PixelShader  = compile ps_2_0 PSMaterialDS(false, WPT_DEPTH);
    }
}

technique RenderDS_Textured_Depth
{
    pass P0
    {      
		StateBlock = (RSMaterial);

        VertexShader = compile vs_2_0 VSMaterialDS(true, WPT_DEPTH);
        PixelShader  = compile ps_2_0 PSMaterialDS(true, WPT_DEPTH);
    }
}

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

        VertexShader = compile vs_2_0 VSMaterialDS(false, WPT_LINEARDEPTH);
        PixelShader  = compile ps_2_0 PSMaterialDS(false, WPT_LINEARDEPTH);
    }
}

technique RenderDS_Textured_LinearDepth
{
    pass P0
    {      
		StateBlock = (RSMaterial);

        VertexShader = compile vs_2_0 VSMaterialDS(true, WPT_LINEARDEPTH);
        PixelShader  = compile ps_2_0 PSMaterialDS(true, WPT_LINEARDEPTH);
    }
}

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

        VertexShader = compile vs_2_0 VSMaterialDS(false, WPT_WORLDPOS);
        PixelShader  = compile ps_2_0 PSMaterialDS(false, WPT_WORLDPOS);
    }
}

technique RenderDS_Textured_World
{
    pass P0
    {      
		StateBlock = (RSMaterial);

        VertexShader = compile vs_2_0 VSMaterialDS(true, WPT_WORLDPOS);
        PixelShader  = compile ps_2_0 PSMaterialDS(true, WPT_WORLDPOS);
    }
}

technique RenderShadowDepth
{
	pass P0
    { 
		StateBlock = (RSMaterial);

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

//everything with ID



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

        VertexShader = compile vs_2_0 VSMaterialDS(false, WPT_DEPTH);
        PixelShader  = compile ps_2_0 PSMaterialDS_ID(false, WPT_DEPTH);
    }
}

technique RenderDS_Textured_Depth_ID
{
    pass P0
    {      
		StateBlock = (RSMaterial);

        VertexShader = compile vs_2_0 VSMaterialDS(true, WPT_DEPTH);
        PixelShader  = compile ps_2_0 PSMaterialDS_ID(true, WPT_DEPTH);
    }
}

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

        VertexShader = compile vs_2_0 VSMaterialDS(false, WPT_LINEARDEPTH);
        PixelShader  = compile ps_2_0 PSMaterialDS_ID(false, WPT_LINEARDEPTH);
    }
}

technique RenderDS_Textured_LinearDepth_ID
{
    pass P0
    {      
		StateBlock = (RSMaterial);

        VertexShader = compile vs_2_0 VSMaterialDS(true, WPT_LINEARDEPTH);
        PixelShader  = compile ps_2_0 PSMaterialDS_ID(true, WPT_LINEARDEPTH);
    }
}

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

        VertexShader = compile vs_2_0 VSMaterialDS(false, WPT_WORLDPOS);
        PixelShader  = compile ps_2_0 PSMaterialDS_ID(false, WPT_WORLDPOS);
    }
}

technique RenderDS_Textured_World_ID
{
    pass P0
    {      
		StateBlock = (RSMaterial);

        VertexShader = compile vs_2_0 VSMaterialDS(true, WPT_WORLDPOS);
        PixelShader  = compile ps_2_0 PSMaterialDS_ID(true, 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 = (RSMaterial);

		VertexShader = compile vs_2_0 DepthVS();
        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 DepthVS();
        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();
	}
}