#version 430

#include <shaders/materials/commons.glsl>

// tess control. for now only static tesselation factor. 

layout(vertices = 3) out;

// TODO: how can we maybe generate this block maybe?
in vec4 vCoords[];
in vec3 vNorm[];
in vec3 vWorldNorm[];
in vec3 vLocalPos[];
in vec4 vWorldPos[];

in float vVtxTweenFactor[];
in float vVtxNoiseFactor[];
in vec3 vLocalNorm[];

out vec4 tcCoords[];
out vec3 tcNorm[];
out vec3 tcWorldNorm[];
out vec3 tcLocalPos[];
out vec4 tcWorldPos[];

out float tcVtxTweenFactor[];
out float tcVtxNoiseFactor[];
out vec3 tcLocalNorm[];

uniform float TessLevelInner;
uniform float TessLevelOuter;
uniform vec3 vCameraPosition;
uniform int materialId;

#define ID gl_InvocationID

void main()
{
	tcCoords[ID] = vCoords[ID];
	tcNorm[ID] = vNorm[ID];
	tcWorldNorm[ID] = vWorldNorm[ID];
	tcLocalPos[ID] = vLocalPos[ID];
	tcWorldPos[ID] = vWorldPos[ID];
	tcVtxTweenFactor[ID] = vVtxTweenFactor[ID];
	tcVtxNoiseFactor[ID] = vVtxNoiseFactor[ID];
	tcLocalNorm[ID] = vLocalNorm[ID];

	if (ID == 0)
	{
		#ifdef SHADOWMAP_PASS
		const float global_f = 0.3; 
		#else
		const float global_f = 1.0;
		#endif

		if (tcVtxNoiseFactor[0] > 0.0)
		{
			// special case for simple terrain. material id 29. TODO: again, make it somehow customizable...
			// NOTE: maybe some code injection to the shader? this could be really short code snippets so maybe
			//       even doable from source?
			// NOTE2: some set of standard modifiers (normal, distance) for geometry shaders? 

			float f = 1.0;

			// for backfacing lower it a lot. even consider having some cracks...
			#if 1
			float d = dot(normalize(vWorldNorm[0]), normalize(vCameraPosition.xyz - vWorldPos[0].xyz));
			if (d > 0.0)
			{
				// NOTE: for now we need 1.0 for backfacing faces just to render shadows properly
				//       (they use oposite side of triangle). We could really use some info in shader
				//       that we are rendering shadowmaps. 1: we could lower tess anyway 2: fix this isue
				f = max(1.0, pow(1.0 - d, 4.0)) * global_f;
				gl_TessLevelInner[0] = TessLevelInner * f;
				gl_TessLevelOuter[0] = TessLevelOuter * f;
				gl_TessLevelOuter[1] = TessLevelOuter * f;
				gl_TessLevelOuter[2] = TessLevelOuter * f;
			}
			else
			#endif
			{
				// f = 1.0
			}
			

			if ((materialId & MATERIAL_ID_MASK_ID) == 29)  // 29 - stippled terrain TODO: create registry
			{
				// distance attenuation
				float dist = length(vWorldPos[0].xyz - vCameraPosition);
				const float thresh = 70.0;	// TODO: make this adjustable
				if (dist > thresh)
				{
					f = f * exp(-min(1.0, dist - thresh) * 1.0);
				}
			}

			// this is mostly for debugging. can kill it later
			if (f <= 0.0)
			{
				gl_TessLevelInner[0] = 0.0;
				gl_TessLevelOuter[0] = 0.0;
				gl_TessLevelOuter[1] = 0.0;
				gl_TessLevelOuter[2] = 0.0;
			}
			else
			{
				gl_TessLevelInner[0] = max(1.0, TessLevelInner * f);
				gl_TessLevelOuter[0] = max(1.0, TessLevelOuter * f);
				gl_TessLevelOuter[1] = max(1.0, TessLevelOuter * f);
				gl_TessLevelOuter[2] = max(1.0, TessLevelOuter * f);
			}
		}
		else
		{
			gl_TessLevelInner[0] = 1.0;
			gl_TessLevelOuter[0] = 1.0;
			gl_TessLevelOuter[1] = 1.0;
			gl_TessLevelOuter[2] = 1.0;
		}
	}
}
