Work on MeshDecimator.

This commit is contained in:
David Williams 2011-01-03 21:46:40 +00:00
parent 996dfac39c
commit 773cfc4887
7 changed files with 223 additions and 335 deletions

View File

@ -52,14 +52,19 @@ void OpenGLWidget::initializeGL()
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL); glDepthFunc(GL_LEQUAL);
//Anable smooth lighting //Enable smooth lighting
glEnable(GL_LIGHTING); //glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0); //glEnable(GL_LIGHT0);
glShadeModel(GL_SMOOTH); glShadeModel(GL_SMOOTH);
//We'll be rendering with index/vertex arrays //We'll be rendering with index/vertex arrays
glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_NORMAL_ARRAY);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
} }
void OpenGLWidget::resizeGL(int w, int h) void OpenGLWidget::resizeGL(int w, int h)
@ -70,7 +75,7 @@ void OpenGLWidget::resizeGL(int w, int h)
//Set up the projection matrix //Set up the projection matrix
glMatrixMode(GL_PROJECTION); glMatrixMode(GL_PROJECTION);
glLoadIdentity(); glLoadIdentity();
float frustumSize = 32.0f; //Half the volume size float frustumSize = 16.0f; //Half the volume size
float aspect = static_cast<float>(width()) / static_cast<float>(height()); float aspect = static_cast<float>(width()) / static_cast<float>(height());
glOrtho(frustumSize*aspect, -frustumSize*aspect, frustumSize, -frustumSize, 1.0, 1000); glOrtho(frustumSize*aspect, -frustumSize*aspect, frustumSize, -frustumSize, 1.0, 1000);
} }
@ -86,7 +91,7 @@ void OpenGLWidget::paintGL()
glTranslatef(0.0f,0.0f,-100.0f); //Centre volume and move back glTranslatef(0.0f,0.0f,-100.0f); //Centre volume and move back
glRotatef(m_xRotation, 1.0f, 0.0f, 0.0f); glRotatef(m_xRotation, 1.0f, 0.0f, 0.0f);
glRotatef(m_yRotation, 0.0f, 1.0f, 0.0f); glRotatef(m_yRotation, 0.0f, 1.0f, 0.0f);
glTranslatef(-32.0f,-32.0f,-32.0f); //Centre volume and move back glTranslatef(-16.0f,-16.0f,-16.0f); //Centre volume and move back
//Bind the index buffer //Bind the index buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);

View File

