/*=============================================================================
	TerrainVertexFactory.hlsl: Local vertex factory shader code.
	Copyright © 2003-2005 Epic Games, Inc. All Rights Reserved.
=============================================================================*/

// This is a temporary hack to simplify terrain pixel shaders.
#define DISABLE_LIGHTMAP_SPECULAR

float4x4 LocalToWorld;
float4x4 PreviousLocalToWorld;
float4 ShadowCoordinateScaleBias;

struct FVertexFactoryInput
{
	float4	Position		: POSITION;
	float	Displacement	: BLENDWEIGHT;
	float2	Gradients		: TANGENT;
};

float2	InvMaxTesselationLevel_ZScale;
float4	InvTerrainSize_SectionBase;
float4	LightMapCoordinateScaleBias;

#define TERRAIN_WEIGHTMAP_RESOLUTION	1.0

struct FVertexFactoryInterpolants
{
#if WORLD_COORDS
	// xyz=normal w=determinant
	float4	TangentBasisNormal	: COLOR0;
	float3	TangentBasisTangent	: COLOR1;
#endif
	float3	LocalPosition		: TEXCOORD0;
	float2	WeightMapTexCoord	: TEXCOORD1;
#if NEEDS_LIGHTMAP_COORDINATE
	float2	LightMapCoordinate	: TEXCOORD2;
#else
#endif
};

