// 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.
////////////////////////////////////////////////////////////////////////////////////////////////////

struct SampleData
{
	float4 Pos   : POSITION;
	float3 N     : TEXCOORD0;
	float2 Field : TEXCOORD1;
	float4 Color : COLOR0;
};

struct SurfaceVertex
{
	float4 Pos	: POSITION;
	float3 N	: TEXCOORD0;
};

uniform int Num_Metaballs = 2;
uniform float4 Metaballs[] = {
	{ -0.5, 0, 0, 0.2 },
	{ 0.6, 0, 0, 0.4 },
};

// Size of the sampling grid
uniform int3 SizeMask = { 63, 63, 63 };
uniform int3 SizeShift = { 0, 6, 12 };

uniform float IsoValue = 1.0;

// Metaball function
// Returns metaball function value in .w and its gradient in .xyz

float4 Metaball(float3 Pos, float3 Center, float RadiusSq)
{
	float epsilon = 0.001;

	float3 Dist = Pos - Center;
	float InvDistSq = 1 / (dot(Dist, Dist) + epsilon);

	float4 o;
	o.xyz = -2 * RadiusSq * InvDistSq * InvDistSq * Dist;
	o.w = RadiusSq * InvDistSq;
	return o;
}

// Vertex shader
SampleData mainVS(float4 pos : POSITION,
						uniform float4x4 WorldViewProj,
						uniform float4x4 origWorldViewIT)
{
	SampleData o;
	
	// Sum up contributions from all metaballs
	float4 Field = 0;
	for (int i = 0; i<2; i++) 
	{
		Field += Metaball(pos.xyz, Metaballs[i].xyz, Metaballs[i].w);
	}
	
	float3x3 WorldViewIT = { origWorldViewIT[0].xyz, origWorldViewIT[1].xyz, origWorldViewIT[2].xyz };
	
	// Transform position and normals
	o.Pos = mul(WorldViewProj, pos);
	o.N = mul(WorldViewIT, Field.xyz);	// we want normals in world space
	o.Field.x = Field.w;
	
	// Generate in-out flags
	o.Field.y = (Field.w < IsoValue) ? 1 : 0;

	o.Color = (Field*0.5+0.5) * (Field.w / 10.0);
	return o;
}



// Estimate where isosurface intersects grid edge with endpoints v0, v1
void CalcIntersection(float4 Pos0,
					  float3 N0,
					  float2 Field0,
					  float4 Pos1,
					  float3 N1,
					  float2 Field1)
{
	float t = (IsoValue - Field0.x) / (Field1.x - Field0.x);
	if ((Field0.x < IsoValue) && (Field1.x > Field0.x))
	{
		if (t > 0 && t < 1)
		{
			float4 Pos = lerp(Pos0, Pos1, t);
			float3 N = lerp(N0, N1, t);
			emitVertex(Pos : POSITION, N : TEXCOORD0);
		}
	}
}
	
// Geometry shader
// input: line with adjacency (tetrahedron)
// outputs: zero, one or two triangles depending if isosurface intersects tetrahedron
LINE_ADJ
TRIANGLE_OUT
void mainGS(
	AttribArray<float4> Pos : POSITION,
	AttribArray<float3> N : TEXCOORD0,
	AttribArray<float2> Field : TEXCOORD1
	)
{
	// construct index for this tetrahedron
	unsigned int index = (int(Field[0].y) << 3) |
				(int(Field[1].y) << 2) |
				(int(Field[2].y) << 1) |
				 int(Field[3].y);
	
	// don't bother if all vertices out or all vertices inside isosurface
	if (index > 0 && index < 15)
	{
		//Uber-compressed version of the edge table.
		unsigned int edgeListHex[] = 
			{0x0001cde0, 0x98b08c9d, 0x674046ce, 0x487bc480, 
			0x21301d2e, 0x139bd910, 0x26376e20, 0x3b700000};
		
		unsigned int edgeValFull = edgeListHex[index/2];
		unsigned int edgeVal = (index % 2 == 1) ? (edgeValFull & 0xFFFF) : ((edgeValFull >> 16) & 0xFFFF);
		int4 e0 = int4((edgeVal >> 14) & 0x3, (edgeVal >> 12) & 0x3, (edgeVal >> 10) & 0x3, (edgeVal >> 8) & 0x3);
		int4 e1 = int4((edgeVal >> 6) & 0x3, (edgeVal >> 4) & 0x3, (edgeVal >> 2) & 0x3, (edgeVal >> 0) & 0x3);
		
		CalcIntersection(Pos[e0.x], N[e0.x], Field[e0.x], Pos[e0.y], N[e0.y], Field[e0.y]);
		CalcIntersection(Pos[e0.z], N[e0.z], Field[e0.z], Pos[e0.w], N[e0.w], Field[e0.w]);
		CalcIntersection(Pos[e1.x], N[e1.x], Field[e1.x], Pos[e1.y], N[e1.y], Field[e1.y]);

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

// Pixel shader
float4 mainPS(float3 N : TEXCOORD0) : COLOR
{
	//Sanitize input
	N = normalize(N);
	float3 L = float3(0, 0, 1);
	//return float4(N*0.5+0.5, 1);
	float3 materials[2] = { float3(1, 1, 1), float3(0, 0, 0.5)};
    float nDotL = dot( N, L);
	return float4(abs(nDotL) * materials[nDotL < 0.0], 0.1);
}