@ -24,10 +24,13 @@ freely, subject to the following restrictions:
#include "OpenGLWidget.h" #include "OpenGLWidget.h"
#include "MaterialDensityPair.h" #include "MaterialDensityPair.h"
#include "CubicSurfaceExtractor.h"
#include "CubicSurfaceExtractorWithNormals.h" #include "CubicSurfaceExtractorWithNormals.h"
#include "SurfaceMesh.h" #include "SurfaceMesh.h"
#include "Volume.h" #include "Volume.h"
#include "MeshDecimator.h"
#include <QApplication> #include <QApplication>
//Use the PolyVox namespace //Use the PolyVox namespace
@ -36,7 +39,9 @@ using namespace PolyVox;
void createSphereInVolume(Volume<MaterialDensityPair44>& volData, float fRadius) void createSphereInVolume(Volume<MaterialDensityPair44>& volData, float fRadius)
{ {
//This vector hold the position of the center of the volume //This vector hold the position of the center of the volume
Vector3DFloat v3dVolCenter(volData.getWidth() / 2, volData.getHeight() / 2, volData.getDepth() / 2); //Vector3DFloat v3dVolCenter(volData.getWidth() / 2, volData.getHeight() / 2, volData.getDepth() / 2);
Vector3DFloat v3dVolCenter(16, 16, 16);
//This three-level for loop iterates over every voxel in the volume //This three-level for loop iterates over every voxel in the volume
for (int z = 0; z < volData.getWidth(); z++) for (int z = 0; z < volData.getWidth(); z++)
@ -55,14 +60,16 @@ void createSphereInVolume(Volume<MaterialDensityPair44>& volData, float fRadius)
{ {
//Our new density value //Our new density value
uint8_t uDensity = MaterialDensityPair44::getMaxDensity(); uint8_t uDensity = MaterialDensityPair44::getMaxDensity();
uint8_t uMaterial = 3;
//Get the old voxel //Get the old voxel
MaterialDensityPair44 voxel = volData.getVoxelAt(x,y,z); MaterialDensityPair44 voxel = volData.getVoxelAt(x,y,z);
//Modify the density //Modify the density
voxel.setDensity(uDensity); voxel.setDensity(uDensity);
voxel.setMaterial(uMaterial);
//Wrte the voxel value into the volume //Write the voxel value into the volume
volData.setVoxelAt(x, y, z, voxel); volData.setVoxelAt(x, y, z, voxel);
} }
} }
@ -70,6 +77,28 @@ void createSphereInVolume(Volume<MaterialDensityPair44>& volData, float fRadius)
} }
} }
void addNormals(const PolyVox::SurfaceMesh<PolyVox::PositionMaterial>& inputMesh, PolyVox::SurfaceMesh<PolyVox::PositionMaterialNormal>& outputMesh)
{
outputMesh.m_Region = inputMesh.m_Region;
outputMesh.m_vecTriangleIndices.clear();
for(int ct = 0; ct < inputMesh.m_vecTriangleIndices.size(); ++ct)
{
outputMesh.m_vecTriangleIndices.push_back(inputMesh.m_vecTriangleIndices[ct]);
}
outputMesh.m_vecVertices.clear();
for(int ct = 0; ct < inputMesh.m_vecVertices.size(); ++ct)
{
PositionMaterialNormal vertex;
vertex.position = inputMesh.m_vecVertices[ct].position;
vertex.material = inputMesh.m_vecVertices[ct].material;
outputMesh.m_vecVertices.push_back(vertex);
}
outputMesh.generateAveragedFaceNormals(true, true);
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
//Create and show the Qt OpenGL window //Create and show the Qt OpenGL window
@ -78,16 +107,77 @@ int main(int argc, char *argv[])
openGLWidget.show(); openGLWidget.show();
//Create an empty volume and then place a sphere in it //Create an empty volume and then place a sphere in it
Volume<MaterialDensityPair44> volData(64, 64, 64); Volume<MaterialDensityPair44> volData(32, 32, 32, 32);
createSphereInVolume(volData, 30); //createSphereInVolume(volData, 30);
//This three-level for loop iterates over every voxel in the volume
/*for (int z = 8; z < 24; z++)
{
for (int y = 8; y < 24; y++)
{
for (int x = 8; x < 16; x++)
{
//Our new density value
uint8_t uDensity = MaterialDensityPair44::getMaxDensity();
//Get the old voxel
MaterialDensityPair44 voxel = volData.getVoxelAt(x,y,z);
//Modify the density
voxel.setDensity(uDensity);
voxel.setMaterial(3);
//Write the voxel value into the volume
volData.setVoxelAt(x, y, z, voxel);
}
}
}
for (int z = 8; z < 24; z++)
{
for (int y = 8; y < 24; y++)
{
for (int x = 16; x < 24; x++)
{
//Our new density value
uint8_t uDensity = MaterialDensityPair44::getMaxDensity();
//Get the old voxel
MaterialDensityPair44 voxel = volData.getVoxelAt(x,y,z);
//Modify the density
voxel.setDensity(uDensity);
voxel.setMaterial(5);
//Write the voxel value into the volume
volData.setVoxelAt(x, y, z, voxel);
}
}
}*/
createSphereInVolume(volData, 10);
//Extract the surface //Extract the surface
SurfaceMesh<PositionMaterialNormal> mesh; Region region(Vector3DInt16(0,0,0), Vector3DInt16(20,20,20));
CubicSurfaceExtractorWithNormals<MaterialDensityPair44> surfaceExtractor(&volData, volData.getEnclosingRegion(), &mesh); SurfaceMesh<PositionMaterial> mesh;
//CubicSurfaceExtractor<MaterialDensityPair44> surfaceExtractor(&volData, volData.getEnclosingRegion(), &mesh);
CubicSurfaceExtractor<MaterialDensityPair44> surfaceExtractor(&volData, region, &mesh);
surfaceExtractor.execute(); surfaceExtractor.execute();
/*SurfaceMesh<PositionMaterialNormal> meshWithNormals;
addNormals(mesh, meshWithNormals);
meshWithNormals.decimate(0.99);*/
MeshDecimator<PositionMaterial> decimator(&mesh);
decimator.execute();
SurfaceMesh<PositionMaterialNormal> meshWithNormals;
addNormals(mesh, meshWithNormals);
//Pass the surface to the OpenGL window //Pass the surface to the OpenGL window
openGLWidget.setSurfaceMeshToRender(mesh); openGLWidget.setSurfaceMeshToRender(meshWithNormals);
//openGLWidget.setSurfaceMeshToRender(mesh);
//Run the message pump. //Run the message pump.
return app.exec(); return app.exec();

View File

@ -28,6 +28,7 @@ freely, subject to the following restrictions:
#include "GradientEstimators.h" #include "GradientEstimators.h"
#include "MaterialDensityPair.h" #include "MaterialDensityPair.h"
#include "SurfaceExtractor.h" #include "SurfaceExtractor.h"
#include "MeshDecimator.h"
#include "Mesh.h" #include "Mesh.h"
@ -125,6 +126,9 @@ void OpenGLWidget::setVolume(PolyVox::Volume<MaterialDensityPair44>* volData)
//mesh->decimate(0.999f); //mesh->decimate(0.999f);
MeshDecimator<PositionMaterialNormal> decimator(mesh.get());
decimator.execute();
//mesh->generateAveragedFaceNormals(true); //mesh->generateAveragedFaceNormals(true);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -183,7 +187,7 @@ void OpenGLWidget::initializeGL()
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
glEnable(GL_LIGHT0); glEnable(GL_LIGHT0);
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glShadeModel(GL_SMOOTH); glShadeModel(GL_SMOOTH);
} }

