float3 LightDir;
float4 g_LightColor;
float4 g_AmbientColor;
float4x4 g_matInvViewProj;
float4x4 g_matInvProj;
float4x4 g_matInvView;
float ShadowConfT;
float2 g_RTSize;
float3 CamPos;
float iPass;
float MaxPasses;
float g_ConfScale;
float g_LambertScale : LAMBERTSCALE = 1;

float g_SampleVisTexSize = 64;

const float DotLNHide = 0; //offset to hide projective aliasing

#include "ShadowMapping.fx"
#include "WPT.fx"

texture Diffuse;
sampler2D SDiffuse = sampler_state
{
    Texture = <Diffuse>;
    AddressU = Clamp;
    AddressV = Clamp;
    MipFilter = NONE;
    MagFilter = POINT;
    MinFilter = POINT;
};

texture Normal;
sampler2D SNormal = sampler_state
{
    Texture = <Normal>;
    AddressU = Clamp;
    AddressV = Clamp;
    MipFilter = NONE;
    MagFilter = POINT;
    MinFilter = POINT;
};

texture ShadowMap;
sampler2D SShadowMap = sampler_state
{
    Texture = <ShadowMap>;
    AddressU = Clamp;
    AddressV = Clamp;
    MipFilter = NONE;	
    MagFilter = POINT;
    MinFilter = POINT;
};

texture ShadowMapRef;
sampler2D SShadowMapRef = sampler_state
{
    Texture = <ShadowMapRef>;
    AddressU = Clamp;
    AddressV = Clamp;
    MipFilter = NONE;	
    MagFilter = POINT;
    MinFilter = POINT;
};

texture SampleVis;
sampler2D SSampleVis = sampler_state
{
    Texture = <SampleVis>;
    AddressU = Clamp;
    AddressV = Clamp;
    MipFilter = NONE;
    MagFilter = POINT;
    MinFilter = POINT;
};

texture PassTex;
sampler2D SPassTex = sampler_state
{
    Texture = <PassTex>;
    AddressU = Clamp;
    AddressV = Clamp;
    MipFilter = NONE;
    MagFilter = POINT;
    MinFilter = POINT;
};

texture OptConf;
sampler2D SOptConf = sampler_state
{
    Texture = <OptConf>;
    AddressU = Clamp;
    AddressV = Clamp;
    MipFilter = NONE;
    MagFilter = POINT;
    MinFilter = POINT;
};

texture g_texID;
sampler2D g_sID = sampler_state
{
    Texture = <g_texID>;
    AddressU = Clamp;
    AddressV = Clamp;
    MipFilter = NONE;
    MagFilter = POINT;
    MinFilter = POINT;
};

texture g_texSSAO;
sampler2D g_sSSAO = sampler_state
{
    Texture = <g_texSSAO>;
    AddressU = Clamp;
    AddressV = Clamp;
    MipFilter = NONE;
    MagFilter = POINT;
    MinFilter = POINT;
};

texture g_texReqRes;
sampler2D g_sReqRes = sampler_state
{
    Texture = <g_texReqRes>;
    AddressU = Clamp;
    AddressV = Clamp;
    MipFilter = NONE;
    MagFilter = POINT;
    MinFilter = POINT;
};

#define VIS_NONE	0
#define VIS_SAMPLE	1
#define VIS_PASS	2
#define VIS_OPTCONF 3
#define VIS_FINALCONF 4
#define VIS_ID 5
#define VIS_SSAO 6
#define VIS_REQRES 7

