/*=============================================================================
	ParticleSubUVVertexFactory.hlsl: Particle vertex factory shader code.
	Copyright © 2004-2005 Epic Games, Inc. All Rights Reserved.
=============================================================================*/

float4		CameraWorldPosition;
float4		CameraRight;
float4		CameraUp;
float4		ScreenAlignment;
float4x4	LocalToWorld;

struct FVertexFactoryInput
{
	float4	Position	: POSITION;
	float4	OldPosition	: NORMAL;
	float3	Size		: TANGENT;
	float2	TexCoord	: TEXCOORD0;
	float	Rotation	: BLENDWEIGHT0;
	float2	TexCoord1	: TEXCOORD1;
	float4	Interp_Sizer: TEXCOORD2;
	float4	Color		: TEXCOORD3;
};

struct FVertexFactoryInterpolants
{
#if WORLD_COORDS
	// xyz=normal w=determinant
	float4	TangentBasisNormal	: COLOR0;
	float3	TangentBasisTangent	: COLOR1;
#endif
	float2	TexCoord			: TEXCOORD0;
	float2	TexCoord1			: TEXCOORD1;
	float4  Interp_Sizer		: TEXCOORD2;
	float4	Color				: TEXCOORD3;
};

FMaterialParameters GetMaterialParameters(FVertexFactoryInterpolants Interpolants)
{
	FMaterialParameters	Result;

#if NUM_MATERIAL_TEXCOORDS
	#if NUM_MATERIAL_TEXCOORDS >= 1
		Result.TexCoords[0] = Interpolants.TexCoord;
	#if NUM_MATERIAL_TEXCOORDS >= 2
		Result.TexCoords[1] = Interpolants.TexCoord1;
	#if NUM_MATERIAL_TEXCOORDS >= 3
		Result.TexCoords[2] = Interpolants.Interp_Sizer.xy;
	#endif	// >= 3
	#endif	// >= 2
	#endif	// >= 1

#if NUM_MATERIAL_TEXCOORDS > 3
	for(int CoordinateIndex = 3;CoordinateIndex < NUM_MATERIAL_TEXCOORDS;CoordinateIndex++)
		Result.TexCoords[CoordinateIndex] = Interpolants.TexCoord;
#endif
#endif

	Result.VertexColor = Interpolants.Color;
	Result.TangentNormal = 0;
	Result.TangentCameraVector = 0;
	Result.TangentReflectionVector = 0;
	Result.TangentLightVector = 0;
	Result.ScreenPosition = 0;
#if WORLD_COORDS
	Result.TangentBasisInverse = CalcInvTangentBasis(Interpolants.TangentBasisNormal,Interpolants.TangentBasisTangent);
#endif
	return Result;
}

#if !VERTEX_LIGHTMAP // AJS: If this is an emissive+vertex light-map shader, we can save an interpolator by skipping the light-map coordinate.
float2 GetLightMapCoordinate(FVertexFactoryInterpolants Interpolants)
{
	return Interpolants.TexCoord[0];
}
#endif	//#if !VERTEX_LIGHTMAP

float3 SafeNormalize(float3 V)
{
	return V / sqrt(max(dot(V,V),0.01));
}

void GetTangents(FVertexFactoryInput Input,out float4 Right,out float4 Up)
{
	float4	Position			= MulMatrix( LocalToWorld, Input.Position ),
			OldPosition			= MulMatrix( LocalToWorld, Input.OldPosition );

	float3	CameraDirection		= SafeNormalize(CameraWorldPosition.xyz - Position.xyz),
			ParticleDirection	= SafeNormalize(Position.xyz - OldPosition.xyz);

	float4	Right_Square		= CameraRight,
			Up_Square			= CameraUp;			
			
	float4	Right_Rotated		= (-1.0 * cos(Input.Rotation) * Up_Square) + (sin(Input.Rotation) * Right_Square),
			Up_Rotated			= (       sin(Input.Rotation) * Up_Square) + (cos(Input.Rotation) * Right_Square);

	float4	Right_Velocity		= float4( SafeNormalize( cross( CameraDirection, ParticleDirection	) ), 0.0 ),
			Up_Velocity			= float4(-ParticleDirection, 0.0 );

	//	enum EParticleScreenAlignment
	//	{
	//		PSA_Square,
	//		PSA_Rectangle,
	//		PSA_Velocity
	//	};
	Right				= ScreenAlignment.x > 1.5f ? Right_Velocity : Right_Rotated;
	Up					= ScreenAlignment.x > 1.5f ? Up_Velocity	: Up_Rotated;
}

float4 CalcWorldPosition(FVertexFactoryInput Input)
{
	float4	WorldPosition = MulMatrix(LocalToWorld,Input.Position);

	float4	Right,
			Up;

	GetTangents(Input,Right,Up);

	WorldPosition += Input.Size.x * ( Input.Interp_Sizer.z - 0.5 ) * Right;
	WorldPosition += Input.Size.y * ( Input.Interp_Sizer.w - 0.5 ) * Up;

	return WorldPosition;
}

/** derive basis vectors */
float3x3 CalcTangentBasis(FVertexFactoryInput Input)
{
	float4	Right,
			Up;
	GetTangents(Input,Right,Up);
	return float3x3(
			Right.xyz,
			Up.xyz,
			normalize(cross(Right.xyz,Up.xyz))
			);
}

float4 VertexFactoryGetWorldPosition(FVertexFactoryInput Input,out FVertexFactoryInterpolants Interpolants)
{
	Interpolants.TexCoord		= Input.TexCoord;
	Interpolants.TexCoord1		= Input.TexCoord1;
	Interpolants.Interp_Sizer	= Input.Interp_Sizer;
	Interpolants.Color			= Input.Color;
	
#if WORLD_COORDS
	float3x3 TangentBasis = CalcTangentBasis(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);
}

float4 VertexFactoryGetPreviousWorldPosition(FVertexFactoryInput Input,out FVertexFactoryInterpolants Interpolants)
{
	return VertexFactoryGetWorldPosition(Input,Interpolants);
}

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(
		CalcTangentBasis(Input),
		WorldVector
		);
}