View File

@ -30,15 +30,9 @@ namespace PolyVox
{ {
struct InitialVertexMetadata struct InitialVertexMetadata
{ {
list<uint32_t> trianglesUsingVertex;
Vector3DFloat normal; Vector3DFloat normal;
bool isOnRegionEdge;
bool isOnMaterialEdge; bool isOnMaterialEdge;
}; std::bitset<VF_NO_OF_FLAGS> vertexFlags;
struct CurrentVertexMetadata
{
list<uint32_t> trianglesUsingVertex;
}; };
struct Triangle struct Triangle
@ -60,16 +54,14 @@ namespace PolyVox
private: private:
void fillInitialVertexMetadata(std::vector<InitialVertexMetadata>& vecInitialVertexMetadata); void fillInitialVertexMetadata(std::vector<InitialVertexMetadata>& vecInitialVertexMetadata);
void fillCurrentVertexMetadata(std::vector<CurrentVertexMetadata>& vecCurrentVertexMetadata);
void buildTriangles(void); void buildConnectivityData(void);
bool attemptEdgeCollapse(uint32_t uSrc, uint32_t uDest); bool attemptEdgeCollapse(uint32_t uSrc, uint32_t uDest);
SurfaceMesh<VertexType>* m_pInputMesh; SurfaceMesh<VertexType>* m_pInputMesh;
//SurfaceMesh<PositionMaterial>* pMeshOutput; //SurfaceMesh<PositionMaterial>* pMeshOutput;
void countNoOfNeighboursUsingMaterial(void);
uint32_t performDecimationPass(float fMinDotProductForCollapse); uint32_t performDecimationPass(float fMinDotProductForCollapse);
bool isSubset(std::bitset<VF_NO_OF_FLAGS> a, std::bitset<VF_NO_OF_FLAGS> b); bool isSubset(std::bitset<VF_NO_OF_FLAGS> a, std::bitset<VF_NO_OF_FLAGS> b);
@ -80,19 +72,14 @@ namespace PolyVox
bool collapseChangesFaceNormals(uint32_t uSrc, uint32_t uDst, float fThreshold); bool collapseChangesFaceNormals(uint32_t uSrc, uint32_t uDst, float fThreshold);
//Data structures used during decimation //Data structures used during decimation
std::vector<uint8_t> m_vecNoOfNeighboursUsingMaterial;
std::vector<bool> vertexLocked; std::vector<bool> vertexLocked;
std::vector<uint32_t> vertexMapper; std::vector<uint32_t> vertexMapper;
//vector< list<uint32_t> > trianglesUsingVertexCurrently;
std::vector<int> vecOfTriCts;
std::vector<Vector3DFloat> vecOfTriNormals;
std::vector<Triangle> m_vecTriangles; std::vector<Triangle> m_vecTriangles;
std::vector< list<uint32_t> > trianglesUsingVertex;
std::vector<InitialVertexMetadata> m_vecInitialVertexMetadata; std::vector<InitialVertexMetadata> m_vecInitialVertexMetadata;
std::vector<CurrentVertexMetadata> m_vecCurrentVertexMetadata;
float fMinDotProductForCollapse; float fMinDotProductForCollapse;
}; };

View File

