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

@ -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]];
}
}
}