// $Id: MC.cxx 1082 2010-11-17 07:34:53Z martin $
/*  This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
//
/////////////////////////////////////////////////////////////////////////////////////////////
//      FileName:       MarchingCubes.cpp
//      Author  :       Michael Y. Polyakov
//      email   :       myp@andrew.cmu.edu  or  mikepolyakov@hotmail.com
//      website :       www.angelfire.com/linux/myp
//      date    :       July 2002
//      
//      Description:    'Straight' and Recursive Marching Cubes Algorithms
//                                      Recursive method is faster than the 'straight' one, especially when intersection does not 
//                                              have to be searched for every time.
//                              Normal vectors are defined for each vertex as a gradients
//                              For function definitions see MarchingCubes.h
//                              For a tutorial on Marching Cubes please visit www.angelfire.com/myp/linux
//
//      Please email me with any suggestion/bugs.
/////////////////////////////////////////////////////////////////////////////////////////////

#include "MC.h"
#include "MCTable.h"
#include <math.h>
#include "drawxtl.h"
#include "draw_ext.h"
//Linear Interpolation between two points
mpVector
LinearInterp (mp4Vector p1, mp4Vector p2, float value)
{
    mpVector p;

    if (fabs (p1.val - p2.val) > 0.00001)
	p = (mpVector) p1 + ((mpVector) p2 - (mpVector) p1) / (p2.val - p1.val) * (value -
										   p1.
										   val);
    else
	p = (mpVector) p1;
    return p;
}


//Macros used to compute gradient vector on each vertex of a cube
//argument should be the name of array of vertices
//can be verts or *verts if done by reference

#define CALC_GRAD_VERT_0(verts) mp4Vector(points[ind-YtimeZ].val-(verts[1]).val,points[ind-pointsZ].val-(verts[4]).val,points[ind-1].val-(verts[3]).val, (verts[0]).val);
#define CALC_GRAD_VERT_1(verts) mp4Vector((verts[0]).val-points[ind+2*YtimeZ].val,points[ind+YtimeZ-pointsZ].val-(verts[5]).val,points[ind+YtimeZ-1].val-(verts[2]).val, (verts[1]).val);
#define CALC_GRAD_VERT_2(verts) mp4Vector((verts[3]).val-points[ind+2*YtimeZ+1].val,points[ind+YtimeZ-ncellsZ].val-(verts[6]).val,(verts[1]).val-points[ind+YtimeZ+2].val, (verts[2]).val);
#define CALC_GRAD_VERT_3(verts) mp4Vector(points[ind-YtimeZ+1].val-(verts[2]).val,points[ind-ncellsZ].val-(verts[7]).val,(verts[0]).val-points[ind+2].val, (verts[3]).val);
#define CALC_GRAD_VERT_4(verts) mp4Vector(points[ind-YtimeZ+ncellsZ+1].val-(verts[5]).val,(verts[0]).val-points[ind+2*pointsZ].val,points[ind+ncellsZ].val-(verts[7]).val, (verts[4]).val);
#define CALC_GRAD_VERT_5(verts) mp4Vector((verts[4]).val-points[ind+2*YtimeZ+ncellsZ+1].val,(verts[1]).val-points[ind+YtimeZ+2*pointsZ].val,points[ind+YtimeZ+ncellsZ].val-(verts[6]).val, (verts[5]).val);
#define CALC_GRAD_VERT_6(verts) mp4Vector((verts[7]).val-points[ind+2*YtimeZ+ncellsZ+2].val,(verts[2]).val-points[ind+YtimeZ+2*ncellsZ+3].val,(verts[5]).val-points[ind+YtimeZ+ncellsZ+3].val, (verts[6]).val);
#define CALC_GRAD_VERT_7(verts) mp4Vector(points[ind-YtimeZ+ncellsZ+2].val-(verts[6]).val,(verts[3]).val-points[ind+2*ncellsZ+3].val,(verts[4]).val-points[ind+ncellsZ+3].val, (verts[7]).val);

///////////////////////////////////////////////////////////////////////////////////////////////////////
// GLOBAL //
//Global variables - so they dont have to be passed into functions
int pointsZ;			//number of points on Z zxis (equal to ncellsZ+1)

int YtimeZ;			//'plane' of cubes on YZ (equal to (ncellsY+1)*pointsZ )

///////////////////////////////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////////////////////////////
//      'STRAIGHT' MARCHING CUBES       ALGORITHM  ///////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
//for gradients at the edges values 1.0, 1.0, 1.0, 1.0  are given
TRIANGLE *
MC (int ncellsX, int ncellsY, int ncellsZ,
    float gradFactorX, float gradFactorY, float gradFactorZ,
    float minValue, mp4Vector * points, int &numTriangles)
{
   TRIANGLE *t;

    //this should be enough space, if not change 3 to 4
    unsigned long maxtriangles = 3 * (ncellsX + 1) * (ncellsY + 1) * (ncellsZ + 1);

//fprintf(stderr,"allocating %d new triangles (%f Mbytes)\n",maxtriangles,(float)(maxtriangles)*sizeof(TRIANGLE)/1024./1024.);

    float ftsize =
	(float) (ncellsX + 1) * 3.0f * (float) (ncellsY + 1) * (float) (ncellsZ +
									1) *
	(float) sizeof (TRIANGLE);
    float maxsize = (float) pow (2, (sizeof (size_t) * 8));	//maximum capacity of the size_t type

    if (ftsize >= maxsize) {
	Error_Box
	    ("The number of triangles in this mesh would exceed\nthe maximum allocatable number for this architecture\nDraw a smaller section or load a coarser mesh");
	fprintf (drvui->flout,
		 "Error: The number of triangles in this mesh (%10.0f),\nwould exceed the maximum allocatable number for this architecture (%10.0f)\n",
		 ftsize, maxsize);
	fprintf (drvui->flout,
		 "(or the one this particular binary was built for, if you are running a 32bit program on a 64bit system).\n");
	fprintf (drvui->flout,
		 "No map will be drawn. (Reduce the map drawing limits or load a coarser map.)\n");
	numTriangles = int (0);

	return NULL;
    }
    TRIANGLE *triangles = (TRIANGLE *) zalloc (maxtriangles * sizeof (TRIANGLE));

    numTriangles = int (0);

    if (!triangles) {
	Error_Box ("Could not allocate memory for surface triangulation");
	fprintf (drvui->fcns, "Triangle allocation failed - probably out of memory\n");
	return NULL;
    }
    pointsZ = ncellsZ + 1;	//initialize global variable (for extra speed) 
    YtimeZ = (ncellsY + 1) * pointsZ;
    int lastX = ncellsX;	//left from older version

    int lastY = ncellsY;

    int lastZ = ncellsZ;

    if (!lastX)
	lastX++;		//if 2d, one of these will be zero - change to 1
    if (!lastY)
	lastY++;
    if (!lastZ)
	lastZ++;
    mp4Vector *verts[8];	//vertices of a cube (array of pointers for extra speed)

    mpVector intVerts[12];	//linearly interpolated vertices on each edge

    int cubeIndex;		//shows which vertices are outside/inside

    int edgeIndex;		//index returned by edgeTable[cubeIndex]

    mp4Vector gradVerts[8];	//gradients at each vertex of a cube            

    mpVector grads[12];		//linearly interpolated gradients on each edge

    int indGrad;		//shows which gradients already have been computed

    int ind, ni, nj;		//ind: index of vertex 0

    //factor by which corresponding coordinates of gradient vectors are scaled
    mpVector factor (1.0f / (2.0f * gradFactorX), 1.0f / (2.0f * gradFactorY),
		     1.0f / (2.0f * gradFactorZ));

    //MAIN LOOP: goes through all the points
    for (int i = 0; i < lastX; i++) {	//x axis
	ni = i * YtimeZ;
	for (int j = 0; j < lastY; j++) {	//y axis
	    nj = j * pointsZ;
	    for (int k = 0; k < lastZ; k++, ind++)	//z axis
	    {
		//initialize vertices
		ind = ni + nj + k;
		verts[0] = &points[ind];
		verts[1] = &points[ind + YtimeZ];
		verts[4] = &points[ind + pointsZ];
		verts[5] = &points[ind + YtimeZ + pointsZ];
		verts[2] = &points[ind + YtimeZ + 1];
		verts[3] = &points[ind + 1];
		verts[6] = &points[ind + YtimeZ + pointsZ + 1];
		verts[7] = &points[ind + pointsZ + 1];

		//get the index
		cubeIndex = int (0);

		for (int n = 0; n < 8; n++)
		    if (verts[n]->val <= minValue)
			cubeIndex |= (1 << n);

		//check if its completely inside or outside
		if (!edgeTable[cubeIndex])
		    continue;

		indGrad = int (0);

		edgeIndex = edgeTable[cubeIndex];

		if (edgeIndex & 1) {
		    intVerts[0] = LinearInterp (*verts[0], *verts[1], minValue);
		    if (i != 0 && j != 0 && k != 0) {
			gradVerts[0] = CALC_GRAD_VERT_0 (*verts)
		    } else {
			gradVerts[0] = mp4Vector (1.0, 1.0, 1.0, 1.0);	//for now do not wrap around
		    }
		    if (i != lastX - 1 && j != 0 && k != 0)
			gradVerts[1] = CALC_GRAD_VERT_1 (*verts)
			    else
			gradVerts[1] = mp4Vector (1.0, 1.0, 1.0, 1.0);
		    indGrad |= 3;
		    grads[0] = LinearInterp (gradVerts[0], gradVerts[1], minValue);
		    grads[0].x *= factor.x;
		    grads[0].y *= factor.y;
		    grads[0].z *= factor.z;
		}
		if (edgeIndex & 2) {
		    intVerts[1] = LinearInterp (*verts[1], *verts[2], minValue);
		    if (!(indGrad & 2)) {
			if (i != lastX - 1 && j != 0 && k != 0)
			    gradVerts[1] = CALC_GRAD_VERT_1 (*verts)
				else
			    gradVerts[1] = mp4Vector (1.0, 1.0, 1.0, 1.0);
			indGrad |= 2;
		    }
		    if (i != lastX - 1 && j != 0 && k != 0)
			gradVerts[2] = CALC_GRAD_VERT_2 (*verts)
			    else
			gradVerts[2] = mp4Vector (1.0, 1.0, 1.0, 1.0);
		    indGrad |= 4;
		    grads[1] = LinearInterp (gradVerts[1], gradVerts[2], minValue);
		    grads[1].x *= factor.x;
		    grads[1].y *= factor.y;
		    grads[1].z *= factor.z;
		}
		if (edgeIndex & 4) {
		    intVerts[2] = LinearInterp (*verts[2], *verts[3], minValue);
		    if (!(indGrad & 4)) {
			if (i != lastX - 1 && j != 0 && k != 0)
			    gradVerts[2] = CALC_GRAD_VERT_2 (*verts)
				else
			    gradVerts[2] = mp4Vector (1.0, 1.0, 1.0, 1.0);
			indGrad |= 4;
		    }
		    if (i != 0 && j != 0 && k != lastZ - 1)
			gradVerts[3] = CALC_GRAD_VERT_3 (*verts)
			    else
			gradVerts[3] = mp4Vector (1.0, 1.0, 1.0, 1.0);
		    indGrad |= 8;
		    grads[2] = LinearInterp (gradVerts[2], gradVerts[3], minValue);
		    grads[2].x *= factor.x;
		    grads[2].y *= factor.y;
		    grads[2].z *= factor.z;
		}
		if (edgeIndex & 8) {
		    intVerts[3] = LinearInterp (*verts[3], *verts[0], minValue);
		    if (!(indGrad & 8)) {
			if (i != 0 && j != 0 && k != lastZ - 1)
			    gradVerts[3] = CALC_GRAD_VERT_3 (*verts)
				else
			    gradVerts[3] = mp4Vector (1.0, 1.0, 1.0, 1.0);
			indGrad |= 8;
		    }
		    if (!(indGrad & 1)) {
			if (i != 0 && j != 0 && k != 0)
			    gradVerts[0] = CALC_GRAD_VERT_0 (*verts)
				else
			    gradVerts[0] = mp4Vector (1.0, 1.0, 1.0, 1.0);
			indGrad |= 1;
		    }
		    grads[3] = LinearInterp (gradVerts[3], gradVerts[0], minValue);
		    grads[3].x *= factor.x;
		    grads[3].y *= factor.y;
		    grads[3].z *= factor.z;
		}
		if (edgeIndex & 16) {
		    intVerts[4] = LinearInterp (*verts[4], *verts[5], minValue);

		    if (i != 0 && j != lastY - 1 && k != 0)
			gradVerts[4] = CALC_GRAD_VERT_4 (*verts)
			    else
			gradVerts[4] = mp4Vector (1.0, 1.0, 1.0, 1.0);

		    if (i != lastX - 1 && j != lastY - 1 && k != 0)
			gradVerts[5] = CALC_GRAD_VERT_5 (*verts)
			    else
			gradVerts[5] = mp4Vector (1.0, 1.0, 1.0, 1.0);

		    indGrad |= 48;
		    grads[4] = LinearInterp (gradVerts[4], gradVerts[5], minValue);
		    grads[4].x *= factor.x;
		    grads[4].y *= factor.y;
		    grads[4].z *= factor.z;
		}
		if (edgeIndex & 32) {
		    intVerts[5] = LinearInterp (*verts[5], *verts[6], minValue);
		    if (!(indGrad & 32)) {
			if (i != lastX - 1 && j != lastY - 1 && k != 0)
			    gradVerts[5] = CALC_GRAD_VERT_5 (*verts)
				else
			    gradVerts[5] = mp4Vector (1.0, 1.0, 1.0, 1.0);
			indGrad |= 32;
		    }

		    if (i != lastX - 1 && j != lastY - 1 && k != lastZ - 1)
			gradVerts[6] = CALC_GRAD_VERT_6 (*verts)
			    else
			gradVerts[6] = mp4Vector (1.0, 1.0, 1.0, 1.0);
		    indGrad |= 64;
		    grads[5] = LinearInterp (gradVerts[5], gradVerts[6], minValue);
		    grads[5].x *= factor.x;
		    grads[5].y *= factor.y;
		    grads[5].z *= factor.z;
		}
		if (edgeIndex & 64) {
		    intVerts[6] = LinearInterp (*verts[6], *verts[7], minValue);
		    if (!(indGrad & 64)) {
			if (i != lastX - 1 && j != lastY - 1 && k != lastZ - 1)
			    gradVerts[6] = CALC_GRAD_VERT_6 (*verts)
				else
			    gradVerts[6] = mp4Vector (1.0, 1.0, 1.0, 1.0);
			indGrad |= 64;
		    }

		    if (i != 0 && j != lastY - 1 && k != lastZ - 1)
			gradVerts[7] = CALC_GRAD_VERT_7 (*verts)
			    else
			gradVerts[7] = mp4Vector (1.0, 1.0, 1.0, 1.0);
		    indGrad |= 128;
		    grads[6] = LinearInterp (gradVerts[6], gradVerts[7], minValue);
		    grads[6].x *= factor.x;
		    grads[6].y *= factor.y;
		    grads[6].z *= factor.z;
		}
		if (edgeIndex & 128) {
		    intVerts[7] = LinearInterp (*verts[7], *verts[4], minValue);
		    if (!(indGrad & 128)) {
			if (i != 0 && j != lastY - 1 && k != lastZ - 1)
			    gradVerts[7] = CALC_GRAD_VERT_7 (*verts)
				else
			    gradVerts[7] = mp4Vector (1.0, 1.0, 1.0, 1.0);
			indGrad |= 128;
		    }
		    if (!(indGrad & 16)) {
			if (i != 0 && j != lastY - 1 && k != 0)
			    gradVerts[4] = CALC_GRAD_VERT_4 (*verts)
				else
			    gradVerts[4] = mp4Vector (1.0, 1.0, 1.0, 1.0);
			indGrad |= 16;
		    }
		    grads[7] = LinearInterp (gradVerts[7], gradVerts[4], minValue);
		    grads[7].x *= factor.x;
		    grads[7].y *= factor.y;
		    grads[7].z *= factor.z;
		}
		if (edgeIndex & 256) {
		    intVerts[8] = LinearInterp (*verts[0], *verts[4], minValue);
		    if (!(indGrad & 1)) {
			if (i != 0 && j != 0 && k != 0)
			    gradVerts[0] = CALC_GRAD_VERT_0 (*verts)
				else
			    gradVerts[0] = mp4Vector (1.0, 1.0, 1.0, 1.0);
			indGrad |= 1;
		    }
		    if (!(indGrad & 16)) {
			if (i != 0 && j != lastY - 1 && k != 0)
			    gradVerts[4] = CALC_GRAD_VERT_4 (*verts)
				else
			    gradVerts[4] = mp4Vector (1.0, 1.0, 1.0, 1.0);
			indGrad |= 16;
		    }
		    grads[8] = LinearInterp (gradVerts[0], gradVerts[4], minValue);
		    grads[8].x *= factor.x;
		    grads[8].y *= factor.y;
		    grads[8].z *= factor.z;
		}
		if (edgeIndex & 512) {
		    intVerts[9] = LinearInterp (*verts[1], *verts[5], minValue);
		    if (!(indGrad & 2)) {
			if (i != lastX - 1 && j != 0 && k != 0)
			    gradVerts[1] = CALC_GRAD_VERT_1 (*verts)
				else
			    gradVerts[1] = mp4Vector (1.0, 1.0, 1.0, 1.0);
			indGrad |= 2;
		    }
		    if (!(indGrad & 32)) {
			if (i != lastX - 1 && j != lastY - 1 && k != 0)
			    gradVerts[5] = CALC_GRAD_VERT_5 (*verts)
				else
			    gradVerts[5] = mp4Vector (1.0, 1.0, 1.0, 1.0);
			indGrad |= 32;
		    }
		    grads[9] = LinearInterp (gradVerts[1], gradVerts[5], minValue);
		    grads[9].x *= factor.x;
		    grads[9].y *= factor.y;
		    grads[9].z *= factor.z;
		}
		if (edgeIndex & 1024) {
		    intVerts[10] = LinearInterp (*verts[2], *verts[6], minValue);
		    if (!(indGrad & 4)) {
			if (i != lastX - 1 && j != 0 && k != 0)
			    gradVerts[2] = CALC_GRAD_VERT_2 (*verts)
				else
			    gradVerts[5] = mp4Vector (1.0, 1.0, 1.0, 1.0);
			indGrad |= 4;
		    }
		    if (!(indGrad & 64)) {
			if (i != lastX - 1 && j != lastY - 1 && k != lastZ - 1)
			    gradVerts[6] = CALC_GRAD_VERT_6 (*verts)
				else
			    gradVerts[6] = mp4Vector (1.0, 1.0, 1.0, 1.0);
			indGrad |= 64;
		    }
		    grads[10] = LinearInterp (gradVerts[2], gradVerts[6], minValue);
		    grads[10].x *= factor.x;
		    grads[10].y *= factor.y;
		    grads[10].z *= factor.z;
		}
		if (edgeIndex & 2048) {
		    intVerts[11] = LinearInterp (*verts[3], *verts[7], minValue);
		    if (!(indGrad & 8)) {
			if (i != 0 && j != 0 && k != lastZ - 1)
			    gradVerts[3] = CALC_GRAD_VERT_3 (*verts)
				else
			    gradVerts[3] = mp4Vector (1.0, 1.0, 1.0, 1.0);
			indGrad |= 8;
		    }
		    if (!(indGrad & 128)) {
			if (i != 0 && j != lastY - 1 && k != lastZ - 1)
			    gradVerts[7] = CALC_GRAD_VERT_7 (*verts)
				else
			    gradVerts[7] = mp4Vector (1.0, 1.0, 1.0, 1.0);
			indGrad |= 128;
		    }
		    grads[11] = LinearInterp (gradVerts[3], gradVerts[7], minValue);
		    grads[11].x *= factor.x;
		    grads[11].y *= factor.y;
		    grads[11].z *= factor.z;
		}
		//now build the triangles using triTable
		for (int nn = 0; triTable[cubeIndex][nn] != -1; nn += 3) {
		    int index[3] =
			{ triTable[cubeIndex][nn + 2], triTable[cubeIndex][nn + 1],
		 triTable[cubeIndex][nn] };
		    float vecnorm;

		    vecnorm = 0.;
		    for (int l1 = 0; l1 < 3; l1++)
			vecnorm += grads[index[l1]] * grads[index[l1]];
		    vecnorm = (float) (1.0 / sqrt (vecnorm));
		    for (int h = 0; h < 3; h++) {	//copy vertices and normals into triangles array
			triangles[numTriangles].p[h] = intVerts[index[h]];
			triangles[numTriangles].norm[h] = grads[index[h]] * vecnorm;
		    }
		    numTriangles++;	//one more triangle has been added
		    if ((unsigned int) numTriangles == maxtriangles) {
			Error_Box ("Too many triangles in fourier mesh - aborting");
			fprintf (drvui->fcns, "Too many triangles in fourier mesh - aborting\n");
			return triangles;
		    }
		}
	    }			//END OF FOR LOOP ON Z AXIS
	}			//END OF FOR LOOP ON Y AXIS
    }				//END OF FOR LOOP ON X AXIS

    //free all wasted space
    //fprintf(stderr,"reducing allocation to %d triangles (%f Mbytes)\n",numTriangles,(float)(numTriangles)*sizeof(TRIANGLE)/1024./1024.);
    t = (TRIANGLE *) realloc (triangles, numTriangles * sizeof (TRIANGLE));
    if (t) //guard against realloc failure, however unlikely
	triangles = t;
    return triangles;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////



///////////////////////////////////////////////////////////////////////////////////////////////////////////////
//      RECURSIVE MARCHING CUBES ALGORITHM ////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

//  MACROS  ///////////////////////////////////////////////////////////////////////////////////////////////////
//these macros initialize data and then run marching cubes on the cube with the surface having the specified 
//      number as 'recieving' data (but number of that surface for the current cube is going to be 'opposite').
//      Each runs the corresponding recursive function
//      For numbering, to see which indices of prevVerts,... correspong to indices of the current cube, see
//      my webpage at www.angelfire.com/linux/myp

#define MC_FACE0																							\
{																											\
  if(! marchedCubes[ind - 1]) {																				\
	passGradIndex = 0;																						\
	if(gradIndex & 1) passGradIndex |= 8;																	\
	if(gradIndex & 2) passGradIndex |= 4;																	\
	if(gradIndex & 32) passGradIndex |= 64;																	\
	if(gradIndex & 16) passGradIndex |= 128;																\
	passEdgeIndex = 0;																						\
	if(edgeIndex & 1) passEdgeIndex |= 4;																	\
	if(edgeIndex & 512) passGradIndex |= 1024;																\
	if(edgeIndex & 16) passEdgeIndex |= 64;																	\
	if(edgeIndex & 256) passGradIndex |= 2048;																\
	prevVerts[0] = verts[0]; prevVerts[1] = verts[1]; prevVerts[2] = verts[5]; prevVerts[3] = verts[4];		\
	prevIntVerts[0] = intVerts[0]; prevIntVerts[1] = intVerts[9];											\
	prevIntVerts[2] = intVerts[4]; prevIntVerts[3] = intVerts[8];											\
	prevGradVerts[0] = gradVerts[0]; prevGradVerts[1] = gradVerts[1];										\
	prevGradVerts[2] = gradVerts[5]; prevGradVerts[3] = gradVerts[4];										\
	prevGrads[0] = grads[0]; prevGrads[1] = grads[9]; prevGrads[2] = grads[4]; prevGrads[3] = grads[8];		\
	triangles = MCFace0(ncellsX, ncellsY, ncellsZ, gradFactorX, gradFactorY, gradFactorZ,					\
							ind-1, i, j, k-1, minValue, points, triangles, numTriangles,					\
							prevVerts, prevIntVerts, passEdgeIndex,											\
							prevGradVerts, prevGrads, passGradIndex, marchedCubes);							\
  }																											\
}

#define MC_FACE1																							\
{																											\
  if(! marchedCubes[ind + YtimeZ]) {																		\
	passGradIndex = 0;																						\
	if(gradIndex & 4) passGradIndex |= 8;																	\
	if(gradIndex & 2) passGradIndex |= 1;																	\
	if(gradIndex & 32) passGradIndex |= 16;																	\
	if(gradIndex & 64) passGradIndex |= 128;																\
	passEdgeIndex = 0;																						\
	if(edgeIndex & 2) passEdgeIndex |= 8;																	\
	if(edgeIndex & 512) passEdgeIndex |= 256;																\
	if(edgeIndex & 32) passEdgeIndex |= 128;																\
	if(edgeIndex & 1024) passEdgeIndex |= 2048;																\
	prevVerts[0] = verts[2]; prevVerts[1] = verts[1]; prevVerts[2] = verts[5]; prevVerts[3] = verts[6];		\
	prevIntVerts[0] = intVerts[1]; prevIntVerts[1] = intVerts[9];											\
	prevIntVerts[2] = intVerts[5]; prevIntVerts[3] = intVerts[10];											\
	prevGradVerts[0] = gradVerts[2]; prevGradVerts[1] = gradVerts[1];										\
	prevGradVerts[2] = gradVerts[5]; prevGradVerts[3] = gradVerts[6];										\
	prevGrads[0] = grads[1]; prevGrads[1] = grads[9]; prevGrads[2] = grads[5]; prevGrads[3] = grads[10];	\
	triangles = MCFace1(ncellsX, ncellsY, ncellsZ, gradFactorX, gradFactorY, gradFactorZ,					\
							ind+YtimeZ, i+1, j, k, minValue, points, triangles, numTriangles,				\
							prevVerts, prevIntVerts, passEdgeIndex,											\
							prevGradVerts, prevGrads, passGradIndex,  marchedCubes);						\
  }																											\
}

#define MC_FACE2																							\
{																											\
  if(! marchedCubes[ind + 1]) {																				\
	passGradIndex = 0;																						\
	if(gradIndex & 8) passGradIndex |= 1;																	\
	if(gradIndex & 4) passGradIndex |= 2;																	\
	if(gradIndex & 64) passGradIndex |= 32;																	\
	if(gradIndex & 128) passGradIndex |= 16;																\
	passEdgeIndex = 0;																						\
	if(edgeIndex & 4) passEdgeIndex |= 1;																	\
	if(edgeIndex & 1024) passEdgeIndex |= 512;																\
	if(edgeIndex & 64) passEdgeIndex |= 16;																	\
	if(edgeIndex & 2048) passEdgeIndex |= 256;																\
	prevVerts[0] = verts[3]; prevVerts[1] = verts[2]; prevVerts[2] = verts[6]; prevVerts[3] = verts[7];		\
	prevIntVerts[0] = intVerts[2]; prevIntVerts[1] = intVerts[10];											\
	prevIntVerts[2] = intVerts[6]; prevIntVerts[3] = intVerts[11];											\
	prevGradVerts[0] = gradVerts[3]; prevGradVerts[1] = gradVerts[2];										\
	prevGradVerts[2] = gradVerts[6]; prevGradVerts[3] = gradVerts[7];										\
	prevGrads[0] = grads[2]; prevGrads[1] = grads[10]; prevGrads[2] = grads[6]; prevGrads[3] = grads[11];	\
	triangles = MCFace2(ncellsX, ncellsY, ncellsZ, gradFactorX, gradFactorY, gradFactorZ,					\
							ind+1, i, j, k+1, minValue, points, triangles, numTriangles,					\
							prevVerts, prevIntVerts, passEdgeIndex,											\
							prevGradVerts, prevGrads, passGradIndex, marchedCubes);							\
  }																											\
}

#define MC_FACE3																							\
{																											\
  if(! marchedCubes[ind - YtimeZ]) {																		\
	passGradIndex = 0;																						\
	if(gradIndex & 8) passGradIndex |= 4;																	\
	if(gradIndex & 1) passGradIndex |= 2;																	\
	if(gradIndex & 128) passGradIndex |= 64;																\
	if(gradIndex & 16) passGradIndex |= 32;																	\
	passEdgeIndex = 0;																						\
	if(edgeIndex & 8) passEdgeIndex |= 2;																	\
	if(edgeIndex & 256) passEdgeIndex |= 512;																\
	if(edgeIndex & 128) passEdgeIndex |= 32;																\
	if(edgeIndex & 2048) passEdgeIndex |= 1024;																\
	prevVerts[0] = verts[3]; prevVerts[1] = verts[0]; prevVerts[2] = verts[4]; prevVerts[3] = verts[7];		\
	prevIntVerts[0] = intVerts[3]; prevIntVerts[1] = intVerts[8];											\
	prevIntVerts[2] = intVerts[7]; prevIntVerts[3] = intVerts[11];											\
	prevGradVerts[0] = gradVerts[3]; prevGradVerts[1] = gradVerts[0];										\
	prevGradVerts[2] = gradVerts[4]; prevGradVerts[3] = gradVerts[7];										\
	prevGrads[0] = grads[3]; prevGrads[1] = grads[8]; prevGrads[2] = grads[7]; prevGrads[3] = grads[11];	\
	triangles = MCFace3(ncellsX, ncellsY, ncellsZ, gradFactorX, gradFactorY, gradFactorZ,					\
							ind-YtimeZ, i-1, j, k, minValue, points, triangles, numTriangles,				\
							prevVerts, prevIntVerts, passEdgeIndex,											\
							prevGradVerts, prevGrads, passGradIndex, marchedCubes);							\
  }																											\
}

//done
#define MC_FACE4																							\
{																											\
  if(! marchedCubes[ind + pointsZ]) {																		\
	passGradIndex = 0;																						\
	if(gradIndex & 128) passGradIndex |= 8;																	\
	if(gradIndex & 64) passGradIndex |= 4;																	\
	if(gradIndex & 32) passGradIndex |= 2;																	\
	if(gradIndex & 16) passGradIndex |= 1;																	\
	passEdgeIndex = 0;																						\
	if(edgeIndex & 128) passEdgeIndex |= 8;																	\
	if(edgeIndex & 64) passEdgeIndex |= 4;																	\
	if(edgeIndex & 32) passEdgeIndex |= 2;																	\
	if(edgeIndex & 16) passEdgeIndex |= 1;																	\
	prevVerts[0] = verts[7]; prevVerts[1] = verts[6]; prevVerts[2] = verts[5]; prevVerts[3] = verts[4];		\
	prevIntVerts[0] = intVerts[6]; prevIntVerts[1] = intVerts[5];											\
	prevIntVerts[2] = intVerts[4]; prevIntVerts[3] = intVerts[7];											\
	prevGradVerts[0] = gradVerts[7]; prevGradVerts[1] = gradVerts[6];										\
	prevGradVerts[2] = gradVerts[5]; prevGradVerts[3] = gradVerts[4];										\
	prevGrads[0] = grads[6]; prevGrads[1] = grads[5]; prevGrads[2] = grads[4]; prevGrads[3] = grads[7];		\
	triangles = MCFace4(ncellsX, ncellsY, ncellsZ, gradFactorX, gradFactorY, gradFactorZ,					\
							ind+pointsZ, i, j+1, k, minValue, points, triangles, numTriangles,				\
							prevVerts, prevIntVerts, passEdgeIndex,											\
							prevGradVerts, prevGrads, passGradIndex, marchedCubes);							\
  }																											\
}

#define MC_FACE5																							\
{																											\
  if(! marchedCubes[ind - ncellsZ - 1]) {																	\
	passGradIndex = (gradIndex << 4) & 240;																	\
	passEdgeIndex = (edgeIndex << 4) & 240;																	\
	prevVerts[0] = verts[3]; prevVerts[1] = verts[2]; prevVerts[2] = verts[1]; prevVerts[3] = verts[0];		\
	prevIntVerts[0] = intVerts[2]; prevIntVerts[1] = intVerts[1];											\
	prevIntVerts[2] = intVerts[0]; prevIntVerts[3] = intVerts[3];											\
	prevGradVerts[0] = gradVerts[3]; prevGradVerts[1] = gradVerts[2];										\
	prevGradVerts[2] = gradVerts[1]; prevGradVerts[3] = gradVerts[0];										\
	prevGrads[0] = grads[2]; prevGrads[1] = grads[1]; prevGrads[2] = grads[0]; prevGrads[3] = grads[3];		\
	triangles = MCFace5(ncellsX, ncellsY, ncellsZ, gradFactorX, gradFactorY, gradFactorZ,					\
							ind-ncellsZ-1, i, j-1, k, minValue, points, triangles, numTriangles,			\
							prevVerts, prevIntVerts, passEdgeIndex,											\
							prevGradVerts, prevGrads, passGradIndex, marchedCubes);							\
  }																											\
}
/// END FACE MACROS /////////////////////////////////////////////////////////////////////////////////////////





// RECURSIVE Marching Cubes Function - cubes at indexes ii, jj, kk intersect the surface
//      Number of intersecting cubes = numCubes
TRIANGLE *
MarchingCubesRec (int ncellsX, int ncellsY, int ncellsZ,
		  float gradFactorX, float gradFactorY, float gradFactorZ,
		  int numCubes, int *ii, int *jj, int *kk,
		  float minValue, mp4Vector * points, int &numTriangles)
{
    TRIANGLE *triangles = new TRIANGLE[3 * ncellsX * ncellsY * ncellsZ];

    numTriangles = int (0);

    //check if none of the starting points are on the outside perimeter
    for (int nn = 0; nn < numCubes; nn++) {
	if (ii[nn] <= 0 || ii[nn] >= ncellsX - 1) {
	    delete[]triangles;
	    return NULL;
	}
	if (jj[nn] <= 0 || jj[nn] >= ncellsY - 1) {
	    delete[]triangles;
	    return NULL;
	}
	if (kk[nn] <= 0 || kk[nn] >= ncellsZ - 1) {
	    delete[]triangles;
	    return NULL;
	}
    }
    //array stores which cubes have been marched through - initialized to FALSE
    int all = ncellsX * ncellsY * ncellsZ;

    bool *marchedCubes = new bool[all];

    for (int iij = 0; iij < all; iij++)
	marchedCubes[iij] = false;	//initialize

    mp4Vector verts[8];		//vertices of a starting cube

    mpVector intVerts[12];	//linearly interpolated vertices on each edge

    int edgeIndex;		//shows which edges are intersected

    mp4Vector gradVerts[8];	//gradients at each vertex of a cube            

    mpVector grads[12];		//linearly interpolated gradients on each edge

    int gradIndex;		//show on which vertices gradients have been computed

    //initialize global variables - for speed - these would be used by all the recursive functions
    pointsZ = ncellsZ + 1;
    YtimeZ = (ncellsY + 1) * pointsZ;
    //arrays used to pass already computed values from this initial cube to the next ones
    mp4Vector prevVerts[4];	//passes vertices

    mpVector prevIntVerts[4];	//passes interpolated vertices on edges

    mp4Vector prevGradVerts[4];	//passes gradients at vertices

    mpVector prevGrads[4];	//passes interpolated gradients on edges

    //two new indexes formed for each face
    int passEdgeIndex, passGradIndex;	//used to tell which vertices and which edges have been initialized

    //indices
    int i = 0, in, j, k;

    //initialize first cubes and 'launch' the recursion for each
    for (int n = 0; n < numCubes; n++) {
	//init vertices
	in = ii[n];
	j = jj[n];
	k = kk[n];
	int ind = in * YtimeZ + j * pointsZ + k;

	verts[0] = points[ind];
	verts[1] = points[ind + YtimeZ];
	verts[2] = points[ind + YtimeZ + 1];
	verts[3] = points[ind + 1];
	verts[4] = points[ind + pointsZ];
	verts[5] = points[ind + YtimeZ + pointsZ];
	verts[6] = points[ind + YtimeZ + pointsZ + 1];
	verts[7] = points[ind + pointsZ + 1];

	//first check if this cube wasnt marched in recursive calls of the previous cube
	if (!marchedCubes[ind]) {
	    //run marching cubes on the initial cube
	    gradIndex = edgeIndex = 0;
	    triangles =
		MarchOneCube (ncellsZ, gradFactorX, gradFactorY,
			      gradFactorZ, ind, minValue, points, triangles,
			      numTriangles, verts, intVerts, edgeIndex, gradVerts, grads,
			      gradIndex);
	    //this cube has been done:
	    marchedCubes[ind] = true;
	    //run M.C. on all 6 faces
	MC_FACE0 MC_FACE1 MC_FACE2 MC_FACE3 MC_FACE4 MC_FACE5}
    }
    //free wasted space
    TRIANGLE *retTriangles = new TRIANGLE[numTriangles];

    for (int im = 0; im < numTriangles; im++)
	retTriangles[im] = triangles[im];
    delete[]triangles;
    delete[]marchedCubes;
    return retTriangles;	//done
}

//SURFACE 0 - Cube ran on surface 0 of previous cube. Recieving side: 2.
TRIANGLE *
MCFace0 (int ncellsX, int ncellsY, int ncellsZ,
	 float gradFactorX, float gradFactorY, float gradFactorZ,
	 int ind, int i, int j, int k,
	 float minValue, mp4Vector * points, TRIANGLE * triangles, int &numTriangles,
	 mp4Vector prevVerts[4], mpVector prevIntVerts[4], int edgeIndex,
	 mp4Vector prevGradVerts[4], mpVector prevGrads[4], int gradIndex,
	 bool * marchedCubes)
{
    //first check if not outside the region
    if (i <= 0 || i >= ncellsX - 1 || j <= 0 || j >= ncellsY - 1 || k <= 0
	|| k >= ncellsZ - 1)
	return triangles;
    //make sure we save that this cube was marched through
    marchedCubes[ind] = true;
    //initialize vertices
    mp4Vector verts[8];

    verts[0] = points[ind];
    verts[1] = points[ind + YtimeZ];
    verts[2] = prevVerts[1];
    verts[3] = prevVerts[0];
    verts[4] = points[ind + ncellsZ + 1];
    verts[5] = points[ind + YtimeZ + ncellsZ + 1];
    verts[6] = prevVerts[2];
    verts[7] = prevVerts[3];
    //initialize edges from the last cube
    mpVector intVerts[12];

    intVerts[2] = prevIntVerts[0];
    intVerts[10] = prevIntVerts[1];
    intVerts[6] = prevIntVerts[2];
    intVerts[11] = prevIntVerts[3];
    //initialize gradients on vertices
    mp4Vector gradVerts[8];

    gradVerts[3] = prevGradVerts[0];
    gradVerts[2] = prevGradVerts[1];
    gradVerts[6] = prevGradVerts[2];
    gradVerts[7] = prevGradVerts[3];
    //initialize gradients on edges from the last cube
    mpVector grads[12];

    grads[2] = prevGrads[0];
    grads[10] = prevGrads[1];
    grads[6] = prevGrads[2];
    grads[11] = prevGrads[3];
    //for test if this cube is intersected:
    int oldNumTriangles = numTriangles;

    //run marching cubes on this cube
    triangles =
	MarchOneCube (ncellsZ, gradFactorX, gradFactorY, gradFactorZ,
		      ind, minValue, points, triangles, numTriangles, verts,
		      intVerts, edgeIndex, gradVerts, grads, gradIndex);
    //check if this cube is intersected
    if (numTriangles == oldNumTriangles)
	return triangles;
    //two new indexes formed for each face to be passed into other recursive functions
    int passEdgeIndex, passGradIndex;

    //run recursive functions on each surface
    MC_FACE0 MC_FACE1 MC_FACE3 MC_FACE4 MC_FACE5 return triangles;
}

//SURFACE 1 - Cube ran on surface 1 of previous cube. Receiving side: 3.
TRIANGLE *
MCFace1 (int ncellsX, int ncellsY, int ncellsZ,
	 float gradFactorX, float gradFactorY, float gradFactorZ,
	 int ind, int i, int j, int k,
	 float minValue, mp4Vector * points, TRIANGLE * triangles, int &numTriangles,
	 mp4Vector prevVerts[4], mpVector prevIntVerts[4], int edgeIndex,
	 mp4Vector prevGradVerts[4], mpVector prevGrads[4], int gradIndex,
	 bool * marchedCubes)
{
    //first check if not outside the region
    if (i <= 0 || i >= ncellsX - 1 || j <= 0 || j >= ncellsY - 1 || k <= 0
	|| k >= ncellsZ - 1)
	return triangles;
    //make sure we save that this cube was marched through
    marchedCubes[ind] = true;
    //initialize vertices
    mp4Vector verts[8];

    verts[0] = prevVerts[1];
    verts[1] = points[ind + YtimeZ];
    verts[2] = points[ind + YtimeZ + 1];
    verts[3] = prevVerts[0];
    verts[4] = prevVerts[2];
    verts[5] = points[ind + YtimeZ + ncellsZ + 1];
    verts[6] = points[ind + YtimeZ + ncellsZ + 2];
    verts[7] = prevVerts[3];
    //initialize edges from the last cube
    mpVector intVerts[12];

    intVerts[3] = prevIntVerts[0];
    intVerts[8] = prevIntVerts[1];
    intVerts[7] = prevIntVerts[2];
    intVerts[11] = prevIntVerts[3];
    //initialize gradients on vertices
    mp4Vector gradVerts[8];

    gradVerts[3] = prevGradVerts[0];
    gradVerts[0] = prevGradVerts[1];
    gradVerts[4] = prevGradVerts[2];
    gradVerts[7] = prevGradVerts[3];
    //initialize gradients on edges from the last cube
    mpVector grads[12];

    grads[3] = prevGrads[0];
    grads[8] = prevGrads[1];
    grads[7] = prevGrads[2];
    grads[11] = prevGrads[3];
    //for test if this cube is intersected:
    int oldNumTriangles = numTriangles;

    //run marching cubes on this cube
    triangles =
	MarchOneCube (ncellsZ, gradFactorX, gradFactorY, gradFactorZ,
		      ind, minValue, points, triangles, numTriangles, verts,
		      intVerts, edgeIndex, gradVerts, grads, gradIndex);
    //check if this cube is intersected
    if (numTriangles == oldNumTriangles)
	return triangles;
    //two new indexes formed for each face to be passed into other recursive functions
    int passEdgeIndex, passGradIndex;

    //run recursive functions on each surface
    MC_FACE0 MC_FACE1 MC_FACE2 MC_FACE4 MC_FACE5 return triangles;
}

//SURFACE 2 - Cube ran on surface 2 of previous cube. Recieving side: 0.
TRIANGLE *
MCFace2 (int ncellsX, int ncellsY, int ncellsZ,
	 float gradFactorX, float gradFactorY, float gradFactorZ,
	 int ind, int i, int j, int k,
	 float minValue, mp4Vector * points, TRIANGLE * triangles, int &numTriangles,
	 mp4Vector prevVerts[4], mpVector prevIntVerts[4], int edgeIndex,
	 mp4Vector prevGradVerts[4], mpVector prevGrads[4], int gradIndex,
	 bool * marchedCubes)
{
    //first check if not outside the region
    if (i <= 0 || i >= ncellsX - 1 || j <= 0 || j >= ncellsY - 1 || k <= 0
	|| k >= ncellsZ - 1)
	return triangles;
    //make sure we save that this cube was marched through
    marchedCubes[ind] = true;
    //initialize vertices
    mp4Vector verts[8];

    verts[0] = prevVerts[0];
    verts[1] = prevVerts[1];
    verts[2] = points[ind + YtimeZ + 1];
    verts[3] = points[ind + 1];
    verts[4] = prevVerts[3];
    verts[5] = prevVerts[2];
    verts[6] = points[ind + YtimeZ + ncellsZ + 2];
    verts[7] = points[ind + ncellsZ + 2];
    //initialize edges from the last cube
    mpVector intVerts[12];

    intVerts[0] = prevIntVerts[0];
    intVerts[9] = prevIntVerts[1];
    intVerts[4] = prevIntVerts[2];
    intVerts[8] = prevIntVerts[3];
    //initialize gradients on vertices
    mp4Vector gradVerts[8];

    gradVerts[0] = prevGradVerts[0];
    gradVerts[1] = prevGradVerts[1];
    gradVerts[5] = prevGradVerts[2];
    gradVerts[4] = prevGradVerts[3];
    //initialize gradients on edges from the last cube
    mpVector grads[12];

    grads[0] = prevGrads[0];
    grads[9] = prevGrads[1];
    grads[4] = prevGrads[2];
    grads[8] = prevGrads[3];
    //for test if this cube is intersected
    int oldNumTriangles = numTriangles;

    //run marching cubes on this cube
    triangles =
	MarchOneCube (ncellsZ, gradFactorX, gradFactorY, gradFactorZ,
		      ind, minValue, points, triangles, numTriangles, verts,
		      intVerts, edgeIndex, gradVerts, grads, gradIndex);
    //check if this cube is intersected
    if (numTriangles == oldNumTriangles)
	return triangles;
    //two new indexes formed for each face
    int passEdgeIndex, passGradIndex;

    //run recursive functions on each surface
    MC_FACE1 MC_FACE2 MC_FACE3 MC_FACE4 MC_FACE5 return triangles;
}

//SURFACE 3 - Cube ran on surface 3 of previous cube. Recieving side: 1.
TRIANGLE *
MCFace3 (int ncellsX, int ncellsY, int ncellsZ,
	 float gradFactorX, float gradFactorY, float gradFactorZ,
	 int ind, int i, int j, int k,
	 float minValue, mp4Vector * points, TRIANGLE * triangles, int &numTriangles,
	 mp4Vector prevVerts[4], mpVector prevIntVerts[4], int edgeIndex,
	 mp4Vector prevGradVerts[4], mpVector prevGrads[4], int gradIndex,
	 bool * marchedCubes)
{
    //first check if not outside the region
    if (i <= 0 || i >= ncellsX - 1 || j <= 0 || j >= ncellsY - 1 || k <= 0
	|| k >= ncellsZ - 1)
	return triangles;
    //make sure we save that this cube was marched through
    marchedCubes[ind] = true;
    //initialize vertices
    mp4Vector verts[8];

    verts[0] = points[ind];
    verts[1] = prevVerts[1];
    verts[2] = prevVerts[0];
    verts[3] = points[ind + 1];
    verts[4] = points[ind + ncellsZ + 1];
    verts[5] = prevVerts[2];
    verts[6] = prevVerts[3];
    verts[7] = points[ind + ncellsZ + 2];
    //initialize edges from the last cube
    mpVector intVerts[12];

    intVerts[1] = prevIntVerts[0];
    intVerts[9] = prevIntVerts[1];
    intVerts[5] = prevIntVerts[2];
    intVerts[10] = prevIntVerts[3];
    //initialize gradients on vertices
    mp4Vector gradVerts[8];

    gradVerts[2] = prevGradVerts[0];
    gradVerts[1] = prevGradVerts[1];
    gradVerts[5] = prevGradVerts[2];
    gradVerts[6] = prevGradVerts[3];
    //initialize gradients on edges from the last cube
    mpVector grads[12];

    grads[1] = prevGrads[0];
    grads[9] = prevGrads[1];
    grads[5] = prevGrads[2];
    grads[10] = prevGrads[3];
    //for test if this cube is intersected
    int oldNumTriangles = numTriangles;

    //run marching cubes on this cube
    triangles =
	MarchOneCube (ncellsZ, gradFactorX, gradFactorY, gradFactorZ,
		      ind, minValue, points, triangles, numTriangles, verts,
		      intVerts, edgeIndex, gradVerts, grads, gradIndex);
    //check if this cube is intersected
    if (numTriangles == oldNumTriangles)
	return triangles;
    //two new indexes formed for each face
    int passEdgeIndex, passGradIndex;

    //run recursive functions on each surface
    MC_FACE0 MC_FACE2 MC_FACE3 MC_FACE4 MC_FACE5 return triangles;
}

//SURFACE 4 - Cube ran on surface 4 of previous cube. Recieving side: 5.
TRIANGLE *
MCFace4 (int ncellsX, int ncellsY, int ncellsZ,
	 float gradFactorX, float gradFactorY, float gradFactorZ,
	 int ind, int i, int j, int k,
	 float minValue, mp4Vector * points, TRIANGLE * triangles, int &numTriangles,
	 mp4Vector prevVerts[4], mpVector prevIntVerts[4], int edgeIndex,
	 mp4Vector prevGradVerts[4], mpVector prevGrads[4], int gradIndex,
	 bool * marchedCubes)
{
    //first check if not outside the region
    if (i <= 0 || i >= ncellsX - 1 || j <= 0 || j >= ncellsY - 1 || k <= 0
	|| k >= ncellsZ - 1)
	return triangles;
    //make sure we save that this cube was marched through
    marchedCubes[ind] = true;
    //initialize vertices
    mp4Vector verts[8];

    verts[0] = prevVerts[3];
    verts[1] = prevVerts[2];
    verts[2] = prevVerts[1];
    verts[3] = prevVerts[0];
    verts[4] = points[ind + ncellsZ + 1];
    verts[5] = points[ind + YtimeZ + ncellsZ + 1];
    verts[6] = points[ind + YtimeZ + ncellsZ + 2];
    verts[7] = points[ind + ncellsZ + 2];
    //initialize edges from the last cube
    mpVector intVerts[12];

    intVerts[2] = prevIntVerts[0];
    intVerts[1] = prevIntVerts[1];
    intVerts[0] = prevIntVerts[2];
    intVerts[3] = prevIntVerts[3];
    //initialize gradients on vertices
    mp4Vector gradVerts[8];

    gradVerts[3] = prevGradVerts[0];
    gradVerts[2] = prevGradVerts[1];
    gradVerts[1] = prevGradVerts[2];
    gradVerts[0] = prevGradVerts[3];
    //initialize gradients on edges from the last cube
    mpVector grads[12];

    grads[2] = prevGrads[0];
    grads[1] = prevGrads[1];
    grads[0] = prevGrads[2];
    grads[3] = prevGrads[3];
    //for test if this cube is intersected
    int oldNumTriangles = numTriangles;

    //run marching cubes on this cube
    triangles =
	MarchOneCube (ncellsZ, gradFactorX, gradFactorY, gradFactorZ,
		      ind, minValue, points, triangles, numTriangles, verts,
		      intVerts, edgeIndex, gradVerts, grads, gradIndex);
    //check if this cube is intersected
    if (numTriangles == oldNumTriangles)
	return triangles;
    //two new indexes formed for each face
    int passEdgeIndex, passGradIndex;

    //run recursive functions on each surface
    MC_FACE0 MC_FACE1 MC_FACE2 MC_FACE3 MC_FACE4 return triangles;
}

//SURFACE 5 - Cube ran on surface 5 of previous cube. Recieving side: 4.
TRIANGLE *
MCFace5 (int ncellsX, int ncellsY, int ncellsZ,
	 float gradFactorX, float gradFactorY, float gradFactorZ,
	 int ind, int i, int j, int k,
	 float minValue, mp4Vector * points, TRIANGLE * triangles, int &numTriangles,
	 mp4Vector prevVerts[4], mpVector prevIntVerts[4], int edgeIndex,
	 mp4Vector prevGradVerts[4], mpVector prevGrads[4], int gradIndex,
	 bool * marchedCubes)
{
    //first check if not outside the region
    if (i <= 0 || i >= ncellsX - 1 || j <= 0 || j >= ncellsY - 1 || k <= 0
	|| k >= ncellsZ - 1)
	return triangles;
    //make sure we save that this cube was marched through
    marchedCubes[ind] = true;
    //initialize vertices
    mp4Vector verts[8];

    verts[0] = points[ind];
    verts[1] = points[ind + YtimeZ];
    verts[2] = points[ind + YtimeZ + 1];
    verts[3] = points[ind + 1];
    verts[4] = prevVerts[3];
    verts[5] = prevVerts[2];
    verts[6] = prevVerts[1];
    verts[7] = prevVerts[0];
    //initialize edges from the last cube
    mpVector intVerts[12];

    intVerts[6] = prevIntVerts[0];
    intVerts[5] = prevIntVerts[1];
    intVerts[4] = prevIntVerts[2];
    intVerts[7] = prevIntVerts[3];
    //initialize gradients on vertices
    mp4Vector gradVerts[8];

    gradVerts[7] = prevGradVerts[0];
    gradVerts[6] = prevGradVerts[1];
    gradVerts[5] = prevGradVerts[2];
    gradVerts[4] = prevGradVerts[3];
    //initialize gradients on edges from the last cube
    mpVector grads[12];

    grads[6] = prevGrads[0];
    grads[5] = prevGrads[1];
    grads[4] = prevGrads[2];
    grads[7] = prevGrads[3];
    //for test if this cube is intersected
    int oldNumTriangles = numTriangles;

    //run marching cubes on this cube
    triangles =
	MarchOneCube (ncellsZ, gradFactorX, gradFactorY, gradFactorZ,
		      ind, minValue, points, triangles, numTriangles, verts,
		      intVerts, edgeIndex, gradVerts, grads, gradIndex);
    //check if this cube is intersected
    if (numTriangles == oldNumTriangles)
	return triangles;
    //two new indexes formed for each face
    int passEdgeIndex, passGradIndex;

    //run recursive functions on each surface
    MC_FACE0 MC_FACE1 MC_FACE2 MC_FACE3 MC_FACE5 return triangles;
}

//Marching Cubes on a single cube (i, j, k)
//      Verts should be initialized before hand
//  Global variables YtimeZ and pointsZ should be defined and initialized
TRIANGLE *
MarchOneCube (int ncellsZ,
	      float gradFactorX, float gradFactorY, float gradFactorZ,
	      int ind, float minValue, mp4Vector * points, TRIANGLE * triangles, int &numTriangles,
	      mp4Vector verts[8], mpVector intVerts[12], int &edgeIndex,
	      mp4Vector gradVerts[8], mpVector grads[12], int &indGrad)
{
    //factor by which gradients are scaled
    mpVector factor (1.0f / (2.0f * gradFactorX), 1.0f / (2.0f * gradFactorY),
		     1.0f / (2.0f * gradFactorZ));
    //get the index
    int cubeIndex = int (0);

    for (int nn = 0; nn < 8; nn++)
	if (verts[nn].val <= minValue)
	    cubeIndex |= (1 << nn);
    //check if its completely inside or outside
    if (!edgeTable[cubeIndex])
	return triangles;

    int prevEdgeIndex = edgeIndex;

    edgeIndex = edgeTable[cubeIndex];

    if ((edgeIndex & 1) && !(prevEdgeIndex & 1)) {
	intVerts[0] = LinearInterp (verts[0], verts[1], minValue);
	if (!(indGrad & 1)) {
	    gradVerts[0] = CALC_GRAD_VERT_0 (verts)
		indGrad |= 1;
	}
	if (!(indGrad & 2)) {
	    gradVerts[1] = CALC_GRAD_VERT_1 (verts)
		indGrad |= 2;
	}
	grads[0] = LinearInterp (gradVerts[0], gradVerts[1], minValue);
	grads[0].x *= factor.x;
	grads[0].y *= factor.y;
	grads[0].z *= factor.z;
    }
    if ((edgeIndex & 2) && !(prevEdgeIndex & 2)) {
	intVerts[1] = LinearInterp (verts[1], verts[2], minValue);
	if (!(indGrad & 2)) {
	    gradVerts[1] = CALC_GRAD_VERT_1 (verts)
		indGrad |= 2;
	}
	if (!(indGrad & 4)) {
	    gradVerts[2] = CALC_GRAD_VERT_2 (verts)
		indGrad |= 4;
	}
	grads[1] = LinearInterp (gradVerts[1], gradVerts[2], minValue);
	grads[1].x *= factor.x;
	grads[1].y *= factor.y;
	grads[1].z *= factor.z;
    }
    if (edgeIndex & 4) {
	intVerts[2] = LinearInterp (verts[2], verts[3], minValue);
	if (!(indGrad & 4)) {
	    gradVerts[2] = CALC_GRAD_VERT_2 (verts)
		indGrad |= 4;
	}
	if (!(indGrad & 8)) {
	    gradVerts[3] = CALC_GRAD_VERT_3 (verts)
		indGrad |= 8;
	}
	grads[2] = LinearInterp (gradVerts[2], gradVerts[3], minValue);
	grads[2].x *= factor.x;
	grads[2].y *= factor.y;
	grads[2].z *= factor.z;
    }
    if ((edgeIndex & 8) && !(prevEdgeIndex & 8)) {
	intVerts[3] = LinearInterp (verts[3], verts[0], minValue);
	if (!(indGrad & 8)) {
	    gradVerts[3] = CALC_GRAD_VERT_3 (verts)
		indGrad |= 8;
	}
	if (!(indGrad & 1)) {
	    gradVerts[0] = CALC_GRAD_VERT_0 (verts)
		indGrad |= 1;
	}
	grads[3] = LinearInterp (gradVerts[3], gradVerts[0], minValue);
	grads[3].x *= factor.x;
	grads[3].y *= factor.y;
	grads[3].z *= factor.z;
    }
    if ((edgeIndex & 16) && !(prevEdgeIndex & 16)) {
	intVerts[4] = LinearInterp (verts[4], verts[5], minValue);
	if (!(indGrad & 16)) {
	    gradVerts[4] = CALC_GRAD_VERT_4 (verts)
		indGrad |= 16;
	}
	if (!(indGrad & 32)) {
	    gradVerts[5] = CALC_GRAD_VERT_5 (verts)
		indGrad |= 32;
	}
	grads[4] = LinearInterp (gradVerts[4], gradVerts[5], minValue);
	grads[4].x *= factor.x;
	grads[4].y *= factor.y;
	grads[4].z *= factor.z;
    }
    if ((edgeIndex & 32) && !(prevEdgeIndex & 32)) {
	intVerts[5] = LinearInterp (verts[5], verts[6], minValue);
	if (!(indGrad & 32)) {
	    gradVerts[5] = CALC_GRAD_VERT_5 (verts)
		indGrad |= 32;
	}
	if (!(indGrad & 64)) {
	    gradVerts[6] = CALC_GRAD_VERT_6 (verts)
		indGrad |= 64;
	}
	grads[5] = LinearInterp (gradVerts[5], gradVerts[6], minValue);
	grads[5].x *= factor.x;
	grads[5].y *= factor.y;
	grads[5].z *= factor.z;
    }
    if ((edgeIndex & 64) && !(prevEdgeIndex & 64)) {
	intVerts[6] = LinearInterp (verts[6], verts[7], minValue);
	if (!(indGrad & 64)) {
	    gradVerts[6] = CALC_GRAD_VERT_6 (verts)
		indGrad |= 64;
	}
	if (!(indGrad & 128)) {
	    gradVerts[7] = CALC_GRAD_VERT_7 (verts)
		indGrad |= 128;
	}
	grads[6] = LinearInterp (gradVerts[6], gradVerts[7], minValue);
	grads[6].x *= factor.x;
	grads[6].y *= factor.y;
	grads[6].z *= factor.z;
    }
    if ((edgeIndex & 128) && !(prevEdgeIndex & 128)) {
	intVerts[7] = LinearInterp (verts[7], verts[4], minValue);
	if (!(indGrad & 128)) {
	    gradVerts[7] = CALC_GRAD_VERT_7 (verts)
		indGrad |= 128;
	}
	if (!(indGrad & 16)) {
	    gradVerts[4] = CALC_GRAD_VERT_4 (verts)
		indGrad |= 16;
	}
	grads[7] = LinearInterp (gradVerts[7], gradVerts[4], minValue);
	grads[7].x *= factor.x;
	grads[7].y *= factor.y;
	grads[7].z *= factor.z;
    }
    if ((edgeIndex & 256) && !(prevEdgeIndex & 256)) {
	intVerts[8] = LinearInterp (verts[0], verts[4], minValue);
	if (!(indGrad & 1)) {
	    gradVerts[0] = CALC_GRAD_VERT_0 (verts)
		indGrad |= 1;
	}
	if (!(indGrad & 16)) {
	    gradVerts[4] = CALC_GRAD_VERT_4 (verts)
		indGrad |= 16;
	}
	grads[8] = LinearInterp (gradVerts[0], gradVerts[4], minValue);
	grads[8].x *= factor.x;
	grads[8].y *= factor.y;
	grads[8].z *= factor.z;
    }
    if ((edgeIndex & 512) && !(prevEdgeIndex & 512)) {
	intVerts[9] = LinearInterp (verts[1], verts[5], minValue);
	if (!(indGrad & 2)) {
	    gradVerts[1] = CALC_GRAD_VERT_1 (verts)
		indGrad |= 2;
	}
	if (!(indGrad & 32)) {
	    gradVerts[5] = CALC_GRAD_VERT_5 (verts)
		indGrad |= 32;
	}
	grads[9] = LinearInterp (gradVerts[1], gradVerts[5], minValue);
	grads[9].x *= factor.x;
	grads[9].y *= factor.y;
	grads[9].z *= factor.z;
    }
    if ((edgeIndex & 1024) && !(prevEdgeIndex & 1024)) {
	intVerts[10] = LinearInterp (verts[2], verts[6], minValue);
	if (!(indGrad & 4)) {
	    gradVerts[2] = CALC_GRAD_VERT_2 (verts)
		indGrad |= 4;
	}
	if (!(indGrad & 64)) {
	    gradVerts[6] = CALC_GRAD_VERT_6 (verts)
		indGrad |= 64;
	}
	grads[10] = LinearInterp (gradVerts[2], gradVerts[6], minValue);
	grads[10].x *= factor.x;
	grads[10].y *= factor.y;
	grads[10].z *= factor.z;
    }
    if ((edgeIndex & 2048) && !(prevEdgeIndex & 2048)) {
	intVerts[11] = LinearInterp (verts[3], verts[7], minValue);
	if (!(indGrad & 8)) {
	    gradVerts[3] = CALC_GRAD_VERT_3 (verts)
		indGrad |= 8;
	}
	if (!(indGrad & 128)) {
	    gradVerts[7] = CALC_GRAD_VERT_7 (verts)
		indGrad |= 128;
	}
	grads[11] = LinearInterp (gradVerts[3], gradVerts[7], minValue);
	grads[11].x *= factor.x;
	grads[11].y *= factor.y;
	grads[11].z *= factor.z;
    }
    //now build the triangles using triTable and add them to the triangle array
    for (int n = 0; triTable[cubeIndex][n] != -1; n += 3) {
	int index[3] =
	    { triTable[cubeIndex][n + 2], triTable[cubeIndex][n + 1],
     triTable[cubeIndex][n] };
	for (int h = 0; h < 3; h++) {
	    triangles[numTriangles].p[h] = intVerts[index[h]];
	    triangles[numTriangles].norm[h] = grads[index[h]];
	}
	numTriangles++;		//one more triangle...
    }
    return triangles;
}



//Finds first intersecting cube and returns its indices (or -1 if nothing is found)
float *
MCFind (int ncellsX, int ncellsY, int ncellsZ, float minValue, mp4Vector * points)
{
    pointsZ = ncellsZ + 1;	//initializes global variables
    YtimeZ = (ncellsY + 1) * pointsZ;
    int lastX = ncellsX - 1, lastY = ncellsY - 1, lastZ = ncellsZ - 1;	//for a little extra speed

    mp4Vector *verts[8];	//store address of each point rather than copying x,y,z, and val

    int cubeIndex, ind, ni;

    float *found = new float[3];	//returned indices: initialized to -1

    found[0] = -1;
    found[0] = -1;
    found[0] = -1;

    for (int i = 1; i < lastX; i++) {
	ni = i * YtimeZ;
	for (int j = 1; j < lastY; j++) {
	    //get the first 4 vertices (0, 1, 4, 5) and place them into verts at indices 3, 2, 7, 6
	    ind = ni + j * pointsZ + 1;
	    verts[3] = &points[ind];
	    verts[2] = &points[ind + YtimeZ];
	    verts[7] = &points[ind + pointsZ];
	    verts[6] = &points[ind + YtimeZ + pointsZ];
	    for (int k = 1; k < lastZ; k++, ind++) {
		//initialize vertices
		verts[0] = verts[3];	//these are saved from the last iteration
		verts[1] = verts[2];
		verts[4] = verts[7];
		verts[5] = verts[6];
		verts[4] = &points[ind + pointsZ];
		verts[5] = &points[ind + YtimeZ + pointsZ];
		verts[6] = &points[ind + YtimeZ + ncellsZ + 2];
		verts[7] = &points[ind + ncellsZ + 2];
		//build index - shows which vertices are outside/inside
		cubeIndex = int (0);

		for (int n = 0; n < 8; n++)
		    if (verts[n]->val <= minValue)
			cubeIndex |= (1 << n);
		if (edgeTable[cubeIndex]) {	//if found then return them
		    found[0] = (float) i;
		    found[1] = (float) j;
		    found[2] = (float) k;
		    return found;
		}
	    }
	}
    }
    return found;
}

//First finds intersecting cube and runs recursive Marching Cubes.
TRIANGLE *
MCRecFind (int ncellsX, int ncellsY, int ncellsZ,
	   float gradFactorX, float gradFactorY, float gradFactorZ,
	   float minValue, mp4Vector * points, int &numTriangles)
{
    float *found = MCFind (ncellsX, ncellsY, ncellsZ, minValue, points);

    if (found[0] == -1) {	//if nothing is found return NULL
	numTriangles = 0;
	return NULL;
    }
    int i[1] = { (int) found[0] }, j[1] = {
    (int) found[1]}, k[1] = {
    (int) found[2]};
    delete[]found;
    return MarchingCubesRec (ncellsX, ncellsY, ncellsZ, gradFactorX, gradFactorY,
			     gradFactorZ, 1, i, j, k, minValue, points, numTriangles);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////// THE END ///////////////////////////////////////////////////////////////