@ -34,11 +34,7 @@ namespace PolyVox
template <typename VertexType> template <typename VertexType>
void MeshDecimator<VertexType>::execute() void MeshDecimator<VertexType>::execute()
{ {
// We will need the information from this function to buildConnectivityData();
// determine when material boundary edges can collapse.
countNoOfNeighboursUsingMaterial();
buildTriangles();
fillInitialVertexMetadata(m_vecInitialVertexMetadata); fillInitialVertexMetadata(m_vecInitialVertexMetadata);
uint32_t noOfEdgesCollapsed; uint32_t noOfEdgesCollapsed;
@ -46,6 +42,12 @@ namespace PolyVox
{ {
noOfEdgesCollapsed = performDecimationPass(fMinDotProductForCollapse); noOfEdgesCollapsed = performDecimationPass(fMinDotProductForCollapse);
m_pInputMesh->removeDegenerateTris(); m_pInputMesh->removeDegenerateTris();
if(noOfEdgesCollapsed > 0)
{
//Build the connectivity data for the next pass. If this is slow, then look
//at adjusting it (based on vertex mapper?) rather than bulding from scratch.
buildConnectivityData();
}
//m_pInputMesh->removeUnusedVertices(); //m_pInputMesh->removeUnusedVertices();
}while(noOfEdgesCollapsed > 0); }while(noOfEdgesCollapsed > 0);
@ -58,36 +60,8 @@ namespace PolyVox
} }
template <typename VertexType> template <typename VertexType>
void MeshDecimator<VertexType>::buildTriangles(void) void MeshDecimator<VertexType>::buildConnectivityData(void)
{ {
// 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;
}
m_vecTriangles.clear(); m_vecTriangles.clear();
m_vecTriangles.resize(m_pInputMesh->m_vecTriangleIndices.size() / 3); m_vecTriangles.resize(m_pInputMesh->m_vecTriangleIndices.size() / 3);
for(int triCt = 0; triCt < m_vecTriangles.size(); triCt++) for(int triCt = 0; triCt < m_vecTriangles.size(); triCt++)
@ -107,25 +81,27 @@ namespace PolyVox
m_vecTriangles[triCt].normal = normal; m_vecTriangles[triCt].normal = normal;
} }
trianglesUsingVertex.clear();
trianglesUsingVertex.resize(m_pInputMesh->m_vecVertices.size());
for(int ct = 0; ct < m_vecTriangles.size(); ct++)
{
trianglesUsingVertex[m_vecTriangles[ct].v0].push_back(ct);
trianglesUsingVertex[m_vecTriangles[ct].v1].push_back(ct);
trianglesUsingVertex[m_vecTriangles[ct].v2].push_back(ct);
}
} }
template <typename VertexType> void MeshDecimator<PositionMaterial>::fillInitialVertexMetadata(std::vector<InitialVertexMetadata>& vecVertexMetadata)
void MeshDecimator<VertexType>::fillInitialVertexMetadata(std::vector<InitialVertexMetadata>& vecVertexMetadata)
{ {
vecVertexMetadata.clear(); vecVertexMetadata.clear();
vecVertexMetadata.resize(m_pInputMesh->m_vecVertices.size()); vecVertexMetadata.resize(m_pInputMesh->m_vecVertices.size());
//Initialise the metadata //Initialise the metadata
for(int ct = 0; ct < vecVertexMetadata.size(); ct++) for(int ct = 0; ct < vecVertexMetadata.size(); ct++)
{ {
vecVertexMetadata[ct].trianglesUsingVertex.clear();
vecVertexMetadata[ct].normal.setElements(0,0,0); vecVertexMetadata[ct].normal.setElements(0,0,0);
vecVertexMetadata[ct].isOnRegionEdge = false;
vecVertexMetadata[ct].isOnMaterialEdge = false; vecVertexMetadata[ct].isOnMaterialEdge = false;
} vecVertexMetadata[ct].vertexFlags.reset();
for(int ct = 0; ct < m_vecTriangles.size(); ct++)
{
vecVertexMetadata[m_vecTriangles[ct].v0].trianglesUsingVertex.push_back(ct);
} }
for(int outerCt = 0; outerCt < m_pInputMesh->m_vecVertices.size()-1; outerCt++) for(int outerCt = 0; outerCt < m_pInputMesh->m_vecVertices.size()-1; outerCt++)
@ -140,13 +116,10 @@ namespace PolyVox
} }
} }
//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++) for(int ct = 0; ct < m_pInputMesh->m_vecVertices.size(); ct++)
{ {
Vector3DFloat sumOfNormals(0.0f,0.0f,0.0f); 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++) for(list<uint32_t>::const_iterator iter = trianglesUsingVertex[ct].cbegin(); iter != trianglesUsingVertex[ct].cend(); iter++)
{ {
sumOfNormals += m_vecTriangles[*iter].normal; sumOfNormals += m_vecTriangles[*iter].normal;
} }
@ -155,51 +128,77 @@ namespace PolyVox
vecVertexMetadata[ct].normal.normalise(); vecVertexMetadata[ct].normal.normalise();
} }
Vector3DFloat offset = static_cast<Vector3DFloat>(m_pInputMesh->m_Region.getLowerCorner()); for(int ct = 0; ct < vecVertexMetadata.size(); ct++)
for(int ct = 0; ct < m_pInputMesh->m_vecVertices.size(); ct++)
{ {
bool bInside = m_pInputMesh->m_Region.containsPoint(m_pInputMesh->m_vecVertices[ct].getPosition() + offset); Region regTransformed = m_pInputMesh->m_Region;
vecVertexMetadata[ct].isOnRegionEdge = !bInside; regTransformed.shift(regTransformed.getLowerCorner() * static_cast<int16_t>(-1));
//Plus and minus X
vecVertexMetadata[ct].vertexFlags.set(VF_ON_GEOMETRY_EDGE_NEG_X, m_pInputMesh->m_vecVertices[ct].getPosition().getX() < regTransformed.getLowerCorner().getX() + 0.001f);
vecVertexMetadata[ct].vertexFlags.set(VF_ON_GEOMETRY_EDGE_POS_X, m_pInputMesh->m_vecVertices[ct].getPosition().getX() > regTransformed.getUpperCorner().getX() - 0.001f);
//Plus and minus Y
vecVertexMetadata[ct].vertexFlags.set(VF_ON_GEOMETRY_EDGE_NEG_Y, m_pInputMesh->m_vecVertices[ct].getPosition().getY() < regTransformed.getLowerCorner().getY() + 0.001f);
vecVertexMetadata[ct].vertexFlags.set(VF_ON_GEOMETRY_EDGE_POS_Y, m_pInputMesh->m_vecVertices[ct].getPosition().getY() > regTransformed.getUpperCorner().getY() - 0.001f);
//Plus and minus Z
vecVertexMetadata[ct].vertexFlags.set(VF_ON_GEOMETRY_EDGE_NEG_Z, m_pInputMesh->m_vecVertices[ct].getPosition().getZ() < regTransformed.getLowerCorner().getZ() + 0.001f);
vecVertexMetadata[ct].vertexFlags.set(VF_ON_GEOMETRY_EDGE_POS_Z, m_pInputMesh->m_vecVertices[ct].getPosition().getZ() > regTransformed.getUpperCorner().getZ() - 0.001f);
} }
} }
template <typename VertexType> void MeshDecimator<PositionMaterialNormal>::fillInitialVertexMetadata(std::vector<InitialVertexMetadata>& vecVertexMetadata)
void MeshDecimator<VertexType>::fillCurrentVertexMetadata(std::vector<CurrentVertexMetadata>& vecVertexMetadata)
{ {
vecVertexMetadata.clear(); vecVertexMetadata.clear();
vecVertexMetadata.resize(m_pInputMesh->m_vecVertices.size()); vecVertexMetadata.resize(m_pInputMesh->m_vecVertices.size());
//Initialise the metadata //Initialise the metadata
for(int ct = 0; ct < vecVertexMetadata.size(); ct++) for(int ct = 0; ct < vecVertexMetadata.size(); ct++)
{ {
vecVertexMetadata[ct].trianglesUsingVertex.clear(); vecVertexMetadata[ct].vertexFlags.reset();
vecVertexMetadata[ct].isOnMaterialEdge = false;
vecVertexMetadata[ct].normal = m_pInputMesh->m_vecVertices[ct].normal;
} }
//Determine triangles using each vertex for(int ct = 0; ct < vecVertexMetadata.size(); ct++)
/*trianglesUsingVertex.clear();
trianglesUsingVertex.resize(m_pInputMesh->m_vecVertices.size());*/
for(int ct = 0; ct < m_pInputMesh->m_vecTriangleIndices.size(); ct++)
{ {
int triangle = ct / 3; Region regTransformed = m_pInputMesh->m_Region;
regTransformed.shift(regTransformed.getLowerCorner() * static_cast<int16_t>(-1));
vecVertexMetadata[m_pInputMesh->m_vecTriangleIndices[ct]].trianglesUsingVertex.push_back(triangle); //Plus and minus X
vecVertexMetadata[ct].vertexFlags.set(VF_ON_GEOMETRY_EDGE_NEG_X, m_pInputMesh->m_vecVertices[ct].getPosition().getX() < regTransformed.getLowerCorner().getX() + 0.001f);
vecVertexMetadata[ct].vertexFlags.set(VF_ON_GEOMETRY_EDGE_POS_X, m_pInputMesh->m_vecVertices[ct].getPosition().getX() > regTransformed.getUpperCorner().getX() - 0.001f);
//Plus and minus Y
vecVertexMetadata[ct].vertexFlags.set(VF_ON_GEOMETRY_EDGE_NEG_Y, m_pInputMesh->m_vecVertices[ct].getPosition().getY() < regTransformed.getLowerCorner().getY() + 0.001f);
vecVertexMetadata[ct].vertexFlags.set(VF_ON_GEOMETRY_EDGE_POS_Y, m_pInputMesh->m_vecVertices[ct].getPosition().getY() > regTransformed.getUpperCorner().getY() - 0.001f);
//Plus and minus Z
vecVertexMetadata[ct].vertexFlags.set(VF_ON_GEOMETRY_EDGE_NEG_Z, m_pInputMesh->m_vecVertices[ct].getPosition().getZ() < regTransformed.getLowerCorner().getZ() + 0.001f);
vecVertexMetadata[ct].vertexFlags.set(VF_ON_GEOMETRY_EDGE_POS_Z, m_pInputMesh->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_pInputMesh->m_vecVertices[v0].material == m_pInputMesh->m_vecVertices[v1].material) &&
(m_pInputMesh->m_vecVertices[v1].material == m_pInputMesh->m_vecVertices[v2].material);
if(!allMatch)
{
vecVertexMetadata[v0].isOnMaterialEdge = true;
vecVertexMetadata[v1].isOnMaterialEdge = true;
vecVertexMetadata[v2].isOnMaterialEdge = true;
}
} }
} }
template <typename VertexType> template <typename VertexType>
uint32_t MeshDecimator<VertexType>::performDecimationPass(float fMinDotProductForCollapse) 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.
/*trianglesUsingVertexCurrently.clear();
trianglesUsingVertexCurrently.resize(m_pInputMesh->m_vecVertices.size());
for(int ct = 0; ct < m_pInputMesh->m_vecTriangleIndices.size(); ct++)
{
int triangle = ct / 3;
trianglesUsingVertexCurrently[m_pInputMesh->m_vecTriangleIndices[ct]].push_back(triangle);
}*/
// Count how many edges we have collapsed // Count how many edges we have collapsed
uint32_t noOfEdgesCollapsed = 0; uint32_t noOfEdgesCollapsed = 0;
@ -223,13 +222,9 @@ namespace PolyVox
vertexLocked[ct] = false; vertexLocked[ct] = false;
} }
buildTriangles(); //For each triangle...
fillCurrentVertexMetadata(m_vecCurrentVertexMetadata);
//For each triange...
for(int ctIter = 0; ctIter < m_vecTriangles.size(); ctIter++) for(int ctIter = 0; ctIter < m_vecTriangles.size(); ctIter++)
{ {
if(attemptEdgeCollapse(m_vecTriangles[ctIter].v0, m_vecTriangles[ctIter].v1)) if(attemptEdgeCollapse(m_vecTriangles[ctIter].v0, m_vecTriangles[ctIter].v1))
{ {
++noOfEdgesCollapsed; ++noOfEdgesCollapsed;
@ -266,6 +261,12 @@ namespace PolyVox
template <typename VertexType> template <typename VertexType>
bool MeshDecimator<VertexType>::attemptEdgeCollapse(uint32_t uSrc, uint32_t uDst) bool MeshDecimator<VertexType>::attemptEdgeCollapse(uint32_t uSrc, uint32_t uDst)
{ {
//A vertex will be locked if it has already been involved in a collapse this pass.
if(vertexLocked[uSrc] || vertexLocked[uDst])
{
return false;
}
if(canCollapseEdge(uSrc, uDst)) if(canCollapseEdge(uSrc, uDst))
{ {
//Move v0 onto v1 //Move v0 onto v1
@ -280,51 +281,13 @@ namespace PolyVox
return false; return false;
} }
//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. // 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> template <typename VertexType>
bool MeshDecimator<VertexType>::isSubset(std::bitset<VF_NO_OF_FLAGS> a, std::bitset<VF_NO_OF_FLAGS> b) bool MeshDecimator<VertexType>::isSubset(std::bitset<VF_NO_OF_FLAGS> a, std::bitset<VF_NO_OF_FLAGS> b)
{ {
bool result = true; bool result = true;
for(int ct = 1; ct < 7; ct++) //Start at '1' to skip material flag for(int ct = 0; ct < VF_NO_OF_FLAGS; ct++)
{ {
if(a.test(ct)) if(a.test(ct))
{ {
@ -342,90 +305,15 @@ namespace PolyVox
//template <typename VertexType> //template <typename VertexType>
bool MeshDecimator<PositionMaterialNormal>::canCollapseEdge(uint32_t uSrc, uint32_t uDst) bool MeshDecimator<PositionMaterialNormal>::canCollapseEdge(uint32_t uSrc, uint32_t uDst)
{ {
//A vertex will be locked if it has already been involved in a collapse this pass.
if(vertexLocked[uSrc] || vertexLocked[uDst])
{
return false;
}
if(m_pInputMesh->m_vecVertices[uSrc].getMaterial() != m_pInputMesh->m_vecVertices[uDst].getMaterial())
{
return false;
}
//For now, don't collapse vertices on material edges... //For now, don't collapse vertices on material edges...
if(m_pInputMesh->m_vecVertices[uSrc].isOnMaterialEdge() || m_pInputMesh->m_vecVertices[uDst].isOnMaterialEdge()) if(m_vecInitialVertexMetadata[uSrc].isOnMaterialEdge || m_vecInitialVertexMetadata[uDst].isOnMaterialEdge)
{ {
if(true) return false;
{
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[uSrc] == m_vecNoOfNeighboursUsingMaterial[uDst])
{
if(m_vecNoOfNeighboursUsingMaterial[uSrc] == 4)
{
allMatch = true;
}
}
bool movementValid = false;
Vector3DFloat movement = m_pInputMesh->m_vecVertices[uDst].getPosition() - m_pInputMesh->m_vecVertices[uSrc].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. // Vertices on the geometrical edge of surface meshes need special handling.
// We check for this by whether any of the edge flags are set. // We check for this by whether any of the edge flags are set.
if(m_pInputMesh->m_vecVertices[uSrc].m_bFlags.any() || m_pInputMesh->m_vecVertices[uDst].m_bFlags.any()) if(m_vecInitialVertexMetadata[uSrc].vertexFlags.any() || m_vecInitialVertexMetadata[uDst].vertexFlags.any())
{ {
// Assume we can't collapse until we prove otherwise... // Assume we can't collapse until we prove otherwise...
bool bCollapseGeometryEdgePair = false; bool bCollapseGeometryEdgePair = false;
@ -433,7 +321,7 @@ namespace PolyVox
// We can collapse normal vertices onto edge vertices, and edge vertices // We can collapse normal vertices onto edge vertices, and edge vertices
// onto corner vertices, but not vice-versa. Hence we check whether all // 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. // the edge flags in the source vertex are also set in the destination vertex.
if(isSubset(m_pInputMesh->m_vecVertices[uSrc].m_bFlags, m_pInputMesh->m_vecVertices[uDst].m_bFlags)) if(isSubset(m_vecInitialVertexMetadata[uSrc].vertexFlags, m_vecInitialVertexMetadata[uDst].vertexFlags))
{ {
// In general adjacent regions surface meshes may collapse differently // In general adjacent regions surface meshes may collapse differently
// and this can cause cracks. We solve this by only allowing the collapse // and this can cause cracks. We solve this by only allowing the collapse
@ -468,38 +356,26 @@ namespace PolyVox
return true; return true;
} }
//template <typename VertexType>
bool MeshDecimator<PositionMaterial>::canCollapseEdge(uint32_t uSrc, uint32_t uDst) bool MeshDecimator<PositionMaterial>::canCollapseEdge(uint32_t uSrc, uint32_t uDst)
{ {
//A vertex will be locked if it has already been involved in a collapse this pass. bool bCanCollapse = true;
if(vertexLocked[uSrc] || vertexLocked[uDst])
{
return false;
}
if(m_vecInitialVertexMetadata[uSrc].isOnMaterialEdge) if(m_vecInitialVertexMetadata[uSrc].isOnMaterialEdge)
{ {
if(m_vecInitialVertexMetadata[uSrc].isOnRegionEdge) bCanCollapse &= canCollapseMaterialEdge(uSrc, uDst);
{
assert(false); //Shouldn't be on both edge types.
return false;
}
else
{
return canCollapseMaterialEdge(uSrc, uDst);
}
} }
else
if(m_vecInitialVertexMetadata[uSrc].vertexFlags.any())
{ {
if(m_vecInitialVertexMetadata[uSrc].isOnRegionEdge) bCanCollapse &= canCollapseRegionEdge(uSrc, uDst);
{
return canCollapseRegionEdge(uSrc, uDst);
}
else
{
return canCollapseNormalEdge(uSrc, uDst);
}
} }
if(bCanCollapse) //Only bother with this is the earlier tests passed.
{
bCanCollapse &= canCollapseNormalEdge(uSrc, uDst);
}
return bCanCollapse;
} }
template <typename VertexType> template <typename VertexType>
@ -510,43 +386,18 @@ namespace PolyVox
template <typename VertexType> template <typename VertexType>
bool MeshDecimator<VertexType>::canCollapseRegionEdge(uint32_t uSrc, uint32_t uDst) bool MeshDecimator<VertexType>::canCollapseRegionEdge(uint32_t uSrc, uint32_t uDst)
{ {
return false; if(isSubset(m_vecInitialVertexMetadata[uSrc].vertexFlags, m_vecInitialVertexMetadata[uDst].vertexFlags) == false)
if(m_vecInitialVertexMetadata[uDst].isOnRegionEdge)
{ {
return false;
int matchingCoordinates = 0;
if(abs(m_pInputMesh->m_vecVertices[uSrc].getPosition().getX() - m_pInputMesh->m_vecVertices[uDst].getPosition().getX()) < 0.001)
{
matchingCoordinates++;
}
if(abs(m_pInputMesh->m_vecVertices[uSrc].getPosition().getY() - m_pInputMesh->m_vecVertices[uDst].getPosition().getY()) < 0.001)
{
matchingCoordinates++;
}
if(abs(m_pInputMesh->m_vecVertices[uSrc].getPosition().getZ() - m_pInputMesh->m_vecVertices[uDst].getPosition().getZ()) < 0.001)
{
matchingCoordinates++;
}
if(matchingCoordinates != 2)
{
return false;
}
if(m_vecInitialVertexMetadata[uSrc].trianglesUsingVertex.size() != m_vecInitialVertexMetadata[uDst].trianglesUsingVertex.size()) //Corner
{
return false;
}
if(m_vecInitialVertexMetadata[uSrc].normal.dot(m_vecInitialVertexMetadata[uDst].normal) < 0.999f)
{
return false;
}
return !collapseChangesFaceNormals(uSrc, uDst, 0.999f);
} }
return false;
if(m_vecInitialVertexMetadata[uSrc].normal.dot(m_vecInitialVertexMetadata[uDst].normal) < 0.999f)
{
return false;
}
return true;
} }
template <typename VertexType> template <typename VertexType>
@ -559,12 +410,7 @@ namespace PolyVox
bool MeshDecimator<VertexType>::collapseChangesFaceNormals(uint32_t uSrc, uint32_t uDst, float fThreshold) bool MeshDecimator<VertexType>::collapseChangesFaceNormals(uint32_t uSrc, uint32_t uDst, float fThreshold)
{ {
bool faceFlipped = false; bool faceFlipped = false;
//list<uint32_t> triangles = trianglesUsingVertexCurrently[v0]; list<uint32_t> triangles = trianglesUsingVertex[uSrc];
list<uint32_t> triangles = m_vecCurrentVertexMetadata[uSrc].trianglesUsingVertex;
/*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++) for(list<uint32_t>::iterator triIter = triangles.begin(); triIter != triangles.end(); triIter++)
{ {

View File

@ -90,12 +90,6 @@ namespace PolyVox
//which cover a whole triangle are counted. Materials which only //which cover a whole triangle are counted. Materials which only
//exist on a material boundary do not count. //exist on a material boundary do not count.
std::set<uint8_t> m_mapUsedMaterials; std::set<uint8_t> m_mapUsedMaterials;
private:
void countNoOfNeighboursUsingMaterial(void);
//Data structures used during decimation
std::vector<uint8_t> m_vecNoOfNeighboursUsingMaterial;
}; };
} }

View File

@ -332,44 +332,6 @@ namespace PolyVox
} }
} }
//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 SurfaceMesh<VertexType>::countNoOfNeighboursUsingMaterial(void)
{
//Find all the neighbouring vertices for each vertex
std::vector< std::set<int> > neighbouringVertices(m_vecVertices.size());
for(int triCt = 0; triCt < m_vecTriangleIndices.size() / 3; triCt++)
{
int v0 = m_vecTriangleIndices[(triCt * 3 + 0)];
int v1 = m_vecTriangleIndices[(triCt * 3 + 1)];
int v2 = 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_vecVertices.size());
for(int vertCt = 0; vertCt < m_vecVertices.size(); vertCt++)
{
m_vecNoOfNeighboursUsingMaterial[vertCt] = 0;
for(std::set<int>::iterator iter = neighbouringVertices[vertCt].begin(); iter != neighbouringVertices[vertCt].end(); iter++)
{
if(m_vecVertices[vertCt].getMaterial() == m_vecVertices[*iter].getMaterial())
{
m_vecNoOfNeighboursUsingMaterial[vertCt]++;
}
}
}
}
template <typename VertexType> template <typename VertexType>
polyvox_shared_ptr< SurfaceMesh<VertexType> > SurfaceMesh<VertexType>::extractSubset(std::set<uint8_t> setMaterials) polyvox_shared_ptr< SurfaceMesh<VertexType> > SurfaceMesh<VertexType>::extractSubset(std::set<uint8_t> setMaterials)
{ {