/* Computer Graphics, Assignment, Volume rendering with cubes/points/isosurface
 *
 * Student name .... Sander van Veen & Taddeus Kroes
 * Student email ... sandervv@gmail.com & taddeuskroes@hotmail.com
 * Collegekaart .... 6169676 & 6054129
 * Date ............ 17.11.2010
 */

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include "marching_tetrahedra.h"

/* Compute a linearly interpolated position where an isosurface cuts
   an edge between two vertices (p1 and p2), each with their own
   scalar value (v1 and v2) */

static vec3
interpolate_points(unsigned char isovalue, vec3 p1, vec3 p2,
	unsigned char v1, unsigned char v2)
{
	// Interpolate between v1 and v2 to determine relative coordinates
	vec3 v = v3_multiply(v3_add(
		v3_multiply(p1, v2 - isovalue),
		v3_multiply(p2, isovalue - v1)
	), 1.0 / (v2 - v1));
	
	// Multiply by voxel dimensions for absolute coordinates
	return v3_create(v.x * sizex, v.y * sizey, v.z * sizez);
}

/* Using the given iso-value generate triangles for the tetrahedron
   defined by corner vertices v0, v1, v2, v3 of cell c.

   Store the resulting triangles in the "triangles" array.

   Return the number of triangles created (either 0, 1, or 2).

   Note: the output array "triangles" should have space for at least
         2 triangles.
*/

/* Some abbreviating definitions that are used in the function below */
#define ADD_VEC(i,a,b) (triangles[tri_count].p[(i)] = \
	interpolate_points(isovalue, c.p[(a)], c.p[(b)], v[(a)], v[(b)]))
#define ADD_TRI(a,b,c,d,e,f) \
	ADD_VEC(0,(a),(b)); ADD_VEC(1,(c),(d)); ADD_VEC(2,(e),(f)); tri_count++

static int
generate_tetrahedron_triangles(triangle *triangles, unsigned char isovalue,
	cell c, int v0, int v1, int v2, int v3)
{
    unsigned char *v = c.value;
	int tri_count = 0;
	
	// Use a 4-bit bitmask to determine the order of "black/white" points of
	// the tetrahedon. Generate 0, 1 or 2 triangle(s) according to the case.
	switch( (v[v0] <= isovalue ? 1 : 0) | (v[v1] <= isovalue ? 2 : 0)
	        | (v[v2] <= isovalue ? 4 : 0) | (v[v3] <= isovalue ? 8 : 0) )
	{
		case 0x1: case 0xE: // 0001 or 1110
			// ADD_TRI usage example: interpolate between edges (v0, v1),
			// (v0, v2) and (v0, v3)
			ADD_TRI(v0, v1, v0, v2, v0, v3);
			break;
		case 0x2: case 0xD: // 0010 or 1101
			ADD_TRI(v1, v0, v1, v3, v1, v2);
			break;
		case 0x4: case 0xB: // 0100 or 1011
			ADD_TRI(v2, v0, v2, v1, v2, v3);
			break;
		case 0x8: case 0x7: // 1000 or 0111
			ADD_TRI(v3, v0, v3, v2, v3, v1);
			break;
		case 0x3: case 0xC: // 0011 or 1100
			ADD_TRI(v0, v3, v0, v2, v1, v3);
			ADD_TRI(v1, v3, v0, v2, v1, v2);
			break;
		case 0x5: case 0xA: // 0101 or 1010
			ADD_TRI(v0, v1, v2, v3, v0, v3);
			ADD_TRI(v0, v1, v1, v2, v2, v3);
			break;
		case 0x6: case 0x9: // 0110 or 1001
			ADD_TRI(v0, v1, v2, v3, v1, v3);
			ADD_TRI(v0, v1, v0, v2, v2, v3);
	}
	
	return tri_count;
}

/* Generate triangles for a single cell for the given iso-value. This function
   should produce at most 6 * 2 triangles (for which the "triangles" array
   should have enough space).

   Use calls to generate_tetrahedron_triangles().

   Return the number of triangles produced
*/

int
generate_cell_triangles(triangle *triangles, cell c, unsigned char isovalue)
{
	// Indices to cell corners for each tetrahedon (6 * 4 indices)
	const int points[24] = {0,1,3,7,0,2,6,7,0,1,5,7,0,2,3,7,0,4,5,7,0,4,6,7};
	int i, tri_count = 0;
	
	// Generate triangles for each tetrahedon
	for( i = 0; i < 21; i += 4 )
	{
		tri_count += generate_tetrahedron_triangles(triangles + tri_count,
			isovalue, c, points[i], points[i+1], points[i+2], points[i+3]);
	}

	return tri_count;
}
