// shadowmapping functions
float4x4	SM_matWorld;
float4x4	SM_matSM;
//float4x4	SM_matSMTex;
float		SM_ShadowMapSize = 1024;
//float		SM_rcpShadowMapSize;

float		SM_DetphBias : SMCONSTBIAS = 1.5f;
float		SM_SlopeBias : SMSLOPEBIAS = 3;

float		SM_ID = 0;

bool g_bUseNVShadowMap = false;
int g_SMFiltering = 1; //2 == LINEAR

texture SM_ShadowMap;
sampler SM_SShadowMap = sampler_state
{
    Texture = <SM_ShadowMap>;
    AddressU = Clamp;
    AddressV = Clamp;
    MipFilter = NONE;
    MagFilter = <g_SMFiltering>;
    MinFilter = <g_SMFiltering>;
};

texture SM_IDBuffer;
sampler SM_SIDBuffer = sampler_state
{
    Texture = <SM_IDBuffer>;
    AddressU = Clamp;
    AddressV = Clamp;
    MipFilter = NONE;
    MagFilter = POINT;
    MinFilter = POINT;
};

//#define SHADOW_EPSILON 0.00005f
#define SHADOW_EPSILON 0

//this will be preshadered
float4x4 GetMatSMTex(void)
{
	float off = 0.5f + 0.5f / SM_ShadowMapSize;
	//float off = 0.5f + 0.5f * SM_rcpShadowMapSize;
	return mul(SM_matSM, float4x4(	0.5f, 0.0f, 0.0f, 0.0f,
									0.0f, -0.5f, 0.0f, 0.0f,
									0.0f, 0.0f, 1, 0.0f,
									off, off, 0.0f, 1.0f));
}

float4 CalcSMTexC(const float4 p)
{
	//return mul(p, SM_matSMTex);
	return mul(p, GetMatSMTex());
}

float2 CalcDepth(const float4 p)
{
	return (mul(p, SM_matSM)).zw;
}

float CalcShadow_Simple(const float4 ShadowMapTexC, const float Depth, const float id)
{
	//return CalcShadow_4xPCF(ShadowMapTexC.xy / ShadowMapTexC.w, Depth);

	if (id != 0)
	{
		//return tex2Dproj(SM_SShadowMap, ShadowMapTexC).g == id ? 0 : 1;

		//test id pcf like

		float2 tc = ShadowMapTexC.xy / ShadowMapTexC.w;

		float4 idvec;
		idvec.x = tex2D(SM_SIDBuffer, tc + float2(-0.5f, -0.5f) / SM_ShadowMapSize).g == id ? 1 : 0;
		idvec.y = tex2D(SM_SIDBuffer, tc + float2(0.5f, -0.5f) / SM_ShadowMapSize).g == id ? 1 : 0;
		idvec.z = tex2D(SM_SIDBuffer, tc + float2(-0.5f, 0.5f) / SM_ShadowMapSize).g == id ? 1 : 0;
		idvec.w = tex2D(SM_SIDBuffer, tc + float2(0.5f, 0.5f) / SM_ShadowMapSize).g == id ? 1 : 0;

		if (!any(idvec)) //self shadowing
		{
			return 1;
		}
		else
		{
			if (g_bUseNVShadowMap)
				return 1 - tex2Dproj(SM_SShadowMap, ShadowMapTexC).r;
			else
				return (tex2Dproj(SM_SShadowMap, ShadowMapTexC).r + SHADOW_EPSILON < Depth)? 1.0f: 0.0f;   
		}
	}
	else
	{
		if (g_bUseNVShadowMap)
			return 1 - tex2Dproj(SM_SShadowMap, ShadowMapTexC).r;
		else
			return (tex2Dproj(SM_SShadowMap, ShadowMapTexC).r + SHADOW_EPSILON < Depth)? 1.0f: 0.0f;  
	}
}

float CalcShadow_4xPCF(const float2 ShadowMapTexC, const float Depth)
{
	if (g_bUseNVShadowMap)
	{
		//return 1 - tex2Dproj(SM_SShadowMap, float4(ShadowMapTexC, Depth, 1)).r; //pcf lookup done by hw
		return 0;
	}
	else
	{
		float2 texelpos = ShadowMapTexC * SM_ShadowMapSize;
		float2 lerps = frac( texelpos );

		float sourcevals[4];
		sourcevals[0] = (tex2Dlod( SM_SShadowMap, float4(ShadowMapTexC, 0, 0) ).r + SHADOW_EPSILON < Depth) ? 1.0f: 0.0f;  
		sourcevals[1] = (tex2Dlod( SM_SShadowMap, float4(ShadowMapTexC + float2(1.f / SM_ShadowMapSize, 0), 0, 0) ).r + SHADOW_EPSILON < Depth) ? 1.0f: 0.0f;  
		sourcevals[2] = (tex2Dlod( SM_SShadowMap, float4(ShadowMapTexC + float2(0, 1.f / SM_ShadowMapSize), 0, 0) ).r + SHADOW_EPSILON < Depth) ? 1.0f: 0.0f;  
		sourcevals[3] = (tex2Dlod( SM_SShadowMap, float4(ShadowMapTexC + float2(1.f / SM_ShadowMapSize, 1.f / SM_ShadowMapSize), 0, 0) ).r + SHADOW_EPSILON < Depth) ? 1.0f: 0.0f;  
		    
		// lerp between the shadow values to calculate the light amount
		return lerp( lerp( sourcevals[0], sourcevals[1], lerps.x ), lerp( sourcevals[2], sourcevals[3], lerps.x ), lerps.y );
	}
}