float4 PSFinal(float2 ScreenCoord:TEXCOORD0, uniform int vistype) : COLOR
{
	float2 NXY = tex2D(SNormal, ScreenCoord);
	float3 Normal = float3(NXY, sqrt(1 - NXY.x*NXY.x - NXY.y*NXY.y));
	float4 Diffuse = tex2D(SDiffuse, ScreenCoord);
	Normal.z *= Diffuse.a * 2 - 1;
	/*float depth = tex2D(SDepth, ScreenCoord).r;
	float4 vp = float4(ScreenCoord * float2(2, -2) - float2(1, -1), depth, 1);
	float4 vcam = mul(vp, matInvProj);
	vcam.xyz /= vcam.w;
	float k = vcam.z / 2000;*/
	//float4 World = tex2D(SWorld, ScreenCoord);
	//return length(World.xyz-CamPos)/1000;

	float4 st = tex2D(SShadowMap, ScreenCoord);
	float Shadow = st.r;

	float dotLN = dot(Normal, -LightDir) ;

	//return dotLN;

	//add something to hide projective aliasing
	//float s = min(saturate(dotLN * (1 + DotLNHide) - DotLNHide), 1-Shadow);
	float s = min(1 + (saturate(dotLN) - 1) * g_LambertScale, 1-Shadow);

	float h = 1; //(Normal.z + 1) / 10 + 0.8; //some hemispheric ambient

	float3 Color = (s * g_LightColor.rgb + h * g_AmbientColor.rgb) * Diffuse.rgb;

	if(vistype == VIS_SAMPLE)
	{
		float4 Sample;
		Sample.x = tex2D(SSampleVis, ScreenCoord + float2(-1,0) / g_RTSize);
		Sample.y = tex2D(SSampleVis, ScreenCoord + float2(1,0) / g_RTSize);
		Sample.z = tex2D(SSampleVis, ScreenCoord + float2(0,-1) / g_RTSize);
		Sample.w = tex2D(SSampleVis, ScreenCoord + float2(0,1) / g_RTSize);
	
		if(!(Sample.x == Sample.y && Sample.y == Sample.z && Sample.z == Sample.w))
			Color *= float3(0.25, 0.5, 0);
	}
	else if(vistype == VIS_PASS)
	{
		//st.g == pass
		//st.b == conf
		float ipass = st.g;
		//only shadow pixels
		if(ipass >= 0)
			//Color = Color * (1-Shadow) + Shadow * float3(1 - ipass/101, 0, ipass/101);
			Color = float3(1 - ipass, 0, ipass);
	}
	else if(vistype == VIS_FINALCONF)
	{
		//st.g == pass
		//st.b == conf
		float conf = st.b;

		Color = float3(conf, 0, 1 - conf);
	}
	else if(vistype == VIS_OPTCONF)
	{
		float optconf = tex2D(SOptConf, ScreenCoord).r * g_ConfScale;

		if (optconf >= 0)
		{
			Color = float3(optconf, 0, 1 - optconf);
		}
		else
		{
			//Color = 0;
			Color = float3(0, -optconf / 5, 1 + optconf / 5);
		}
	}
	else if(vistype == VIS_ID)
	{
		Color = tex2D(g_sID, ScreenCoord).r;
	}
	else if(vistype == VIS_SSAO)
	{
		Color = tex2D(g_sSSAO, ScreenCoord).r;
	}
	else if(vistype == VIS_REQRES)
	{
		Color = float4(tex2D(g_sReqRes, ScreenCoord).zzww * 10);
		//Color = tex2D(g_sReqRes, ScreenCoord);
	}

	return float4(Color, 1);
}

