From 5891d56e121a367fd1ffcfd123d642ad32ed1365 Mon Sep 17 00:00:00 2001 From: David Williams Date: Fri, 18 Mar 2011 22:04:53 +0000 Subject: [PATCH] Fix for linker errors when using MeshDecimator. --- library/PolyVoxCore/CMakeLists.txt | 1 + library/PolyVoxCore/include/MeshDecimator.h | 5 +- library/PolyVoxCore/include/MeshDecimator.inl | 151 ----------------- library/PolyVoxCore/source/MeshDecimator.cpp | 157 ++++++++++++++++++ 4 files changed, 162 insertions(+), 152 deletions(-) create mode 100644 library/PolyVoxCore/source/MeshDecimator.cpp diff --git a/library/PolyVoxCore/CMakeLists.txt b/library/PolyVoxCore/CMakeLists.txt index d3505650..73f3ca6e 100644 --- a/library/PolyVoxCore/CMakeLists.txt +++ b/library/PolyVoxCore/CMakeLists.txt @@ -8,6 +8,7 @@ SET(CORE_SRC_FILES source/AStarPathfinder.cpp source/GradientEstimators.cpp source/Log.cpp + source/MeshDecimator.cpp source/Region.cpp source/VertexTypes.cpp source/VoxelFilters.cpp diff --git a/library/PolyVoxCore/include/MeshDecimator.h b/library/PolyVoxCore/include/MeshDecimator.h index 82f3d849..9bd79fea 100644 --- a/library/PolyVoxCore/include/MeshDecimator.h +++ b/library/PolyVoxCore/include/MeshDecimator.h @@ -24,7 +24,10 @@ freely, subject to the following restrictions: #ifndef __PolyVox_MeshDecimator_H__ #define __PolyVox_MeshDecimator_H__ +#include "Vector.h" + #include +#include namespace PolyVox { @@ -64,7 +67,7 @@ namespace PolyVox { //Used to keep track of when a vertex is //on one or more faces of the region - enum POLYVOXCORE_API RegionFaceFlags + enum RegionFaceFlags { RFF_ON_REGION_FACE_NEG_X, RFF_ON_REGION_FACE_POS_X , diff --git a/library/PolyVoxCore/include/MeshDecimator.inl b/library/PolyVoxCore/include/MeshDecimator.inl index 1ad48e25..3936b047 100644 --- a/library/PolyVoxCore/include/MeshDecimator.inl +++ b/library/PolyVoxCore/include/MeshDecimator.inl @@ -116,131 +116,6 @@ namespace PolyVox } } - template<> - void MeshDecimator::fillInitialVertexMetadata(std::vector& vecVertexMetadata) - { - vecVertexMetadata.clear(); - vecVertexMetadata.resize(m_pOutputMesh->m_vecVertices.size()); - //Initialise the metadata - for(int ct = 0; ct < vecVertexMetadata.size(); ct++) - { - vecVertexMetadata[ct].normal.setElements(0,0,0); - vecVertexMetadata[ct].isOnMaterialEdge = false; - vecVertexMetadata[ct].isOnRegionFace.reset(); - } - - //Identify duplicate vertices, as they lie on the material edge. To do this we convert into integers and sort - //(first on z, then y, then x). They should be mostly in order as this is the order they come out of the - //CubicSurfaceExtractor in. Duplicates are now neighbours in the resulting list so just scan through for pairs. - std::vector intVertices; - intVertices.reserve(m_pOutputMesh->m_vecVertices.size()); - for(int ct = 0; ct < m_pOutputMesh->m_vecVertices.size(); ct++) - { - const Vector3DFloat& floatPos = m_pOutputMesh->m_vecVertices[ct].position; - IntVertex intVertex(static_cast(floatPos.getX()), static_cast(floatPos.getY()), static_cast(floatPos.getZ()), ct); - intVertices.push_back(intVertex); - } - - //Do the sorting so that duplicate become neighbours - sort(intVertices.begin(), intVertices.end()); - - //Find neighbours which are duplicates. - for(int ct = 0; ct < intVertices.size() - 1; ct++) - { - const IntVertex& v0 = intVertices[ct+0]; - const IntVertex& v1 = intVertices[ct+1]; - - if((v0.x == v1.x) && (v0.y == v1.y) && (v0.z == v1.z)) - { - vecVertexMetadata[v0.index].isOnMaterialEdge = true; - vecVertexMetadata[v1.index].isOnMaterialEdge = true; - } - } - - //Compute an approcimation to the normal, used when deciding if an edge can collapse. - for(int ct = 0; ct < m_pOutputMesh->m_vecVertices.size(); ct++) - { - Vector3DFloat sumOfNormals(0.0f,0.0f,0.0f); - for(vector::const_iterator iter = trianglesUsingVertex[ct].cbegin(); iter != trianglesUsingVertex[ct].cend(); iter++) - { - sumOfNormals += m_vecTriangles[*iter].normal; - } - - vecVertexMetadata[ct].normal = sumOfNormals; - vecVertexMetadata[ct].normal.normalise(); - } - - //Identify those vertices on the edge of a region. Care will need to be taken when moving them. - for(int ct = 0; ct < vecVertexMetadata.size(); ct++) - { - Region regTransformed = m_pOutputMesh->m_Region; - regTransformed.shift(regTransformed.getLowerCorner() * static_cast(-1)); - - //Plus and minus X - vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_NEG_X, m_pOutputMesh->m_vecVertices[ct].getPosition().getX() < regTransformed.getLowerCorner().getX() + 0.001f); - vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_POS_X, m_pOutputMesh->m_vecVertices[ct].getPosition().getX() > regTransformed.getUpperCorner().getX() - 0.001f); - //Plus and minus Y - vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_NEG_Y, m_pOutputMesh->m_vecVertices[ct].getPosition().getY() < regTransformed.getLowerCorner().getY() + 0.001f); - vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_POS_Y, m_pOutputMesh->m_vecVertices[ct].getPosition().getY() > regTransformed.getUpperCorner().getY() - 0.001f); - //Plus and minus Z - vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_NEG_Z, m_pOutputMesh->m_vecVertices[ct].getPosition().getZ() < regTransformed.getLowerCorner().getZ() + 0.001f); - vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_POS_Z, m_pOutputMesh->m_vecVertices[ct].getPosition().getZ() > regTransformed.getUpperCorner().getZ() - 0.001f); - } - } - - template<> - void MeshDecimator::fillInitialVertexMetadata(std::vector& vecVertexMetadata) - { - vecVertexMetadata.clear(); - vecVertexMetadata.resize(m_pOutputMesh->m_vecVertices.size()); - - //Initialise the metadata - for(int ct = 0; ct < vecVertexMetadata.size(); ct++) - { - vecVertexMetadata[ct].isOnRegionFace.reset(); - vecVertexMetadata[ct].isOnMaterialEdge = false; - vecVertexMetadata[ct].normal = m_pOutputMesh->m_vecVertices[ct].normal; - } - - //Identify those vertices on the edge of a region. Care will need to be taken when moving them. - for(int ct = 0; ct < vecVertexMetadata.size(); ct++) - { - Region regTransformed = m_pOutputMesh->m_Region; - regTransformed.shift(regTransformed.getLowerCorner() * static_cast(-1)); - - //Plus and minus X - vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_NEG_X, m_pOutputMesh->m_vecVertices[ct].getPosition().getX() < regTransformed.getLowerCorner().getX() + 0.001f); - vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_POS_X, m_pOutputMesh->m_vecVertices[ct].getPosition().getX() > regTransformed.getUpperCorner().getX() - 0.001f); - //Plus and minus Y - vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_NEG_Y, m_pOutputMesh->m_vecVertices[ct].getPosition().getY() < regTransformed.getLowerCorner().getY() + 0.001f); - vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_POS_Y, m_pOutputMesh->m_vecVertices[ct].getPosition().getY() > regTransformed.getUpperCorner().getY() - 0.001f); - //Plus and minus Z - vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_NEG_Z, m_pOutputMesh->m_vecVertices[ct].getPosition().getZ() < regTransformed.getLowerCorner().getZ() + 0.001f); - vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_POS_Z, m_pOutputMesh->m_vecVertices[ct].getPosition().getZ() > regTransformed.getUpperCorner().getZ() - 0.001f); - } - - //If all three vertices have the same material then we are not on a material edge. If any vertex has a different - //material then all three vertices are on a material edge. E.g. If one vertex has material 'a' and the other two - //have material 'b', then the two 'b's are still on an edge (with 'a') even though they are the same as eachother. - for(int ct = 0; ct < m_vecTriangles.size(); ct++) - { - uint32_t v0 = m_vecTriangles[ct].v0; - uint32_t v1 = m_vecTriangles[ct].v1; - uint32_t v2 = m_vecTriangles[ct].v2; - - bool allMatch = - (m_pOutputMesh->m_vecVertices[v0].material == m_pOutputMesh->m_vecVertices[v1].material) && - (m_pOutputMesh->m_vecVertices[v1].material == m_pOutputMesh->m_vecVertices[v2].material); - - if(!allMatch) - { - vecVertexMetadata[v0].isOnMaterialEdge = true; - vecVertexMetadata[v1].isOnMaterialEdge = true; - vecVertexMetadata[v2].isOnMaterialEdge = true; - } - } - } - template uint32_t MeshDecimator::performDecimationPass(float m_fMinDotProductForCollapse) { @@ -349,32 +224,6 @@ namespace PolyVox return bCanCollapse; } - 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); - } - - 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. - //It's not sufficient to just check the normals, there can be holes in the middle - //of the mesh for example. - - //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); - } - template bool MeshDecimator::canCollapseRegionEdge(uint32_t uSrc, uint32_t uDst) { diff --git a/library/PolyVoxCore/source/MeshDecimator.cpp b/library/PolyVoxCore/source/MeshDecimator.cpp new file mode 100644 index 00000000..e95a58e7 --- /dev/null +++ b/library/PolyVoxCore/source/MeshDecimator.cpp @@ -0,0 +1,157 @@ +#include "MeshDecimator.h" + +#include "SurfaceMesh.h" + +namespace PolyVox +{ + template<> + POLYVOXCORE_API void MeshDecimator::fillInitialVertexMetadata(std::vector& vecVertexMetadata) + { + vecVertexMetadata.clear(); + vecVertexMetadata.resize(m_pOutputMesh->m_vecVertices.size()); + //Initialise the metadata + for(int ct = 0; ct < vecVertexMetadata.size(); ct++) + { + vecVertexMetadata[ct].normal.setElements(0,0,0); + vecVertexMetadata[ct].isOnMaterialEdge = false; + vecVertexMetadata[ct].isOnRegionFace.reset(); + } + + //Identify duplicate vertices, as they lie on the material edge. To do this we convert into integers and sort + //(first on z, then y, then x). They should be mostly in order as this is the order they come out of the + //CubicSurfaceExtractor in. Duplicates are now neighbours in the resulting list so just scan through for pairs. + std::vector intVertices; + intVertices.reserve(m_pOutputMesh->m_vecVertices.size()); + for(int ct = 0; ct < m_pOutputMesh->m_vecVertices.size(); ct++) + { + const Vector3DFloat& floatPos = m_pOutputMesh->m_vecVertices[ct].position; + IntVertex intVertex(static_cast(floatPos.getX()), static_cast(floatPos.getY()), static_cast(floatPos.getZ()), ct); + intVertices.push_back(intVertex); + } + + //Do the sorting so that duplicate become neighbours + sort(intVertices.begin(), intVertices.end()); + + //Find neighbours which are duplicates. + for(int ct = 0; ct < intVertices.size() - 1; ct++) + { + const IntVertex& v0 = intVertices[ct+0]; + const IntVertex& v1 = intVertices[ct+1]; + + if((v0.x == v1.x) && (v0.y == v1.y) && (v0.z == v1.z)) + { + vecVertexMetadata[v0.index].isOnMaterialEdge = true; + vecVertexMetadata[v1.index].isOnMaterialEdge = true; + } + } + + //Compute an approcimation to the normal, used when deciding if an edge can collapse. + for(int ct = 0; ct < m_pOutputMesh->m_vecVertices.size(); ct++) + { + Vector3DFloat sumOfNormals(0.0f,0.0f,0.0f); + for(vector::const_iterator iter = trianglesUsingVertex[ct].cbegin(); iter != trianglesUsingVertex[ct].cend(); iter++) + { + sumOfNormals += m_vecTriangles[*iter].normal; + } + + vecVertexMetadata[ct].normal = sumOfNormals; + vecVertexMetadata[ct].normal.normalise(); + } + + //Identify those vertices on the edge of a region. Care will need to be taken when moving them. + for(int ct = 0; ct < vecVertexMetadata.size(); ct++) + { + Region regTransformed = m_pOutputMesh->m_Region; + regTransformed.shift(regTransformed.getLowerCorner() * static_cast(-1)); + + //Plus and minus X + vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_NEG_X, m_pOutputMesh->m_vecVertices[ct].getPosition().getX() < regTransformed.getLowerCorner().getX() + 0.001f); + vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_POS_X, m_pOutputMesh->m_vecVertices[ct].getPosition().getX() > regTransformed.getUpperCorner().getX() - 0.001f); + //Plus and minus Y + vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_NEG_Y, m_pOutputMesh->m_vecVertices[ct].getPosition().getY() < regTransformed.getLowerCorner().getY() + 0.001f); + vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_POS_Y, m_pOutputMesh->m_vecVertices[ct].getPosition().getY() > regTransformed.getUpperCorner().getY() - 0.001f); + //Plus and minus Z + vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_NEG_Z, m_pOutputMesh->m_vecVertices[ct].getPosition().getZ() < regTransformed.getLowerCorner().getZ() + 0.001f); + vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_POS_Z, m_pOutputMesh->m_vecVertices[ct].getPosition().getZ() > regTransformed.getUpperCorner().getZ() - 0.001f); + } + } + + template<> + POLYVOXCORE_API void MeshDecimator::fillInitialVertexMetadata(std::vector& vecVertexMetadata) + { + vecVertexMetadata.clear(); + vecVertexMetadata.resize(m_pOutputMesh->m_vecVertices.size()); + + //Initialise the metadata + for(int ct = 0; ct < vecVertexMetadata.size(); ct++) + { + vecVertexMetadata[ct].isOnRegionFace.reset(); + vecVertexMetadata[ct].isOnMaterialEdge = false; + vecVertexMetadata[ct].normal = m_pOutputMesh->m_vecVertices[ct].normal; + } + + //Identify those vertices on the edge of a region. Care will need to be taken when moving them. + for(int ct = 0; ct < vecVertexMetadata.size(); ct++) + { + Region regTransformed = m_pOutputMesh->m_Region; + regTransformed.shift(regTransformed.getLowerCorner() * static_cast(-1)); + + //Plus and minus X + vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_NEG_X, m_pOutputMesh->m_vecVertices[ct].getPosition().getX() < regTransformed.getLowerCorner().getX() + 0.001f); + vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_POS_X, m_pOutputMesh->m_vecVertices[ct].getPosition().getX() > regTransformed.getUpperCorner().getX() - 0.001f); + //Plus and minus Y + vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_NEG_Y, m_pOutputMesh->m_vecVertices[ct].getPosition().getY() < regTransformed.getLowerCorner().getY() + 0.001f); + vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_POS_Y, m_pOutputMesh->m_vecVertices[ct].getPosition().getY() > regTransformed.getUpperCorner().getY() - 0.001f); + //Plus and minus Z + vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_NEG_Z, m_pOutputMesh->m_vecVertices[ct].getPosition().getZ() < regTransformed.getLowerCorner().getZ() + 0.001f); + vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_POS_Z, m_pOutputMesh->m_vecVertices[ct].getPosition().getZ() > regTransformed.getUpperCorner().getZ() - 0.001f); + } + + //If all three vertices have the same material then we are not on a material edge. If any vertex has a different + //material then all three vertices are on a material edge. E.g. If one vertex has material 'a' and the other two + //have material 'b', then the two 'b's are still on an edge (with 'a') even though they are the same as eachother. + for(int ct = 0; ct < m_vecTriangles.size(); ct++) + { + uint32_t v0 = m_vecTriangles[ct].v0; + uint32_t v1 = m_vecTriangles[ct].v1; + uint32_t v2 = m_vecTriangles[ct].v2; + + bool allMatch = + (m_pOutputMesh->m_vecVertices[v0].material == m_pOutputMesh->m_vecVertices[v1].material) && + (m_pOutputMesh->m_vecVertices[v1].material == m_pOutputMesh->m_vecVertices[v2].material); + + if(!allMatch) + { + vecVertexMetadata[v0].isOnMaterialEdge = true; + vecVertexMetadata[v1].isOnMaterialEdge = true; + vecVertexMetadata[v2].isOnMaterialEdge = true; + } + } + } + + template<> + POLYVOXCORE_API 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); + } + + template<> + POLYVOXCORE_API 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. + //It's not sufficient to just check the normals, there can be holes in the middle + //of the mesh for example. + + //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); + } +}