diff --git a/library/PolyVoxCore/include/MeshDecimator.h b/library/PolyVoxCore/include/MeshDecimator.h index bc375ec6..158ea6b1 100644 --- a/library/PolyVoxCore/include/MeshDecimator.h +++ b/library/PolyVoxCore/include/MeshDecimator.h @@ -28,6 +28,37 @@ freely, subject to the following restrictions: namespace PolyVox { + /// The MeshDecimator reduces the number of triangles in a mesh. + //////////////////////////////////////////////////////////////////////////////// + /// Meshes generated by the PolyVox surface extractors typically have a very high + /// number of triangles in them. This can pose difficulties both for the rendering + /// storage of such meshes. The MeshDecimator provides a way of reducing the triangle + /// count with minimal visual effect. + /// + /// The MeshDecimator is based on the principle of edge collapse, and currently works + /// with meshes generated by the SurfaceExtractor or CubicSurfaceExtractor. It does + /// not work with meshes generated by the CubicSurfaceExtractorWithNormals, although + /// this may be addressed in the future. The algorithm iterates over each pair of + /// connected vertices in the mesh and attemps to determine if they can be collapsed + /// into a single vertex. + /// + /// The main criteria used in deciding whether two vertices can collapse is whether + /// they have the same normal. In the case of the cubic surfaces the normals must be + /// exactly the same, whereas in the case of the Marching Cubes surfaces a threshold + /// is used to determine whether two normals are 'close enough'. Additional constraints + /// apply to vertices which lie on the edges of regions or on the boundary between two + /// regions - these vertices are much less likely to be collapsed. + /// + /// Given a mesh called 'mesh', you can create a decimated version as follows: + /// \code + /// SurfaceMesh decimatedMesh; + /// MeshDecimator decimator(&mesh, &decimatedMesh); + /// decimator.execute(); + /// \endcode + /// + /// The above applies for a cubic mesh, for a Marching Cubes mesh you need to parametise + /// the MeshDecimator and resulting SurfaceMesh on the 'PositionMaterialNormal' type + /// instead of the 'PositionMaterial' type. template class MeshDecimator { @@ -62,8 +93,10 @@ namespace PolyVox Vector3DFloat normal; }; public: + ///Constructor MeshDecimator(const SurfaceMesh* pInputMesh, SurfaceMesh* pOutputMesh, float fEdgeCollapseThreshold = 0.95f); + ///Performs the decimation. void execute(); private: diff --git a/library/PolyVoxCore/include/MeshDecimator.inl b/library/PolyVoxCore/include/MeshDecimator.inl index 6139554b..87b17e18 100644 --- a/library/PolyVoxCore/include/MeshDecimator.inl +++ b/library/PolyVoxCore/include/MeshDecimator.inl @@ -23,6 +23,16 @@ freely, subject to the following restrictions: namespace PolyVox { + //////////////////////////////////////////////////////////////////////////////// + /// Builds a MeshDecimator. + /// \param pInputMesh A pointer to the mesh to be decimated. + /// \param pOutputMesh A pointer to where the result should be stored. Any existing + /// contents will be deleted. + /// \param fEdgeCollapseThreshold This is only use in the case of a Marching Cubes + /// surface and controls how close two normals must be to collapse. The dot product + /// between the normals is computed and compared to this threshold. A threshold of + /// 1.0 means nothing will collapse, a threshold of 0.0 means everything will collapse. + //////////////////////////////////////////////////////////////////////////////// template MeshDecimator::MeshDecimator(const SurfaceMesh* pInputMesh, SurfaceMesh* pOutputMesh, float fEdgeCollapseThreshold) :m_pInputMesh(pInputMesh) @@ -324,6 +334,11 @@ namespace PolyVox template<> bool MeshDecimator::canCollapseNormalEdge(uint32_t uSrc, uint32_t uDst) { + if(m_vecInitialVertexMetadata[uSrc].normal.dot(m_vecInitialVertexMetadata[uDst].normal) < m_fMinDotProductForCollapse) + { + return false; + } + //With the marching cubes surface we honour the user specified threshold return !collapseChangesFaceNormals(uSrc, uDst, m_fMinDotProductForCollapse); } @@ -331,6 +346,10 @@ namespace PolyVox template<> bool MeshDecimator::canCollapseNormalEdge(uint32_t uSrc, uint32_t uDst) { + //We don't actually use the normal here, because we want to allow face + //vertices to collapse onto edge vertices. Simply checking whether anything + //has flipped has proved to be the most robust approach, though rather slow... + //User specified threshold is not used for cubic surface, any //movement is too much (but allow for floating point error). return !collapseChangesFaceNormals(uSrc, uDst, 0.999f); @@ -365,11 +384,13 @@ namespace PolyVox return false; } + //This function should really use some work. For a start we already have the + //faces normals for the input mesh yet we are computing them on the fly here. template bool MeshDecimator::collapseChangesFaceNormals(uint32_t uSrc, uint32_t uDst, float fThreshold) { bool faceFlipped = false; - list triangles = trianglesUsingVertex[uSrc]; + list& triangles = trianglesUsingVertex[uSrc]; for(list::iterator triIter = triangles.begin(); triIter != triangles.end(); triIter++) { @@ -410,14 +431,6 @@ namespace PolyVox Vector3DFloat v1NewPos = m_pOutputMesh->m_vecVertices[vertexMapper[v1New]].getPosition(); Vector3DFloat v2NewPos = m_pOutputMesh->m_vecVertices[vertexMapper[v2New]].getPosition(); - /*Vector3DFloat v0OldPos = m_vecVertices[v0Old].getPosition(); - Vector3DFloat v1OldPos = m_vecVertices[v1Old].getPosition(); - Vector3DFloat v2OldPos = m_vecVertices[v2Old].getPosition(); - - Vector3DFloat v0NewPos = m_vecVertices[v0New].getPosition(); - Vector3DFloat v1NewPos = m_vecVertices[v1New].getPosition(); - Vector3DFloat v2NewPos = m_vecVertices[v2New].getPosition();*/ - Vector3DFloat OldNormal = (v1OldPos - v0OldPos).cross(v2OldPos - v1OldPos); Vector3DFloat NewNormal = (v1NewPos - v0NewPos).cross(v2NewPos - v1NewPos);