float4 PSFinalShadow(float2 ScreenCoord:TEXCOORD0, uniform int vistype, uniform bool id, uniform int wpt, uniform bool filter) : COLOR
{
	//float depth = tex2D(SDepth, ScreenCoord).r;
	//float4 vp = float4(ScreenCoord * float2(2, -2) - float2(1, -1), 1, 1);

	float3 wpos = GetFragmentWorldPos(ScreenCoord, wpt);

	float4 Diffuse = tex2D(SDiffuse, ScreenCoord);

	//return Diffuse;

	float2 NXY = tex2D(SNormal, ScreenCoord).rg;
	float3 Normal = float3(NXY, sqrt(1 - NXY.x*NXY.x - NXY.y*NXY.y));
	Normal.z *= Diffuse.a * 2 - 1;

	//return Normal.xyzz *0.5 + 0.5;
	/*float depth = tex2D(SDepth, ScreenCoord).r;
	float4 vp = float4(ScreenCoord * float2(2, -2) - float2(1, -1), depth, 1);
	float4 vcam = mul(vp, matInvProj);
	vcam.xyz /= vcam.w;
	float k = vcam.z / 2000;*/
	//float4 World = tex2D(SWorld, ScreenCoord);
	//return length(World.xyz-CamPos)/1000;

	float slope = (1-dot(Normal, -LightDir));
    float bias = slope * SM_SlopeBias + SM_DetphBias;
	//bias *= (1 + (vcam.z/2000)*20);
	float4 swpos = float4(wpos.xyz + bias * -LightDir, 1);
	//float4 swpos = wpos;

	float2 ShadowDepth = CalcDepth(swpos);
	float shdepth = ShadowDepth.x/ShadowDepth.y;
	float4 SMTexC = CalcSMTexC(float4(wpos,1));

	float Shadow;
	
	if (filter)
	{
		//Shadow = CalcShadow_9xGaussPCF(SMTexC.xy / SMTexC.w, shdepth);
		Shadow = CalcShadow_4xPCF(SMTexC.xy / SMTexC.w, shdepth);
	}
	else
	{
		Shadow = CalcShadow_Simple(SMTexC, shdepth, id ? tex2D(g_sID, ScreenCoord).r : 0);
	}

	float dotLN = dot(Normal, -LightDir);

	//return dotLN;

	//wrap around light
	float s = saturate(dotLN + 1 - g_LambertScale) / (2 - g_LambertScale) * (1 - Shadow * 0.98);

	//add something to hide projective aliasing
	//float s = min(1 + (saturate(dotLN) - 1) * g_LambertScale, 1-Shadow);

	float h = (Normal.z + 1) / 20 + 0.9; //some hemispheric ambient

	float3 Color = (s * g_LightColor.rgb + h * g_AmbientColor.rgb) * Diffuse.rgb;

	if (vistype == VIS_SAMPLE)
	{
		float4 Sample;
		Sample.x = tex2D(SSampleVis, ScreenCoord + float2(-1,0) / g_RTSize);
		Sample.y = tex2D(SSampleVis, ScreenCoord + float2(1,0) / g_RTSize);
		Sample.z = tex2D(SSampleVis, ScreenCoord + float2(0,-1) / g_RTSize);
		Sample.w = tex2D(SSampleVis, ScreenCoord + float2(0,1) / g_RTSize);
	
		if(!(Sample.x == Sample.y && Sample.y == Sample.z && Sample.z == Sample.w))
			Color *= float3(0.25, 0.5, 0);
	}

	return float4(Color, 1);
}

struct SHADOW_OUT
{
	float4 Shadow : COLOR0;
	float Z : DEPTH;
	//float4 SPass : COLOR1;
};

float4 PSSampleVis(float2 ScreenCoord:TEXCOORD0, uniform bool bclip, uniform int wpt) : COLOR
{
	//float4 wpos = tex2D(SWorld, ScreenCoord);

	float3 wpos = GetFragmentWorldPos(ScreenCoord, wpt);
	
	if (bclip)
	{
		float4 SMTexC = CalcSMTexC(float4(wpos, 1));
		float2 tc = SMTexC.xy/SMTexC.w;

		float2 rsdepthproj = CalcDepth(float4(wpos, 1));
		float rsdepth = rsdepthproj.x / rsdepthproj.y;

		//this is from biased world so its not exactly 1 and 0
		if(rsdepth <= 0 || rsdepth >= 1)
			clip(-1);

		//clip if outside shadow map
		float4 vclip = float4(tc, 1-tc);
		clip(vclip);
	}
	float4 smtex = CalcSMTexC(float4(wpos, 1));
	float2 tc = smtex.xy / smtex.w;
	int2 itex = tc * g_SampleVisTexSize;
	
	return tex2D(SM_SShadowMap, itex / g_SampleVisTexSize).r;

	/*float4 smtc = CalcSMTexC(wpos);
	float2 tc = smtc.xy / smtc.w;

	float sdepth = tex2D(SM_SShadowMap, tc).r;

	if(sdepth <= 0 || sdepth >= 1)
		return 0.5;
	else
		return 1;*/

	//float4 vclip = float4(tc, 1-tc);
	//clip(vclip);

	//return 0.5;

	//return depth;
}

