/*=============================================================================
	PNTriangles.usf: Shader fragments for PN-AEN triangles tessellation
	Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
=============================================================================*/

#define IN_CONTROL_POINTS 12
#define OUT_CONTROL_POINTS 3  

struct FHullShaderConstantOutput
{
	// Tess factor for the FF HW block
	float TessFactor[3]    : SV_TessFactor;
	float InsideTessFactor : SV_InsideTessFactor;

	float4 WorldB111		: PN_POSITION9;
};

struct FPNTessellationHSToDS
{
	FPassSpecificVSToDS PassSpecificData;
	//@todo - Should be storing View position, then we can do a faster projection matrix multiply in the 
	//domain shader
	float4 WorldPosition[3]		: PN_POSITION;
	float4 WorldNormal			: PN_NORMAL;

	// scale factors in tangent space
	float3 DisplacementScale	: PN_DisplacementScales;

	// Tessellation multiplier.
	float TessellationMultiplier : PN_TessellationMultiplier;

#if DISPLACEMENT_ANTICRACK
	// dominant edge and vertex data used to avoid cracking when displacing
	FHullShaderConstantDominantVertexData DominantVertex : PN_DominantVertex;
	FHullShaderConstantDominantEdgeData DominantEdge : PN_DominantEdge;
#endif
};

float4 ComputeControlPoint(float4 PositionA, float4 PositionB, float4 NormalA)
{
    return (2 * PositionA + PositionB - (dot((PositionB - PositionA), NormalA) * NormalA)) / 3.0f;
}

