/*=============================================================================
	GpuSkinVertexFactory.hlsl: GPU skinned vertex factory shader code
	Copyright © 2006 Epic Games, Inc. All Rights Reserved.
=============================================================================*/
float4x4 LocalToWorld REGISTER(228);
float4x4 PreviousLocalToWorld REGISTER(232);
float3x3 WorldToLocal REGISTER(236);

#if PS3
#define MaxBoneInfluences 4
#else
float MaxBoneInfluences;
#endif

#define MAX_BONES 75

struct FVertexFactoryInput
{
	float4	Position		: POSITION;
	half3	TangentX		: TANGENT;
	half3	TangentY		: BINORMAL;
	half3	TangentZ		: NORMAL;	
	int4	BlendIndices	: BLENDINDICES0;
	float4	BlendWeights	: BLENDWEIGHT0;	
#if NUM_MATERIAL_TEXCOORDS
	float2	TexCoords[NUM_MATERIAL_TEXCOORDS] : TEXCOORD0;
#endif
};

struct FVertexFactoryInterpolants
{
#if WORLD_COORDS
	// xyz=normal w=determinant
	float4	TangentBasisNormal	: COLOR0;
	float3	TangentBasisTangent	: COLOR1;
#endif
#if NUM_MATERIAL_TEXCOORDS
	float2	TexCoords[NUM_MATERIAL_TEXCOORDS]	: TEXCOORD0;
#endif
// Cg needs to have something in here
#if COMPILER_CG //&& !WORLD_COORDS && !NUM_MATERIAL_TEXCOORDS
	float4 Dummy : TEXCOORD9;
#endif
};

FMaterialParameters GetMaterialParameters(FVertexFactoryInterpolants Interpolants)
{
	FMaterialParameters	Result;
#if NUM_MATERIAL_TEXCOORDS
	for(int CoordinateIndex = 0;CoordinateIndex < NUM_MATERIAL_TEXCOORDS;CoordinateIndex++)
		Result.TexCoords[CoordinateIndex] = Interpolants.TexCoords[CoordinateIndex];
#endif
	Result.VertexColor = 1;
	Result.TangentNormal = 0;
	Result.TangentCameraVector = 0;
	Result.TangentReflectionVector = 0;
	Result.ScreenPosition = 0;
	Result.TangentLightVector = 0;
#if WORLD_COORDS
	Result.TangentBasisInverse = CalcInvTangentBasis(Interpolants.TangentBasisNormal,Interpolants.TangentBasisTangent);
#endif
	return Result;
}

#if NEEDS_LIGHTMAP_COORDINATE
float2 GetLightMapCoordinate(FVertexFactoryInterpolants Interpolants)
{
	return float2(0,0);
}
#endif


/** skinning matrices stored in 3x4 order */
#if PS3
float3x4 BoneMatrices[MAX_BONES] REGISTER(0);
#else
float4x3 BoneMatrices[MAX_BONES]; 
#endif


/** transform point by weighted sum of skinning matrices */
float3 SkinPoint( float4 Point, FVertexFactoryInput Input )
{
	float3 Result=0;
	for( int i=0; i < MaxBoneInfluences; i++ )
	{
		if( Input.BlendWeights[i] > 0 )
		{
			Result += Input.BlendWeights[i] * MulBone(BoneMatrices[Input.BlendIndices[i]], float4(Point.xyz,1));
		}
	}
	return Result;	
}

/** transform the tangent basis vectors */
float3x3 SkinTangents( FVertexFactoryInput Input )
{
	float3x3 Result=0;	
	for( int i=0; i < MaxBoneInfluences; i++ )
	{
		if( Input.BlendWeights[i] > 0 )
		{
			Result[0] += Input.BlendWeights[i] * MulBone(BoneMatrices[Input.BlendIndices[i]], float4(Input.TangentX / 127.5 - 1,0));
			Result[1] += Input.BlendWeights[i] * MulBone(BoneMatrices[Input.BlendIndices[i]], float4(Input.TangentY / 127.5 - 1,0));
			Result[2] += Input.BlendWeights[i] * MulBone(BoneMatrices[Input.BlendIndices[i]], float4(Input.TangentZ / 127.5 - 1,0));
		}
	}
	return Result;
}

float4 CalcWorldPosition(FVertexFactoryInput Input)
{
	return MulMatrix( LocalToWorld, float4(SkinPoint(Input.Position,Input),1) );
}

float4 VertexFactoryGetWorldPosition(FVertexFactoryInput Input,out FVertexFactoryInterpolants Interpolants)
{
#if NUM_MATERIAL_TEXCOORDS
	for(int CoordIndex = 0;CoordIndex < NUM_MATERIAL_TEXCOORDS;CoordIndex++)
		Interpolants.TexCoords[CoordIndex] = Input.TexCoords[CoordIndex];
#endif

#if WORLD_COORDS
	float3x3 TangentBasis = SkinTangents(Input);	
	Interpolants.TangentBasisNormal = float4(TangentBasis[2],determinant(TangentBasis)) * 0.5 + 0.5;
	Interpolants.TangentBasisTangent = TangentBasis[0] * 0.5 + 0.5;	
#endif

	return CalcWorldPosition(Input);
}

/** for depth-only pass */
float4 VertexFactoryGetWorldPositionOnly(FVertexFactoryInput Input)
{
	return CalcWorldPosition(Input);
}

/** for motion blur */
float4 VertexFactoryGetPreviousWorldPosition(FVertexFactoryInput Input,out FVertexFactoryInterpolants Interpolants)
{
#if NUM_MATERIAL_TEXCOORDS
	for(int CoordIndex = 0;CoordIndex < NUM_MATERIAL_TEXCOORDS;CoordIndex++)
		Interpolants.TexCoords[CoordIndex] = Input.TexCoords[CoordIndex];
#endif
#if WORLD_COORDS
	float3x3 TangentBasis = SkinTangents(Input);	
	Interpolants.TangentBasisNormal = float4(TangentBasis[2],determinant(TangentBasis)) * 0.5 + 0.5;
	Interpolants.TangentBasisTangent = TangentBasis[0] * 0.5 + 0.5;	
#endif
	return MulMatrix( PreviousLocalToWorld, float4(SkinPoint(Input.Position,Input),1) );
}

float3 VertexFactoryWorldToTangentSpace(FVertexFactoryInput Input,half3 WorldVector)
{
	// we use a straight mul here because we are generating the matrix, so we don't worry about column major vs row major (which is what MulMatrix manages per-platform)
	return mul( SkinTangents(Input), MulMatrix( WorldToLocal, WorldVector) );
}