float4 OverblendSSAO(float2 ScreenCoord:TEXCOORD0) : COLOR
{
	const int SIZE = 2;

	const float Rho = 1.2f;
	
	float Sum = 0;
	float Wheight = 0;

	for(int y = -SIZE; y <= SIZE; y++)
	{
		for(int x = -SIZE; x <= SIZE; x++)
		{
			[branch]if (x*x + y*y <= SIZE*SIZE)
			{                  
				float2 stc = ScreenCoord + float2(x, y) / g_RTSize;
                float gauss = 1 / (2 * 3.141592654f * Rho * Rho) * exp(-0.5 * (x * x + y * y) / (Rho * Rho)); 
				Sum += tex2Dlod(g_sSSAO, float4(stc, 0, 0)).r * gauss;
				Wheight += gauss;
			}
        }
    }

	//return float4(0, 0, 0, 1 - tex2D(g_sSSAO, ScreenCoord).r) * 0.5;
	//float ao = tex2D(g_sSSAO, ScreenCoord).r * 0.3;
	float ao = Sum / Wheight * 0.06f;
	return float4(ao, ao, ao, 1);
}

float4 PSErode(float2 ScreenCoord:TEXCOORD0) : COLOR
{
	float4 Sample;
	Sample.x = tex2D(SShadowMap, ScreenCoord + float2(-1,0) / g_RTSize);
	Sample.y = tex2D(SShadowMap, ScreenCoord + float2(1,0) / g_RTSize);
	Sample.z = tex2D(SShadowMap, ScreenCoord + float2(0,-1) / g_RTSize);
	Sample.w = tex2D(SShadowMap, ScreenCoord + float2(0,1) / g_RTSize);
	float self = tex2D(SShadowMap, ScreenCoord);

	float Out;
	if(all(Sample) && self > 0)
		Out = 1;
	else
		Out = 0;

	return Out;
}

float4 PSDilate(float2 ScreenCoord:TEXCOORD0) : COLOR
{
	float4 Sample;
	Sample.x = tex2D(SShadowMap, ScreenCoord + float2(-1,0) / g_RTSize);
	Sample.y = tex2D(SShadowMap, ScreenCoord + float2(1,0) / g_RTSize);
	Sample.z = tex2D(SShadowMap, ScreenCoord + float2(0,-1) / g_RTSize);
	Sample.w = tex2D(SShadowMap, ScreenCoord + float2(0,1) / g_RTSize);
	float self = tex2D(SShadowMap, ScreenCoord);

	float Out;
	if(all(1-Sample) && self != 1)
		Out = 0;
	else
		Out = 1;

	return Out;
}

float4 PSEdge(float2 ScreenCoord:TEXCOORD0) : COLOR
{
	float4 Sample;
	Sample.x = tex2D(SShadowMap, ScreenCoord + float2(0,-1) / g_RTSize);
	Sample.y = tex2D(SShadowMap, ScreenCoord + float2(0,1) / g_RTSize);
	Sample.z = tex2D(SShadowMap, ScreenCoord + float2(-1,0) / g_RTSize);
	Sample.w = tex2D(SShadowMap, ScreenCoord + float2(1,0) / g_RTSize);

	float Out;
	if(Sample.x == Sample.y == Sample.z == Sample.w)
		Out = 0;
	else
		Out = 1;

	return Out;
}

float4 PSShadowDiff(float2 ScreenCoord:TEXCOORD0) : COLOR
{
	float s = tex2D(SShadowMap, ScreenCoord).r;
	float sref = tex2D(SShadowMapRef, ScreenCoord).r;
	
	float diff = abs(s - sref);
	//texkill if diff == 0
	if(diff == 0)
		clip(-1);
	
	return float4(0, 0, 0, 0);
}


void VSCellConf(float2 ScreenCoord : TEXCOORD0, out float4 oPos : POSITION, out float2 oTexC : TEXCOORD0)
{
	float depth = tex2Dlod(g_sDepth, float4(ScreenCoord, 0, 0)).r;
	
	float4 vp = float4(ScreenCoord * float2(2, -2) - float2(1, -1), depth, 1);
	float4 v = mul(vp, g_matInvViewProj);
	float4 wpos = float4(v.xyz / v.w, 1);
		
	float4 smtc = CalcSMTexC(wpos);
	
	oPos = smtc;
	oTexC = ScreenCoord;
}

SHADOW_OUT PSCellConf(float2 iTexC : TEXCOORD0)
{
	SHADOW_OUT o;
	float reqconf = tex2D(SOptConf, iTexC).r;
	o.Z = reqconf;
	o.Shadow = reqconf;
	return o;
}