#if HULLSHADER

	// Note that we input all control point from AEN, but only their PassSpecificData is valid (see MainHull shader)
	FHullShaderConstantOutput PNSharedConstantHull( const OutputPatch<FPNTessellationHSToDS, OUT_CONTROL_POINTS> I )
	{
		FHullShaderConstantOutput O = (FHullShaderConstantOutput)0;
		
		float4 B300 = I[0].WorldPosition[0],
			   B210 = I[0].WorldPosition[1],
			   B120 = I[0].WorldPosition[2],
			   B030 = I[1].WorldPosition[0],
			   B021 = I[1].WorldPosition[1],
			   B012 = I[1].WorldPosition[2],
			   B003 = I[2].WorldPosition[0],
			   B102 = I[2].WorldPosition[1],
			   B201 = I[2].WorldPosition[2];

		float4 E = (B210 + B120 + B021 + B012 + B102 + B201) / 6.0f;
		float4 V = (B003 + B030 + B300) / 3.0f;
		O.WorldB111 = E + ((E - V) / 2.0f);

		float4 TessellationMultipliers;
		TessellationMultipliers.x = 0.5 * (I[0].TessellationMultiplier + I[1].TessellationMultiplier);
		TessellationMultipliers.y = 0.5 * (I[1].TessellationMultiplier + I[2].TessellationMultiplier);
		TessellationMultipliers.z = 0.5 * (I[2].TessellationMultiplier + I[0].TessellationMultiplier);
		TessellationMultipliers.w = (I[0].TessellationMultiplier + I[1].TessellationMultiplier + I[2].TessellationMultiplier) / 3.0;

		//@todo: This should be vectorized and done in the MainHull shader.
		float4 CompositeTessellationFactors = TessellationMultipliers * CalculateCompositeTessellationFactors(B300.xyz,B030.xyz,B003.xyz);
		
		O.TessFactor[0] = CompositeTessellationFactors.x; // 1->2 edge
		O.TessFactor[1] = CompositeTessellationFactors.y; // 2->0 edge
		O.TessFactor[2] = CompositeTessellationFactors.z; // 0->1 edge
		O.InsideTessFactor = CompositeTessellationFactors.w;
		
		return O;
	}

	[domain("tri")]
	[partitioning("fractional_odd")]
	[outputtopology("triangle_cw")]
	[patchconstantfunc("PNSharedConstantHull")]
	[outputcontrolpoints(OUT_CONTROL_POINTS)]
	FPNTessellationHSToDS MainHull( InputPatch<FPassSpecificVSToDS, IN_CONTROL_POINTS> I, uint ControlPointID : SV_OutputControlPointID )
	{
		FPNTessellationHSToDS O = (FPNTessellationHSToDS) 0;

		float3x3 TangentBasis = VertexFactoryGetTangentBasis(I[ControlPointID].FactoryInterpolants);
		float4 Normal = VertexFactoryGetWorldNormal(TangentBasis);

		O.DisplacementScale = GetTangentSpaceNonUniformScales(TangentBasis);

		const uint NextControlPointID = ControlPointID < 2 ? ControlPointID + 1 : 0; // (ControlPointID + 1) % 3
		const uint NeighborControlPointID = 3 + 2 * ControlPointID;
		const uint NeighborNextControlPointID = NeighborControlPointID + 1;

		const uint OtherNeighborControlPointID = ((NeighborControlPointID+2)%6)+3;

		O.PassSpecificData = I[ControlPointID];
		O.WorldPosition[0] = I[ControlPointID].Position;
		O.WorldNormal = Normal;
		O.TessellationMultiplier = GetTessellationMultiplier( I[ControlPointID] );

#if DISPLACEMENT_ANTICRACK
		// NextControlPointID is first opposite edge vert
		// NextNextControlPointID is second opposite edge vert
		const uint NextNextControlPointID = NextControlPointID < 2 ? NextControlPointID + 1 : 0; // (NextControlPointID + 1) % 3 

		// we also need the neghbors of the opposite edge verts
		const uint NextNeighborControlPointID = 3 + 2 * NextControlPointID;	// neighbor for first opposite edge vert
		const uint NextNeighborNextControlPointID = NextNeighborControlPointID+1; // neighbor for second opposite edge vert

		// Dominant vertex is provided explicitly
		const uint DominantVertexControlPointID = 9 + ControlPointID;
		O.DominantVertex = GenerateDominantVertexData(I[DominantVertexControlPointID]);

		// we need the other verts from each prim to generate the edges, clock wise winding
		O.DominantEdge = GenerateDominantEdgeData(I[NextControlPointID],I[NextNextControlPointID],I[NextNeighborControlPointID],I[NextNeighborNextControlPointID]);
#endif	// DISPLACEMENT_ANTICRACK

		// AEN Computations
		float4 NextNormal = VertexFactoryGetWorldNormal(I[NextControlPointID].FactoryInterpolants);
		float4 NeighborNormal = VertexFactoryGetWorldNormal(I[NeighborControlPointID].FactoryInterpolants);
		float4 NeighborNextNormal = VertexFactoryGetWorldNormal(I[NeighborNextControlPointID].FactoryInterpolants);

		float4 ThisControlPoint, NeighborControlPoint;
		
		ThisControlPoint = ComputeControlPoint(I[ControlPointID].Position, I[NextControlPointID].Position, Normal);
		NeighborControlPoint = ComputeControlPoint(I[NeighborControlPointID].Position, I[NeighborNextControlPointID].Position, NeighborNormal);
		O.WorldPosition[1] = (ThisControlPoint + NeighborControlPoint) / 2;
		
		ThisControlPoint = ComputeControlPoint(I[NextControlPointID].Position, I[ControlPointID].Position, NextNormal);
		NeighborControlPoint = ComputeControlPoint(I[NeighborNextControlPointID].Position, I[NeighborControlPointID].Position, NeighborNextNormal);
		O.WorldPosition[2] = (ThisControlPoint + NeighborControlPoint) / 2;
		
		return O;
	}
#endif

