void shadow_caster_vs(
	float4 position		 : POSITION,
	out float4 oPosition : POSITION,
	out float2 oDepthInfo : TEXCOORD0,
	
	uniform float4x4 wvpMatrix)
{
	// output position.
	oPosition = mul(wvpMatrix, position);

	// depth info for the fragment.
	oDepthInfo.x = oPosition.z;
	oDepthInfo.y = oPosition.w;
}

void shadow_caster_skinning_1weight_vs(
	float4 position		 : POSITION,
	out float4 oPosition : POSITION,
	out float2 oDepthInfo : TEXCOORD0,
	float4  blendIdx : BLENDINDICES,
	float4 blendWgt : BLENDWEIGHT,
	uniform float4x4 viewProjectionMatrix,
	uniform float3x4  worldMatrix3x4Array[80])
{
	// output position.
	oPosition.xyz = mul(worldMatrix3x4Array[blendIdx.x], position) * blendWgt.x;
	oPosition.w = 1;
	oPosition = mul(viewProjectionMatrix, oPosition);


	// depth info for the fragment.
	oDepthInfo.x = oPosition.z;
	oDepthInfo.y = oPosition.w;
}

void shadow_caster_skinning_2weight_vs(
	float4 position		 : POSITION,
	out float4 oPosition : POSITION,
	out float2 oDepthInfo : TEXCOORD0,
	float4 blendIdx : BLENDINDICES,
	float4 blendWgt : BLENDWEIGHT,
	
	uniform float4x4 viewProjectionMatrix,
	uniform float3x4  worldMatrix3x4Array[80])
{
	// output position.
	oPosition.xyz = mul(worldMatrix3x4Array[blendIdx.x], position) * blendWgt.x +
				mul(worldMatrix3x4Array[blendIdx.y], position) * blendWgt.y;
	oPosition.w = 1;
	oPosition = mul(viewProjectionMatrix, oPosition);


	// depth info for the fragment.
	oDepthInfo.x = oPosition.z;
	oDepthInfo.y = oPosition.w;
}


void shadow_caster_skinning_3weight_vs(
	float4 position		 : POSITION,
	out float4 oPosition : POSITION,
	out float2 oDepthInfo : TEXCOORD0,
	float4 blendIdx : BLENDINDICES,
	float4 blendWgt : BLENDWEIGHT,
	
	uniform float4x4 viewProjectionMatrix,
	uniform float3x4  worldMatrix3x4Array[80])
{
	// output position.
	oPosition.xyz = mul(worldMatrix3x4Array[blendIdx.x], position) * blendWgt.x +
				mul(worldMatrix3x4Array[blendIdx.y], position) * blendWgt.y +
				mul(worldMatrix3x4Array[blendIdx.z], position) * blendWgt.z;
	oPosition.w = 1;
	oPosition = mul(viewProjectionMatrix, oPosition);


	// depth info for the fragment.
	oDepthInfo.x = oPosition.z;
	oDepthInfo.y = oPosition.w;
}


void shadow_caster_skinning_4weight_vs(
	float4 position		 : POSITION,
	out float4 oPosition : POSITION,
	out float2 oDepthInfo : TEXCOORD0,
	float4 blendIdx : BLENDINDICES,
	float4 blendWgt : BLENDWEIGHT,
	
	uniform float4x4 viewProjectionMatrix,
	uniform float3x4  worldMatrix3x4Array[80])
{
	// output position.
	oPosition.xyz = mul(worldMatrix3x4Array[blendIdx.x], position) * blendWgt.x +
				mul(worldMatrix3x4Array[blendIdx.y], position) * blendWgt.y +
				mul(worldMatrix3x4Array[blendIdx.z], position) * blendWgt.z +
				mul(worldMatrix3x4Array[blendIdx.w], position) * blendWgt.w;
	oPosition.w = 1;
	oPosition = mul(viewProjectionMatrix, oPosition);


	// depth info for the fragment.
	oDepthInfo.x = oPosition.z;
	oDepthInfo.y = oPosition.w;
}

void shadow_receiver_vs(
	float4 position				: POSITION,
	float3 normal				: NORMAL,
	float2 uv					: TEXCOORD0,

	out float4 oPosition		: POSITION,
	out float3 oUv				: TEXCOORD0,
	out float3 oLightDir		: TEXCOORD1,
	out float3 oHalfAngle		: TEXCOORD2,
	out float4 oLightPosition0	: TEXCOORD3,
	out float4 oLightPosition1	: TEXCOORD4,
	out float4 oLightPosition2	: TEXCOORD5,
	out float3 oNormal			: TEXCOORD6,

	uniform float4 lightPosition,				// object space
	uniform float3 eyePosition,					// object space
	uniform float4x4 worldViewProjMatrix,
	uniform float4x4 texViewProjMatrix0,
	uniform float4x4 texViewProjMatrix1,
	uniform float4x4 texViewProjMatrix2)
{
	// calculate output position
	oPosition = mul(worldViewProjMatrix, position);

	// pass the main uvs straight through unchanged
	oUv.xy = uv;
	oUv.z = oPosition.z;

	// calculate tangent space light vector
	// Get object space light direction
	oLightDir = normalize(lightPosition.xyz - (position * lightPosition.w).xyz);

	// Calculate half-angle in tangent space
	float3 eyeDir = normalize(eyePosition - position.xyz);
	oHalfAngle = normalize(eyeDir + oLightDir);	

	// Calculate the position of vertex in light space
	oLightPosition0 = mul(texViewProjMatrix0, position);
	oLightPosition1 = mul(texViewProjMatrix1, position);
	oLightPosition2 = mul(texViewProjMatrix2, position);

	oNormal = normal;
}

