MarchingCubes.cpp

Go to the documentation of this file.
00001 /**<!-------------------------------------------------------------------->
00002    @file   MarchingCubes.cpp
00003    @author Travis Fischer (fisch0920@gmail.com)
00004    @author Matthew Jacobs (jacobs.mh@gmail.com)
00005    @date   Spring 2008
00006    
00007    @brief
00008       Marching Cubes is a well-known algorithm for generating a polygonal 
00009    approximation to an isosurface defined over a scalar field. This 
00010    class is a fairly standard implementation of Marching Cubes in 3D that 
00011    generates triangular meshes as output.
00012    
00013    References:
00014       http://www.ia.hiof.no/~borres/cgraph/explain/marching/p-march.html
00015    <!-------------------------------------------------------------------->**/
00016 
00017 #include "MarchingCubes.h"
00018 #include "Mesh.h"
00019 
00020 #include "MarchingCubesData.inl"
00021 
00022 DECLARE_STL_TYPEDEF2(std::map<unsigned, unsigned>, VertexIDMap);
00023 
00024 struct CubeVertex {
00025    real_t value;
00026 };
00027 
00028 struct MCData {
00029    MeshData meshData;
00030    
00031    unsigned width;
00032    unsigned height;
00033    unsigned depth;
00034    
00035    real_t   xScale;
00036    real_t   yScale;
00037    real_t   zScale;
00038    
00039    real_t   threshold;
00040    
00041    unsigned i;
00042    unsigned j;
00043    unsigned k;
00044    
00045    CubeVertex *grid;
00046    VertexIDMap vertexIDMap;
00047    
00048    inline const CubeVertex &cell(unsigned i, unsigned j, unsigned k) const {
00049       return grid[(k * height + j) * width + i];
00050    }
00051    
00052    inline CubeVertex &cell(unsigned i, unsigned j, unsigned k) {
00053       return grid[(k * height + j) * width + i];
00054    }
00055 };
00056 
00057 Mesh *MarchingCubes::polygonize(const ScalarField &field, real_t threshold) {
00058    MCData data;
00059    
00060    const AABB &aabb = field.getAABB();
00061    data.width   = m_resolution[0];
00062    data.height  = m_resolution[1];
00063    data.depth   = m_resolution[2];
00064    
00065    ASSERT(data.width  > 1);
00066    ASSERT(data.height > 1);
00067    ASSERT(data.depth  > 1);
00068    
00069    const Vector3 &dimensions = aabb.max - aabb.min;
00070    data.xScale = dimensions[0] / (data.width  - 1);
00071    data.yScale = dimensions[1] / (data.height - 1);
00072    data.zScale = dimensions[2] / (data.depth  - 1);
00073    Point3 point;
00074    
00075    data.threshold = threshold;
00076    data.grid      = (CubeVertex*)
00077       malloc(data.width * data.height * data.depth * sizeof(CubeVertex));
00078    
00079    // precompute field values at vertices
00080    for(unsigned i = data.width; i--;) {
00081       point[0] = aabb.min[0] + i * data.xScale;
00082       
00083       for(unsigned j = data.height; j--;) {
00084          point[1] = aabb.min[1] + j * data.yScale;
00085          
00086          for(unsigned k = data.depth; k--;) {
00087             point[2] = aabb.min[2] + k * data.zScale;
00088             
00089             data.cell(i, j, k).value = field.evaluate(point);
00090             //cout << data.cell(i, j, k).value << endl;
00091          }
00092       }
00093    }
00094    
00095    // march over each square, creating triangles on a case-by-case basis
00096    for(unsigned i = data.width - 1; i--;) {
00097       data.i = i;
00098       
00099       for(unsigned j = data.height - 1; j--;) {
00100          data.j = j;
00101          
00102          for(unsigned k = data.depth - 1; k--;) {
00103             data.k = k;
00104             
00105             const unsigned mcCase = 
00106                ((1 << 0) * (data.cell(i + 0, j + 0, k + 0).value <= threshold)) | 
00107                ((1 << 1) * (data.cell(i + 0, j + 1, k + 0).value <= threshold)) | 
00108                ((1 << 2) * (data.cell(i + 1, j + 1, k + 0).value <= threshold)) | 
00109                ((1 << 3) * (data.cell(i + 1, j + 0, k + 0).value <= threshold)) | 
00110                ((1 << 4) * (data.cell(i + 0, j + 0, k + 1).value <= threshold)) | 
00111                ((1 << 5) * (data.cell(i + 0, j + 1, k + 1).value <= threshold)) | 
00112                ((1 << 6) * (data.cell(i + 1, j + 1, k + 1).value <= threshold)) | 
00113                ((1 << 7) * (data.cell(i + 1, j + 0, k + 1).value <= threshold));
00114             
00115             const unsigned edge = EdgeTable[mcCase];
00116             
00117             if (edge != 0) {
00118                if (edge & 8) {
00119                   const Vertex &pt = _calculateIntersection(data, 3);
00120                   unsigned id = _getVertexID(data, 3);
00121                   
00122                   _addVertex(pt, id, data);
00123                }
00124                
00125                if (edge & 1) {
00126                   const Vertex &pt = _calculateIntersection(data, 0);
00127                   unsigned id = _getVertexID(data, 0);
00128                   
00129                   _addVertex(pt, id, data);
00130                }
00131                
00132                if (edge & 256) {
00133                   const Vertex &pt = _calculateIntersection(data, 8);
00134                   unsigned id = _getVertexID(data, 8);
00135                   
00136                   _addVertex(pt, id, data);
00137                }
00138                
00139                if (i == data.width - 2) {
00140                   if (edge & 4) {
00141                      const Vertex &pt = _calculateIntersection(data, 2);
00142                      unsigned id = _getVertexID(data, 2);
00143                      
00144                      _addVertex(pt, id, data);
00145                   }
00146                   
00147                   if (edge & 2048) {
00148                      const Vertex &pt = _calculateIntersection(data, 11);
00149                      unsigned id = _getVertexID(data, 11);
00150                      
00151                      _addVertex(pt, id, data);
00152                   }
00153                }
00154                
00155                if (j == data.height - 2) {
00156                   if (edge & 2) {
00157                      const Vertex &pt = _calculateIntersection(data, 1);
00158                      unsigned id = _getVertexID(data, 1);
00159                      
00160                      _addVertex(pt, id, data);
00161                   }
00162                   
00163                   if (edge & 512) {
00164                      const Vertex &pt = _calculateIntersection(data, 9);
00165                      unsigned id = _getVertexID(data, 9);
00166                      
00167                      _addVertex(pt, id, data);
00168                   }
00169                }
00170                
00171                if (k == data.depth - 2) {
00172                   if (edge & 16) {
00173                      const Vertex &pt = _calculateIntersection(data, 4);
00174                      unsigned id = _getVertexID(data, 4);
00175                      
00176                      _addVertex(pt, id, data);
00177                   }
00178                   
00179                   if (edge & 128) {
00180                      const Vertex &pt = _calculateIntersection(data, 7);
00181                      unsigned id = _getVertexID(data, 7);
00182                      
00183                      _addVertex(pt, id, data);
00184                   }
00185                }
00186                
00187                if (i == data.width - 2 && j == data.height - 2) {
00188                   if (edge & 1024) {
00189                      const Vertex &pt = _calculateIntersection(data, 10);
00190                      unsigned id = _getVertexID(data, 10);
00191                      
00192                      _addVertex(pt, id, data);
00193                   }
00194                }
00195                
00196                if (i == data.width - 2 && k == data.depth - 2) {
00197                   if (edge & 64) {
00198                      const Vertex &pt = _calculateIntersection(data, 6);
00199                      unsigned id = _getVertexID(data, 6);
00200                      
00201                      _addVertex(pt, id, data);
00202                   }
00203                }
00204                
00205                if (j == data.height - 2 && k == data.depth - 2) {
00206                   if (edge & 32) {
00207                      const Vertex &pt = _calculateIntersection(data, 5);
00208                      unsigned id = _getVertexID(data, 5);
00209                      
00210                      _addVertex(pt, id, data);
00211                   }
00212                }
00213                
00214                for (unsigned t = 0; TriangleTable[mcCase][t] != -1; t += 3) {
00215                   const unsigned vertexA = 
00216                      _getVertexID(data, TriangleTable[mcCase][t]);
00217                   const unsigned vertexB = 
00218                      _getVertexID(data, TriangleTable[mcCase][t + 2]);
00219                   const unsigned vertexC = 
00220                      _getVertexID(data, TriangleTable[mcCase][t + 1]);
00221                   
00222                   data.meshData.triangles.push_back(
00223                      MeshTriangle(vertexA, vertexB, vertexC)
00224                   );
00225                }
00226             }
00227          }
00228       }
00229    }
00230    
00231    _cleanup(data);
00232    
00233    return new Mesh(data.meshData);
00234 }
00235 
00236 Vertex MarchingCubes::_calculateIntersection(const MCData &data, 
00237                                              unsigned edge) const
00238 {
00239    unsigned v1x = data.i, v1y = data.j, v1z = data.k;
00240    unsigned v2x = data.i, v2y = data.j, v2z = data.k;
00241    
00242    switch (edge) {
00243       case 0:
00244          ++v2y;
00245          break;
00246       case 1:
00247          ++v1y;
00248          ++v2x;
00249          ++v2y;
00250          break;
00251       case 2:
00252          ++v1x;
00253          ++v1y;
00254          ++v2x;
00255          break;
00256       case 3:
00257          ++v1x;
00258          break;
00259       case 4:
00260          ++v1z;
00261          ++v2y;
00262          ++v2z;
00263          break;
00264       case 5:
00265          ++v1y;
00266          ++v1z;
00267          ++v2x;
00268          ++v2y;
00269          ++v2z;
00270          break;
00271       case 6:
00272          ++v1x;
00273          ++v1y;
00274          ++v1z;
00275          ++v2x;
00276          ++v2z;
00277          break;
00278       case 7:
00279          ++v1x;
00280          ++v1z;
00281          ++v2z;
00282          break;
00283       case 8:
00284          ++v2z;
00285          break;
00286       case 9:
00287          ++v1y;
00288          ++v2y;
00289          ++v2z;
00290          break;
00291       case 10:
00292          ++v1x;
00293          ++v1y;
00294          ++v2x;
00295          ++v2y;
00296          ++v2z;
00297          break;
00298       case 11:
00299          ++v1x;
00300          ++v2x;
00301          ++v2z;
00302          break;
00303    }
00304    
00305    const Vertex &v1 = Vertex(v1x * data.xScale, 
00306                              v1y * data.yScale, 
00307                              v1z * data.zScale);
00308    const Vertex &v2 = Vertex(v2x * data.xScale, 
00309                              v2y * data.yScale, 
00310                              v2z * data.zScale);
00311    
00312    const real_t coeff1 = data.cell(v1x, v1y, v1z).value;
00313    const real_t coeff2 = data.cell(v2x, v2y, v2z).value;
00314    
00315    return _interpolate(v1, v2, coeff1, coeff2, data.threshold);
00316 }
00317 
00318 inline unsigned MarchingCubes::_getVertexID(const MCData &data, 
00319                                             unsigned edge) const
00320 {
00321    switch (edge) {
00322       case 0:
00323          return _getVertexID(data.i, data.j, data.k, data.width, data.height) + 1;
00324       case 1:
00325          return _getVertexID(data.i, data.j + 1, data.k, data.width, data.height);
00326       case 2:
00327          return _getVertexID(data.i + 1, data.j, data.k, data.width, data.height) + 1;
00328       case 3:
00329          return _getVertexID(data.i, data.j, data.k, data.width, data.height);
00330       case 4:
00331          return _getVertexID(data.i, data.j, data.k + 1, data.width, data.height) + 1;
00332       case 5:
00333          return _getVertexID(data.i, data.j + 1, data.k + 1, data.width, data.height);
00334       case 6:
00335          return _getVertexID(data.i + 1, data.j, data.k + 1, data.width, data.height) + 1;
00336       case 7:
00337          return _getVertexID(data.i, data.j, data.k + 1, data.width, data.height);
00338       case 8:
00339          return _getVertexID(data.i, data.j, data.k, data.width, data.height) + 2;
00340       case 9:
00341          return _getVertexID(data.i, data.j + 1, data.k, data.width, data.height) + 2;
00342       case 10:
00343          return _getVertexID(data.i + 1, data.j + 1, data.k, data.width, data.height) + 2;
00344       case 11:
00345          return _getVertexID(data.i + 1, data.j, data.k, data.width, data.height) + 2;
00346       default:
00347          ASSERT(0);
00348          // invalid edge number
00349          return -1;
00350    }
00351 }
00352 
00353 inline unsigned MarchingCubes::_getVertexID(const MCData &data) const {
00354    return _getVertexID(data.i, data.j, data.k, data.width, data.height);
00355 }
00356 
00357 inline unsigned MarchingCubes::_getVertexID(unsigned i, unsigned j, unsigned k,
00358                                      unsigned width, unsigned height) const
00359 {
00360    return 3 * ((k * height + j) * width + i);
00361 }
00362 
00363 Vertex MarchingCubes::_interpolate(const Vertex &v1, const Vertex &v2, 
00364                                    real_t coeff1, real_t coeff2, 
00365                                    real_t threshold) const
00366 {
00367    const real_t alpha = (threshold - coeff1) / (coeff2 - coeff1);
00368    
00369    // v1 * (1 - alpha) + v2 * alpha
00370    return Vertex(v1 + alpha * (v2 - v1));
00371 }
00372 
00373 void MarchingCubes::_addVertex(const Vertex &vertex, unsigned id, 
00374                                MCData &data) const
00375 {
00376    // record generation id -> real vertex id mapping
00377    data.vertexIDMap[id] = data.meshData.vertices.size();
00378    
00379    data.meshData.vertices.push_back(vertex);
00380 }
00381 
00382 void MarchingCubes::_cleanup(MCData &data) const {
00383    std::vector<MeshTriangle> &triangles = data.meshData.triangles;
00384    
00385    for(std::vector<MeshTriangle>::iterator iter = 
00386        triangles.begin(); iter != triangles.end(); ++iter)
00387    {
00388       MeshTriangle &triangle = *iter;
00389       
00390       for(int i = 3; i--;)
00391          triangle.data[i] = data.vertexIDMap[triangle.data[i]];
00392    }
00393    
00394    safeFree(data.grid);
00395 }
00396 

Generated on 28 Feb 2009 for Milton by doxygen 1.5.6