diff --git a/library/PolyVoxCore/include/AStarPathfinder.inl b/library/PolyVoxCore/include/AStarPathfinder.inl index e6e1279d..9e97ed83 100644 --- a/library/PolyVoxCore/include/AStarPathfinder.inl +++ b/library/PolyVoxCore/include/AStarPathfinder.inl @@ -21,8 +21,6 @@ freely, subject to the following restrictions: distribution. *******************************************************************************/ -#include //For numeric_limits - #include namespace PolyVox diff --git a/library/PolyVoxCore/include/CubicSurfaceExtractor.inl b/library/PolyVoxCore/include/CubicSurfaceExtractor.inl index 1102f8cd..5b016be5 100644 --- a/library/PolyVoxCore/include/CubicSurfaceExtractor.inl +++ b/library/PolyVoxCore/include/CubicSurfaceExtractor.inl @@ -77,7 +77,7 @@ namespace PolyVox if((currentVoxelIsSolid != negXVoxelIsSolid) && (finalY == false) && (finalZ == false)) { - int material = std::max(currentVoxel.getMaterial(), negXVoxel.getMaterial()); + int material = (std::max)(currentVoxel.getMaterial(), negXVoxel.getMaterial()); /*uint32_t v0 = m_meshCurrent->addVertex(PositionMaterial(Vector3DFloat(regX - 0.5f, regY - 0.5f, regZ - 0.5f), material)); uint32_t v1 = m_meshCurrent->addVertex(PositionMaterial(Vector3DFloat(regX - 0.5f, regY - 0.5f, regZ + 0.5f), material)); @@ -106,7 +106,7 @@ namespace PolyVox if((currentVoxelIsSolid != negYVoxelIsSolid) && (finalX == false) && (finalZ == false)) { - int material = std::max(currentVoxel.getMaterial(),negYVoxel.getMaterial()); + int material = (std::max)(currentVoxel.getMaterial(),negYVoxel.getMaterial()); uint32_t v0 = addVertex(regX - 0.5f, regY - 0.5f, regZ - 0.5f, material, m_previousSliceVertices); uint32_t v1 = addVertex(regX - 0.5f, regY - 0.5f, regZ + 0.5f, material, m_currentSliceVertices); @@ -130,7 +130,7 @@ namespace PolyVox if((currentVoxelIsSolid != negZVoxelIsSolid) && (finalX == false) && (finalY == false)) { - int material = std::max(currentVoxel.getMaterial(), negZVoxel.getMaterial()); + int material = (std::max)(currentVoxel.getMaterial(), negZVoxel.getMaterial()); uint32_t v0 = addVertex(regX - 0.5f, regY - 0.5f, regZ - 0.5f, material, m_previousSliceVertices); uint32_t v1 = addVertex(regX - 0.5f, regY + 0.5f, regZ - 0.5f, material, m_previousSliceVertices); diff --git a/library/PolyVoxCore/include/MeshDecimator.h b/library/PolyVoxCore/include/MeshDecimator.h new file mode 100644 index 00000000..ccac91d9 --- /dev/null +++ b/library/PolyVoxCore/include/MeshDecimator.h @@ -0,0 +1,62 @@ +/******************************************************************************* +Copyright (c) 2005-2009 David Williams + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*******************************************************************************/ + +#ifndef __PolyVox_MeshDecimator_H__ +#define __PolyVox_MeshDecimator_H__ + +namespace PolyVox +{ + template + class MeshDecimator + { + public: + MeshDecimator(SurfaceMesh* pInputMesh/*, SurfaceMesh* pMeshOutput*/); + + void execute(); + + private: + SurfaceMesh* m_pInputMesh; + //SurfaceMesh* pMeshOutput; + + void countNoOfNeighboursUsingMaterial(void); + uint32_t performDecimationPass(float fMinDotProductForCollapse); + bool isSubset(std::bitset a, std::bitset b); + + bool canCollapseEdge(uint32_t uSrc, uint32_t uDest); + + //Data structures used during decimation + std::vector m_vecNoOfNeighboursUsingMaterial; + + vector vertexLocked; + vector vertexMapper; + + vector< list > trianglesUsingVertex; + vector noOfDifferentNormals; + + float fMinDotProductForCollapse; + }; +} + +#include "MeshDecimator.inl" + +#endif //__PolyVox_MeshDecimator_H__ diff --git a/library/PolyVoxCore/include/MeshDecimator.inl b/library/PolyVoxCore/include/MeshDecimator.inl new file mode 100644 index 00000000..d733b7ca --- /dev/null +++ b/library/PolyVoxCore/include/MeshDecimator.inl @@ -0,0 +1,570 @@ +/******************************************************************************* +Copyright (c) 2005-2009 David Williams + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*******************************************************************************/ + +namespace PolyVox +{ + template + MeshDecimator::MeshDecimator(SurfaceMesh* pInputMesh/*, SurfaceMesh* pMeshOutput*/) + :m_pInputMesh(pInputMesh) + //,m_pOutputMesh(pOutputMesh) + { + fMinDotProductForCollapse = 0.999; + } + + template + void MeshDecimator::execute() + { + // We will need the information from this function to + // determine when material boundary edges can collapse. + countNoOfNeighboursUsingMaterial(); + + uint32_t noOfEdgesCollapsed; + do + { + noOfEdgesCollapsed = performDecimationPass(fMinDotProductForCollapse); + m_pInputMesh->removeDegenerateTris(); + m_pInputMesh->removeUnusedVertices(); + }while(noOfEdgesCollapsed > 0); + + //Decimation will have invalidated LOD levels. + m_pInputMesh->m_vecLodRecords.clear(); + LodRecord lodRecord; + lodRecord.beginIndex = 0; + lodRecord.endIndex = m_pInputMesh->getNoOfIndices(); + m_pInputMesh->m_vecLodRecords.push_back(lodRecord); + } + + template + uint32_t MeshDecimator::performDecimationPass(float fMinDotProductForCollapse) + { + // I'm using a vector of lists here, rather than a vector of sets, + // because I don't believe that duplicaes should occur. But this + // might be worth checking if we have problems in the future. + trianglesUsingVertex.clear(); + trianglesUsingVertex.resize(m_pInputMesh->m_vecVertices.size()); + for(int ct = 0; ct < m_pInputMesh->m_vecTriangleIndices.size(); ct++) + { + int triangle = ct / 3; + + trianglesUsingVertex[m_pInputMesh->m_vecTriangleIndices[ct]].push_back(triangle); + } + + // Count how many edges we have collapsed + uint32_t noOfEdgesCollapsed = 0; + + // The vertex mapper track whick vertices collapse onto which. + vertexMapper.clear(); + vertexMapper.resize(m_pInputMesh->m_vecVertices.size()); + + // Once a vertex is involved in a collapse (either because it + // moves onto a different vertex, or because a different vertex + // moves onto it) it is forbidden to take part in another collapse + // this pass. We enforce this by setting the vertex locked flag. + vertexLocked.clear(); + vertexLocked.resize(m_pInputMesh->m_vecVertices.size()); + + // Initialise the vectors + for(uint32_t ct = 0; ct < m_pInputMesh->m_vecVertices.size(); ct++) + { + // Initiall all vertices points to themselves + vertexMapper[ct] = ct; + // All vertices are initially unlocked + vertexLocked[ct] = false; + } + + + // Each triangle exists in this vector once. + vector vecOfTriCts(m_pInputMesh->m_vecTriangleIndices.size() / 3); + for(int triCt = 0; triCt < vecOfTriCts.size(); triCt++) + { + vecOfTriCts[triCt] = triCt; + } + + // It *may* be beneficial to randomise the order in which triangles + // are processed to get a more uniform distribution off collapses and + // more equally sized triangles at the end. This need more testing really. + //random_shuffle(vecOfTriCts.begin(), vecOfTriCts.end()); + + vector vecOfTriNormals(vecOfTriCts.size()); + for(int ct = 0; ct < vecOfTriCts.size(); ct++) + { + int triCt = vecOfTriCts[ct]; + int v0 = m_pInputMesh->m_vecTriangleIndices[triCt * 3 + 0]; + int v1 = m_pInputMesh->m_vecTriangleIndices[triCt * 3 + 1]; + int v2 = m_pInputMesh->m_vecTriangleIndices[triCt * 3 + 2]; + + //Handle degenerates? + + Vector3DFloat v0v1 = m_pInputMesh->m_vecVertices[v1].position - m_pInputMesh->m_vecVertices[v0].position; + Vector3DFloat v0v2 = m_pInputMesh->m_vecVertices[v2].position - m_pInputMesh->m_vecVertices[v0].position; + Vector3DFloat normal = v0v1.cross(v0v2); + + vecOfTriNormals[ct] = normal; + } + + noOfDifferentNormals.clear(); + noOfDifferentNormals.resize(m_pInputMesh->m_vecVertices.size()); + std::fill(noOfDifferentNormals.begin(), noOfDifferentNormals.end(), 0); + for(int ct = 0; ct < m_pInputMesh->m_vecVertices.size(); ct++) + { + //const list& trianglesUsingVertex = trianglesUsingVertex[ct]; + + Vector3DFloat sumOfNormals(0.0f,0.0f,0.0f); + for(list::const_iterator iter = trianglesUsingVertex[ct].cbegin(); iter != trianglesUsingVertex[ct].cend(); iter++) + { + sumOfNormals += vecOfTriNormals[*iter]; + } + + noOfDifferentNormals[ct] = 0; + if(abs(sumOfNormals.getX()) > 0.001) + noOfDifferentNormals[ct]++; + if(abs(sumOfNormals.getY()) > 0.001) + noOfDifferentNormals[ct]++; + if(abs(sumOfNormals.getZ()) > 0.001) + noOfDifferentNormals[ct]++; + + assert(noOfDifferentNormals[ct] > 0); + } + + //For each triange... + for(int ctIter = 0; ctIter < vecOfTriCts.size(); ctIter++) + { + int triCt = vecOfTriCts[ctIter]; + + //For each edge in each triangle + for(int edgeCt = 0; edgeCt < 3; edgeCt++) + { + int v0 = m_pInputMesh->m_vecTriangleIndices[triCt * 3 + (edgeCt)]; + int v1 = m_pInputMesh->m_vecTriangleIndices[triCt * 3 + ((edgeCt +1) % 3)]; + + bool bCanCollapseEdge = canCollapseEdge(v0, v1); + + //////////////////////////////////////////////////////////////////////////////// + + if(bCanCollapseEdge) + { + //Move v0 onto v1 + vertexMapper[v0] = v1; //vertexMapper[v1]; + vertexLocked[v0] = true; + vertexLocked[v1] = true; + + //Increment the counter + ++noOfEdgesCollapsed; + } + } + } + + if(noOfEdgesCollapsed > 0) + { + //Fix up the indices + for(int triCt = 0; triCt < m_pInputMesh->m_vecTriangleIndices.size(); triCt++) + { + uint32_t before = m_pInputMesh->m_vecTriangleIndices[triCt]; + uint32_t after = vertexMapper[m_pInputMesh->m_vecTriangleIndices[triCt]]; + if(before != after) + { + m_pInputMesh->m_vecTriangleIndices[triCt] = vertexMapper[m_pInputMesh->m_vecTriangleIndices[triCt]]; + } + } + } + + return noOfEdgesCollapsed; + } + + //This function looks at every vertex in the mesh and determines + //how many of it's neighbours have the same material. + template + void MeshDecimator::countNoOfNeighboursUsingMaterial(void) + { + //Find all the neighbouring vertices for each vertex + std::vector< std::set > neighbouringVertices(m_pInputMesh->m_vecVertices.size()); + for(int triCt = 0; triCt < m_pInputMesh->m_vecTriangleIndices.size() / 3; triCt++) + { + int v0 = m_pInputMesh->m_vecTriangleIndices[(triCt * 3 + 0)]; + int v1 = m_pInputMesh->m_vecTriangleIndices[(triCt * 3 + 1)]; + int v2 = m_pInputMesh->m_vecTriangleIndices[(triCt * 3 + 2)]; + + neighbouringVertices[v0].insert(v1); + neighbouringVertices[v0].insert(v2); + + neighbouringVertices[v1].insert(v0); + neighbouringVertices[v1].insert(v2); + + neighbouringVertices[v2].insert(v0); + neighbouringVertices[v2].insert(v1); + } + + //For each vertex, check how many neighbours have the same material + m_vecNoOfNeighboursUsingMaterial.resize(m_pInputMesh->m_vecVertices.size()); + for(int vertCt = 0; vertCt < m_pInputMesh->m_vecVertices.size(); vertCt++) + { + m_vecNoOfNeighboursUsingMaterial[vertCt] = 0; + for(std::set::iterator iter = neighbouringVertices[vertCt].begin(); iter != neighbouringVertices[vertCt].end(); iter++) + { + if(m_pInputMesh->m_vecVertices[vertCt].getMaterial() == m_pInputMesh->m_vecVertices[*iter].getMaterial()) + { + m_vecNoOfNeighboursUsingMaterial[vertCt]++; + } + } + } + } + + // Returns true if every bit which is set in 'a' is also set in 'b'. The reverse does not need to be true. + template + bool MeshDecimator::isSubset(std::bitset a, std::bitset b) + { + bool result = true; + + for(int ct = 1; ct < 7; ct++) //Start at '1' to skip material flag + { + if(a.test(ct)) + { + if(b.test(ct) == false) + { + result = false; + break; + } + } + } + + return result; + } + + //template + bool MeshDecimator::canCollapseEdge(uint32_t v0, uint32_t v1) + { + //A vertex will be locked if it has already been involved in a collapse this pass. + if(vertexLocked[v0] || vertexLocked[v1]) + { + return false; + } + + if(m_pInputMesh->m_vecVertices[v0].getMaterial() != m_pInputMesh->m_vecVertices[v1].getMaterial()) + { + return false; + } + + //For now, don't collapse vertices on material edges... + if(m_pInputMesh->m_vecVertices[v0].isOnMaterialEdge() || m_pInputMesh->m_vecVertices[v1].isOnMaterialEdge()) + { + if(true) + { + bool pass = false; + + bool allMatch = false; + + // On the original undecimated mesh a material boundary vertex on a straight edge will + // have four neighbours with the same material. If it's on a corner it will have a + // different number. We only collapse straight edges to avoid changingthe shape of the + // material boundary. + if(m_vecNoOfNeighboursUsingMaterial[v0] == m_vecNoOfNeighboursUsingMaterial[v1]) + { + if(m_vecNoOfNeighboursUsingMaterial[v0] == 4) + { + allMatch = true; + } + } + + bool movementValid = false; + Vector3DFloat movement = m_pInputMesh->m_vecVertices[v1].getPosition() - m_pInputMesh->m_vecVertices[v0].getPosition(); + movement.normalise(); + if(movement.dot(Vector3DFloat(0,0,1)) > 0.999) + { + movementValid = true; + } + + if(movement.dot(Vector3DFloat(0,1,0)) > 0.999) + { + movementValid = true; + } + + if(movement.dot(Vector3DFloat(1,0,0)) > 0.999) + { + movementValid = true; + } + + if(movement.dot(Vector3DFloat(0,0,-1)) > 0.999) + { + movementValid = true; + } + + if(movement.dot(Vector3DFloat(0,-1,0)) > 0.999) + { + movementValid = true; + } + + if(movement.dot(Vector3DFloat(-1,0,0)) > 0.999) + { + movementValid = true; + } + + if(movementValid && allMatch) + { + pass = true; + } + + if(!pass) + { + return false; + } + } + else //Material collapses not allowed + { + return false; + } + } + + // Vertices on the geometrical edge of surface meshes need special handling. + // We check for this by whether any of the edge flags are set. + if(m_pInputMesh->m_vecVertices[v0].m_bFlags.any() || m_pInputMesh->m_vecVertices[v1].m_bFlags.any()) + { + // Assume we can't collapse until we prove otherwise... + bool bCollapseGeometryEdgePair = false; + + // We can collapse normal vertices onto edge vertices, and edge vertices + // onto corner vertices, but not vice-versa. Hence we check whether all + // the edge flags in the source vertex are also set in the destination vertex. + if(isSubset(m_pInputMesh->m_vecVertices[v0].m_bFlags, m_pInputMesh->m_vecVertices[v1].m_bFlags)) + { + // In general adjacent regions surface meshes may collapse differently + // and this can cause cracks. We solve this by only allowing the collapse + // is the normals are exactly the same. We do not use the user provided + // tolerence here (but do allow for floating point error). + if(m_pInputMesh->m_vecVertices[v0].getNormal().dot(m_pInputMesh->m_vecVertices[v1].getNormal()) > 0.999) + { + // Ok, this pair can collapse. + bCollapseGeometryEdgePair = true; + } + } + + // Use the result. + if(!bCollapseGeometryEdgePair) + { + return false; + } + } + + //Check the normals are within the threashold. + if(m_pInputMesh->m_vecVertices[v0].getNormal().dot(m_pInputMesh->m_vecVertices[v1].getNormal()) < fMinDotProductForCollapse) + { + return false; + } + + //////////////////////////////////////////////////////////////////////////////// + //The last test is whether we will flip any of the faces + + bool faceFlipped = false; + list triangles = trianglesUsingVertex[v0]; + /*set triangles; + std::set_union(trianglesUsingVertex[v0].begin(), trianglesUsingVertex[v0].end(), + trianglesUsingVertex[v1].begin(), trianglesUsingVertex[v1].end(), + std::inserter(triangles, triangles.begin()));*/ + + for(list::iterator triIter = triangles.begin(); triIter != triangles.end(); triIter++) + { + uint32_t tri = *triIter; + + uint32_t v0Old = m_pInputMesh->m_vecTriangleIndices[tri * 3]; + uint32_t v1Old = m_pInputMesh->m_vecTriangleIndices[tri * 3 + 1]; + uint32_t v2Old = m_pInputMesh->m_vecTriangleIndices[tri * 3 + 2]; + + //Check if degenerate + if((v0Old == v1Old) || (v1Old == v2Old) || (v2Old == v0Old)) + { + continue; + } + + uint32_t v0New = v0Old; + uint32_t v1New = v1Old; + uint32_t v2New = v2Old; + + if(v0New == v0) + v0New = v1; + if(v1New == v0) + v1New = v1; + if(v2New == v0) + v2New = v1; + + //Check if degenerate + if((v0New == v1New) || (v1New == v2New) || (v2New == v0New)) + { + continue; + } + + Vector3DFloat v0OldPos = m_pInputMesh->m_vecVertices[vertexMapper[v0Old]].getPosition(); + Vector3DFloat v1OldPos = m_pInputMesh->m_vecVertices[vertexMapper[v1Old]].getPosition(); + Vector3DFloat v2OldPos = m_pInputMesh->m_vecVertices[vertexMapper[v2Old]].getPosition(); + + Vector3DFloat v0NewPos = m_pInputMesh->m_vecVertices[vertexMapper[v0New]].getPosition(); + Vector3DFloat v1NewPos = m_pInputMesh->m_vecVertices[vertexMapper[v1New]].getPosition(); + Vector3DFloat v2NewPos = m_pInputMesh->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); + + OldNormal.normalise(); + NewNormal.normalise(); + + // Note for after holiday - We are still getting faces flipping despite the following test. I tried changing + // the 0.0 to 0.9 (which should still let coplanar faces merge) but oddly nothing then merged. Investigate this. + float dotProduct = OldNormal.dot(NewNormal); + //cout << dotProduct << endl; + if(dotProduct < 0.9f) + { + //cout << " Face flipped!!" << endl; + + faceFlipped = true; + + /*vertexLocked[v0] = true; + vertexLocked[v1] = true;*/ + + break; + } + } + + if(faceFlipped == true) + { + return false; + } + return true; + } + + //template + bool MeshDecimator::canCollapseEdge(uint32_t v0, uint32_t v1) + { + //A vertex will be locked if it has already been involved in a collapse this pass. + if(vertexLocked[v0] || vertexLocked[v1]) + { + return false; + } + + if(m_pInputMesh->m_vecVertices[v0].getMaterial() != m_pInputMesh->m_vecVertices[v1].getMaterial()) + { + return false; + } + + if(noOfDifferentNormals[v0] == 3) + { + return false; + } + + if(noOfDifferentNormals[v0] > noOfDifferentNormals[v1]) + { + return false; + } + + //////////////////////////////////////////////////////////////////////////////// + //The last test is whether we will flip any of the faces + + bool faceFlipped = false; + list triangles = trianglesUsingVertex[v0]; + /*set triangles; + std::set_union(trianglesUsingVertex[v0].begin(), trianglesUsingVertex[v0].end(), + trianglesUsingVertex[v1].begin(), trianglesUsingVertex[v1].end(), + std::inserter(triangles, triangles.begin()));*/ + + for(list::iterator triIter = triangles.begin(); triIter != triangles.end(); triIter++) + { + uint32_t tri = *triIter; + + uint32_t v0Old = m_pInputMesh->m_vecTriangleIndices[tri * 3]; + uint32_t v1Old = m_pInputMesh->m_vecTriangleIndices[tri * 3 + 1]; + uint32_t v2Old = m_pInputMesh->m_vecTriangleIndices[tri * 3 + 2]; + + //Check if degenerate + if((v0Old == v1Old) || (v1Old == v2Old) || (v2Old == v0Old)) + { + continue; + } + + uint32_t v0New = v0Old; + uint32_t v1New = v1Old; + uint32_t v2New = v2Old; + + if(v0New == v0) + v0New = v1; + if(v1New == v0) + v1New = v1; + if(v2New == v0) + v2New = v1; + + //Check if degenerate + if((v0New == v1New) || (v1New == v2New) || (v2New == v0New)) + { + continue; + } + + Vector3DFloat v0OldPos = m_pInputMesh->m_vecVertices[vertexMapper[v0Old]].getPosition(); + Vector3DFloat v1OldPos = m_pInputMesh->m_vecVertices[vertexMapper[v1Old]].getPosition(); + Vector3DFloat v2OldPos = m_pInputMesh->m_vecVertices[vertexMapper[v2Old]].getPosition(); + + Vector3DFloat v0NewPos = m_pInputMesh->m_vecVertices[vertexMapper[v0New]].getPosition(); + Vector3DFloat v1NewPos = m_pInputMesh->m_vecVertices[vertexMapper[v1New]].getPosition(); + Vector3DFloat v2NewPos = m_pInputMesh->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); + + OldNormal.normalise(); + NewNormal.normalise(); + + // Note for after holiday - We are still getting faces flipping despite the following test. I tried changing + // the 0.0 to 0.9 (which should still let coplanar faces merge) but oddly nothing then merged. Investigate this. + float dotProduct = OldNormal.dot(NewNormal); + //cout << dotProduct << endl; + if(dotProduct < 0.9f) + { + //cout << " Face flipped!!" << endl; + + faceFlipped = true; + + /*vertexLocked[v0] = true; + vertexLocked[v1] = true;*/ + + break; + } + } + + if(faceFlipped == true) + { + return false; + } + return true; + } +} diff --git a/library/PolyVoxCore/include/SurfaceMesh.h b/library/PolyVoxCore/include/SurfaceMesh.h index a7352507..17df715d 100644 --- a/library/PolyVoxCore/include/SurfaceMesh.h +++ b/library/PolyVoxCore/include/SurfaceMesh.h @@ -70,13 +70,9 @@ namespace PolyVox void generateAveragedFaceNormals(bool bNormalise, bool bIncludeEdgeVertices = false); - bool isSubset(std::bitset a, std::bitset b); - - void decimate(float fMinDotProductForCollapse = 0.999f); - - uint32_t performDecimationPass(float fMinDotProductForCollapse); int noOfDegenerateTris(void); void removeDegenerateTris(void); + void removeUnusedVertices(void); Region m_Region; diff --git a/library/PolyVoxCore/include/SurfaceMesh.inl b/library/PolyVoxCore/include/SurfaceMesh.inl index 65eda4da..e2afd3f6 100644 --- a/library/PolyVoxCore/include/SurfaceMesh.inl +++ b/library/PolyVoxCore/include/SurfaceMesh.inl @@ -476,339 +476,6 @@ namespace PolyVox m_vecVertices = vecNewVertices; }*/ - template - void SurfaceMesh::decimate(float fMinDotProductForCollapse) - { - // We will need the information from this function to - // determine when material boundary edges can collapse. - countNoOfNeighboursUsingMaterial(); - - uint32_t noOfEdgesCollapsed; - do - { - noOfEdgesCollapsed = performDecimationPass(fMinDotProductForCollapse); - removeDegenerateTris(); - }while(noOfEdgesCollapsed > 0); - - //Decimation will have invalidated LOD levels. - m_vecLodRecords.clear(); - LodRecord lodRecord; - lodRecord.beginIndex = 0; - lodRecord.endIndex = getNoOfIndices(); - m_vecLodRecords.push_back(lodRecord); - } - - // Returns true if every bit which is set in 'a' is also set in 'b'. The reverse does not need to be true. - template - bool SurfaceMesh::isSubset(std::bitset a, std::bitset b) - { - bool result = true; - - for(int ct = 1; ct < 7; ct++) //Start at '1' to skip material flag - { - if(a.test(ct)) - { - if(b.test(ct) == false) - { - result = false; - break; - } - } - } - - return result; - } - - template - uint32_t SurfaceMesh::performDecimationPass(float fMinDotProductForCollapse) - { - // I'm using a vector of lists here, rather than a vector of sets, - // because I don't believe that duplicaes should occur. But this - // might be worth checking if we have problems in the future. - vector< list > trianglesUsingVertex(m_vecVertices.size()); - for(int ct = 0; ct < m_vecTriangleIndices.size(); ct++) - { - int triangle = ct / 3; - - trianglesUsingVertex[m_vecTriangleIndices[ct]].push_back(triangle); - } - - // Count how many edges we have collapsed - uint32_t noOfEdgesCollapsed = 0; - - // The vertex mapper track whick vertices collapse onto which. - vector vertexMapper(m_vecVertices.size()); - - // Once a vertex is involved in a collapse (either because it - // moves onto a different vertex, or because a different vertex - // moves onto it) it is forbidden to take part in another collapse - // this pass. We enforce this by setting the vertex locked flag. - vector vertexLocked(m_vecVertices.size()); - - // Initialise the vectors - for(uint32_t ct = 0; ct < m_vecVertices.size(); ct++) - { - // Initiall all vertices points to themselves - vertexMapper[ct] = ct; - // All vertices are initially unlocked - vertexLocked[ct] = false; - } - - - // Each triangle exists in this vector once. - vector vecOfTriCts(m_vecTriangleIndices.size() / 3); - for(int triCt = 0; triCt < vecOfTriCts.size(); triCt++) - { - vecOfTriCts[triCt] = triCt; - } - - // It *may* be beneficial to randomise the order in which triangles - // are processed to get a more uniform distribution off collapses and - // more equally sized triangles at the end. This need more testing really. - random_shuffle(vecOfTriCts.begin(), vecOfTriCts.end()); - - //For each triange... - for(int ctIter = 0; ctIter < vecOfTriCts.size(); ctIter++) - { - int triCt = vecOfTriCts[ctIter]; - - //For each edge in each triangle - for(int edgeCt = 0; edgeCt < 3; edgeCt++) - { - int v0 = m_vecTriangleIndices[triCt * 3 + (edgeCt)]; - int v1 = m_vecTriangleIndices[triCt * 3 + ((edgeCt +1) % 3)]; - - //A vertex will be locked if it has already been involved in a collapse this pass. - if(vertexLocked[v0] || vertexLocked[v1]) - { - continue; - } - - if(m_vecVertices[v0].getMaterial() != m_vecVertices[v1].getMaterial()) - { - continue; - } - - //For now, don't collapse vertices on material edges... - if(m_vecVertices[v0].isOnMaterialEdge() || m_vecVertices[v1].isOnMaterialEdge()) - { - if(true) - { - bool pass = false; - - bool allMatch = false; - - // On the original undecimated mesh a material boundary vertex on a straight edge will - // have four neighbours with the same material. If it's on a corner it will have a - // different number. We only collapse straight edges to avoid changingthe shape of the - // material boundary. - if(m_vecNoOfNeighboursUsingMaterial[v0] == m_vecNoOfNeighboursUsingMaterial[v1]) - { - if(m_vecNoOfNeighboursUsingMaterial[v0] == 4) - { - allMatch = true; - } - } - - bool movementValid = false; - Vector3DFloat movement = m_vecVertices[v1].getPosition() - m_vecVertices[v0].getPosition(); - movement.normalise(); - if(movement.dot(Vector3DFloat(0,0,1)) > 0.999) - { - movementValid = true; - } - - if(movement.dot(Vector3DFloat(0,1,0)) > 0.999) - { - movementValid = true; - } - - if(movement.dot(Vector3DFloat(1,0,0)) > 0.999) - { - movementValid = true; - } - - if(movement.dot(Vector3DFloat(0,0,-1)) > 0.999) - { - movementValid = true; - } - - if(movement.dot(Vector3DFloat(0,-1,0)) > 0.999) - { - movementValid = true; - } - - if(movement.dot(Vector3DFloat(-1,0,0)) > 0.999) - { - movementValid = true; - } - - if(movementValid && allMatch) - { - pass = true; - } - - if(!pass) - { - continue; - } - } - else //Material collapses not allowed - { - continue; - } - } - - // Vertices on the geometrical edge of surface meshes need special handling. - // We check for this by whether any of the edge flags are set. - if(m_vecVertices[v0].m_bFlags.any() || m_vecVertices[v1].m_bFlags.any()) - { - // Assume we can't collapse until we prove otherwise... - bool bCollapseGeometryEdgePair = false; - - // We can collapse normal vertices onto edge vertices, and edge vertices - // onto corner vertices, but not vice-versa. Hence we check whether all - // the edge flags in the source vertex are also set in the destination vertex. - if(isSubset(m_vecVertices[v0].m_bFlags, m_vecVertices[v1].m_bFlags)) - { - // In general adjacent regions surface meshes may collapse differently - // and this can cause cracks. We solve this by only allowing the collapse - // is the normals are exactly the same. We do not use the user provided - // tolerence here (but do allow for floating point error). - if(m_vecVertices[v0].getNormal().dot(m_vecVertices[v1].getNormal()) > 0.999) - { - // Ok, this pair can collapse. - bCollapseGeometryEdgePair = true; - } - } - - // Use the result. - if(!bCollapseGeometryEdgePair) - { - continue; - } - } - - //Check the normals are within the threashold. - if(m_vecVertices[v0].getNormal().dot(m_vecVertices[v1].getNormal()) < fMinDotProductForCollapse) - { - continue; - } - - //////////////////////////////////////////////////////////////////////////////// - //The last test is whether we will flip any of the faces - - bool faceFlipped = false; - list triangles = trianglesUsingVertex[v0]; - /*set triangles; - std::set_union(trianglesUsingVertex[v0].begin(), trianglesUsingVertex[v0].end(), - trianglesUsingVertex[v1].begin(), trianglesUsingVertex[v1].end(), - std::inserter(triangles, triangles.begin()));*/ - - for(list::iterator triIter = triangles.begin(); triIter != triangles.end(); triIter++) - { - uint32_t tri = *triIter; - - uint32_t v0Old = m_vecTriangleIndices[tri * 3]; - uint32_t v1Old = m_vecTriangleIndices[tri * 3 + 1]; - uint32_t v2Old = m_vecTriangleIndices[tri * 3 + 2]; - - //Check if degenerate - if((v0Old == v1Old) || (v1Old == v2Old) || (v2Old == v0Old)) - { - continue; - } - - uint32_t v0New = v0Old; - uint32_t v1New = v1Old; - uint32_t v2New = v2Old; - - if(v0New == v0) - v0New = v1; - if(v1New == v0) - v1New = v1; - if(v2New == v0) - v2New = v1; - - //Check if degenerate - if((v0New == v1New) || (v1New == v2New) || (v2New == v0New)) - { - continue; - } - - Vector3DFloat v0OldPos = m_vecVertices[vertexMapper[v0Old]].getPosition(); - Vector3DFloat v1OldPos = m_vecVertices[vertexMapper[v1Old]].getPosition(); - Vector3DFloat v2OldPos = m_vecVertices[vertexMapper[v2Old]].getPosition(); - - Vector3DFloat v0NewPos = m_vecVertices[vertexMapper[v0New]].getPosition(); - Vector3DFloat v1NewPos = m_vecVertices[vertexMapper[v1New]].getPosition(); - Vector3DFloat v2NewPos = 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); - - OldNormal.normalise(); - NewNormal.normalise(); - - // Note for after holiday - We are still getting faces flipping despite the following test. I tried changing - // the 0.0 to 0.9 (which should still let coplanar faces merge) but oddly nothing then merged. Investigate this. - float dotProduct = OldNormal.dot(NewNormal); - //cout << dotProduct << endl; - if(dotProduct < 0.9f) - { - //cout << " Face flipped!!" << endl; - - faceFlipped = true; - - /*vertexLocked[v0] = true; - vertexLocked[v1] = true;*/ - - break; - } - } - - if(faceFlipped == true) - { - continue; - } - - //////////////////////////////////////////////////////////////////////////////// - - //Move v0 onto v1 - vertexMapper[v0] = v1; //vertexMapper[v1]; - vertexLocked[v0] = true; - vertexLocked[v1] = true; - - //Increment the counter - ++noOfEdgesCollapsed; - } - } - - if(noOfEdgesCollapsed > 0) - { - //Fix up the indices - for(int triCt = 0; triCt < m_vecTriangleIndices.size(); triCt++) - { - uint32_t before = m_vecTriangleIndices[triCt]; - uint32_t after = vertexMapper[m_vecTriangleIndices[triCt]]; - if(before != after) - { - m_vecTriangleIndices[triCt] = vertexMapper[m_vecTriangleIndices[triCt]]; - } - } - } - - return noOfEdgesCollapsed; - } - template int SurfaceMesh::noOfDegenerateTris(void) { @@ -859,4 +526,36 @@ namespace PolyVox m_vecTriangleIndices.resize(noOfNonDegenerate * 3); } + + template + void SurfaceMesh::removeUnusedVertices(void) + { + vector isVertexUsed(m_vecVertices.size()); + fill(isVertexUsed.begin(), isVertexUsed.end(), false); + + for(int triCt = 0; triCt < m_vecTriangleIndices.size(); triCt++) + { + int v = m_vecTriangleIndices[triCt]; + isVertexUsed[v] = true; + } + + int noOfUsedVertices = 0; + vector newPos(m_vecVertices.size()); + for(int vertCt = 0; vertCt < m_vecVertices.size(); vertCt++) + { + if(isVertexUsed[vertCt]) + { + m_vecVertices[noOfUsedVertices] = m_vecVertices[vertCt]; + newPos[vertCt] = noOfUsedVertices; + noOfUsedVertices++; + } + } + + m_vecVertices.resize(noOfUsedVertices); + + for(int triCt = 0; triCt < m_vecTriangleIndices.size(); triCt++) + { + m_vecTriangleIndices[triCt] = newPos[m_vecTriangleIndices[triCt]]; + } + } }