float shadowPCF(sampler2D shadowMap, float4 shadowMapPos, float2 offset)
{
	shadowMapPos = shadowMapPos / shadowMapPos.w;
	float2 uv = shadowMapPos.xy;
	float3 o = float3(offset, -offset.x) * 0.3f;

	// Note: We using 2x2 PCF. Good enough and is alot faster.
	float c =	(shadowMapPos.z <= tex2D(shadowMap, uv.xy - o.xy).r) ? 1 : 0; // top left
	c +=		(shadowMapPos.z <= tex2D(shadowMap, uv.xy + o.xy).r) ? 1 : 0; // bottom right
	c +=		(shadowMapPos.z <= tex2D(shadowMap, uv.xy + o.zy).r) ? 1 : 0; // bottom left
	c +=		(shadowMapPos.z <= tex2D(shadowMap, uv.xy - o.zy).r) ? 1 : 0; // top right
	//float c =	(shadowMapPos.z <= tex2Dlod(shadowMap, uv.xyyy - o.xyyy).r) ? 1 : 0; // top left
	//c +=		(shadowMapPos.z <= tex2Dlod(shadowMap, uv.xyyy + o.xyyy).r) ? 1 : 0; // bottom right
	//c +=		(shadowMapPos.z <= tex2Dlod(shadowMap, uv.xyyy + o.zyyy).r) ? 1 : 0; // bottom left
	//c +=		(shadowMapPos.z <= tex2Dlod(shadowMap, uv.xyyy - o.zyyy).r) ? 1 : 0; // top right
	return c / 4;
}

void shadow_receiver_ps(
	float3 uv				: TEXCOORD0,
	float3 OSlightDir		: TEXCOORD1,
	float3 OShalfAngle		: TEXCOORD2,
	float4 LightPosition0	: TEXCOORD3,
	float4 LightPosition1	: TEXCOORD4,
	float4 LightPosition2	: TEXCOORD5,
	float3 normal			: TEXCOORD6,

	out float4 oColour		: COLOR,

	uniform float4 invShadowMapSize0,
	uniform float4 invShadowMapSize1,
	uniform float4 invShadowMapSize2,
	uniform float4 pssmSplitPoints,
	uniform sampler2D diffuse,
	uniform sampler2D specular,
	uniform sampler2D normalMap,
	uniform sampler2D shadowMap0,
	uniform sampler2D shadowMap1,
	uniform sampler2D shadowMap2,
	uniform float4 lightDiffuse,
	uniform float4 lightSpecular,
	uniform float4 ambient
	)
{
	// calculate shadow
	float shadowing = 1.0f;
	float4 splitColour;
	if (uv.z <= pssmSplitPoints.y)
	{
		splitColour = float4(0.1, 0, 0, 1);
		shadowing = shadowPCF(shadowMap0, LightPosition0, invShadowMapSize0.xy);
	}
	else if (uv.z <= pssmSplitPoints.z)
	{
		splitColour = float4(0, 0.1, 0, 1);
		shadowing = shadowPCF(shadowMap1, LightPosition1, invShadowMapSize1.xy);
	}
	else
	{
		splitColour = float4(0.1, 0.1, 0, 1);
		shadowing = shadowPCF(shadowMap2, LightPosition2, invShadowMapSize2.xy);
	}

	// retrieve normalised light vector, expand from range-compressed
	float3 lightVec = normalize(OSlightDir);

	// retrieve half angle and normalise through cube map
	float3 halfAngle = normalize(OShalfAngle);

	// get diffuse colour
	float4 diffuseColour = tex2D(diffuse, uv.xy);

	// specular
	float4 specularColour = tex2D(specular, uv.xy);
	float shininess = specularColour.w;
	specularColour.w = 1;

	// calculate lit value.
	float4 lighting = lit(dot(normal, lightVec), dot(normal, halfAngle), shininess * 128) * shadowing;

	// final lighting with diffuse and spec
	oColour = (diffuseColour * clamp(ambient + lightDiffuse * lighting.y, 0, 1)) + (lightSpecular * specularColour * lighting.z);
	oColour.w = diffuseColour.w;

	//oColour += splitColour;
}

void shadow_caster_ps(
	out float4 oColour	: COLOR)
{	
	oColour = 0.5;
}
