// 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     : TEXCOORD1,
			 // 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
	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 = normalize(mul(rotation, lightDir));
	
	
}

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

			  out float4 colour	: COLOR,

			  uniform float4 lightDiffuse,
			  uniform sampler2D   normalMap,
			  uniform samplerCUBE normalCubeMap)
{
	// 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);
	
}

/* Vertex program which includes specular component */
void specular_vp(float4 position	: POSITION,
			 	 float3 normal		: NORMAL,
			 	 float2 uv			: TEXCOORD0,
			 	 float3 tangent     : TEXCOORD1,
			 	 // 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 = normalize(mul(rotation, lightDir));

	// Calculate half-angle in tangent space
	float3 eyeDir = 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,
			  uniform samplerCUBE normalCubeMap, 
			  uniform samplerCUBE normalCubeMap2) // 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);
	
}
			 