#if DOMAINSHADER
	[domain("tri")]
	FPassSpecificVSToPS MainDomain(
		FHullShaderConstantOutput HSConstantData,
		const OutputPatch<FPNTessellationHSToDS, OUT_CONTROL_POINTS> I,
		float3 BarycentricCoords : SV_DomainLocation
		)
	{
		// Get the barycentric coords
		float U = BarycentricCoords.x;
		float V = BarycentricCoords.y;
		float W = BarycentricCoords.z;
		
		// Precompute squares and squares * 3 
		float UU = U * U;
		float VV = V * V;
		float WW = W * W;
		float UU3 = UU * 3.0f;
		float VV3 = VV * 3.0f;
		float WW3 = WW * 3.0f;
		
		float4 B300 = I[0].WorldPosition[0],
			   B210 = I[0].WorldPosition[1],
			   B120 = I[0].WorldPosition[2],
			   B030 = I[1].WorldPosition[0],
			   B021 = I[1].WorldPosition[1],
			   B012 = I[1].WorldPosition[2],
			   B003 = I[2].WorldPosition[0],
			   B102 = I[2].WorldPosition[1],
			   B201 = I[2].WorldPosition[2];


		// Compute position from cubic control points and barycentric coords
		float4 WorldPosition =
			B300 * UU * U +
			B030 * VV * V + 
			B003 * WW * W + 
			B210 * UU3 * V +
			B120 * VV3 * U +
			B021 * VV3 * W +
			B012 * WW3 * V +
			B102 * WW3 * U +
			B201 * UU3 * W +
			HSConstantData.WorldB111 * 6.0f * W * U * V;


		// Interp remaining attributes (construct barycentric interp from bilerp primitives)
		// NB: the HLSL compiler resolves the 1.f to something efficient (i.e. no-op)
		FPassSpecificVSToDS Interp = PassInterpolate( PassInterpolate(I[0].PassSpecificData, U, I[1].PassSpecificData, V), 1.f, I[2].PassSpecificData, W );

		// get our base material params that we will potentially override
		FMaterialTessellationParameters MaterialParameters = GetMaterialTessellationParameters(Interp.FactoryInterpolants, WorldPosition.xyz); // VF ops

#if DISPLACEMENT_ANTICRACK

		float2 DisplacementTexCoords = 0;
#if NUM_MATERIAL_TEXCOORDS
		DisplacementTexCoords = GetTextureCoordinateDS( Interp );
#endif

		FMaterialTessellationParameters DisplacementMaterialParameters = MaterialParameters;

		// Override to dominant data if along edge or at control vert.  This is necessary to avoid cracks causes by primitives
		// from disjoint locations in UV space abutting each other.
		ApplyDominantData(	DisplacementTexCoords, DisplacementMaterialParameters.TangentToWorld,
							UnpackColor4( GetNormalDS( Interp ) ), UnpackColor3( GetTangentDS( Interp ) ),
							U, V, W,
							I[0].DominantEdge, I[1].DominantEdge, I[2].DominantEdge,
							I[0].DominantVertex, I[1].DominantVertex, I[2].DominantVertex
							);
		
		// Override texture coordinate 0.
#if NUM_MATERIAL_TEXCOORDS
		DisplacementMaterialParameters.TexCoords[0].xy = DisplacementTexCoords.xy;
#endif

		// We calculate separate params for displacement because these depend on the potentially-modified
		// tanget space (as a result of crack-free)				
		CalcMaterialTessellationParameters(MaterialParameters);
		CalcMaterialTessellationParameters(DisplacementMaterialParameters); 
							
#else

		CalcMaterialTessellationParameters(MaterialParameters);
		FMaterialTessellationParameters DisplacementMaterialParameters = MaterialParameters;
							
#endif	// DISPLACEMENT_ANTICRACK

		// tangent space displacement scaling
		DisplacementMaterialParameters.TangentToWorldPreScale = I[0].DisplacementScale*U + I[1].DisplacementScale*V + I[2].DisplacementScale*W;

		// world space displacement value.  This will likely use the tangent2world transform.
		float3 WorldDisplacement = GetMaterialWorldDisplacement(DisplacementMaterialParameters);
		WorldPosition.xyz += WorldDisplacement;
																	
		return PassFinalizeTessellationOutput(Interp, WorldPosition, MaterialParameters);
	}
#endif