float CalcShadow_9xGaussPCF(const float2 ShadowMapTexC, const float Depth)
{
	//return CalcShadow_4xPCF(ShadowMapTexC, Depth);

	float2 texelpos = ShadowMapTexC * SM_ShadowMapSize;
	
	float ret = 0;
	
	float w[] = {0.0625, 0.125, 0.0625,
				0.125, 0.25, 0.125,
				0.0625, 0.125, 0.0625};

	for (int y = -1; y <= 1; y++)
	{
		for (int x = -1; x <= 1; x++)
		{
			if (g_bUseNVShadowMap)
				ret += tex2Dproj(SM_SShadowMap, float4(ShadowMapTexC + float2(x, y) / SM_ShadowMapSize, Depth, 1)).r * w[(x+1) + (y+1)*3]; //does hw pcf lookup
			else
				ret += CalcShadow_4xPCF(ShadowMapTexC + float2(x, y) / SM_ShadowMapSize, Depth) * w[(x+1) + (y+1)*3];
			
		}
	}

	if (g_bUseNVShadowMap)
		return 1 - ret;
	else
		return ret;
}

float CalcShadow_25xGaussPCF(const float2 ShadowMapTexC, const float Depth, const float offsetscale)
{
	//return CalcShadow_4xPCF(ShadowMapTexC, Depth);

	float2 texelpos = ShadowMapTexC * SM_ShadowMapSize;
	
	float ret = 0;
	
	float w[] = {	0.02, 0.03, 0.04, 0.03, 0.02,
					0.03, 0.05, 0.06, 0.05, 0.03,
					0.04, 0.06, 0.08, 0.06, 0.04,
					0.03, 0.05, 0.06, 0.05, 0.03,
					0.02, 0.03, 0.04, 0.03, 0.02};

	for (int y = -2; y <= 2; y++)
	{
		for (int x = -2; x <= 2; x++)
		{
			//if (g_bUseNVShadowMap)
			//	ret += tex2Dproj(SM_SShadowMap, float4(ShadowMapTexC + float2(x, y) * offsetscale / SM_ShadowMapSize, Depth, 1)).r * w[(x+2) + (y+2)*5]; //does hw pcf lookup
			//else
				ret += CalcShadow_4xPCF(ShadowMapTexC + float2(x, y) * offsetscale / SM_ShadowMapSize, Depth) * w[(x+2) + (y+2)*5];
			
		}
	}

	if (g_bUseNVShadowMap)
		return 1 - ret;
	else
		return ret;
}

float CalcShadow_XXxGaussPCF(const float2 ShadowMapTexC, const float Depth, const float rho)
{
	//return CalcShadow_4xPCF(ShadowMapTexC, Depth);

	float2 texelpos = ShadowMapTexC * SM_ShadowMapSize;
	
	//2*floor(3*sigma)+1

	//int samples = rho * 3 + 1;
	int samples = rho * 2 + 1;
	//int s = min(samples/2, 5);
	int s = 5;

	float ret = 0;
	float sumw = 0;

	[loop] for (int y = -s; y <= s; y++)
	{
		[loop] for (int x = -s; x <= s; x++)
		{
			float w = 1 / (2 * 3.1415 * rho * rho) * exp(-0.5 * ( (x * x + y * y) /rho/rho));
			sumw += w;
			ret += CalcShadow_4xPCF(ShadowMapTexC + float2(x, y) / SM_ShadowMapSize, Depth) * w;
			
		}
	}
	return ret / sumw;
}

float CalcShadow_XxAvg(const float2 ShadowMapTexC, const float Depth, const int size, const float scale)
{
	float2 texelpos = ShadowMapTexC * SM_ShadowMapSize;

	int s = size - 1;

	float ret = 0;

	[loop] for (int y = 0; y <= s && y <= 20; y++)
	{
		[loop] for (int x = 0; x <= s && x <= 20; x++)
		{
			ret += CalcShadow_4xPCF(ShadowMapTexC + float2(x - (s / 2.f), y - (s / 2.f)) * scale / SM_ShadowMapSize, Depth);
			
		}
	}
	return ret / (size * size);
}

struct SHADOW_VERTEX_IN
{
	float3 Pos : POSITION;
};

struct SHADOW_VERTEX_OUT
{
	float4 Pos : POSITION;
	float2 Depth : TEXCOORD0;
};

SHADOW_VERTEX_OUT DepthVS(SHADOW_VERTEX_IN i)
{
	SHADOW_VERTEX_OUT o;
			
	o.Pos = mul(mul(float4(i.Pos, 1.f), SM_matWorld), SM_matSM);

	o.Depth.xy = o.Pos.zw;
	
	return o;
}

float4 DepthPS(SHADOW_VERTEX_OUT i) : COLOR
{
	return g_bUseNVShadowMap ? 0 : i.Depth.x/i.Depth.y;
}

float4 DepthIDPS(SHADOW_VERTEX_OUT i) : COLOR
{
	return float4(g_bUseNVShadowMap ? 0 : i.Depth.x/i.Depth.y, SM_ID, 0, 0);
}

/*technique ShadowDepth
{
	pass P0
    { 
		VertexShader = compile vs_2_0 DepthVS();
        PixelShader  = compile ps_2_0 DepthPS();
	}
}
*/