/*=============================================================================
	LocalDecalVertexFactory.hlsl: Local decal vertex factory shader code.
	Copyright 1998-2007 Epic Games, Inc. All Rights Reserved.
=============================================================================*/

float4x4 LocalToWorld;
// @todo DB: PreviousLocalToWorld not needed for decals
float4x4 PreviousLocalToWorld;
float3x3 WorldToLocal;
float4 ShadowCoordinateScaleBias;

float4x4	BoneToDecal;
float3		DecalLocation;
float2		DecalOffset;

struct FVertexFactoryInput
{
	float4	Position	: POSITION;
	half3	TangentX	: TANGENT;
	// TangentZ.w contains sign of tangent basis determinant
	half4	TangentZ	: NORMAL;
#if NUM_MATERIAL_TEXCOORDS
	float2	TexCoords[NUM_MATERIAL_TEXCOORDS] : TEXCOORD0;
#endif
#if NEEDS_VERTEX_LIGHTMAP
	float4 LightMapA : TEXCOORD5;
	float4 LightMapB : TEXCOORD6;
	float4 LightMapC : TEXCOORD7;
#elif NEEDS_SIMPLE_VERTEX_LIGHTMAP
	float4 LightMapA : TEXCOORD5;
#endif

#if NEEDS_LIGHTMAP_COORDINATE
	float2	LightMapCoordinate : COLOR;
#endif
};

struct FVertexFactoryInterpolants
{
#if WORLD_COORDS
	// xyz=normal w=determinant
	float4	TangentBasisNormal	: COLOR0;
	float3	TangentBasisTangent	: COLOR1;
#endif
#if NEEDS_LIGHTMAP_COORDINATE
	float2	LightMapCoordinate					: TEXCOORD0;
#if NUM_MATERIAL_TEXCOORDS
	float4	TexCoords[(NUM_MATERIAL_TEXCOORDS+1)/2]	: TEXCOORD1;
#endif
#else
#if NUM_MATERIAL_TEXCOORDS
	float4	TexCoords[(NUM_MATERIAL_TEXCOORDS+1)/2]	: TEXCOORD0;
#endif
#endif

#if !COMPILER_SUPPORTS_EMPTY_STRUCTS && !WORLD_COORDS && !NEEDS_LIGHTMAP_COORDINATE && !NUM_MATERIAL_TEXCOORDS
	float4 Dummy : TEXCOORD0;
#endif
};

FMaterialParameters GetMaterialParameters(FVertexFactoryInterpolants Interpolants)
{
	FMaterialParameters	Result;
#if NUM_MATERIAL_TEXCOORDS
	UNROLL
	for(int CoordinateIndex = 0;CoordinateIndex < NUM_MATERIAL_TEXCOORDS;CoordinateIndex += 2)
	{
		Result.TexCoords[CoordinateIndex] = Interpolants.TexCoords[CoordinateIndex/2].xy;
		if(CoordinateIndex + 1 < NUM_MATERIAL_TEXCOORDS)
		{
			Result.TexCoords[CoordinateIndex + 1] = Interpolants.TexCoords[CoordinateIndex/2].wz;
		}
	}
#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 Interpolants.LightMapCoordinate;
}
#endif

#if NEEDS_VERTEX_LIGHTMAP
void VertexFactoryGetVertexLightMap(FVertexFactoryInput Input,out float4 LightMapA,out float4 LightMapB,out float4 LightMapC)
{
	LightMapA = Input.LightMapA;
	LightMapB = Input.LightMapB;
	LightMapC = Input.LightMapC;
}
#elif NEEDS_SIMPLE_VERTEX_LIGHTMAP
void VertexFactoryGetSimpleVertexLightMap(FVertexFactoryInput Input,out float4 LightMapA)
{
	LightMapA = Input.LightMapA;
}
#endif

float4 CalcWorldPosition(FVertexFactoryInput Input)
{
	return MulMatrix(LocalToWorld,Input.Position);
}

float2 ComputeDecalTexCoord(float4 Point)
{
	float2	OutPos = MulMatrix(BoneToDecal, Point-float4(DecalLocation,1)).xy;
	return float2(-OutPos.x+0.5+DecalOffset.x, -OutPos.y+0.5+DecalOffset.y );
}

