//Terrain tessellation hull shader - calculates tessellation factors

struct InputType
{
	float4 position : POSITION;
	float2 tex : TEXCOORD0;
	float3 normal : NORMAL;
};

struct ConstantOutputType
{
	float edges[4] : SV_TessFactor;
	float inside[2] : SV_InsideTessFactor;
};

cbuffer DynamicTessBuffer : register(b0)
{
	//Tessellation factors
	float minTessFactor;
	float3 cameraPosition;
	float maxTessFactor;
	float3 padding;
}

struct OutputType
{
	float4 position : POSITION;
	float2 tex : TEXCOORD0;
	float3 normal : NORMAL;
};

ConstantOutputType PatchConstantFunction(InputPatch<InputType, 4> inputPatch, uint patchId : SV_PrimitiveID)
{
	ConstantOutputType output;

	float3 midpoints[4];
	float midpointDistances[4];
	float tessFactors[4];

	//Calculate the midpoints
	midpoints[0] = lerp(inputPatch[0].position, inputPatch[1].position, 0.5f);
	midpoints[1] = lerp(inputPatch[0].position, inputPatch[3].position, 0.5f);
	midpoints[2] = lerp(inputPatch[2].position, inputPatch[3].position, 0.5f);
	midpoints[3] = lerp(inputPatch[1].position, inputPatch[2].position, 0.5f);

	for (int j = 0; j < 4; j++)
	{
		//Loop through all the midpoints and determine their distance from the camera
		midpointDistances[j] = abs(distance(cameraPosition.x, midpoints[j].x) + distance(cameraPosition.y, midpoints[j].y) + distance(cameraPosition.z, midpoints[j].z));
	}

	for (int k = 0; k < 4; k++)
	{
		//Check if the distance is greater than the max factor or below the minimum factor
		if (midpointDistances[k] >= maxTessFactor || maxTessFactor - midpointDistances[k] < minTessFactor)
		{
			//Set the minimum factor
			tessFactors[k] = minTessFactor;
		}

		else
		{
			//Calculate the factor based on the maximum
			tessFactors[k] = maxTessFactor - midpointDistances[k];
		}
	}

	//Set the edge factors based on the calculations
	output.edges[0] = tessFactors[0];
	output.edges[1] = tessFactors[1];
	output.edges[2] = tessFactors[2];
	output.edges[3] = tessFactors[3];

	float3 centrePoint = (inputPatch[0].position + inputPatch[1].position + inputPatch[2].position + inputPatch[3].position) / 4.0f;	//Calculate the position of the centre of the quad
	float centrePointDistance = abs(distance(cameraPosition.x, centrePoint.x) + distance(cameraPosition.y, centrePoint.y) + distance(cameraPosition.z, centrePoint.z));	//Calculate the distance from the centre point
	if (centrePointDistance >= maxTessFactor || maxTessFactor - centrePointDistance < minTessFactor)
	{
		//Set the minimum factor
		output.inside[0] = minTessFactor;
		output.inside[1] = minTessFactor;
	}

	else
	{
		//Calculate the factor based on the maximum
		output.inside[0] = maxTessFactor - centrePointDistance;
		output.inside[1] = maxTessFactor - centrePointDistance;
	}

	return output;
}

//Plane uses quads, set quad domain with integer partioning and 4 output points
[domain("quad")]
[partitioning("integer")]
[outputtopology("triangle_ccw")]
[outputcontrolpoints(4)]
[patchconstantfunc("PatchConstantFunction")]
OutputType main(InputPatch<InputType, 4> patch, uint pointId : SV_OutputControlPointID, uint patchId : SV_PrimitiveID)
{
	OutputType output;

	//Set output data
	output.position = patch[pointId].position;
	output.tex = patch[pointId].tex;
	output.normal = patch[pointId].normal;

	return output;
}

