#include "DualQuaternion_Common.cg"

//Dual quaternion skinning with per-vertex antipodality handling (this is the most robust, but not the most efficient way):
void dualQuaternionHardwareSkinningTwoWeights_vp(	
	float4 position : POSITION,
	float3 normal   : NORMAL,
	float2 uv       : TEXCOORD0,
	float4 blendIdx : BLENDINDICES,
	float4 blendWgt : BLENDWEIGHT,
	
	out float4 oPosition : POSITION,
	out float2 oUv       : TEXCOORD0,
	out float4 colour    : COLOR,
	// Support up to 24 bones of float3x4
	// vs_1_1 only supports 96 params so more than this is not feasible
	uniform float2x4 worldDualQuaternion2x4Array[24],
	uniform float4x4 viewProjectionMatrix,
	uniform float4   lightPos[2],
	uniform float4   lightDiffuseColour[2],
	uniform float4   ambient,
	uniform float4   diffuse)
{		
	float2x4 blendDQ = blendTwoWeightsAntipod(blendWgt, blendIdx, worldDualQuaternion2x4Array);

	float len = length(blendDQ[0]);
	blendDQ /= len;

	float3 blendPosition = calculateBlendPosition(position.xyz, blendDQ);

	//No need to normalize, the magnitude of the normal is preserved because only rotation is performed
	float3 blendNormal = calculateBlendNormal(normal, blendDQ);
	
	oPosition = mul(viewProjectionMatrix, float4(blendPosition, 1.0));
	
	// Lighting - support point and directional
	float3 lightDir0 = normalize(lightPos[0].xyz - (blendPosition.xyz * lightPos[0].w));
	float3 lightDir1 = normalize(lightPos[1].xyz - (blendPosition.xyz * lightPos[1].w));

	oUv = uv;

	colour = diffuse * (ambient + (saturate(dot(lightDir0, blendNormal)) * lightDiffuseColour[0]) + 
		(saturate(dot(lightDir1, blendNormal)) * lightDiffuseColour[1]));		
}


//Shadow caster pass
void dualQuaternionHardwareSkinningTwoWeightsCaster_vp(
	float4 position : POSITION,
	float4 blendIdx : BLENDINDICES,
	float4 blendWgt : BLENDWEIGHT,
	
	out float4 oPosition : POSITION,
	out float4 colour    : COLOR,
	// Support up to 24 bones of float3x4
	// vs_1_1 only supports 96 params so more than this is not feasible
	uniform float2x4 worldDualQuaternion2x4Array[24],
	uniform float4x4 viewProjectionMatrix,
	uniform float4   ambient)
{
	float2x4 blendDQ = blendTwoWeightsAntipod(blendWgt, blendIdx, worldDualQuaternion2x4Array);

	float len = length(blendDQ[0]);
	blendDQ /= len;

	float3 blendPosition = calculateBlendPosition(position.xyz, blendDQ);

	// view / projection
	oPosition = mul(viewProjectionMatrix, float4(blendPosition, 1.0));
	
	colour = ambient;
}

//Two-phase skinning: dual quaternion skinning with scale and shear transformations
void dualQuaternionHardwareSkinningTwoWeightsTwoPhase_vp(
	float4 position : POSITION,
	float3 normal   : NORMAL,
	float2 uv       : TEXCOORD0,
	float4 blendIdx : BLENDINDICES,
	float4 blendWgt : BLENDWEIGHT,
	

	out float4 oPosition : POSITION,
	out float2 oUv       : TEXCOORD0,
	out float4 colour    : COLOR,
	// Support up to 24 bones of float3x4
	// vs_1_1 only supports 96 params so more than this is not feasible
	uniform float2x4 worldDualQuaternion2x4Array[24],
	uniform float3x4 scaleM[24],
	uniform float4x4 viewProjectionMatrix,
	uniform float4   lightPos[2],
	uniform float4   lightDiffuseColour[2],
	uniform float4   ambient,
	uniform float4   diffuse)
{
	//First phase - applies scaling and shearing:
	float3x4 blendS = blendWgt.x*scaleM[blendIdx.x];
	blendS += blendWgt.y*scaleM[blendIdx.y];	
		
	float3 pass1_position = mul(blendS, position);
	float3x3 blendSrotAT = adjointTransposeMatrix(float3x3(blendS));
	float3 pass1_normal = normalize(mul(blendSrotAT, normal.xyz));

	//Second phase
	float2x4 blendDQ = blendTwoWeightsAntipod(blendWgt, blendIdx, worldDualQuaternion2x4Array);
	
	float len = length(blendDQ[0]);
	blendDQ /= len;

	float3 blendPosition = calculateBlendPosition(pass1_position, blendDQ);

	//No need to normalize, the magnitude of the normal is preserved because only rotation is performed
	float3 blendNormal = calculateBlendNormal(pass1_normal, blendDQ);

	oPosition = mul(viewProjectionMatrix, float4(blendPosition, 1.0));

	// Lighting - support point and directional
	float3 lightDir0 = normalize(lightPos[0].xyz - (blendPosition.xyz * lightPos[0].w));
	float3 lightDir1 = normalize(lightPos[1].xyz - (blendPosition.xyz * lightPos[1].w));

	oUv = uv;
	colour = diffuse * (ambient + (saturate(dot(lightDir0, blendNormal)) * lightDiffuseColour[0]) + 
		(saturate(dot(lightDir1, blendNormal)) * lightDiffuseColour[1]));
}

//Two-phase skinning shadow caster pass
void dualQuaternionHardwareSkinningTwoWeightsTwoPhaseCaster_vp(
	float4 position : POSITION,
	float4 blendIdx : BLENDINDICES,
	float4 blendWgt : BLENDWEIGHT,
	
	out float4 oPosition : POSITION,
	out float4 colour    : COLOR,
	// Support up to 24 bones of float3x4
	// vs_1_1 only supports 96 params so more than this is not feasible
	uniform float2x4 worldDualQuaternion2x4Array[24],
	uniform float3x4 scaleM[24],
	uniform float4x4 viewProjectionMatrix,
	uniform float4   ambient)
{
	//First phase - applies scaling and shearing:
	float3x4 blendS = blendWgt.x*scaleM[blendIdx.x];
	blendS += blendWgt.y*scaleM[blendIdx.y];	
		
	float3 pass1_position = mul(blendS, position);

	//Second phase
	float2x4 blendDQ = blendTwoWeightsAntipod(blendWgt, blendIdx, worldDualQuaternion2x4Array);
	
	float len = length(blendDQ[0]);
	blendDQ /= len;

	float3 blendPosition = calculateBlendPosition(pass1_position, blendDQ);

	// view / projection
	oPosition = mul(viewProjectionMatrix, float4(blendPosition, 1.0));
	
	colour = ambient;
}

