Initial check in of MeshDecimator.

This commit is contained in:
David Williams 2010-12-18 17:42:17 +00:00
parent 7a93879fa7
commit d30ea9d709
6 changed files with 668 additions and 343 deletions

View File

@ -21,8 +21,6 @@ freely, subject to the following restrictions:
distribution.
*******************************************************************************/
#include <cfloat> //For numeric_limits
#include <Material.h>
namespace PolyVox

View File

@ -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);

View File

@ -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 <typename VertexType>
class MeshDecimator
{
public:
MeshDecimator(SurfaceMesh<VertexType>* pInputMesh/*, SurfaceMesh<PositionMaterial>* pMeshOutput*/);
void execute();
private:
SurfaceMesh<VertexType>* m_pInputMesh;
//SurfaceMesh<PositionMaterial>* pMeshOutput;
void countNoOfNeighboursUsingMaterial(void);
uint32_t performDecimationPass(float fMinDotProductForCollapse);
bool isSubset(std::bitset<VF_NO_OF_FLAGS> a, std::bitset<VF_NO_OF_FLAGS> b);
bool canCollapseEdge(uint32_t uSrc, uint32_t uDest);
//Data structures used during decimation
std::vector<uint8_t> m_vecNoOfNeighboursUsingMaterial;
vector<bool> vertexLocked;
vector<uint32_t> vertexMapper;
vector< list<uint32_t> > trianglesUsingVertex;
vector<int> noOfDifferentNormals;
float fMinDotProductForCollapse;
};
}
#include "MeshDecimator.inl"
#endif //__PolyVox_MeshDecimator_H__

View File

@ -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 <typename VertexType>
MeshDecimator<VertexType>::MeshDecimator(SurfaceMesh<VertexType>* pInputMesh/*, SurfaceMesh<PositionMaterial>* pMeshOutput*/)
:m_pInputMesh(pInputMesh)
//,m_pOutputMesh(pOutputMesh)
{
fMinDotProductForCollapse = 0.999;
}
template <typename VertexType>
void MeshDecimator<VertexType>::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 <typename VertexType>
uint32_t MeshDecimator<VertexType>::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<int> 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<Vector3DFloat> 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<uint32_t>& trianglesUsingVertex = trianglesUsingVertex[ct];
Vector3DFloat sumOfNormals(0.0f,0.0f,0.0f);
for(list<uint32_t>::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 <typename VertexType>
void MeshDecimator<VertexType>::countNoOfNeighboursUsingMaterial(void)
{
//Find all the neighbouring vertices for each vertex
std::vector< std::set<int> > 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<int>::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 <typename VertexType>
bool MeshDecimator<VertexType>::isSubset(std::bitset<VF_NO_OF_FLAGS> a, std::bitset<VF_NO_OF_FLAGS> 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 <typename VertexType>
bool MeshDecimator<PositionMaterialNormal>::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<uint32_t> triangles = trianglesUsingVertex[v0];
/*set<uint32_t> triangles;
std::set_union(trianglesUsingVertex[v0].begin(), trianglesUsingVertex[v0].end(),
trianglesUsingVertex[v1].begin(), trianglesUsingVertex[v1].end(),
std::inserter(triangles, triangles.begin()));*/
for(list<uint32_t>::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 <typename VertexType>
bool MeshDecimator<PositionMaterial>::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<uint32_t> triangles = trianglesUsingVertex[v0];
/*set<uint32_t> triangles;
std::set_union(trianglesUsingVertex[v0].begin(), trianglesUsingVertex[v0].end(),
trianglesUsingVertex[v1].begin(), trianglesUsingVertex[v1].end(),
std::inserter(triangles, triangles.begin()));*/
for(list<uint32_t>::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;
}
}

View File

@ -70,13 +70,9 @@ namespace PolyVox
void generateAveragedFaceNormals(bool bNormalise, bool bIncludeEdgeVertices = false);
bool isSubset(std::bitset<VF_NO_OF_FLAGS> a, std::bitset<VF_NO_OF_FLAGS> b);
void decimate(float fMinDotProductForCollapse = 0.999f);
uint32_t performDecimationPass(float fMinDotProductForCollapse);
int noOfDegenerateTris(void);
void removeDegenerateTris(void);
void removeUnusedVertices(void);
Region m_Region;

View File

@ -476,339 +476,6 @@ namespace PolyVox
m_vecVertices = vecNewVertices;
}*/
template <typename VertexType>
void SurfaceMesh<VertexType>::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 <typename VertexType>
bool SurfaceMesh<VertexType>::isSubset(std::bitset<VF_NO_OF_FLAGS> a, std::bitset<VF_NO_OF_FLAGS> 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 <typename VertexType>
uint32_t SurfaceMesh<VertexType>::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<uint32_t> > 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<uint32_t> 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<bool> 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<int> 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<uint32_t> triangles = trianglesUsingVertex[v0];
/*set<uint32_t> triangles;
std::set_union(trianglesUsingVertex[v0].begin(), trianglesUsingVertex[v0].end(),
trianglesUsingVertex[v1].begin(), trianglesUsingVertex[v1].end(),
std::inserter(triangles, triangles.begin()));*/
for(list<uint32_t>::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 <typename VertexType>
int SurfaceMesh<VertexType>::noOfDegenerateTris(void)
{
@ -859,4 +526,36 @@ namespace PolyVox
m_vecTriangleIndices.resize(noOfNonDegenerate * 3);
}
template <typename VertexType>
void SurfaceMesh<VertexType>::removeUnusedVertices(void)
{
vector<bool> 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<uint32_t> 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]];
}
}
}