#version 150

// Ogre port of Nvidia's IsoSurf.cg file
// Modified code follows. See http://developer.download.nvidia.com/SDK/10/opengl/samples.html for original
//
// Cg port of Yury Uralsky's metaball FX shader
//
// Authors: Simon Green and Yury Urlasky
// Email: sdkfeedback@nvidia.com
//
// Copyright (c) NVIDIA Corporation. All rights reserved.
////////////////////////////////////////////////////////////////////////////////////////////////////

// Size of the sampling grid
in VertexData {
    vec3 N;
    vec2 Field;
} VertexIn[];

out vec3 oNormal;

uniform float IsoValue;

layout(lines_adjacency) in;
layout(triangle_strip, max_vertices = 4) out;

// TODO: Change this from outputting triangles to a triangle strip


// Estimate where isosurface intersects grid edge with endpoints v0, v1
void CalcIntersection(vec4 Pos0,
					  vec3 N0,
					  vec2 Field0,
					  vec4 Pos1,
					  vec3 N1,
					  vec2 Field1)
{
	float t = (IsoValue - Field0.x) / (Field1.x - Field0.x);
	gl_Position = mix(Pos0, Pos1, t);
	oNormal = mix(N0, N1, t);
    EmitVertex();
}

// Geometry shader
// input: line with adjacency (tetrahedron)
// outputs: zero, one or two triangles depending if isosurface intersects tetrahedron
void main()
{
	// construct index for this tetrahedron
	uint index = uint((int(VertexIn[0].Field.y) << 3) |
                      (int(VertexIn[1].Field.y) << 2) |
                      (int(VertexIn[2].Field.y) << 1) |
                       int(VertexIn[3].Field.y));
	
	// don't bother if all vertices out or all vertices inside isosurface
	if (index > uint(0) && index < uint(15))
	{
		// Uber-compressed version of the edge table.
		uint edgeListHex[8] = 
			uint[8](uint(0x0001cde0), uint(0x98b08c9d), uint(0x674046ce), uint(0x487bc480), 
                    uint(0x21301d2e), uint(0x139bd910), uint(0x26376e20), uint(0x3b700000));

		uint edgeValFull = edgeListHex[index/uint(2)];
		uint three = uint(0x3);
		uint edgeVal = (index % uint(2) == uint(1)) ? (edgeValFull & uint(0xFFFF)) : ((edgeValFull >> 16) & uint(0xFFFF));
		ivec4 e0 = ivec4((edgeVal >> 14) & three, (edgeVal >> 12) & three, (edgeVal >> 10) & three, (edgeVal >> 8) & three);
		ivec4 e1 = ivec4((edgeVal >> 6) & three, (edgeVal >> 4) & three, (edgeVal >> 2) & three, (edgeVal >> 0) & three);

		CalcIntersection(gl_in[e0.x].gl_Position, VertexIn[e0.x].N, VertexIn[e0.x].Field,
		                 gl_in[e0.y].gl_Position, VertexIn[e0.y].N, VertexIn[e0.y].Field);
		CalcIntersection(gl_in[e0.z].gl_Position, VertexIn[e0.z].N, VertexIn[e0.z].Field,
		                 gl_in[e0.w].gl_Position, VertexIn[e0.w].N, VertexIn[e0.w].Field);
		CalcIntersection(gl_in[e1.x].gl_Position, VertexIn[e1.x].N, VertexIn[e1.x].Field,
		                 gl_in[e1.y].gl_Position, VertexIn[e1.y].N, VertexIn[e1.y].Field);

		// Emit additional triangle, if necessary
		if (e1.z != -1) {
			CalcIntersection(gl_in[e1.z].gl_Position, VertexIn[e1.z].N, VertexIn[e1.z].Field,
			                 gl_in[e1.w].gl_Position, VertexIn[e1.w].N, VertexIn[e1.w].Field);
		}
        EndPrimitive();
	}
}
