#version 410 core

layout ( vertices = 25 ) out;

uniform ivec2 vpWidthHeight;
uniform float maxPixelLength = 25.0;

in PVA
{
	vec3 ecPosition;
	vec3 ecUnitNormal;
} pvaIn[];

out PVA
{
	vec3 ecPosition;
	vec3 ecUnitNormal;
} pvaOut[];

bool validLDSCoord(float v)
{
	if (isinf(v) || isnan(v))
		return false;
	return true;
}

// "getPixelMeasure" implements one very simple scheme for trying to
// tie tessellation level to current size of the surface on the screen.
// There are many other approaches one could imagine. And probably
// better ones!

float getPixelMeasure()
{
	// Get a bounding box around the control points

	vec2 p = gl_in[0].gl_Position.xy / gl_in[0].gl_Position.w;
	float xmin = 1.0;
	float xmax = 0.0;
	float ymin = 1.0;
	float ymax = 0.0;
	for (int i=0 ; i<gl_in.length() ; i++)
	{
		p = gl_in[i].gl_Position.xy / gl_in[i].gl_Position.w;
		if ( validLDSCoord(p.x) )
		{
			if (xmin > xmax)
				xmin = xmax = p.x;
			else if (p.x < xmin)
				xmin = p.x;
			else if (p.x > xmax)
				xmax = p.x;
		}
		if ( validLDSCoord(p.y) )
		{
			if (ymin > ymax)
				ymin = ymax = p.y;
			else if (p.y < ymin)
				ymin = p.y;
			else if (p.y > ymax)
				ymax = p.y;
		}
	}
	const float minVal = 25.0; // failsafe in case no measure in x or y
	// The coordinates are in 3D LDS, hence the deltas should all
	// be: 0<=(max-min)<=2. Scale to pixels.
	float fractionX = 0.5 * (xmax - xmin);
	float valX = (fractionX > 0.0) ? fractionX * vpWidthHeight[0] : minVal;
	float fractionY = 0.5 * (ymax - ymin);
	float valY = (fractionY > 0.0) ? fractionY * vpWidthHeight[1] : minVal;
	return max(valX, valY);
}

void main()
{
	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;

	pvaOut[gl_InvocationID].ecPosition = pvaIn[gl_InvocationID].ecPosition;
	pvaOut[gl_InvocationID].ecUnitNormal = pvaIn[gl_InvocationID].ecUnitNormal;

	// Guess at what reasonable tessellation levels should be. Compute the maximum
	// individual coordinate delta in LDS. Then convert to pixels.

	float pixelMeasure = getPixelMeasure();

	// Our heuristic: set the tessellation level so that triangle edges are no more
	// than "maxPixelLength" long.
	float outerTessLevel = max(pixelMeasure / maxPixelLength, 1.0);
	float innerTessLevel = max(outerTessLevel-1.0, 1.0);

	gl_TessLevelOuter[0] = gl_TessLevelOuter[2] = outerTessLevel;
	gl_TessLevelOuter[1] = gl_TessLevelOuter[3] = outerTessLevel;
	gl_TessLevelInner[0] = innerTessLevel;
	gl_TessLevelInner[1] = innerTessLevel;
}
