//vMarchCube1 performs the Marching Cubes algorithm on a single cube GLvoid vMarchCube1(GLfloat fX, GLfloat fY, GLfloat fZ, GLfloat fScale) { extern GLint aiCubeEdgeFlags[256]; extern GLint a2iTriangleConnectionTable[256][16]; GLint iCorner, iVertex, iVertexTest, iEdge, iTriangle, iFlagIndex, iEdgeFlags; GLfloat fOffset; GLvector sColor; GLfloat afCubeValue[8]; GLvector asEdgeVertex[12]; GLvector asEdgeNorm[12]; //Make a local copy of the values at the cube's corners for(iVertex = 0; iVertex < 8; iVertex++) { afCubeValue[iVertex] = fSample(fX + a2fVertexOffset[iVertex][0]*fScale, fY + a2fVertexOffset[iVertex][1]*fScale, fZ + a2fVertexOffset[iVertex][2]*fScale); } //Find which vertices are inside of the surface and which are outside iFlagIndex = 0; for(iVertexTest = 0; iVertexTest < 8; iVertexTest++) { if(afCubeValue[iVertexTest] <= fTargetValue) iFlagIndex |= 1<<iVertexTest; } //Find which edges are intersected by the surface iEdgeFlags = aiCubeEdgeFlags[iFlagIndex]; //If the cube is entirely inside or outside of the surface, then there will be no intersections if(iEdgeFlags == 0) { return; } //Find the point of intersection of the surface with each edge //Then find the normal to the surface at those points for(iEdge = 0; iEdge < 12; iEdge++) { //if there is an intersection on this edge if(iEdgeFlags & (1<<iEdge)) { fOffset = fGetOffset(afCubeValue[ a2iEdgeConnection[iEdge][0] ], afCubeValue[ a2iEdgeConnection[iEdge][1] ], fTargetValue); asEdgeVertex[iEdge].fX = fX + (a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][0] + fOffset * a2fEdgeDirection[iEdge][0]) * fScale; asEdgeVertex[iEdge].fY = fY + (a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][1] + fOffset * a2fEdgeDirection[iEdge][1]) * fScale; asEdgeVertex[iEdge].fZ = fZ + (a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][2] + fOffset * a2fEdgeDirection[iEdge][2]) * fScale; vGetNormal(asEdgeNorm[iEdge], asEdgeVertex[iEdge].fX, asEdgeVertex[iEdge].fY, asEdgeVertex[iEdge].fZ); } } //Draw the triangles that were found. There can be up to five per cube for(iTriangle = 0; iTriangle < 5; iTriangle++) { if(a2iTriangleConnectionTable[iFlagIndex][3*iTriangle] < 0) break; for(iCorner = 0; iCorner < 3; iCorner++) { iVertex = a2iTriangleConnectionTable[iFlagIndex][3*iTriangle+iCorner]; vGetColor(sColor, asEdgeVertex[iVertex], asEdgeNorm[iVertex]); glColor3f(sColor.fX, sColor.fY, sColor.fZ); glNormal3f(asEdgeNorm[iVertex].fX, asEdgeNorm[iVertex].fY, asEdgeNorm[iVertex].fZ); glVertex3f(asEdgeVertex[iVertex].fX, asEdgeVertex[iVertex].fY, asEdgeVertex[iVertex].fZ); } } }
//vMarchTetrahedron performs the Marching Tetrahedrons algorithm on a single tetrahedron GLvoid vMarchTetrahedron(GLvector *pasTetrahedronPosition, GLfloat *pafTetrahedronValue) { extern GLint aiTetrahedronEdgeFlags[16]; extern GLint a2iTetrahedronTriangles[16][7]; GLint iEdge, iVert0, iVert1, iEdgeFlags, iTriangle, iCorner, iVertex, iFlagIndex = 0; GLfloat fOffset, fInvOffset, fValue = 0.0; GLvector asEdgeVertex[6]; GLvector asEdgeNorm[6]; GLvector sColor; //Find which vertices are inside of the surface and which are outside for(iVertex = 0; iVertex < 4; iVertex++) { if(pafTetrahedronValue[iVertex] <= fTargetValue) iFlagIndex |= 1<<iVertex; } //Find which edges are intersected by the surface iEdgeFlags = aiTetrahedronEdgeFlags[iFlagIndex]; //If the tetrahedron is entirely inside or outside of the surface, then there will be no intersections if(iEdgeFlags == 0) { return; } //Find the point of intersection of the surface with each edge // Then find the normal to the surface at those points for(iEdge = 0; iEdge < 6; iEdge++) { //if there is an intersection on this edge if(iEdgeFlags & (1<<iEdge)) { iVert0 = a2iTetrahedronEdgeConnection[iEdge][0]; iVert1 = a2iTetrahedronEdgeConnection[iEdge][1]; fOffset = fGetOffset(pafTetrahedronValue[iVert0], pafTetrahedronValue[iVert1], fTargetValue); fInvOffset = 1.0 - fOffset; asEdgeVertex[iEdge].fX = fInvOffset*pasTetrahedronPosition[iVert0].fX + fOffset*pasTetrahedronPosition[iVert1].fX; asEdgeVertex[iEdge].fY = fInvOffset*pasTetrahedronPosition[iVert0].fY + fOffset*pasTetrahedronPosition[iVert1].fY; asEdgeVertex[iEdge].fZ = fInvOffset*pasTetrahedronPosition[iVert0].fZ + fOffset*pasTetrahedronPosition[iVert1].fZ; vGetNormal(asEdgeNorm[iEdge], asEdgeVertex[iEdge].fX, asEdgeVertex[iEdge].fY, asEdgeVertex[iEdge].fZ); } } //Draw the triangles that were found. There can be up to 2 per tetrahedron for(iTriangle = 0; iTriangle < 2; iTriangle++) { if(a2iTetrahedronTriangles[iFlagIndex][3*iTriangle] < 0) break; for(iCorner = 0; iCorner < 3; iCorner++) { iVertex = a2iTetrahedronTriangles[iFlagIndex][3*iTriangle+iCorner]; vGetColor(sColor, asEdgeVertex[iVertex], asEdgeNorm[iVertex]); glColor3f(sColor.fX, sColor.fY, sColor.fZ); glNormal3f(asEdgeNorm[iVertex].fX, asEdgeNorm[iVertex].fY, asEdgeNorm[iVertex].fZ); glVertex3f(asEdgeVertex[iVertex].fX, asEdgeVertex[iVertex].fY, asEdgeVertex[iVertex].fZ); } } }
//vMarchCube performs the Marching Cubes algorithm on a single cube void vMarchCube(int iX, int iY, int iZ) { extern GLint aiCubeEdgeFlags[256]; extern GLint a2iTriangleConnectionTable[256][16]; int iValueIndex; GLfloat fX, fY, fZ; GLfloat fXScale, fYScale, fZScale; GLfloat fCentralPoint = 0.0; GLfloat fDeltaValue, fDeltaX, fDeltaY, fDeltaZ; GLint iCorner, iVertex, iVertexTest, iEdge, iTriangle, iFlagIndex, iEdgeFlags; GLfloat fOffset; GLvector sColor; GLfloat afCubeValue[8]; GLvector asEdgeVertex[12]; GLvector asEdgeNorm[12]; int iX0, iY0, iZ0; int iX1, iY1, iZ1; GLfloat fValue0, fValue1; //Make a local copy of the values at the cube's corners for(iVertex = 0; iVertex < 8; iVertex++) { afCubeValue[iVertex] = fSample(iX + a2fVertexOffset[iVertex][0]*iXStep, iY + a2fVertexOffset[iVertex][1]*iYStep, iZ + a2fVertexOffset[iVertex][2]*iZStep); fCentralPoint += afCubeValue[iVertex]; } if (iUseGridPointers) { fX = fSourceXPointer[iX]; fY = fSourceYPointer[iY]; fZ = fSourceZPointer[iZ]; /* this can be calculated beforehand ... */ if ((iX+iXStep) < iXDataSetSize) { fXScale = fSourceXPointer[iX+iXStep] - fX; }else{ fXScale = 0.0; } if ((iY+iYStep) < iYDataSetSize) { fYScale = fSourceYPointer[iY+iYStep] - fY; }else{ fYScale = 0.0; } if ((iZ+iZStep) < iZDataSetSize) { fZScale = fSourceZPointer[iZ+iZStep] - fZ; }else{ fZScale = 0.0; } } else { iValueIndex = iX * (iYDataSetSize * iZDataSetSize) + \ iY * (iZDataSetSize) + iZ; fX = fSourceDataVerticesPointer[iValueIndex].fX; fY = fSourceDataVerticesPointer[iValueIndex].fY; fZ = fSourceDataVerticesPointer[iValueIndex].fZ; iValueIndex = (iX+iXStep) * (iYDataSetSize * iZDataSetSize) + \ (iY +iYStep) * (iZDataSetSize) + (iZ+iZStep); fXScale = fSourceDataVerticesPointer[iValueIndex].fX - fX; fYScale = fSourceDataVerticesPointer[iValueIndex].fY - fY; fZScale = fSourceDataVerticesPointer[iValueIndex].fZ - fZ; } /* Normal calucation */ /* Store the value of the scalar field at the center of the cube */ fCentralPoint *= 0.125; /* The central point has coordinates fCentralPointX = fX + 0.5 * fXScale fCentralPointY = fY + 0.5 * fYScale fCentralPointZ = fZ + 0.5 * fZScale */ //Find which vertices are inside of the surface and which are outside iFlagIndex = 0; for(iVertexTest = 0; iVertexTest < 8; iVertexTest++) { if(afCubeValue[iVertexTest] <= fTargetValue) iFlagIndex |= 1<<iVertexTest; } //Find which edges are intersected by the surface iEdgeFlags = aiCubeEdgeFlags[iFlagIndex]; //If the cube is entirely inside or outside of the surface, then there will be no intersections if((iEdgeFlags == 0) || (iEdgeFlags == 255)) { return; } //Find the point of intersection of the surface with each edge //Then find the normal to the surface at those points for(iEdge = 0; iEdge < 12; iEdge++) { //if there is an intersection on this edge if(iEdgeFlags & (1<<iEdge)) { fOffset = fGetOffset(afCubeValue[ a2iEdgeConnection[iEdge][0] ], afCubeValue[ a2iEdgeConnection[iEdge][1] ], fTargetValue); //The vertex value in actual coordenates asEdgeVertex[iEdge].fX = fX + fXScale * (a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][0] + fOffset * a2fEdgeDirection[iEdge][0]); asEdgeVertex[iEdge].fY = fY + fYScale * (a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][1] + fOffset * a2fEdgeDirection[iEdge][1]); asEdgeVertex[iEdge].fZ = fZ + fZScale * (a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][2] + fOffset * a2fEdgeDirection[iEdge][2]); if (0) { //This would be for the interpolating case: vGetNormal(&asEdgeNorm[iEdge], asEdgeVertex[iEdge].fX, asEdgeVertex[iEdge].fY, asEdgeVertex[iEdge].fZ); } else { /* This is for the "regular" grid */ if (1){ /* the correct way ... (hopefully) */ /* calculate the indices of the two vertices */ iX0 = a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][0]; iY0 = a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][1]; iZ0 = a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][2]; iX1 = a2fVertexOffset[ a2iEdgeConnection[iEdge][1] ][0]; iY1 = a2fVertexOffset[ a2iEdgeConnection[iEdge][1] ][1]; iZ1 = a2fVertexOffset[ a2iEdgeConnection[iEdge][1] ][2]; /* I have the indices */ /* The derivative in the first vertex respect to X*/ if (fXScale != 0){ fValue0 = fSample(iX + (iX0 + 1) * iXStep, iY + iY0 * iYStep, iZ + iZ0 * iZStep) -\ fSample(iX + (iX0 - 1) * iXStep, iY + iY0 * iYStep, iZ + iZ0 * iZStep); fValue1 = fSample(iX + (iX1 + 1) * iXStep, iY + iY1 * iYStep, iZ + iZ1 * iZStep) -\ fSample(iX + (iX1 - 1) * iXStep, iY + iY1 * iYStep, iZ + iZ1 * iZStep); asEdgeNorm[iEdge].fX = 0.5 * (fValue1 - fValue0) / fXScale; }else{ asEdgeNorm[iEdge].fX = 0.0; } /* The derivative in the first vertex respect to X */ if (fXScale != 0){ fValue0 = fSample(iX + (iX0 + 1) * iXStep, iY + iY0 * iYStep, iZ + iZ0 * iZStep) -\ fSample(iX + (iX0 - 1) * iXStep, iY + iY0 * iYStep, iZ + iZ0 * iZStep); fValue1 = fSample(iX + (iX1 + 1) * iXStep, iY + iY1 * iYStep, iZ + iZ1 * iZStep) -\ fSample(iX + (iX1 - 1) * iXStep, iY + iY1 * iYStep, iZ + iZ1 * iZStep); asEdgeNorm[iEdge].fX = 0.5 * (fValue0 + fOffset * fValue1) / fXScale; }else{ asEdgeNorm[iEdge].fX = 0.0; } /* The derivative in the first vertex respect to Y */ if (fYScale != 0){ fValue0 = fSample(iX * iXStep, iY + (iY0 + 1) * iYStep, iZ + iZ0 * iZStep) -\ fSample(iX * iXStep, iY + (iY0 - 1) * iYStep, iZ + iZ0 * iZStep); fValue1 = fSample(iX + iX1 * iXStep, iY + (iY1 + 1) * iYStep, iZ + iZ1 * iZStep) -\ fSample(iX + iX1 * iXStep, iY + (iY1 - 1) * iYStep, iZ + iZ1 * iZStep); asEdgeNorm[iEdge].fY = 0.5 * (fValue0 + fOffset * fValue1) / fYScale; }else{ asEdgeNorm[iEdge].fY = 0.0; } /* The derivative in the first vertex respect to Z */ if (fYScale != 0){ fValue0 = fSample(iX * iXStep, iY + iY0 * iYStep, iZ + (iZ0 + 1) * iZStep) -\ fSample(iX * iXStep, iY + iY0 * iYStep, iZ + (iZ0 - 1) * iZStep); fValue1 = fSample(iX + iX1 * iXStep, iY + iY1 * iYStep, iZ + (iZ1 + 1) * iZStep) -\ fSample(iX + iX1 * iXStep, iY + iY1 * iYStep, iZ + (iZ1 - 1) * iZStep); asEdgeNorm[iEdge].fZ = 0.5 * (fValue0 + fOffset * fValue1) / fZScale; }else{ asEdgeNorm[iEdge].fZ = 0.0; } }else { /* calculate all respect to the center */ fDeltaValue = fTargetValue - fCentralPoint; fDeltaX = asEdgeVertex[iEdge].fX - fX - 0.5 * fXScale; fDeltaY = asEdgeVertex[iEdge].fY - fY - 0.5 * fYScale; fDeltaZ = asEdgeVertex[iEdge].fZ - fZ - 0.5 * fZScale; if (fDeltaX > 0) { asEdgeNorm[iEdge].fX = fDeltaValue/fDeltaX; }else{ asEdgeNorm[iEdge].fX = 0.0; } if (fDeltaY > 0) { asEdgeNorm[iEdge].fY = fDeltaValue/fDeltaY; }else{ asEdgeNorm[iEdge].fY = 0.0; } if (fDeltaZ > 0) { asEdgeNorm[iEdge].fZ = fDeltaValue/fDeltaZ; }else{ asEdgeNorm[iEdge].fZ = 0.0; } } vNormalizeVector(&asEdgeNorm[iEdge], asEdgeNorm[iEdge]); } } } //Draw the triangles that were found. There can be up to five per cube for(iTriangle = 0; iTriangle < 5; iTriangle++) { if(a2iTriangleConnectionTable[iFlagIndex][3*iTriangle] < 0) break; for(iCorner = 0; iCorner < 3; iCorner++) { iVertex = a2iTriangleConnectionTable[iFlagIndex][3*iTriangle+iCorner]; if ((fIsoColor[0] < 0) || (fIsoColor[2] < 0) || (fIsoColor[3] < 0)) { vGetColor(&sColor, asEdgeVertex[iVertex], asEdgeNorm[iVertex]); glColor3f(sColor.fX, sColor.fY, sColor.fZ); } else { //glColor4f(fIsoColor[0], fIsoColor[1], fIsoColor[2], fIsoColor[3]); } glNormal3f(asEdgeNorm[iVertex].fX, asEdgeNorm[iVertex].fY, asEdgeNorm[iVertex].fZ); glVertex3f(asEdgeVertex[iVertex].fX, asEdgeVertex[iVertex].fY, asEdgeVertex[iVertex].fZ); /* if ((asEdgeVertex[iVertex].fZ > 1.93) || (asEdgeVertex[iVertex].fZ < 1.8)) { printf("iVertex = %d Z = %f\n", iVertex, asEdgeVertex[iVertex].fZ); printf("iX = %d iY = %d iZ = %d\n", iX, iY, iZ); printf("fx = %f, fy = %f, fz = %f\n", fX, fY, fZ); printf("scalex = %f, scaley = %f, scalez = %f\n", fXScale , fYScale , fZScale ); exit(1); } */ } iNTotalTriangles++; /* This was for some tests */ if (0) { if (iNTotalTriangles < 2) { printf("Triangle %d\n", iNTotalTriangles); printf("Target value =%f\n", fTargetValue); printf("Indices = %d, %d, %d\n", iX, iY, iZ); iValueIndex = iX * (iYDataSetSize * iZDataSetSize) + \ iY * (iZDataSetSize) + iZ; printf("Value index = %d, value =%f, vertex = %f, %f, %f\n", iValueIndex, *fSourceDataValuesPointer, fX, fY, fZ); printf("iFlagIndex=%d\n", iFlagIndex); printf("Edge flags =%d\n", iEdgeFlags); printf("Cube limits\n"); for(iVertex = 0; iVertex < 8; iVertex++) { printf("vertex = %d, value = %f\n", iVertex, afCubeValue[iVertex]); } } } } }