// General functions

// Expand a range-compressed vector
float3 expand(float3 v)
{
	return (v - 0.5) * 2;
}


/* Bump mapping vertex program
   In this program, we want to calculate the tangent space light vector
   on a per-vertex level which will get passed to the fragment program,
   or to the fixed function dot3 operation, to produce the per-pixel
   lighting effect. 
*/
void main_vp(float4 position	: POSITION,
			 float3 normal		: NORMAL,
			 float2 uv			: TEXCOORD0,
			 float3 tangent     : TANGENT0,
			 // outputs
			 out float4 oPosition    : POSITION,
			 out float2 oUv			 : TEXCOORD0,
			 out float3 oTSLightDir	 : TEXCOORD1,
			 // parameters
			 uniform float4 lightPosition, // object space
			 uniform float4x4 worldViewProj)
{
	// calculate output position
	oPosition = mul(worldViewProj, position);

	// pass the main uvs straight through unchanged
	oUv = uv;

	// calculate tangent space light vector
	// Get object space light direction
	// Non-normalised since we'll do that in the fragment program anyway
	float3 lightDir = lightPosition.xyz -  (position * lightPosition.w);

	// Calculate the binormal (NB we assume both normal and tangent are
	// already normalised)
	// NB looks like nvidia cross params are BACKWARDS to what you'd expect
	// this equates to NxT, not TxN
	float3 binormal = cross(tangent, normal);
	
	// Form a rotation matrix out of the vectors
	float3x3 rotation = float3x3(tangent, binormal, normal);
	
	// Transform the light vector according to this matrix
	oTSLightDir = mul(rotation, lightDir);
	
	
}

/* Bump mapping vertex program for shadow receiving
   In this program, we want to calculate the tangent space light vector
   on a per-vertex level which will get passed to the fragment program,
   or to the fixed function dot3 operation, to produce the per-pixel
   lighting effect. 
*/
void main_shadowreceiver_vp(float4 position	: POSITION,
			 float3 normal		: NORMAL,
			 float2 uv			: TEXCOORD0,
			 float3 tangent     : TANGENT0,
			 
			 // outputs
			 out float4 oPosition    : POSITION,
			 out float4 uvproj		 : TEXCOORD0,
			 out float2 oUv	 		 : TEXCOORD1,
			 out float3 oTSLightDir  : TEXCOORD2,
			 
			 // parameters
			 uniform float4 lightPosition, // object space
			 uniform float4x4 worldViewProj,
			 uniform float4x4 worldMatrix,
			 uniform float4x4 texViewProj)
{
	// calculate output position
	oPosition = mul(worldViewProj, position);

	// pass the main uvs straight through unchanged
	oUv = uv;

	// calculate tangent space light vector
	// Get object space light direction
	// Non-normalised since we'll do that in the fragment program anyway
	float3 lightDir = lightPosition.xyz -  (position * lightPosition.w);

	// Calculate the binormal (NB we assume both normal and tangent are
	// already normalised)
	// NB looks like nvidia cross params are BACKWARDS to what you'd expect
	// this equates to NxT, not TxN
	float3 binormal = cross(tangent, normal);
	
	// Form a rotation matrix out of the vectors
	float3x3 rotation = float3x3(tangent, binormal, normal);
	
	// Transform the light vector according to this matrix
	oTSLightDir = mul(rotation, lightDir);

	// Projection
    uvproj = mul(worldMatrix, position);
	uvproj = mul(texViewProj, uvproj);
	
}


void main_fp( float2 uv			: TEXCOORD0,
			  float3 TSlightDir : TEXCOORD1,

			  out float4 colour	: COLOR,

			  uniform float4 lightDiffuse,
			  uniform sampler2D   normalMap : register(s0),
			  uniform samplerCUBE normalCubeMap : register(s1) )
{
	// retrieve normalised light vector, expand from range-compressed
	float3 lightVec = expand(texCUBE(normalCubeMap, TSlightDir).xyz);

	// get bump map vector, again expand from range-compressed
	float3 bumpVec = expand(tex2D(normalMap, uv).xyz);

	// Calculate dot product
	colour = lightDiffuse * dot(bumpVec, lightVec);
	
}