float4 VertexFactoryGetWorldPosition(FVertexFactoryInput Input,out FVertexFactoryInterpolants Interpolants)
{
#if NUM_MATERIAL_TEXCOORDS
	// Ensure the unused components of the last packed texture coordinate are initialized.
	Interpolants.TexCoords[(NUM_MATERIAL_TEXCOORDS + 1) / 2 - 1] = 0;

	float2 DecalTexCoords = ComputeDecalTexCoord( Input.Position );
	UNROLL
	for(int CoordinateIndex = 0;CoordinateIndex < NUM_MATERIAL_TEXCOORDS;CoordinateIndex += 2)
	{
		Interpolants.TexCoords[CoordinateIndex / 2].xy = DecalTexCoords.xy;
		if(CoordinateIndex + 1 < NUM_MATERIAL_TEXCOORDS)
		{
			Interpolants.TexCoords[CoordinateIndex / 2].wz = DecalTexCoords.xy;
		}
	}
#endif

#if NEEDS_LIGHTMAP_COORDINATE
	Interpolants.LightMapCoordinate = Input.LightMapCoordinate * ShadowCoordinateScaleBias.xy + ShadowCoordinateScaleBias.wz;
#endif

#if WORLD_COORDS
	Interpolants.TangentBasisNormal = TangentNorm(Input.TangentZ);
	Interpolants.TangentBasisTangent = TangentNorm(Input.TangentX);	
#endif

#if !COMPILER_SUPPORTS_EMPTY_STRUCTS && !WORLD_COORDS && !NEEDS_LIGHTMAP_COORDINATE && !NUM_MATERIAL_TEXCOORDS
	Interpolants.Dummy = float4(0,0,0,0);
#endif

	return CalcWorldPosition(Input);
}

/** for depth-only pass */
float4 VertexFactoryGetWorldPositionOnly(FVertexFactoryInput Input)
{
	return CalcWorldPosition(Input);
}

float4 VertexFactoryGetPreviousWorldPosition(FVertexFactoryInput Input,out FVertexFactoryInterpolants Interpolants)
{
#if NUM_MATERIAL_TEXCOORDS
	// Ensure the unused components of the last packed texture coordinate are initialized.
	Interpolants.TexCoords[(NUM_MATERIAL_TEXCOORDS + 1) / 2 - 1] = 0;

	float2 DecalTexCoords = ComputeDecalTexCoord( Input.Position );
	UNROLL
	for(int CoordinateIndex = 0;CoordinateIndex < NUM_MATERIAL_TEXCOORDS;CoordinateIndex += 2)
	{
		Interpolants.TexCoords[CoordinateIndex / 2].xy = DecalTexCoords.xy;
		if(CoordinateIndex + 1 < NUM_MATERIAL_TEXCOORDS)
		{
			Interpolants.TexCoords[CoordinateIndex / 2].wz = DecalTexCoords.xy;
		}
	}
#endif

#if NEEDS_LIGHTMAP_COORDINATE
	Interpolants.LightMapCoordinate = Input.LightMapCoordinate * ShadowCoordinateScaleBias.xy + ShadowCoordinateScaleBias.wz;
#endif

#if WORLD_COORDS
	Interpolants.TangentBasisNormal = TangentNorm(Input.TangentZ);
	Interpolants.TangentBasisTangent = TangentNorm(Input.TangentX);	
#endif

#if !COMPILER_SUPPORTS_EMPTY_STRUCTS && !WORLD_COORDS && !NEEDS_LIGHTMAP_COORDINATE && !NUM_MATERIAL_TEXCOORDS
	Interpolants.Dummy = float4(0,0,0,0);
#endif

	return MulMatrix(PreviousLocalToWorld,Input.Position);
}

/**
* Get the 3x3 tangent basis vectors for this vertex factory
* this vertex factory will calculate the binormal on-the-fly
*
* @param Input - vertex input stream structure
* @return 3x3 matrix
*/
float3x3 VertexFactoryGetTangentBasis( FVertexFactoryInput Input )
{
	float3x3 Result=0;	

	half4 TangentZ = TangentBias(Input.TangentZ);
	// pass-thru the tangent
	Result[0] = TangentBias(Input.TangentX);
	// pass-thru the normal
	Result[2] = float3(TangentZ.x,TangentZ.y,TangentZ.z);
	// derive the binormal by getting the cross product of the normal and tangent
	Result[1] = cross(Result[2], Result[0]) * TangentZ.w;

	return Result;
}

/**
* Transform a vector from world space to tangent space
*
* @param Input - vertex input stream structure
* @param TangentBasis - 3x3 matrix to transform to tangent space
* @param WorldVector - vector in world space to transform 
* @return vector in tangent space
*/
float3 VertexFactoryWorldToTangentSpace( FVertexFactoryInput Input, float3x3 TangentBasis, float3 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(TangentBasis, MulMatrix(WorldToLocal,WorldVector));
}
