/**************************************************************************** * VCGLib o o * * Visual and Computer Graphics Library o o * * _ O _ * * Copyright(C) 2004 \/)\/ * * Visual Computing Lab /\/| * * ISTI - Italian National Research Council | * * \ * * All rights reserved. * * * * 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 (http://www.gnu.org/licenses/gpl.txt) * * for more details. * * * ****************************************************************************/ /***************************************************************************/ #ifndef __VCG_MARCHING_CUBES #define __VCG_MARCHING_CUBES #include #include #include #include "mc_lookup_table.h" namespace vcg { namespace tri { // Doxygen documentation /** \addtogroup trimesh */ /*@{*/ /* * Cube description: * 3 ________ 2 _____2__ * /| /| / | /| * / | / | 11/ 3 10/ | * 7 /_______ / | /__6_|__ / |1 * | | |6 | | | | | * | 0|__|_____|1 | |__|__0__| * | / | / 7 8/ 5 / * | / | / | / | /9 * |/_______|/ |/___4___|/ * 4 5 */ //! This class implements the Marching Cubes algorithm. /*! * The implementation is enough generic: this class works only on one volume cell for each * call to ProcessCell. Using the field value at the cell corners, it adds to the * mesh the triangles set approximating the surface that cross that cell. The ambiguities * are resolved using an enhanced topologically controlled lookup table. * @param TRIMESH_TYPE (Template parameter) the mesh type that will be constructed * @param WALKER_TYPE (Template parameter) the class that implement the traversal ordering of the volume **/ template class MarchingCubes { public: enum Dimension {X, Y, Z}; #if defined(__GNUC__) typedef unsigned int size_t; #else #ifdef _WIN64 typedef unsigned __int64 size_t; #else typedef _W64 unsigned int size_t; #endif #endif typedef typename vcg::tri::Allocator< TRIMESH_TYPE > AllocatorType; typedef typename TRIMESH_TYPE::ScalarType ScalarType; typedef typename TRIMESH_TYPE::VertexType VertexType; typedef typename TRIMESH_TYPE::VertexPointer VertexPointer; typedef typename TRIMESH_TYPE::VertexIterator VertexIterator; typedef typename TRIMESH_TYPE::FaceType FaceType; typedef typename TRIMESH_TYPE::FacePointer FacePointer; typedef typename TRIMESH_TYPE::FaceIterator FaceIterator; typedef typename TRIMESH_TYPE::CoordType CoordType; typedef typename TRIMESH_TYPE::CoordType* CoordPointer; /*! * Constructor * \param mesh the mesh that will be constructed * \param walker the class implementing the traversal policy */ MarchingCubes(TRIMESH_TYPE &mesh, WALKER_TYPE &walker) { _mesh = &mesh; _walker = &walker; }; /*! * Execute the initialiazation. * This method must be executed before the first call to ApplyMC */ void Initialize() { _mesh->Clear(); }; // end of Initialize() /*! * * This method must be executed after the last call to ApplyMC */ void Finalize() { _mesh = NULL; _walker = NULL; }; // end of Finalize() /*! * Apply the marching cubes algorithm to the volume cell identified by the two points min and max. * All the three coordinates of the first point must be smaller than the respectives three coordinatas of the second point. * \param min the first point * \param max the second point */ void ProcessCell(const vcg::Point3i &min, const vcg::Point3i &max) { _case = _subconfig = _config = -1; assert(min[0]V( _corners[i].X(), _corners[i].Y(), _corners[i].Z() ); unsigned char cubetype = 0; for (int i=0; i<8; i++) if (_field[i]>0) cubetype += 1<= 0 ; // face and A invert signs }; // end of TestFace /*! * Tests if the components of the tesselation of the cube should be connected * through the interior of the cube */ inline bool TestInterior(signed char s) { ScalarType t, At=0, Bt=0, Ct=0, Dt=0, a, b ; char test = 0 ; char edge = -1 ; // reference edge of the triangulation switch( _case ) { case 4 : case 10 : { a = (_field[4]-_field[0])*(_field[6]-_field[2]) - (_field[7]-_field[3])*(_field[5]-_field[1]); b = _field[2]*(_field[4]-_field[0])+_field[0]*(_field[6]-_field[2])-_field[1]*(_field[7]-_field[3])-_field[3]*(_field[5]-_field[1]); t = - b / (2*a) ; if( t<0 || t>1 ) return s>0 ; At = _field[0] + ( _field[4] - _field[0] ) * t ; Bt = _field[3] + ( _field[7] - _field[3] ) * t ; Ct = _field[2] + ( _field[6] - _field[2] ) * t ; Dt = _field[1] + ( _field[5] - _field[1] ) * t ; break ; } case 6 : case 7 : case 12 : case 13 : switch( _case ) { case 6 : edge = MCLookUpTable::Test6 (_config, 2) ; break ; case 7 : edge = MCLookUpTable::Test7 (_config, 4) ; break ; case 12 : edge = MCLookUpTable::Test12(_config, 3) ; break ; case 13 : edge = MCLookUpTable::Tiling13_5_1(_config, _subconfig)[0] ; break ; } switch( edge ) { case 0 : t = _field[0] / ( _field[0] - _field[1] ) ; At = 0 ; Bt = _field[3] + ( _field[2] - _field[3] ) * t ; Ct = _field[7] + ( _field[6] - _field[7] ) * t ; Dt = _field[4] + ( _field[5] - _field[4] ) * t ; break ; case 1 : t = _field[1] / ( _field[1] - _field[2] ) ; At = 0 ; Bt = _field[0] + ( _field[3] - _field[0] ) * t ; Ct = _field[4] + ( _field[7] - _field[4] ) * t ; Dt = _field[5] + ( _field[6] - _field[5] ) * t ; break ; case 2 : t = _field[2] / ( _field[2] - _field[3] ) ; At = 0 ; Bt = _field[1] + ( _field[0] - _field[1] ) * t ; Ct = _field[5] + ( _field[4] - _field[5] ) * t ; Dt = _field[6] + ( _field[7] - _field[6] ) * t ; break ; case 3 : t = _field[3] / ( _field[3] - _field[0] ) ; At = 0 ; Bt = _field[2] + ( _field[1] - _field[2] ) * t ; Ct = _field[6] + ( _field[5] - _field[6] ) * t ; Dt = _field[7] + ( _field[4] - _field[7] ) * t ; break ; case 4 : t = _field[4] / ( _field[4] - _field[5] ) ; At = 0 ; Bt = _field[7] + ( _field[6] - _field[7] ) * t ; Ct = _field[3] + ( _field[2] - _field[3] ) * t ; Dt = _field[0] + ( _field[1] - _field[0] ) * t ; break ; case 5 : t = _field[5] / ( _field[5] - _field[6] ) ; At = 0 ; Bt = _field[4] + ( _field[7] - _field[4] ) * t ; Ct = _field[0] + ( _field[3] - _field[0] ) * t ; Dt = _field[1] + ( _field[2] - _field[1] ) * t ; break ; case 6 : t = _field[6] / ( _field[6] - _field[7] ) ; At = 0 ; Bt = _field[5] + ( _field[4] - _field[5] ) * t ; Ct = _field[1] + ( _field[0] - _field[1] ) * t ; Dt = _field[2] + ( _field[3] - _field[2] ) * t ; break ; case 7 : t = _field[7] / ( _field[7] - _field[4] ) ; At = 0 ; Bt = _field[6] + ( _field[5] - _field[6] ) * t ; Ct = _field[2] + ( _field[1] - _field[2] ) * t ; Dt = _field[3] + ( _field[0] - _field[3] ) * t ; break ; case 8 : t = _field[0] / ( _field[0] - _field[4] ) ; At = 0 ; Bt = _field[3] + ( _field[7] - _field[3] ) * t ; Ct = _field[2] + ( _field[6] - _field[2] ) * t ; Dt = _field[1] + ( _field[5] - _field[1] ) * t ; break ; case 9 : t = _field[1] / ( _field[1] - _field[5] ) ; At = 0 ; Bt = _field[0] + ( _field[4] - _field[0] ) * t ; Ct = _field[3] + ( _field[7] - _field[3] ) * t ; Dt = _field[2] + ( _field[6] - _field[2] ) * t ; break ; case 10 : t = _field[2] / ( _field[2] - _field[6] ) ; At = 0 ; Bt = _field[1] + ( _field[5] - _field[1] ) * t ; Ct = _field[0] + ( _field[4] - _field[0] ) * t ; Dt = _field[3] + ( _field[7] - _field[3] ) * t ; break ; case 11 : t = _field[3] / ( _field[3] - _field[7] ) ; At = 0 ; Bt = _field[2] + ( _field[6] - _field[2] ) * t ; Ct = _field[1] + ( _field[5] - _field[1] ) * t ; Dt = _field[0] + ( _field[4] - _field[0] ) * t ; break ; default: { assert(false); /* Invalid edge */ break ; } } break ; default : assert(false); /* Invalid ambiguous case */ break; } if( At >= 0 ) test ++ ; if( Bt >= 0 ) test += 2 ; if( Ct >= 0 ) test += 4 ; if( Dt >= 0 ) test += 8 ; switch( test ) { case 0 : return s>0 ; case 1 : return s>0 ; case 2 : return s>0 ; case 3 : return s>0 ; case 4 : return s>0 ; case 5 : if( At * Ct < Bt * Dt ) return s>0 ; break ; case 6 : return s>0 ; case 7 : return s<0 ; case 8 : return s>0 ; case 9 : return s>0 ; case 10 : if( At * Ct >= Bt * Dt ) return s>0 ; break ; case 11 : return s<0 ; case 12 : return s>0 ; case 13 : return s<0 ; case 14 : return s<0 ; case 15 : return s<0 ; } return s<0 ; }; //end of TestInterior /*! * Adds a vertex inside the current cube * \param v The pointer to the new vertex along the edge */ inline void ComputeCVertex(VertexPointer &v12) { v12 = &*AllocatorType::AddVertices(*_mesh, 1); v12->P() = CoordType(0.0, 0.0, 0.0); unsigned int count = 0; VertexPointer v = NULL; if (_walker->Exist(_corners[0], _corners[1], v) ) { count++; v12->P() += v->P(); } if (_walker->Exist(_corners[1], _corners[2], v) ) { count++; v12->P() += v->P(); } if (_walker->Exist(_corners[3], _corners[2], v) ) { count++; v12->P() += v->P(); } if (_walker->Exist(_corners[0], _corners[3], v) ) { count++; v12->P() += v->P(); } if (_walker->Exist(_corners[4], _corners[5], v) ) { count++; v12->P() += v->P(); } if (_walker->Exist(_corners[5], _corners[6], v) ) { count++; v12->P() += v->P(); } if (_walker->Exist(_corners[7], _corners[6], v) ) { count++; v12->P() += v->P(); } if (_walker->Exist(_corners[4], _corners[7], v) ) { count++; v12->P() += v->P(); } if (_walker->Exist(_corners[0], _corners[4], v) ) { count++; v12->P() += v->P(); } if (_walker->Exist(_corners[1], _corners[5], v) ) { count++; v12->P() += v->P(); } if (_walker->Exist(_corners[2], _corners[6], v) ) { count++; v12->P() += v->P(); } if (_walker->Exist(_corners[3], _corners[7], v) ) { count++; v12->P() += v->P(); } v12->P() /= (float) count; } // end of AddCVertex /*! * Adds new triangles to the mesh * \param vertices_list The list of vertex indices * \param n The number of triangles that will be added to the mesh * \param v12 The pointer to the vertex inside the current cell */ inline void AddTriangles(const char *vertices_list, char n, VertexPointer v12=NULL) { VertexPointer vp = NULL; size_t face_idx = _mesh->face.size(); size_t v12_idx = -1; size_t vertices_idx[3]; if (v12 != NULL) v12_idx = v12 - &_mesh->vert[0]; AllocatorType::AddFaces(*_mesh, (int) n); for (int trig=0; trig<3*n; face_idx++ ) { vp = NULL; memset(vertices_idx, -1, 3*sizeof(size_t)); for (int vert=0; vert<3; vert++, trig++) //ok { switch ( vertices_list[trig] ) { case 0: { _walker->GetXIntercept(_corners[0], _corners[1], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; } case 1: { _walker->GetYIntercept(_corners[1], _corners[2], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; } case 2: { _walker->GetXIntercept(_corners[3], _corners[2], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; } case 3: { _walker->GetYIntercept(_corners[0], _corners[3], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; } case 4: { _walker->GetXIntercept(_corners[4], _corners[5], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; } case 5: { _walker->GetYIntercept(_corners[5], _corners[6], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; } case 6: { _walker->GetXIntercept(_corners[7], _corners[6], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; } case 7: { _walker->GetYIntercept(_corners[4], _corners[7], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; } case 8: { _walker->GetZIntercept(_corners[0], _corners[4], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; } case 9: { _walker->GetZIntercept(_corners[1], _corners[5], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; } case 10: { _walker->GetZIntercept(_corners[2], _corners[6], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; } case 11: { _walker->GetZIntercept(_corners[3], _corners[7], vp); vertices_idx[vert] = vp - &_mesh->vert[0]; break; } case 12: { assert(v12 != NULL); vertices_idx[vert] = v12_idx; break; } default: { assert(false); /* Invalid edge identifier */ } } // end of switch assert(vertices_idx[vert]>=0 && vertices_idx[vert]<_mesh->vert.size()); } // end for (int vert=0 ...) _mesh->face[face_idx].V(0) = &_mesh->vert[vertices_idx[0]]; _mesh->face[face_idx].V(1) = &_mesh->vert[vertices_idx[1]]; _mesh->face[face_idx].V(2) = &_mesh->vert[vertices_idx[2]]; } // end for (int trig=0...) }; // end of AddTriangles }; // end of class MarchingCubes /*! @} */ //end of Doxygen documentation }; // end of namespace tri }; // end of namespace vcg #endif //__VCG_MARCHING_CUBES