FMaterialParameters GetMaterialParameters(FVertexFactoryInterpolants Interpolants)
{
	FMaterialParameters	Result;

#if NUM_MATERIAL_TEXCOORDS
	#if NUM_MATERIAL_TEXCOORDS >= 1
		Result.TexCoords[0] = Interpolants.WeightMapTexCoord;
	#if NUM_MATERIAL_TEXCOORDS >= 2
		#if NEEDS_LIGHTMAP_COORDINATE
			Result.TexCoords[1] = Interpolants.LightMapCoordinate;
		#else	//#if NEEDS_LIGHTMAP_COORDINATE
			Result.TexCoords[1] = Interpolants.WeightMapTexCoord;
		#endif	//#if NEEDS_LIGHTMAP_COORDINATE
	#if NUM_MATERIAL_TEXCOORDS >= 3
		Result.TexCoords[2] = Interpolants.LocalPosition.xy;
	#if NUM_MATERIAL_TEXCOORDS >= 4
		Result.TexCoords[3] = Interpolants.LocalPosition.xz;
	#if NUM_MATERIAL_TEXCOORDS >= 5
		Result.TexCoords[4] = Interpolants.LocalPosition.yz;
	#endif
	#endif
	#endif
	#endif
	#endif

//	for (int CoordinateIndex = 5; CoordinateIndex < NUM_MATERIAL_TEXCOORDS; CoordinateIndex++)
//		Result.TexCoords[CoordinateIndex] = Interpolants.WeightMapTexCoord;
#endif

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

float2 GetLightMapCoordinate(FVertexFactoryInterpolants Interpolants)
{
#if NEEDS_LIGHTMAP_COORDINATE
	return Interpolants.LightMapCoordinate;
#else	//#if NEEDS_LIGHTMAP_COORDINATE
	return Interpolants.WeightMapTexCoord;
#endif	//#if NEEDS_LIGHTMAP_COORDINATE
}

/** derive basis vectors */
float3x3 CalcTangentBasis(FVertexFactoryInput Input)
{
	float3	LocalTangentX = float3(1,0,Input.Gradients.x * InvMaxTesselationLevel_ZScale.y),
			LocalTangentY = float3(0,1,Input.Gradients.y * InvMaxTesselationLevel_ZScale.y),
			LocalTangentZ = normalize(cross(LocalTangentX,LocalTangentY));

	return float3x3(LocalTangentX,LocalTangentY,LocalTangentZ);
}

float4 VertexFactoryGetWorldPosition(FVertexFactoryInput Input,out FVertexFactoryInterpolants Interpolants)
{
	float3	LocalPosition = float3(Input.Position.xy * InvMaxTesselationLevel_ZScale.x,(-32768 + (Input.Position.z + 256.0 * Input.Position.w)) * InvMaxTesselationLevel_ZScale.y),
			LocalTangentX = float3(1,0,Input.Gradients.x * InvMaxTesselationLevel_ZScale.y),
			LocalTangentY = float3(0,1,Input.Gradients.y * InvMaxTesselationLevel_ZScale.y),
			LocalTangentZ = normalize(cross(LocalTangentX,LocalTangentY));

	Interpolants.LocalPosition = float3(InvTerrainSize_SectionBase.zw,0) + LocalPosition;
	Interpolants.WeightMapTexCoord = (Interpolants.LocalPosition.xy + 0.5 / TERRAIN_WEIGHTMAP_RESOLUTION) * InvTerrainSize_SectionBase.xy;

#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

#if NEEDS_LIGHTMAP_COORDINATE
	Interpolants.LightMapCoordinate = (LocalPosition.xy * LightMapCoordinateScaleBias.xy + LightMapCoordinateScaleBias.wz) * ShadowCoordinateScaleBias.xy + ShadowCoordinateScaleBias.wz;
#endif	//#if NEEDS_LIGHTMAP_COORDINATE
	return mul(LocalToWorld,float4(LocalPosition + LocalTangentZ * Input.Displacement,1));
}

/** for depth-only pass */
float4 VertexFactoryGetWorldPositionOnly(FVertexFactoryInput Input)
{
	float3	LocalPosition = float3(Input.Position.xy * InvMaxTesselationLevel_ZScale.x,(-32768 + (Input.Position.z + 256.0 * Input.Position.w)) * InvMaxTesselationLevel_ZScale.y),
			LocalTangentX = float3(1,0,Input.Gradients.x * InvMaxTesselationLevel_ZScale.y),
			LocalTangentY = float3(0,1,Input.Gradients.y * InvMaxTesselationLevel_ZScale.y),
			LocalTangentZ = normalize(cross(LocalTangentX,LocalTangentY));

	return mul(LocalToWorld,float4(LocalPosition + LocalTangentZ * Input.Displacement,1));
}

float4 VertexFactoryGetPreviousWorldPosition(FVertexFactoryInput Input,out FVertexFactoryInterpolants Interpolants)
{
	float3	LocalPosition = float3(Input.Position.xy * InvMaxTesselationLevel_ZScale.x,(-32768 + (Input.Position.z + 256.0 * Input.Position.w)) * InvMaxTesselationLevel_ZScale.y),
			LocalTangentX = float3(1,0,Input.Gradients.x * InvMaxTesselationLevel_ZScale.y),
			LocalTangentY = float3(0,1,Input.Gradients.y * InvMaxTesselationLevel_ZScale.y),
			LocalTangentZ = normalize(cross(LocalTangentX,LocalTangentY));

	Interpolants.LocalPosition = float3(InvTerrainSize_SectionBase.zw,0) + LocalPosition;
	Interpolants.WeightMapTexCoord = (Interpolants.LocalPosition.xy + 0.5 / TERRAIN_WEIGHTMAP_RESOLUTION) * InvTerrainSize_SectionBase.xy;

#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

#if NEEDS_LIGHTMAP_COORDINATE
	Interpolants.LightMapCoordinate = (LocalPosition.xy * LightMapCoordinateScaleBias.xy + LightMapCoordinateScaleBias.wz) * ShadowCoordinateScaleBias.xy + ShadowCoordinateScaleBias.wz;
#endif	//#if NEEDS_LIGHTMAP_COORDINATE
	return mul(PreviousLocalToWorld,float4(LocalPosition + LocalTangentZ * Input.Displacement,1));
}

float3 VertexFactoryWorldToTangentSpace(FVertexFactoryInput Input,float3 WorldVector)
{
	return mul(CalcTangentBasis(Input),mul(float4(WorldVector,0),LocalToWorld).xyz);
}
