Work on MeshDecimator.

This commit is contained in:
David Williams 2011-01-01 18:18:32 +00:00
parent f39e2f0935
commit 6b78768ba0
3 changed files with 557 additions and 370 deletions

View File

@ -36,6 +36,8 @@ SET(CORE_INC_FILES
include/Material.h
include/MaterialDensityPair.h
include/Mesh.h
include/MeshDecimator.h
include/MeshDecimator.inl
include/MeshEdge.h
include/MeshFace.h
include/MeshVertex.h

View File

@ -24,8 +24,38 @@ freely, subject to the following restrictions:
#ifndef __PolyVox_MeshDecimator_H__
#define __PolyVox_MeshDecimator_H__
#include <bitset>
namespace PolyVox
{
enum POLYVOXCORE_API NormalFlags
{
NF_NORMAL_NEG_X,
NF_NORMAL_POS_X,
NF_NORMAL_NEG_Y,
NF_NORMAL_POS_Y,
NF_NORMAL_NEG_Z,
NF_NORMAL_POS_Z,
NF_NO_OF_FLAGS
};
enum Stages
{
STAGE_FACE,
STAGE_EDGE,
NO_OF_STAGES
};
struct VertexMetadata
{
bool hasDuplicate;
uint64_t materialKey;
list<uint32_t> trianglesUsingVertex;
int noOfDifferentNormals;
Vector3DFloat normal;
std::bitset<NF_NO_OF_FLAGS> m_bNormalFlags;
};
template <typename VertexType>
class MeshDecimator
{
@ -35,12 +65,18 @@ namespace PolyVox
void execute();
private:
void fillVertexMetadata(std::vector<VertexMetadata>& vecVertexMetadata);
int countZeros(void);
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 isSubsetCubic(std::bitset<NF_NO_OF_FLAGS> a, std::bitset<NF_NO_OF_FLAGS> b);
bool canCollapseEdge(uint32_t uSrc, uint32_t uDest);
@ -50,14 +86,21 @@ namespace PolyVox
vector<bool> vertexLocked;
vector<uint32_t> vertexMapper;
vector< list<uint32_t> > trianglesUsingVertex;
vector<int> noOfDifferentNormals;
//vector< list<uint32_t> > trianglesUsingVertexCurrently;
vector<uint64_t> materialKey;
vector<int> vecOfTriCts;
vector<Vector3DFloat> vecOfTriNormals;
vector<bool> hasDuplicate;
int m_NoOfZeros;
//vector<int> noOfDifferentNormals;
vector<VertexMetadata> m_vecInitialVertexMetadata;
vector<VertexMetadata> m_vecCurrentVertexMetadata;
float fMinDotProductForCollapse;
uint8_t m_uStage;
};
}

View File

@ -38,12 +38,15 @@ namespace PolyVox
// determine when material boundary edges can collapse.
countNoOfNeighboursUsingMaterial();
fillVertexMetadata(m_vecInitialVertexMetadata);
m_uStage = STAGE_FACE;
uint32_t noOfEdgesCollapsed;
do
{
noOfEdgesCollapsed = performDecimationPass(fMinDotProductForCollapse);
m_pInputMesh->removeDegenerateTris();
m_pInputMesh->removeUnusedVertices();
//m_pInputMesh->removeUnusedVertices();
}while(noOfEdgesCollapsed > 0);
//Decimation will have invalidated LOD levels.
@ -55,58 +58,144 @@ namespace PolyVox
}
template <typename VertexType>
uint32_t MeshDecimator<VertexType>::performDecimationPass(float fMinDotProductForCollapse)
void MeshDecimator<VertexType>::fillVertexMetadata(std::vector<VertexMetadata>& vecVertexMetadata)
{
hasDuplicate.clear();
vecVertexMetadata.clear();
vecVertexMetadata.resize(m_pInputMesh->m_vecVertices.size());
//Initialise the metadata
for(int ct = 0; ct < vecVertexMetadata.size(); ct++)
{
vecVertexMetadata[ct].hasDuplicate = false;
vecVertexMetadata[ct].materialKey = 0;
vecVertexMetadata[ct].trianglesUsingVertex.clear();
vecVertexMetadata[ct].noOfDifferentNormals = 0;
}
//Determine triangles using each vertex
/*trianglesUsingVertex.clear();
trianglesUsingVertex.resize(m_pInputMesh->m_vecVertices.size());*/
for(int ct = 0; ct < m_pInputMesh->m_vecTriangleIndices.size(); ct++)
{
int triangle = ct / 3;
vecVertexMetadata[m_pInputMesh->m_vecTriangleIndices[ct]].trianglesUsingVertex.push_back(triangle);
}
/*hasDuplicate.clear();
hasDuplicate.resize(m_pInputMesh->m_vecVertices.size());
std::fill(hasDuplicate.begin(), hasDuplicate.end(), false);
std::fill(hasDuplicate.begin(), hasDuplicate.end(), false);*/
for(int outerCt = 0; outerCt < m_pInputMesh->m_vecVertices.size()-1; outerCt++)
{
for(int innerCt = outerCt+1; innerCt < m_pInputMesh->m_vecVertices.size(); innerCt++)
{
if((m_pInputMesh->m_vecVertices[innerCt].position - m_pInputMesh->m_vecVertices[outerCt].position).lengthSquared() < 0.001f)
{
hasDuplicate[innerCt] = true;
hasDuplicate[outerCt] = true;
vecVertexMetadata[innerCt].hasDuplicate = true;
vecVertexMetadata[outerCt].hasDuplicate = true;
}
}
}
int duplicates = 0;
for(int ct = 0; ct < hasDuplicate.size(); ct++)
{
if(hasDuplicate[ct])
{
duplicates++;
}
}
std::cout << duplicates << std::endl;
materialKey.clear();
/*materialKey.clear();
materialKey.resize(m_pInputMesh->m_vecVertices.size());
std::fill(materialKey.begin(), materialKey.end(), 0);
std::fill(materialKey.begin(), materialKey.end(), 0);*/
for(int ct = 0; ct < m_pInputMesh->m_vecTriangleIndices.size(); ct++)
{
uint32_t vertex = m_pInputMesh->m_vecTriangleIndices[ct];
//NOTE: uint8_t may not always be large engouh?
uint8_t uMaterial = m_pInputMesh->m_vecVertices[vertex].material;
materialKey[vertex] <<= 8;
materialKey[vertex] |= uMaterial;
vecVertexMetadata[vertex].materialKey <<= 8;
vecVertexMetadata[vertex].materialKey |= uMaterial;
}
// Each triangle exists in this vector once.
vecOfTriCts.clear();
vecOfTriCts.resize(m_pInputMesh->m_vecTriangleIndices.size() / 3);
for(int triCt = 0; triCt < vecOfTriCts.size(); triCt++)
{
vecOfTriCts[triCt] = triCt;
}
vecOfTriNormals.clear();
vecOfTriNormals.resize(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);
normal.normalise();
vecOfTriNormals[ct] = normal;
}
//noOfDifferentNormals.clear();
//noOfDifferentNormals.resize(m_pInputMesh->m_vecVertices.size());
//std::fill(vecVertexMetadata.noOfDifferentNormals.begin(), vecVertexMetadata.noOfDifferentNormals.end(), 0);
for(int ct = 0; ct < m_pInputMesh->m_vecVertices.size(); ct++)
{
Vector3DFloat sumOfNormals(0.0f,0.0f,0.0f);
for(list<uint32_t>::const_iterator iter = vecVertexMetadata[ct].trianglesUsingVertex.cbegin(); iter != vecVertexMetadata[ct].trianglesUsingVertex.cend(); iter++)
{
sumOfNormals += vecOfTriNormals[*iter];
}
vecVertexMetadata[ct].noOfDifferentNormals = 0;
if(abs(sumOfNormals.getX()) > 0.001)
vecVertexMetadata[ct].noOfDifferentNormals++;
if(abs(sumOfNormals.getY()) > 0.001)
vecVertexMetadata[ct].noOfDifferentNormals++;
if(abs(sumOfNormals.getZ()) > 0.001)
vecVertexMetadata[ct].noOfDifferentNormals++;
if(sumOfNormals.getX() < -0.001)
vecVertexMetadata[ct].m_bNormalFlags.set(NF_NORMAL_NEG_X);
if(sumOfNormals.getX() > 0.001)
vecVertexMetadata[ct].m_bNormalFlags.set(NF_NORMAL_POS_X);
if(sumOfNormals.getY() < -0.001)
vecVertexMetadata[ct].m_bNormalFlags.set(NF_NORMAL_NEG_Y);
if(sumOfNormals.getY() > 0.001)
vecVertexMetadata[ct].m_bNormalFlags.set(NF_NORMAL_POS_Y);
if(sumOfNormals.getZ() < -0.001)
vecVertexMetadata[ct].m_bNormalFlags.set(NF_NORMAL_NEG_Z);
if(sumOfNormals.getZ() > 0.001)
vecVertexMetadata[ct].m_bNormalFlags.set(NF_NORMAL_POS_Z);
vecVertexMetadata[ct].normal = sumOfNormals;
vecVertexMetadata[ct].normal.normalise();
/*if(vecVertexMetadata[ct].noOfDifferentNormals == 3)
std::cout << "CORNER at " << ct <<std::endl;*/
//assert(noOfDifferentNormals[ct] > 0);
}
//std::cout << "----------" <<std::endl;
}
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 duplicates should occur. But this
// might be worth checking if we have problems in the future.
trianglesUsingVertex.clear();
trianglesUsingVertex.resize(m_pInputMesh->m_vecVertices.size());
/*trianglesUsingVertexCurrently.clear();
trianglesUsingVertexCurrently.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);
}
trianglesUsingVertexCurrently[m_pInputMesh->m_vecTriangleIndices[ct]].push_back(triangle);
}*/
// Count how many edges we have collapsed
uint32_t noOfEdgesCollapsed = 0;
@ -131,59 +220,7 @@ namespace PolyVox
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);
}
fillVertexMetadata(m_vecCurrentVertexMetadata);
//For each triange...
for(int ctIter = 0; ctIter < vecOfTriCts.size(); ctIter++)
@ -213,6 +250,8 @@ namespace PolyVox
}
}
m_NoOfZeros = countZeros();
if(noOfEdgesCollapsed > 0)
{
//Fix up the indices
@ -227,6 +266,8 @@ namespace PolyVox
}
}
m_NoOfZeros = countZeros();
return noOfEdgesCollapsed;
}
@ -289,6 +330,26 @@ namespace PolyVox
return result;
}
template <typename VertexType>
bool MeshDecimator<VertexType>::isSubsetCubic(std::bitset<NF_NO_OF_FLAGS> a, std::bitset<NF_NO_OF_FLAGS> b)
{
bool result = true;
for(int ct = 0; ct < NF_NO_OF_FLAGS; 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)
{
@ -413,7 +474,8 @@ namespace PolyVox
//The last test is whether we will flip any of the faces
bool faceFlipped = false;
list<uint32_t> triangles = trianglesUsingVertex[v0];
//list<uint32_t> triangles = trianglesUsingVertexCurrently[v0];
list<uint32_t> triangles = m_vecCurrentVertexMetadata[v0].trianglesUsingVertex;
/*set<uint32_t> triangles;
std::set_union(trianglesUsingVertex[v0].begin(), trianglesUsingVertex[v0].end(),
trianglesUsingVertex[v1].begin(), trianglesUsingVertex[v1].end(),
@ -510,57 +572,62 @@ namespace PolyVox
return false;
}
if(noOfDifferentNormals[v0] == 3)
if(m_vecInitialVertexMetadata[v0].m_bNormalFlags.count() == 3) //Corner
{
return false;
}
/*if(materialKey[v0] != materialKey[v1])
if(m_vecInitialVertexMetadata[v0].m_bNormalFlags.count() == 2) //Edge
{
if(isSubsetCubic(m_vecInitialVertexMetadata[v0].m_bNormalFlags, m_vecInitialVertexMetadata[v1].m_bNormalFlags) == false)
{
return false;
}
}
if(m_vecInitialVertexMetadata[v0].m_bNormalFlags.count() == 1) //Face
{
if(isSubsetCubic(m_vecInitialVertexMetadata[v0].m_bNormalFlags, m_vecInitialVertexMetadata[v1].m_bNormalFlags) == false)
{
return false;
}
}
/*if(m_vecInitialVertexMetadata[v0].normal.dot(m_vecInitialVertexMetadata[v1].normal) < 0.999f)
{
return false;
}*/
/*if((hasDuplicate[v0]) || (hasDuplicate[v1]))
if(m_vecInitialVertexMetadata[v0].hasDuplicate)
{
return false;
bool canMerge = false;
/*if(m_vecInitialVertexMetadata[v1].hasDuplicate)
{
if(isSubsetCubic(m_vecInitialVertexMetadata[v0].m_bNormalFlags, m_vecInitialVertexMetadata[v1].m_bNormalFlags))
{
canMerge = true;
}
}*/
if(hasDuplicate[v0])
{
//if(!hasDuplicate[v1])
{
return false;
}
}
if(noOfDifferentNormals[v0] > noOfDifferentNormals[v1])
{
return false;
return canMerge;
}
Vector3DFloat offset = static_cast<Vector3DFloat>(m_pInputMesh->m_Region.getLowerCorner());
bool v0Inside = m_pInputMesh->m_Region.containsPoint(m_pInputMesh->m_vecVertices[v0].getPosition() + offset);
bool v1Inside = m_pInputMesh->m_Region.containsPoint(m_pInputMesh->m_vecVertices[v1].getPosition() + offset);
if(v0Inside == false)
{
/*if(v1Inside == false)
{
if(noOfDifferentNormals[v0] > 1)
{
return false;
}
}
else*/
{
return false;
}
}
////////////////////////////////////////////////////////////////////////////////
//The last test is whether we will flip any of the faces
bool faceFlipped = false;
list<uint32_t> triangles = trianglesUsingVertex[v0];
//list<uint32_t> triangles = trianglesUsingVertexCurrently[v0];
list<uint32_t> triangles = m_vecCurrentVertexMetadata[v0].trianglesUsingVertex;
/*set<uint32_t> triangles;
std::set_union(trianglesUsingVertex[v0].begin(), trianglesUsingVertex[v0].end(),
trianglesUsingVertex[v1].begin(), trianglesUsingVertex[v1].end(),
@ -640,6 +707,81 @@ namespace PolyVox
{
return false;
}
/*if(m_vecInitialVertexMetadata[v0].trianglesUsingVertex.size() != m_vecInitialVertexMetadata[v1].trianglesUsingVertex.size())
{
return false;
}*/
/*if(m_vecInitialVertexMetadata[v0].noOfDifferentNormals == 3)
{
return false;
}*/
/*if(m_vecInitialVertexMetadata[v1].noOfDifferentNormals == 3)
{
return false;
}*/
/*if(m_vecInitialVertexMetadata[v0].noOfDifferentNormals > m_vecInitialVertexMetadata[v1].noOfDifferentNormals)
{
return false;
}*/
/*if(m_vecInitialVertexMetadata[v0].materialKey != m_vecInitialVertexMetadata[v1].materialKey)
{
return false;
}*/
/*if((hasDuplicate[v0]) || (hasDuplicate[v1]))
{
return false;
}*/
/*if(m_vecInitialVertexMetadata[v0].hasDuplicate)
{
//if(!hasDuplicate[v1])
{
return false;
}
}*/
return true;
}
template <typename VertexType>
int MeshDecimator<VertexType>::countZeros(void)
{
int total = 0;
for(int ct = 0; ct < m_pInputMesh->m_vecTriangleIndices.size(); ct++)
{
if(m_pInputMesh->m_vecTriangleIndices[ct] == 0)
{
total++;
}
}
return total;
}
}
#ifdef BLAH
Vector3DFloat offset = static_cast<Vector3DFloat>(m_pInputMesh->m_Region.getLowerCorner());
bool v0Inside = m_pInputMesh->m_Region.containsPoint(m_pInputMesh->m_vecVertices[v0].getPosition() + offset);
bool v1Inside = m_pInputMesh->m_Region.containsPoint(m_pInputMesh->m_vecVertices[v1].getPosition() + offset);
if(v0Inside == false)
{
/*if(v1Inside == false)
{
//if(noOfDifferentNormals[v0] > 1)
if(trianglesUsingVertex[v0].size() != trianglesUsingVertex[v1].size())
//if(materialKey[v0] != materialKey[v1])
{
return false;
}
}
else*/
{
return false;
}
}
#endif