stateblock RSDeferred = stateblock_state
{
	CullMode = NONE;
    ZEnable = false;
    AlphaBlendEnable = false;
    AlphaTestEnable = false;
	ColorWriteEnable = RED | GREEN | BLUE | ALPHA;
};

#define RenderFinalShadow(nameext, id, wpt, filter)									\
technique RenderFinalShadow_##nameext												\
{																					\
    pass P0																			\
    {																				\
		StateBlock = (RSDeferred);													\
																					\
        VertexShader = null;														\
        PixelShader  = compile ps_3_0 PSFinalShadow(VIS_NONE, id, wpt, filter);		\
    }																				\
}																					\
technique RenderFinalShadow_SampleVis_##nameext										\
{																					\
    pass P0																			\
    {																				\
		StateBlock = (RSDeferred);													\
																					\
        VertexShader = null;														\
        PixelShader  = compile ps_3_0 PSFinalShadow(VIS_SAMPLE, id, wpt, filter);	\
    }																				\
}

RenderFinalShadow(World, false, WPT_WORLDPOS, false);
RenderFinalShadow(Depth, false, WPT_DEPTH, false);
RenderFinalShadow(LinearDepth, false, WPT_LINEARDEPTH, false);

RenderFinalShadow(ID_World, true, WPT_WORLDPOS, false);
RenderFinalShadow(ID_Depth, true, WPT_DEPTH, false);
RenderFinalShadow(ID_LinearDepth, true, WPT_LINEARDEPTH, false);

RenderFinalShadow(Filter_World, false, WPT_WORLDPOS, true);
RenderFinalShadow(Filter_Depth, false, WPT_DEPTH, true);
RenderFinalShadow(Filter_LinearDepth, false, WPT_LINEARDEPTH, true);


#define RenderFinalX(nameext, vistype)						\
technique RenderFinal##nameext								\
{															\
    pass P0													\
    {														\
		StateBlock = (RSDeferred);							\
															\
        VertexShader = null;								\
        PixelShader  = compile ps_2_b PSFinal(vistype);		\
    }														\
}

RenderFinalX(_, VIS_NONE);
RenderFinalX(_SampleVis, VIS_SAMPLE);
RenderFinalX(_PassVis, VIS_PASS);
RenderFinalX(_FinalConfVis, VIS_FINALCONF);
RenderFinalX(_OptConfVis, VIS_OPTCONF);
RenderFinalX(_IDVis, VIS_ID);
RenderFinalX(_SSAOVis, VIS_SSAO);
RenderFinalX(_ReqResVis, VIS_REQRES);

technique RenderErode
{
    pass P0
    {      
		StateBlock = (RSDeferred);

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

technique RenderDilate
{
    pass P0
    {      
		StateBlock = (RSDeferred);

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

technique RenderEdge
{
    pass P0
    {      
		StateBlock = (RSDeferred);

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

technique RenderSampleVis_Depth
{
    pass P0
    {      
		StateBlock = (RSDeferred);

        VertexShader = null;
        PixelShader  = compile ps_2_0 PSSampleVis(true, WPT_DEPTH);
    }
}

technique RenderSampleVis_LinearDepth
{
    pass P0
    {      
		StateBlock = (RSDeferred);

        VertexShader = null;
        PixelShader  = compile ps_2_0 PSSampleVis(true, WPT_LINEARDEPTH);
    }
}

technique RenderSampleVis_World
{
    pass P0
    {      
		StateBlock = (RSDeferred);

        VertexShader = null;
        PixelShader  = compile ps_2_0 PSSampleVis(true, WPT_WORLDPOS);
    }
}

technique RenderShadowDiff
{
    pass P0
    {      
		StateBlock = (RSDeferred);

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

technique RenderSSAO
{
    pass P0
    {      
		CullMode = NONE;
		ZEnable = false;
		//ZWriteEnable = false;
		//ZFunc = GREATER;
		AlphaBlendEnable = true;
		SrcBlend = ONE;
		DestBlend = ONE;
		BlendOP = ADD;
		AlphaTestEnable = false;

        VertexShader = null;
        PixelShader  = compile ps_3_0 OverblendSSAO();
    }
}