From 89c48cdc27b07c875798c510c16e98221dcf6ee7 Mon Sep 17 00:00:00 2001 From: David Williams Date: Tue, 2 Feb 2010 23:18:17 +0000 Subject: [PATCH] Working version of mesh decimation code which acts directly on vertex/index buffers. Also initial work on a 'dynamic' mesh for simplification... but this probably won't be needed now. --- examples/OpenGL/OpenGLWidget.cpp | 38 +- library/PolyVoxCore/CMakeLists.txt | 8 + .../PolyVoxCore/include/IndexedSurfacePatch.h | 10 +- library/PolyVoxCore/include/Mesh.h | 61 +++ library/PolyVoxCore/include/MeshEdge.h | 61 +++ library/PolyVoxCore/include/MeshFace.h | 49 ++ library/PolyVoxCore/include/MeshVertex.h | 52 ++ .../include/PolyVoxForwardDeclarations.h | 7 + library/PolyVoxCore/include/SurfaceVertex.h | 28 +- .../source/IndexedSurfacePatch.cpp | 325 ++++++++++++- library/PolyVoxCore/source/Mesh.cpp | 448 ++++++++++++++++++ library/PolyVoxCore/source/MeshEdge.cpp | 158 ++++++ library/PolyVoxCore/source/MeshFace.cpp | 114 +++++ library/PolyVoxCore/source/MeshVertex.cpp | 45 ++ .../PolyVoxCore/source/SurfaceExtractor.cpp | 15 +- library/PolyVoxCore/source/SurfaceVertex.cpp | 50 +- 16 files changed, 1445 insertions(+), 24 deletions(-) create mode 100644 library/PolyVoxCore/include/Mesh.h create mode 100644 library/PolyVoxCore/include/MeshEdge.h create mode 100644 library/PolyVoxCore/include/MeshFace.h create mode 100644 library/PolyVoxCore/include/MeshVertex.h create mode 100644 library/PolyVoxCore/source/Mesh.cpp create mode 100644 library/PolyVoxCore/source/MeshEdge.cpp create mode 100644 library/PolyVoxCore/source/MeshFace.cpp create mode 100644 library/PolyVoxCore/source/MeshVertex.cpp diff --git a/examples/OpenGL/OpenGLWidget.cpp b/examples/OpenGL/OpenGLWidget.cpp index 68f307f5..0038e242 100644 --- a/examples/OpenGL/OpenGLWidget.cpp +++ b/examples/OpenGL/OpenGLWidget.cpp @@ -28,6 +28,9 @@ freely, subject to the following restrictions: #include "GradientEstimators.h" #include "SurfaceExtractor.h" +#include "RenderDynamicMesh.h" +#include "Mesh.h" + //Some namespaces we need using namespace std; using namespace PolyVox; @@ -97,7 +100,40 @@ void OpenGLWidget::setVolume(PolyVox::Volume* volData) if(isp->m_vecTriangleIndices.size() > 0) { - isp->makeProgressiveMesh(); + //isp->makeProgressiveMesh(); + + /*RenderDynamicMesh rdm; + rdm.buildFromIndexedSurfacePatch(*isp);*/ + + //computeNormalsForVertices(m_volData, *(isp.get()), SOBEL_SMOOTHED); + + //isp->smoothPositions(0.3f); + //isp->generateAveragedFaceNormals(true); + + /*for(int ct = 0; ct < 20; ct ++) + { + //cout << "Before: " << isp->noOfDegenerateTris() << endl; + isp->decimate(); + //cout << "After: " << isp->noOfDegenerateTris() << endl; + isp->removeDegenerateTris(); + //cout << "After Remove: " << isp->noOfDegenerateTris() << endl << endl; + }*/ + + //////////////////////////////////////////////////////////////////////////////// + //For decimation built into ISP + //isp->generateAveragedFaceNormals(true); + + isp->decimate(0.999f); + + //isp->generateAveragedFaceNormals(true); + //////////////////////////////////////////////////////////////////////////////// + + /*isp->generateAveragedFaceNormals(true); + Mesh mesh; + mesh.buildFromISP(isp.get()); + //mesh.removeEdge(*(mesh.m_edges.begin())); + mesh.decimateAll(); + mesh.fillISP(isp.get());*/ Vector3DUint8 v3dRegPos(uRegionX,uRegionY,uRegionZ); diff --git a/library/PolyVoxCore/CMakeLists.txt b/library/PolyVoxCore/CMakeLists.txt index 3d72fcb8..d2e70fd8 100644 --- a/library/PolyVoxCore/CMakeLists.txt +++ b/library/PolyVoxCore/CMakeLists.txt @@ -7,6 +7,10 @@ SET(CORE_SRC_FILES source/GradientEstimators.cpp source/IndexedSurfacePatch.cpp source/Log.cpp + source/Mesh.cpp + source/MeshEdge.cpp + source/MeshFace.cpp + source/MeshVertex.cpp source/progmesh.cpp source/Region.cpp source/SurfaceExtractor.cpp @@ -21,6 +25,10 @@ SET(CORE_INC_FILES include/IndexedSurfacePatch.h include/list.h include/Log.h + include/Mesh.h + include/MeshEdge.h + include/MeshFace.h + include/MeshVertex.h include/PolyVoxForwardDeclarations.h include/progmesh.h include/Region.h diff --git a/library/PolyVoxCore/include/IndexedSurfacePatch.h b/library/PolyVoxCore/include/IndexedSurfacePatch.h index a8b513bb..458db54c 100644 --- a/library/PolyVoxCore/include/IndexedSurfacePatch.h +++ b/library/PolyVoxCore/include/IndexedSurfacePatch.h @@ -64,7 +64,7 @@ namespace PolyVox void clear(void); const bool isEmpty(void) const; - void smoothPositions(float fAmount, bool bIncludeEdgeVertices = false); + void smoothPositions(float fAmount, bool bIncludeGeometryEdgeVertices = false); void sumNearbyNormals(bool bNormaliseResult = true); POLYVOX_SHARED_PTR extractSubset(std::set setMaterials); @@ -76,6 +76,14 @@ namespace PolyVox /*void growMaterialBoundary(void); int countMaterialBoundary(void);*/ + bool isSubset(std::bitset<4> a, std::bitset<4> b); + + void decimate(float fMinDotProductForCollapse = 0.999f); + + uint32_t performDecimationPass(float fMinDotProductForCollapse); + int noOfDegenerateTris(void); + void removeDegenerateTris(void); + void makeProgressiveMesh(void); Region m_Region; diff --git a/library/PolyVoxCore/include/Mesh.h b/library/PolyVoxCore/include/Mesh.h new file mode 100644 index 00000000..392fbce3 --- /dev/null +++ b/library/PolyVoxCore/include/Mesh.h @@ -0,0 +1,61 @@ +#pragma region License +/******************************************************************************* +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. +*******************************************************************************/ +#pragma endregion + +#ifndef __PolyVox_Mesh_H__ +#define __PolyVox_Mesh_H__ + +#include "MeshEdge.h" +#include "MeshFace.h" +#include "MeshVertex.h" + +#include "PolyVoxImpl/TypeDef.h" + +#include + +namespace PolyVox +{ + class POLYVOXCORE_API Mesh + { + public: + + void buildFromISP(IndexedSurfacePatch* pIsp); + void fillISP(IndexedSurfacePatch* pIsp); + void matchEdgePairs(void); + void computeEdgeCosts(void); + + void removeEdge(MeshEdge* pMeshEdge); + + void decimateAll(void); + bool decimateOne(void); + + bool isSane(void); + + std::set m_edges; + std::set m_faces; + std::set m_vertices; + }; +} + +#endif \ No newline at end of file diff --git a/library/PolyVoxCore/include/MeshEdge.h b/library/PolyVoxCore/include/MeshEdge.h new file mode 100644 index 00000000..add607d9 --- /dev/null +++ b/library/PolyVoxCore/include/MeshEdge.h @@ -0,0 +1,61 @@ +#pragma region License +/******************************************************************************* +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. +*******************************************************************************/ +#pragma endregion + +#ifndef __PolyVox_MeshEdge_H__ +#define __PolyVox_MeshEdge_H__ + +#include "PolyVoxForwardDeclarations.h" + +#include "PolyVoxImpl/TypeDef.h" + +namespace PolyVox +{ + class POLYVOXCORE_API MeshEdge + { + public: + MeshEdge(); + + bool isSane(void); + + void computeEdgeCost(Mesh* pParentMesh); + + MeshEdge* m_pOtherEdge; + MeshEdge* m_pNextEdge; + MeshEdge* m_pPreviousEdge; + + MeshVertex* m_pSrc; + MeshVertex* m_pDest; + + MeshFace* m_pFace; + + float m_fCost; + }; + + //FIXME - Rather than being global, these should just be used to sort within the set. + bool operator==(const MeshEdge& lhs, const MeshEdge& rhs); + bool operator<(const MeshEdge& lhs, const MeshEdge& rhs); +} + +#endif \ No newline at end of file diff --git a/library/PolyVoxCore/include/MeshFace.h b/library/PolyVoxCore/include/MeshFace.h new file mode 100644 index 00000000..4c7e7a8b --- /dev/null +++ b/library/PolyVoxCore/include/MeshFace.h @@ -0,0 +1,49 @@ +#pragma region License +/******************************************************************************* +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. +*******************************************************************************/ +#pragma endregion + +#ifndef __PolyVox_MeshFace_H__ +#define __PolyVox_MeshFace_H__ + +#include "MeshEdge.h" + +#include "PolyVoxImpl/TypeDef.h" + +namespace PolyVox +{ + class POLYVOXCORE_API MeshFace + { + public: + MeshFace(); + bool isSane(void); + + MeshEdge* m_pEdge; + + Vector3DFloat getNormal(void); + bool collapseFlipsFace(MeshEdge* pEdgeToCollapse); + bool collapseFlipsFaceImpl(Vector3DFloat fixed0, Vector3DFloat fixed1, Vector3DFloat oldPos, Vector3DFloat newPos); + }; +} + +#endif \ No newline at end of file diff --git a/library/PolyVoxCore/include/MeshVertex.h b/library/PolyVoxCore/include/MeshVertex.h new file mode 100644 index 00000000..914751fe --- /dev/null +++ b/library/PolyVoxCore/include/MeshVertex.h @@ -0,0 +1,52 @@ +#pragma region License +/******************************************************************************* +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. +*******************************************************************************/ +#pragma endregion + +#ifndef __PolyVox_MeshVertex_H__ +#define __PolyVox_MeshVertex_H__ + +#include "PolyVoxForwardDeclarations.h" +#include "SurfaceVertex.h" + +#include "PolyVoxImpl/TypeDef.h" + +#include + +namespace PolyVox +{ + class POLYVOXCORE_API MeshVertex + { + public: + MeshVertex(); + SurfaceVertex m_vertexData; + //MeshEdge* m_pEdge; + //std::set m_faces; + //std::set m_edges; //Edges which have this vertex as the src + long int m_index; //Bit wasteful to store this the whle time, as it's only used when converting to ISPs? + + bool isSane(void); + }; +} + +#endif \ No newline at end of file diff --git a/library/PolyVoxCore/include/PolyVoxForwardDeclarations.h b/library/PolyVoxCore/include/PolyVoxForwardDeclarations.h index 6d571c25..2e450c6b 100644 --- a/library/PolyVoxCore/include/PolyVoxForwardDeclarations.h +++ b/library/PolyVoxCore/include/PolyVoxForwardDeclarations.h @@ -39,6 +39,13 @@ namespace PolyVox typedef Volume UInt16Volume; //--------------------------------- + //---------- Mesh ---------- + class Mesh; + class MeshEdge; + class MeshFace; + class MeshVertex; + //--------------------------------- + class IndexedSurfacePatch; class Region; class SurfaceVertex; diff --git a/library/PolyVoxCore/include/SurfaceVertex.h b/library/PolyVoxCore/include/SurfaceVertex.h index ea5a4006..0f57de0a 100644 --- a/library/PolyVoxCore/include/SurfaceVertex.h +++ b/library/PolyVoxCore/include/SurfaceVertex.h @@ -29,8 +29,18 @@ freely, subject to the following restrictions: #include "PolyVoxImpl/TypeDef.h" #include "Vector.h" +#include + namespace PolyVox { + enum POLYVOXCORE_API VertexFlags + { + VF_ON_MATERIAL_EDGE = 0x00, + VF_ON_GEOMETRY_EDGE_X = 0x01, + VF_ON_GEOMETRY_EDGE_Y = 0x02, + VF_ON_GEOMETRY_EDGE_Z = 0x03 + }; + class POLYVOXCORE_API SurfaceVertex { public: @@ -42,20 +52,26 @@ namespace PolyVox const Vector3DFloat& getNormal(void) const; const Vector3DFloat& getPosition(void) const; - bool isEdgeVertex(void) const; - - void setEdgeVertex(bool isEdgeVertex); + bool isOnEdge(void) const; + bool isOnGeometryEdge(void) const; + bool isOnGeometryEdgeX(void) const; + bool isOnGeometryEdgeY(void) const; + bool isOnGeometryEdgeZ(void) const; + bool isOnMaterialEdge(void) const; + void setMaterial(float materialToSet); void setNormal(const Vector3DFloat& normalToSet); + void setOnGeometryEdgeX(bool bOnRegionEdge); + void setOnGeometryEdgeY(bool bOnRegionEdge); + void setOnGeometryEdgeZ(bool bOnRegionEdge); + void setOnMaterialEdge(bool bOnMaterialEdge); void setPosition(const Vector3DFloat& positionToSet); public: Vector3DFloat position; Vector3DFloat normal; float material; //FIXME: This shouldn't be float on CPU? - bool m_bIsEdgeVertex; - bool m_bIsMaterialEdgeVertex; - + std::bitset<4> m_bFlags; }; diff --git a/library/PolyVoxCore/source/IndexedSurfacePatch.cpp b/library/PolyVoxCore/source/IndexedSurfacePatch.cpp index 2dec4035..04290ef4 100644 --- a/library/PolyVoxCore/source/IndexedSurfacePatch.cpp +++ b/library/PolyVoxCore/source/IndexedSurfacePatch.cpp @@ -27,6 +27,8 @@ freely, subject to the following restrictions: #include "progmesh.h" +#include + using namespace std; namespace PolyVox @@ -108,9 +110,9 @@ namespace PolyVox } else { - m_vecVertices[index0].m_bIsMaterialEdgeVertex = true; - m_vecVertices[index1].m_bIsMaterialEdgeVertex = true; - m_vecVertices[index2].m_bIsMaterialEdgeVertex = true; + m_vecVertices[index0].setOnMaterialEdge(true); + m_vecVertices[index1].setOnMaterialEdge(true); + m_vecVertices[index2].setOnMaterialEdge(true); } } @@ -124,6 +126,8 @@ namespace PolyVox { m_vecVertices.clear(); m_vecTriangleIndices.clear(); + m_vecLodRecords.clear(); + m_mapUsedMaterials.clear(); } const bool IndexedSurfacePatch::isEmpty(void) const @@ -138,11 +142,11 @@ namespace PolyVox /// normals must hve been set to something sensible before this functions is called. /// \param fAmount A factor controlling how much the vertices move by. Find a good /// value by experimentation, starting with something small such as 0.1f. - /// \param bIncludeEdgeVertices Indicates whether vertices on the edge of an + /// \param bIncludeGeometryEdgeVertices Indicates whether vertices on the edge of an /// IndexedSurfacePatch should be smoothed. This can cause dicontinuities between /// neighbouring patches. //////////////////////////////////////////////////////////////////////////////// - void IndexedSurfacePatch::smoothPositions(float fAmount, bool bIncludeEdgeVertices) + void IndexedSurfacePatch::smoothPositions(float fAmount, bool bIncludeGeometryEdgeVertices) { if(m_vecVertices.size() == 0) //FIXME - I don't think we should need this test, but I have seen crashes otherwise... { @@ -203,7 +207,7 @@ namespace PolyVox //Update with the new positions for(uint32_t uIndex = 0; uIndex < newPositions.size(); uIndex++) { - if((bIncludeEdgeVertices) || (m_vecVertices[uIndex].isEdgeVertex() == false)) + if((bIncludeGeometryEdgeVertices) || (m_vecVertices[uIndex].isOnGeometryEdge() == false)) { m_vecVertices[uIndex].setPosition(newPositions[uIndex]); } @@ -413,6 +417,313 @@ namespace PolyVox m_vecVertices = vecNewVertices; }*/ + void IndexedSurfacePatch::decimate(float fMinDotProductForCollapse) + { + /* + Note for after holiday - This decimation is half working, but we get some undesirable collpses still. The face flip check + should stop these but doesn't quite seem to work. Also, note that before calling this function it is better if + 'generateAveragedFaceNormals(true);' has been called first, as this seems to give better normals for our purposes. + */ + uint32_t noOfEdgesCollapsed = 0; + do + { + //generateAveragedFaceNormals(true); + noOfEdgesCollapsed = performDecimationPass(fMinDotProductForCollapse); + removeDegenerateTris(); + }while(noOfEdgesCollapsed > 0); + + //cout << "Collapsed " << performDecimationPass(fMinDotProductForCollapse) << " edges." << endl; removeDegenerateTris(); + /*cout << "Collapsed " << performDecimationPass(fMinDotProductForCollapse) << " edges." << endl; removeDegenerateTris(); + cout << "Collapsed " << performDecimationPass(fMinDotProductForCollapse) << " edges." << endl; removeDegenerateTris(); + cout << "Collapsed " << performDecimationPass(fMinDotProductForCollapse) << " edges." << endl; removeDegenerateTris(); + cout << "Collapsed " << performDecimationPass(fMinDotProductForCollapse) << " edges." << endl; removeDegenerateTris();*/ + + + //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.*/ + bool IndexedSurfacePatch::isSubset(std::bitset<4> a, std::bitset<4> b) + { + bool result = true; + + for(int ct = 0; ct < 4; ct++) + { + if(a.test(ct)) + { + if(b.test(ct) == false) + { + result = false; + break; + } + } + } + + return result; + } + + uint32_t IndexedSurfacePatch::performDecimationPass(float fMinDotProductForCollapse) + { + /* + Note for after holiday - breaking this function down into sub functions will + likely help identify where the problem is... + */ + + //Do we need a set? Do we actually get duplications? + vector< set > trianglesUsingVertex(m_vecVertices.size()); + for(int ct = 0; ct < m_vecTriangleIndices.size(); ct++) + { + int triangle = ct / 3; + + trianglesUsingVertex[m_vecTriangleIndices[ct]].insert(triangle); + } + + + uint32_t noOfEdgesCollapsed = 0; + + /* + Note for after holiday - Check the use of this vertex mapper, and make + sure that when we access a vertex we go through the mapper if necessary. + */ + vector vertexMapper(m_vecVertices.size()); + vector vertexLocked(m_vecVertices.size()); + + //Initialise the mapper. + for(uint32_t ct = 0; ct < vertexMapper.size(); ct++) + { + vertexMapper[ct] = ct; + } + + for(uint32_t ct = 0; ct < vertexLocked.size(); ct++) + { + vertexLocked[ct] = false; + } + + //It *may* be beneficial to do this randomisation of the order in which we process the triangles + //in order to help the resulting vertices/triangles be more uniformly distributed. As a reminder, + //comment out the shuffle line to see what it does. + vector vecOfTriCts; + for(int triCt = 0; triCt < m_vecTriangleIndices.size() / 3; triCt++) + { + vecOfTriCts.push_back(triCt); + } + random_shuffle(vecOfTriCts.begin(), vecOfTriCts.end()); + + //For each triange + //for(int triCt = 0; triCt < m_vecTriangleIndices.size() / 3; triCt++) + 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)]; + + /*v0 = vertexMapper[v0]; + v1 = vertexMapper[v1];*/ + + //A vertex will be locked if it has already been involved in a collapse this pass. + if(vertexLocked[v0] || vertexLocked[v1]) + { + continue; + } + + //For now, don't collapse vertices on mateial edges... + if(m_vecVertices[v0].isOnMaterialEdge() || m_vecVertices[v1].isOnMaterialEdge()) + { + continue; + } + + //...or those on geometry (region) edges. + if(m_vecVertices[v0].isOnGeometryEdge() || m_vecVertices[v1].isOnGeometryEdge()) + { + continue; + } + + //After holiday, consider using the following line so that 'internal' vertices can collapse onto + //edges (but not vice-versa) and edges can collapse onto corners (but not vice-versa). + //FIXME - Stop corners collapsing onto corners! + /*if(isSubset(m_vecVertices[v0].m_bFlags, m_vecVertices[v1].m_bFlags) == false) + { + 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; + set triangles = trianglesUsingVertex[v0]; + /*set triangles; + std::set_union(trianglesUsingVertex[v0].begin(), trianglesUsingVertex[v0].end(), + trianglesUsingVertex[v1].begin(), trianglesUsingVertex[v1].end(), + std::inserter(triangles, triangles.begin()));*/ + + for(set::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; + } + + int IndexedSurfacePatch::noOfDegenerateTris(void) + { + int count = 0; + for(int triCt = 0; triCt < m_vecTriangleIndices.size();) + { + int v0 = m_vecTriangleIndices[triCt]; + triCt++; + int v1 = m_vecTriangleIndices[triCt]; + triCt++; + int v2 = m_vecTriangleIndices[triCt]; + triCt++; + + if((v0 == v1) || (v1 == v2) || (v2 == v0)) + { + count++; + } + } + return count; + } + + void IndexedSurfacePatch::removeDegenerateTris(void) + { + int noOfNonDegenerate = 0; + int targetCt = 0; + for(int triCt = 0; triCt < m_vecTriangleIndices.size();) + { + int v0 = m_vecTriangleIndices[triCt]; + triCt++; + int v1 = m_vecTriangleIndices[triCt]; + triCt++; + int v2 = m_vecTriangleIndices[triCt]; + triCt++; + + if((v0 != v1) && (v1 != v2) & (v2 != v0)) + { + m_vecTriangleIndices[targetCt] = v0; + targetCt++; + m_vecTriangleIndices[targetCt] = v1; + targetCt++; + m_vecTriangleIndices[targetCt] = v2; + targetCt++; + + noOfNonDegenerate++; + } + } + + m_vecTriangleIndices.resize(noOfNonDegenerate * 3); + } + void IndexedSurfacePatch::makeProgressiveMesh(void) { @@ -425,7 +736,7 @@ namespace PolyVox vec.y = m_vecVertices[vertCt].getPosition().getY(); vec.z = m_vecVertices[vertCt].getPosition().getZ(); - if(m_vecVertices[vertCt].isEdgeVertex() || m_vecVertices[vertCt].m_bIsMaterialEdgeVertex) + if(m_vecVertices[vertCt].isOnEdge()) { vec.fBoundaryCost = 1.0f; } diff --git a/library/PolyVoxCore/source/Mesh.cpp b/library/PolyVoxCore/source/Mesh.cpp new file mode 100644 index 00000000..49fccc63 --- /dev/null +++ b/library/PolyVoxCore/source/Mesh.cpp @@ -0,0 +1,448 @@ +#pragma region License +/******************************************************************************* +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. +*******************************************************************************/ +#pragma endregion + +#include "Mesh.h" + +#include "IndexedSurfacePatch.h" + +#include + +namespace PolyVox +{ + void Mesh::buildFromISP(IndexedSurfacePatch* pIsp) + { + //First we copy the vertices across. + //We also keep track of where each vertex went + std::vector< std::set::iterator > vertexMapper(pIsp->getNoOfVertices()); + + for(int ct = 0; ct < pIsp->getNoOfVertices(); ct++) + { + MeshVertex* pMeshVertex = new MeshVertex; + pMeshVertex->m_vertexData = pIsp->m_vecVertices[ct]; + vertexMapper[ct] = m_vertices.insert(pMeshVertex).first; + } + + //Next, we add each triangle to the mesh + for(int triCt = 0; triCt < pIsp->getNoOfIndices() / 3; triCt++) + { + int index0 = pIsp->m_vecTriangleIndices[triCt * 3]; + int index1 = pIsp->m_vecTriangleIndices[triCt * 3 + 1]; + int index2 = pIsp->m_vecTriangleIndices[triCt * 3 + 2]; + + MeshVertex* meshVertex0 = *(vertexMapper[index0]); + MeshVertex* meshVertex1 = *(vertexMapper[index1]); + MeshVertex* meshVertex2 = *(vertexMapper[index2]); + + MeshEdge* v0v1 = new MeshEdge; + v0v1->m_pSrc = meshVertex0; + v0v1->m_pDest = meshVertex1; + //meshVertex0->m_edges.insert(v0v1); + + MeshEdge* v1v2 = new MeshEdge; + v1v2->m_pSrc = meshVertex1; + v1v2->m_pDest = meshVertex2; + //meshVertex1->m_edges.insert(v1v2); + + MeshEdge* v2v0 = new MeshEdge; + v2v0->m_pSrc = meshVertex2; + v2v0->m_pDest = meshVertex0; + //meshVertex2->m_edges.insert(v2v0); + + v0v1->m_pNextEdge = v1v2; + v1v2->m_pNextEdge = v2v0; + v2v0->m_pNextEdge = v0v1; + + v0v1->m_pPreviousEdge = v2v0; + v1v2->m_pPreviousEdge = v0v1; + v2v0->m_pPreviousEdge = v1v2; + + v0v1->m_pOtherEdge = 0; + v1v2->m_pOtherEdge = 0; + v2v0->m_pOtherEdge = 0; + + m_edges.insert(v0v1); + m_edges.insert(v1v2); + m_edges.insert(v2v0); + + //Create the face + MeshFace* meshFace = new MeshFace; + + v0v1->m_pFace = meshFace; + v1v2->m_pFace = meshFace; + v2v0->m_pFace = meshFace; + + meshFace->m_pEdge = v0v1; //Just use the first edge + + m_faces.insert(meshFace); + + //The the vertices they are used by the face + //meshVertex0->m_faces.insert(meshFace); + //meshVertex1->m_faces.insert(meshFace); + //meshVertex2->m_faces.insert(meshFace); + } + + //Loop over the edges and try to match up any pairs. + /*for(std::set::iterator edgeIter = m_edges.begin(); edgeIter != m_edges.end(); edgeIter++) + { + MeshEdge* pMeshEdge = *edgeIter; + + if(pMeshEdge->m_pOtherEdge == 0) + { + //Let's see if we can find the other edge somewhere. + //Create an example of what we are looking for... + MeshEdge otherEdgeExample; + otherEdgeExample.m_pSrc = pMeshEdge->m_pDest; + otherEdgeExample.m_pDest = pMeshEdge->m_pSrc; + + m_edges.find( + } + }*/ + + /*int counter = 0; + + //Lastly, we loop over the edges and determine all those which should be adjacent + for(std::set::iterator outerIter = m_edges.begin(); outerIter != m_edges.end(); outerIter++) + { + std::cout << counter++ << std::endl; + + for(std::set::iterator innerIter = m_edges.begin(); innerIter != m_edges.end(); innerIter++) + { + MeshEdge* edge0 = *outerIter; + MeshEdge* edge1 = *innerIter; + + if((edge0->m_pSrc == edge1->m_pDest) && (edge0->m_pDest == edge1->m_pSrc)) + { + edge0->m_pOtherEdge = edge1; + edge1->m_pOtherEdge = edge0; + } + } + }*/ + + //Match up the edge pains + matchEdgePairs(); + + //Compute the edge costs + computeEdgeCosts(); + + //Check sanity + isSane(); + } + + void Mesh::matchEdgePairs(void) + { + std::set matchedEdges; + while(m_edges.empty() == false) + { + std::set::iterator firstEdgeIter = m_edges.begin(); + MeshEdge* firstEdgePtr = *firstEdgeIter; + m_edges.erase(firstEdgeIter); + matchedEdges.insert(firstEdgePtr); + + //Attempt to find the opposite edge + for(std::set::iterator edgeIter = m_edges.begin(); edgeIter != m_edges.end(); edgeIter++) + { + MeshEdge* candidate = *edgeIter; + if((firstEdgePtr->m_pSrc == candidate->m_pDest) && (firstEdgePtr->m_pDest == candidate->m_pSrc)) + { + m_edges.erase(edgeIter); + matchedEdges.insert(candidate); + + firstEdgePtr->m_pOtherEdge = candidate; + candidate->m_pOtherEdge = firstEdgePtr; + + break; + } + } + } + + m_edges = matchedEdges; + } + + void Mesh::computeEdgeCosts(void) + { + for(std::set::iterator edgeIter = m_edges.begin(); edgeIter != m_edges.end(); edgeIter++) + { + (*edgeIter)->computeEdgeCost(this); + } + } + + void Mesh::fillISP(IndexedSurfacePatch* pIsp) + { + pIsp->clear(); + + for(std::set::iterator vertItor = m_vertices.begin(); vertItor != m_vertices.end(); vertItor++) + { + MeshVertex* meshVertex = *vertItor; + meshVertex->m_index = pIsp->addVertex(meshVertex->m_vertexData); + } + + for(std::set::iterator faceItor = m_faces.begin(); faceItor != m_faces.end(); faceItor++) + { + MeshFace* meshFace = *faceItor; + MeshVertex* v0 = meshFace->m_pEdge->m_pSrc; + MeshVertex* v1 = meshFace->m_pEdge->m_pNextEdge->m_pSrc; + MeshVertex* v2 = meshFace->m_pEdge->m_pNextEdge->m_pNextEdge->m_pSrc; + + pIsp->addTriangle(v0->m_index, v1->m_index, v2->m_index); + } + + pIsp->m_vecLodRecords.clear(); + LodRecord lodRecord; + lodRecord.beginIndex = 0; + lodRecord.endIndex = pIsp->getNoOfIndices(); + pIsp->m_vecLodRecords.push_back(lodRecord); + } + + void Mesh::removeEdge(MeshEdge* pMeshEdge) + { + //Get the src and dest vertices + MeshVertex* pSrc = pMeshEdge->m_pSrc; + MeshVertex* pDest = pMeshEdge->m_pDest; + + //Get the other half of the edge + MeshEdge* pOtherEdge = pMeshEdge->m_pOtherEdge; + + //Get the faces + MeshFace* pMeshFace = pMeshEdge->m_pFace; + MeshFace* pOtherFace = 0; + if(pOtherEdge != 0) + { + pOtherFace = pOtherEdge->m_pFace; + } + + //Remove the faces (the edges keep pointers to the faces, but those edges will be deleted soon anyway...) + m_faces.erase(pMeshFace); + delete pMeshFace; + pMeshFace = 0; + + if(pOtherFace) + { + m_faces.erase(pOtherFace); + delete pOtherFace; + pOtherFace = 0; + } + + //Erase the edges + m_edges.erase(pMeshEdge); + m_edges.erase(pMeshEdge->m_pNextEdge); + m_edges.erase(pMeshEdge->m_pPreviousEdge); + + /*pMeshEdge->m_pSrc->m_edges.erase(pMeshEdge); + pMeshEdge->m_pNextEdge->m_pSrc->m_edges.erase(pMeshEdge->m_pNextEdge); + pMeshEdge->m_pPreviousEdge->m_pSrc->m_edges.erase(pMeshEdge->m_pPreviousEdge);*/ + + delete pMeshEdge->m_pNextEdge; + pMeshEdge->m_pNextEdge = 0; + delete pMeshEdge->m_pPreviousEdge; + pMeshEdge->m_pPreviousEdge = 0; + delete pMeshEdge; + //pMeshEdge = 0; + + + if(pOtherEdge) + { + m_edges.erase(pOtherEdge); + m_edges.erase(pOtherEdge->m_pNextEdge); + m_edges.erase(pOtherEdge->m_pPreviousEdge); + + /*pOtherEdge->m_pSrc->m_edges.erase(pOtherEdge); + pOtherEdge->m_pNextEdge->m_pSrc->m_edges.erase(pOtherEdge->m_pNextEdge); + pOtherEdge->m_pPreviousEdge->m_pSrc->m_edges.erase(pOtherEdge->m_pPreviousEdge);*/ + + delete pOtherEdge->m_pNextEdge; + pOtherEdge->m_pNextEdge = 0; + delete pOtherEdge->m_pPreviousEdge; + pOtherEdge->m_pPreviousEdge = 0; + delete pOtherEdge; + //pOtherEdge = 0; + } + + //Find the affected edges + /*std::set affectedEdges = edgesStartingatEitherVertex; + for(std::set::iterator edgeIter = edgesStartingatEitherVertex.begin(); edgeIter != edgesStartingatEitherVertex.end(); edgeIter++) + { + affectedEdges.insert((*edgeIter)->m_pNextEdge); + affectedEdges.insert((*edgeIter)->m_pPreviousEdge); + //if((*edgeIter)->m_pOtherEdge != 0) + //{ + // affectedEdges.insert((*edgeIter)->m_pOtherEdge); + //} + }*/ + + //Update the source and destinations + for(std::set::iterator edgeIter = m_edges.begin(); edgeIter != m_edges.end(); edgeIter++) + { + MeshEdge* pEdge = *edgeIter; + if(pEdge->m_pSrc == pSrc) + { + pEdge->m_pSrc = pDest; + } + if(pEdge->m_pDest == pSrc) + { + pEdge->m_pDest = pDest; + } + } + + /*for(std::set::iterator vertexIter = m_vertices.begin(); vertexIter != m_vertices.end(); vertexIter++) + { + (*vertexIter)->m_edges.clear(); + } + + for(std::set::iterator edgeIter = m_edges.begin(); edgeIter != m_edges.end(); edgeIter++) + { + MeshEdge* pEdge = *edgeIter; + pEdge->m_pSrc->m_edges.insert(pEdge); + }*/ + + + /*for(std::set::iterator edgeIter = affectedEdges.begin(); edgeIter != affectedEdges.end(); edgeIter++) + { + MeshEdge* pEdge = *edgeIter; + if(pEdge->m_pSrc == pSrc) + { + pEdge->m_pSrc = pDest; + } + if(pEdge->m_pDest == pSrc) + { + pEdge->m_pDest = pDest; + } + }*/ + + //Deal with the vertices + /*pSrc->m_edges.erase(pMeshEdge); + if(pOtherEdge) + { + pDest->m_edges.erase(pOtherEdge); + }*/ + + //Find the edges starting at either vertex + /*std::set edgesStartingatEitherVertex; + std::set_union(pSrc->m_edges.begin(), pSrc->m_edges.end(), + pDest->m_edges.begin(), pDest->m_edges.end(), + std::inserter(edgesStartingatEitherVertex, edgesStartingatEitherVertex.begin()));*/ + + //Remove the old vertex + //pDest->m_edges = edgesStartingatEitherVertex; + m_vertices.erase(pSrc); + delete pSrc; //Don't set to 0 yet as we will use the value + + //Update any pairings + matchEdgePairs(); + /*for(std::set::iterator outerIter = affectedEdges.begin(); outerIter != affectedEdges.end(); outerIter++) + { + for(std::set::iterator innerIter = affectedEdges.begin(); innerIter != affectedEdges.end(); innerIter++) + { + MeshEdge* edge0 = *outerIter; + MeshEdge* edge1 = *innerIter; + + if((edge0->m_pSrc == edge1->m_pDest) && (edge0->m_pDest == edge1->m_pSrc)) + { + edge0->m_pOtherEdge = edge1; + edge1->m_pOtherEdge = edge0; + } + } + }*/ + + //Recompute the affected edge costs + computeEdgeCosts(); + /*for(std::set::iterator edgeIter = affectedEdges.begin(); edgeIter != affectedEdges.end(); edgeIter++) + { + (*edgeIter)->computeEdgeCost(); + }*/ + } + + void Mesh::decimateAll(void) + { + for(int ct = 0; ct < 100; ct++) + { + std::cout << ct << std::endl; + decimateOne(); + //isSane(); + } + /*int ct = 0; + while(decimateOne()) + { + std::cout << ct++ << std::endl; + }*/ + + //decimateOne(); + } + + bool Mesh::decimateOne(void) + { + for(std::set::iterator edgeIter = m_edges.begin(); edgeIter != m_edges.end(); edgeIter++) + { + /*for(int ct = 0; ct < 30; ct++) + { + edgeIter++; + }*/ + + MeshEdge* pMeshEdge = *edgeIter; + if(pMeshEdge->m_fCost < 1.0) + { + removeEdge(pMeshEdge); + return true; + } + } + + return false; + } + + bool Mesh::isSane(void) + { + for(std::set::iterator vertexIter = m_vertices.begin(); vertexIter != m_vertices.end(); vertexIter++) + { + //This would be eaiser if we just propergated exceptions? + if((*vertexIter)->isSane() == false) + { + std::cout << "SANITY CHECK FAIL: Problem found in vertex set" << std::endl; + return false; + } + } + + for(std::set::iterator faceIter = m_faces.begin(); faceIter != m_faces.end(); faceIter++) + { + //This would be eaiser if we just propergated exceptions? + if((*faceIter)->isSane() == false) + { + std::cout << "SANITY CHECK FAIL: Problem found in face set" << std::endl; + return false; + } + } + + for(std::set::iterator edgeIter = m_edges.begin(); edgeIter != m_edges.end(); edgeIter++) + { + //This would be eaiser if we just propergated exceptions? + if((*edgeIter)->isSane() == false) + { + std::cout << "SANITY CHECK FAIL: Problem found in edge set" << std::endl; + return false; + } + } + + std::cout << "SANITY CHECK SUCCESS" << std::endl; + return true; + } +} \ No newline at end of file diff --git a/library/PolyVoxCore/source/MeshEdge.cpp b/library/PolyVoxCore/source/MeshEdge.cpp new file mode 100644 index 00000000..b6574f06 --- /dev/null +++ b/library/PolyVoxCore/source/MeshEdge.cpp @@ -0,0 +1,158 @@ +#pragma region License +/******************************************************************************* +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. +*******************************************************************************/ +#pragma endregion + +#include "MeshEdge.h" + +#include "Mesh.h" +#include "MeshFace.h" +#include "MeshVertex.h" +#include "SurfaceVertex.h" + +#include + +namespace PolyVox +{ + + bool operator==(const MeshEdge& lhs, const MeshEdge& rhs) + { + return lhs.m_pSrc == rhs.m_pSrc && lhs.m_pDest == rhs.m_pDest; + } + + bool operator<(const MeshEdge& lhs, const MeshEdge& rhs) + { + if(lhs == rhs) + return false; + + if (lhs.m_pSrc < rhs.m_pSrc) + return true; + if (rhs.m_pSrc < lhs.m_pSrc) + return false; + + if (lhs.m_pDest < rhs.m_pDest) + return true; + if (rhs.m_pDest < lhs.m_pDest) + return false; + + return false; + } + + MeshEdge::MeshEdge() + { + m_pOtherEdge = 0; + m_pNextEdge = 0; + m_pPreviousEdge = 0; + + m_pSrc = 0; + m_pDest = 0; + + m_pFace = 0; + + m_fCost = 1000000.0f; + } + + void MeshEdge::computeEdgeCost(Mesh* pParentMesh) + { + SurfaceVertex v0Data = m_pSrc->m_vertexData; + SurfaceVertex v1Data = m_pDest->m_vertexData; + + m_fCost = 1000000.0f; + + //For now, don't collapse vertices on material edges... + if(v0Data.isOnMaterialEdge() || v1Data.isOnMaterialEdge()) + { + return; + } + + //...or those on geometry (region) edges. + if(v0Data.isOnGeometryEdge() || v1Data.isOnGeometryEdge()) + { + return; + } + + //Check the normals are within the threshold. + if(v0Data.getNormal().dot(v1Data.getNormal()) < 0.999) + { + return; + } + + //Test for face flips + for(std::set::iterator faceIter = pParentMesh->m_faces.begin(); faceIter != pParentMesh->m_faces.end(); faceIter++) + { + if((*faceIter)->collapseFlipsFace(this)) + { + return; + } + } + + m_fCost = 0.0f; + return; + } + + bool MeshEdge::isSane(void) + { + if(m_pSrc == 0) + { + std::cout << "SANITY CHECK FAIL: Edge has no source vertex" << std::endl; + return false; + } + + if(m_pDest == 0) + { + std::cout << "SANITY CHECK FAIL: Edge has no destination vertex" << std::endl; + return false; + } + + if(m_pFace == 0) + { + std::cout << "SANITY CHECK FAIL: Edge has no face attached" << std::endl; + return false; + } + + //Check loops + if(this != m_pNextEdge->m_pNextEdge->m_pNextEdge) + { + std::cout << "SANITY CHECK FAIL: Next edge loop is broken" << std::endl; + return false; + } + + if(this != m_pPreviousEdge->m_pPreviousEdge->m_pPreviousEdge) + { + std::cout << "SANITY CHECK FAIL: Previous edge loop is broken" << std::endl; + return false; + } + + //Make sure the other edge points back here + if(m_pOtherEdge != 0) + { + if(this != m_pOtherEdge->m_pOtherEdge) + { + std::cout << "SANITY CHECK FAIL: Other edge exists but does not point back here." << std::endl; + return false; + } + } + + return true; + } +} \ No newline at end of file diff --git a/library/PolyVoxCore/source/MeshFace.cpp b/library/PolyVoxCore/source/MeshFace.cpp new file mode 100644 index 00000000..fbfd10a8 --- /dev/null +++ b/library/PolyVoxCore/source/MeshFace.cpp @@ -0,0 +1,114 @@ +#pragma region License +/******************************************************************************* +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. +*******************************************************************************/ +#pragma endregion + +#include "MeshFace.h" + +#include "MeshEdge.h" +#include "MeshVertex.h" + +#include "Vector.h" + +#include + +namespace PolyVox +{ + MeshFace::MeshFace() + { + m_pEdge = 0; + } + + bool MeshFace::isSane(void) + { + if(m_pEdge == 0) + { + std::cout << "SANITY CHECK FAIL: Face has no edge attached" << std::endl; + return false; + } + + return true; + } + + Vector3DFloat MeshFace::getNormal(void) + { + //Find the three vertices + Vector3DFloat pV0 = m_pEdge->m_pSrc->m_vertexData.getPosition(); + Vector3DFloat pV1 = m_pEdge->m_pNextEdge->m_pSrc->m_vertexData.getPosition(); + Vector3DFloat pV2 = m_pEdge->m_pNextEdge->m_pNextEdge->m_pSrc->m_vertexData.getPosition(); + + Vector3DFloat crossProduct = (pV1 - pV0).cross(pV2 - pV0); + + assert(crossProduct.length() > 0.001); + + crossProduct.normalise(); + + return crossProduct; + } + + bool MeshFace::collapseFlipsFace(MeshEdge* pEdgeToCollapse) + { + MeshVertex* pSrc = pEdgeToCollapse->m_pSrc; + MeshVertex* pDest = pEdgeToCollapse->m_pDest; + + MeshVertex* pV0 = m_pEdge->m_pSrc; + MeshVertex* pV1 = m_pEdge->m_pNextEdge->m_pSrc; + MeshVertex* pV2 = m_pEdge->m_pNextEdge->m_pNextEdge->m_pSrc; + + if(pSrc == pV0) + { + return collapseFlipsFaceImpl(pV1->m_vertexData.getPosition(), pV2->m_vertexData.getPosition(), pSrc->m_vertexData.getPosition(), pDest->m_vertexData.getPosition()); + } + if(pSrc == pV1) + { + return collapseFlipsFaceImpl(pV0->m_vertexData.getPosition(), pV2->m_vertexData.getPosition(), pSrc->m_vertexData.getPosition(), pDest->m_vertexData.getPosition()); + } + if(pSrc == pV2) + { + return collapseFlipsFaceImpl(pV0->m_vertexData.getPosition(), pV1->m_vertexData.getPosition(), pSrc->m_vertexData.getPosition(), pDest->m_vertexData.getPosition()); + } + + return false; + } + + bool MeshFace::collapseFlipsFaceImpl(Vector3DFloat fixed0, Vector3DFloat fixed1, Vector3DFloat oldPos, Vector3DFloat newPos) + { + Vector3DFloat oldCrossProduct = (fixed0 - oldPos).cross(fixed1 - oldPos); + Vector3DFloat newCrossProduct = (fixed0 - newPos).cross(fixed1 - newPos); + + if(oldCrossProduct.length() < 0.001f) + { + return false; + } + + if(newCrossProduct.length() < 0.001f) + { + return false; + } + + oldCrossProduct.normalise(); + newCrossProduct.normalise(); + + return oldCrossProduct.dot(newCrossProduct) < 0.999f; + } +} \ No newline at end of file diff --git a/library/PolyVoxCore/source/MeshVertex.cpp b/library/PolyVoxCore/source/MeshVertex.cpp new file mode 100644 index 00000000..f67c8c64 --- /dev/null +++ b/library/PolyVoxCore/source/MeshVertex.cpp @@ -0,0 +1,45 @@ +#pragma region License +/******************************************************************************* +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. +*******************************************************************************/ +#pragma endregion + +#include "MeshVertex.h" + +namespace PolyVox +{ + MeshVertex::MeshVertex() + { + m_index = -1; + } + + bool MeshVertex::isSane(void) + { + /*if(m_edges.empty()) + { + std::cout << "SANITY CHECK FAIL: Vertex has no edges attached" << std::endl; + return false; + }*/ + + return true; + } +} \ No newline at end of file diff --git a/library/PolyVoxCore/source/SurfaceExtractor.cpp b/library/PolyVoxCore/source/SurfaceExtractor.cpp index 3ae468ab..06710b28 100644 --- a/library/PolyVoxCore/source/SurfaceExtractor.cpp +++ b/library/PolyVoxCore/source/SurfaceExtractor.cpp @@ -623,7 +623,10 @@ namespace PolyVox const Vector3DFloat v3dNormal(v000 > v100 ? 1.0f : -1.0f,0.0,0.0); const uint8_t uMaterial = v000 | v100; //Because one of these is 0, the or operation takes the max. SurfaceVertex surfaceVertex(v3dPosition, v3dNormal, uMaterial); - surfaceVertex.setEdgeVertex(isXEdge || isYEdge || isZEdge); + //surfaceVertex.setOnGeometryEdge(isXEdge || isYEdge || isZEdge); + surfaceVertex.setOnGeometryEdgeX(isXEdge); + surfaceVertex.setOnGeometryEdgeY(isYEdge); + surfaceVertex.setOnGeometryEdgeZ(isZEdge); uint32_t uLastVertexIndex = m_ispCurrent->addVertex(surfaceVertex); m_pCurrentVertexIndicesX[getIndex(uXVolSpace - m_regInputCropped.getLowerCorner().getX(),uYVolSpace - m_regInputCropped.getLowerCorner().getY())] = uLastVertexIndex; } @@ -635,7 +638,10 @@ namespace PolyVox const Vector3DFloat v3dNormal(0.0,v000 > v010 ? 1.0f : -1.0f,0.0); const uint8_t uMaterial = v000 | v010; //Because one of these is 0, the or operation takes the max. SurfaceVertex surfaceVertex(v3dPosition, v3dNormal, uMaterial); - surfaceVertex.setEdgeVertex(isXEdge || isYEdge || isZEdge); + //surfaceVertex.setOnGeometryEdge(isXEdge || isYEdge || isZEdge); + surfaceVertex.setOnGeometryEdgeX(isXEdge); + surfaceVertex.setOnGeometryEdgeY(isYEdge); + surfaceVertex.setOnGeometryEdgeZ(isZEdge); uint32_t uLastVertexIndex = m_ispCurrent->addVertex(surfaceVertex); m_pCurrentVertexIndicesY[getIndex(uXVolSpace - m_regInputCropped.getLowerCorner().getX(),uYVolSpace - m_regInputCropped.getLowerCorner().getY())] = uLastVertexIndex; } @@ -647,7 +653,10 @@ namespace PolyVox const Vector3DFloat v3dNormal(0.0,0.0,v000 > v001 ? 1.0f : -1.0f); const uint8_t uMaterial = v000 | v001; //Because one of these is 0, the or operation takes the max. SurfaceVertex surfaceVertex(v3dPosition, v3dNormal, uMaterial); - surfaceVertex.setEdgeVertex(isXEdge || isYEdge || isZEdge); + //surfaceVertex.setOnGeometryEdge(isXEdge || isYEdge || isZEdge); + surfaceVertex.setOnGeometryEdgeX(isXEdge); + surfaceVertex.setOnGeometryEdgeY(isYEdge); + surfaceVertex.setOnGeometryEdgeZ(isZEdge); uint32_t uLastVertexIndex = m_ispCurrent->addVertex(surfaceVertex); m_pCurrentVertexIndicesZ[getIndex(uXVolSpace - m_regInputCropped.getLowerCorner().getX(),uYVolSpace - m_regInputCropped.getLowerCorner().getY())] = uLastVertexIndex; } diff --git a/library/PolyVoxCore/source/SurfaceVertex.cpp b/library/PolyVoxCore/source/SurfaceVertex.cpp index 6e7bba32..c4dce510 100644 --- a/library/PolyVoxCore/source/SurfaceVertex.cpp +++ b/library/PolyVoxCore/source/SurfaceVertex.cpp @@ -36,7 +36,6 @@ namespace PolyVox SurfaceVertex::SurfaceVertex(Vector3DFloat positionToSet, float materialToSet) :position(positionToSet) ,material(materialToSet) - ,m_bIsMaterialEdgeVertex(false) { } @@ -45,7 +44,6 @@ namespace PolyVox :position(positionToSet) ,normal(normalToSet) ,material(materialToSet) - ,m_bIsMaterialEdgeVertex(false) { } @@ -64,14 +62,34 @@ namespace PolyVox return position; } - bool SurfaceVertex::isEdgeVertex(void) const + bool SurfaceVertex::isOnEdge(void) const { - return m_bIsEdgeVertex; + return (isOnMaterialEdge() || isOnGeometryEdge()); } - void SurfaceVertex::setEdgeVertex(bool isEdgeVertex) + bool SurfaceVertex::isOnMaterialEdge(void) const { - m_bIsEdgeVertex = isEdgeVertex; + return m_bFlags[VF_ON_MATERIAL_EDGE]; + } + + bool SurfaceVertex::isOnGeometryEdge(void) const + { + return m_bFlags[VF_ON_GEOMETRY_EDGE_X] || m_bFlags[VF_ON_GEOMETRY_EDGE_Y] || m_bFlags[VF_ON_GEOMETRY_EDGE_Z]; + } + + bool SurfaceVertex::isOnGeometryEdgeX(void) const + { + return m_bFlags[VF_ON_GEOMETRY_EDGE_X]; + } + + bool SurfaceVertex::isOnGeometryEdgeY(void) const + { + return m_bFlags[VF_ON_GEOMETRY_EDGE_Y]; + } + + bool SurfaceVertex::isOnGeometryEdgeZ(void) const + { + return m_bFlags[VF_ON_GEOMETRY_EDGE_Z]; } void SurfaceVertex::setMaterial(float materialToSet) @@ -84,6 +102,26 @@ namespace PolyVox normal = normalToSet; } + void SurfaceVertex::setOnMaterialEdge(bool bOnMaterialEdge) + { + m_bFlags[VF_ON_MATERIAL_EDGE] = bOnMaterialEdge; + } + + void SurfaceVertex::setOnGeometryEdgeX(bool bOnRegionEdge) + { + m_bFlags[VF_ON_GEOMETRY_EDGE_X] = bOnRegionEdge; + } + + void SurfaceVertex::setOnGeometryEdgeY(bool bOnRegionEdge) + { + m_bFlags[VF_ON_GEOMETRY_EDGE_Y] = bOnRegionEdge; + } + + void SurfaceVertex::setOnGeometryEdgeZ(bool bOnRegionEdge) + { + m_bFlags[VF_ON_GEOMETRY_EDGE_Z] = bOnRegionEdge; + } + void SurfaceVertex::setPosition(const Vector3DFloat& positionToSet) { position = positionToSet;