void main_shadowreceiver_fp(
			  float4 uvproj		: TEXCOORD0,
			  float2 uv			: TEXCOORD1,
			  float3 TSlightDir : TEXCOORD2,

			  out float4 colour	: COLOR,

			  uniform float4 lightDiffuse,
			  uniform sampler2D   shadowMap : register(s0),
			  uniform sampler2D   normalMap : register(s1),
			  uniform samplerCUBE normalCubeMap : register(s2))
{


	// retrieve normalised light vector, expand from range-compressed
	float3 lightVec = expand(texCUBE(normalCubeMap, TSlightDir).xyz);

	// get bump map vector, again expand from range-compressed
	float3 bumpVec = expand(tex2D(normalMap, uv).xyz);

	// get shadow value
	float3 shadow = tex2Dproj(shadowMap, uvproj).xyz;

	// Calculate dot product
	colour = float4(shadow * lightDiffuse * dot(bumpVec, lightVec), 1.0f);
	
}

/* Vertex program which includes specular component */
void specular_vp(float4 position	: POSITION,
			 	 float3 normal		: NORMAL,
			 	 float2 uv			: TEXCOORD0,
			 	 float3 tangent     : TANGENT0,
			 	 // outputs
			 	 out float4 oPosition    : POSITION,
			 	 out float2 oUv			 : TEXCOORD0,
			 	 out float3 oTSLightDir	 : TEXCOORD1,
				 out float3 oTSHalfAngle : TEXCOORD2,
			 	 // parameters
				 uniform float4 lightPosition, // object space
			 	 uniform float3 eyePosition,   // object space
			 	 uniform float4x4 worldViewProj)
{
	// calculate output position
	oPosition = mul(worldViewProj, position);

	// pass the main uvs straight through unchanged
	oUv = uv;

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

	// Calculate the binormal (NB we assume both normal and tangent are
	// already normalised)
	// NB looks like nvidia cross params are BACKWARDS to what you'd expect
	// this equates to NxT, not TxN
	float3 binormal = cross(tangent, normal);
	
	// Form a rotation matrix out of the vectors
	float3x3 rotation = float3x3(tangent, binormal, normal);
	
	// Transform the light vector according to this matrix
	oTSLightDir = mul(rotation, lightDir);

	// Calculate half-angle in tangent space
	float3 eyeDir = normalize(eyePosition - position.xyz);
	float3 halfAngle = normalize(eyeDir + lightDir);
	oTSHalfAngle = mul(rotation, halfAngle);
	
	
}

/* Fragment program which supports specular component */
void specular_fp( float2 uv			: TEXCOORD0,
			  float3 TSlightDir : TEXCOORD1,
			  float3 TShalfAngle: TEXCOORD2,

			  out float4 colour	: COLOR,

			  uniform float4 lightDiffuse,
			  uniform float4 lightSpecular,
			  uniform sampler2D   normalMap : register(s0),
			  uniform samplerCUBE normalCubeMap : register(s1), 
			  uniform samplerCUBE normalCubeMap2 : register(s2)) // we need this second binding to be compatible with ps_1_1, ps_2_0 could reuse the other
{
	// retrieve normalised light vector, expand from range-compressed
	float3 lightVec = expand(texCUBE(normalCubeMap, TSlightDir).xyz);

	// retrieve half angle and normalise through cube map
	float3 halfAngle = expand(texCUBE(normalCubeMap2, TShalfAngle).xyz);

	// get bump map vector, again expand from range-compressed
	float3 bumpVec = expand(tex2D(normalMap, uv).xyz);

	// Pre-raise the specular exponent to the eight power
	// Note we have no 'pow' function in basic fragment programs, if we were willing to accept compatibility
	// with ps_2_0 / arbfp1 and above, we could have a variable shininess parameter
	// This is equivalent to 
	float specFactor = dot(bumpVec, halfAngle);
	for (int i = 0; i < 3; ++i)
		specFactor *= specFactor;
	

	// Calculate dot product for diffuse
	colour = (lightDiffuse * saturate(dot(bumpVec, lightVec))) + 
			(lightSpecular * specFactor);
	
}
			 
