
#version 400
#extension GL_EXT_gpu_shader4 : enable

// Enable/disable appropriate postprocessing
#pragma IGLU_ENABLE   COLOR_LOGIC_OP
#pragma IGLU_DISABLE  DEPTH_TEST

// Input attributes from OpenGL.
layout(location = IGLU_VERTEX)   in vec2 vertex;   

// Uniform variables from OpenGL
uniform vec4            esLightPos;
uniform uint            numLayers, numEpipoleLines;
uniform float           maxAngle_L_V, minAngle_L_V;
uniform float           lNear, lFar;
uniform sampler2DArray  shadowMap;

// Data passed to our geometry shader
out vec3 epiSpaceAngles;


float GetOmniShadowMapTexelDistance( vec3 lightPos, vec3 fragPos )
{
	vec3 toVert = fragPos - lightPos;
	float dist = length( toVert );
	toVert = normalize( toVert );
	
	// Compute the coordinates in the + & - hemi-paraboloids
	vec2 posMap = vec2(toVert.xy)/(toVert.z+1);
	vec2 negMap = vec2(toVert.xy)/(toVert.z-1);
	
	// Select if we're using the + or - hemi-paraboloid.
	bool usePos = dot(posMap,posMap) < dot(negMap,negMap);
	
	// Now map this into texture space (i.e., [0..1])
	posMap = 0.5*posMap + 0.5;
	negMap = 0.5*negMap + 0.5;
	
	// Lookup the distance in the shadow map
	vec4 smapTex = texture( shadowMap, usePos ? vec3(posMap, 0) : vec3(negMap, 1) );
	float smapDist = max( smapTex.x, max( smapTex.y, smapTex.z ) );
	
	// Unproject the distance back into a eye-space distance
	float zNDC     = 2.0f * smapDist - 1.0f;
	float esSmapDist = (2*lNear*lFar) / ((lNear-lFar)*zNDC + lNear + lFar);
	
	// Because GL eye-space	has negative z-values, take the absolute value.
	return abs( esSmapDist ); // abs(smapDist); //abs( esSmapDist );
}


void main( void )
{	
	// Get the number of voxels per pixel (128 per layer)
	uint voxPerPix = numLayers * 128u;  

	// Get the current discretized voxel and epipolar line
	uint curLayer   = uint(gl_VertexID) % voxPerPix;
	uint curEpiLine = uint(gl_VertexID) / voxPerPix;
	
	float tmpX = 2.0 * (float(curLayer) / 1024.0) - 1.0;
	float tmpY = 2.0 * (float(curEpiLine) / 1024.0) - 1.0;

	// Transform this 2D coordinate into an epipoloar coordinate (epipolar plane angle & light angle)
	float epiNDC_yCoord = 2.0 * float(curEpiLine)/float(numEpipoleLines) - 1.0;
	float epiPlaneAngle = clamp( epiNDC_yCoord*3.14159265358979, -3.14159265358979f , 3.14159265358979f );
	
	float epiNDC_zCoord = 2.0 * float(curLayer)/float(voxPerPix) - 1.0;
	float lightAngle    = clamp( (float(curLayer)/float(voxPerPix))*3.14159265358979, 0.0f, 3.14159265358979f );
	
	// Get the vector for the epipole
	vec3 EtoL = normalize( esLightPos.xyz );
	
	// Get the rest of the orthonormal basis for the epipolar plane
	vec3 up      = normalize( cross( EtoL, vec3(0,0,-1) ) );
	vec3 forward = cross( up, EtoL );
	
	// Based on the epipolar plane angle, get a unit vector from the eye in the epipolar plane.
	vec3 vecInEpiPlane = sin( epiPlaneAngle ) * up + cos( epiPlaneAngle ) * forward;
	
	// Now compute a eye-space point along the specified z (light-ray) a distance 1 unit from the light.
	vec3 esPosOnLightRay = esLightPos.xyz + sin( lightAngle ) * vecInEpiPlane - cos( lightAngle ) * EtoL;
	
	// Find out how far the fragment in the shadow map is from the light
	float distToLight = clamp( GetOmniShadowMapTexelDistance( esLightPos.xyz, esPosOnLightRay ), 0.0f, lFar );
	
	// Now find the actual point in eye-space corresponding to this shadow map texel
	vec3 esFrag = esLightPos.xyz + distToLight * ( sin( lightAngle ) * vecInEpiPlane - cos( lightAngle ) * EtoL );
	
	// Compute the vector from the eye to the fragment in the shadow map.
	vec3 EtoF = normalize( esFrag );
	
	// Compute the epipolar space x-coordinate of this fragment
	float eyeAngle = clamp( dot( EtoF, EtoL), -1.0f, 1.0f );
	eyeAngle = (acos( eyeAngle ) - minAngle_L_V) / (maxAngle_L_V - minAngle_L_V);
	float epiNDC_xCoord = 2.0 * eyeAngle - 1.0f;

	// If our shadow map texel is the far plane, it's not actually very useful.
	float valid = distToLight >= (lFar-0.01) ? -1.0 : 1.0;
	
	// OK!  Draw the sucker.
	epiSpaceAngles = vec3( eyeAngle, epiPlaneAngle, lightAngle );
	//epiSpaceAngles = vec3( distToLight );
	
	gl_Position = vec4( epiNDC_xCoord, epiNDC_yCoord, epiNDC_zCoord, valid ); 
	//gl_Position = vec4( distToLight-0.5, epiNDC_yCoord, 0.0, valid ); 
	//gl_Position = vec4( tmpX, epiNDC_yCoord, 0.0, 1.0 ); 

}
