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
1.5.6