From 52dd0189d526350e60fe7db0a5b0cae33f081695 Mon Sep 17 00:00:00 2001 From: David Williams Date: Thu, 7 Aug 2014 23:14:57 +0200 Subject: [PATCH 01/36] Simplified/removed multiple versions of functions, as we're now moving to VS2013 which supports default parameters for template functions. --- .../PolyVoxCore/CubicSurfaceExtractor.h | 24 ++----------------- .../MarchingCubesSurfaceExtractor.h | 23 ++---------------- .../PolyVoxCore/PolyVoxForwardDeclarations.h | 3 ++- 3 files changed, 6 insertions(+), 44 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h index 5e059e10..02fe92c4 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h @@ -147,14 +147,7 @@ namespace PolyVox }; public: - // This is a bit ugly - it seems that the C++03 syntax is different from the C++11 syntax? See this thread: http://stackoverflow.com/questions/6076015/typename-outside-of-template - // Long term we should probably come back to this and if the #ifdef is still needed then maybe it should check for C++11 mode instead of MSVC? -#if defined(_MSC_VER) - CubicSurfaceExtractor(VolumeType* volData, Region region, MeshType* result, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = VolumeType::VoxelType(), bool bMergeQuads = true, IsQuadNeeded isQuadNeeded = IsQuadNeeded()); -#else CubicSurfaceExtractor(VolumeType* volData, Region region, MeshType* result, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), bool bMergeQuads = true, IsQuadNeeded isQuadNeeded = IsQuadNeeded()); -#endif - void execute(); @@ -195,8 +188,8 @@ namespace PolyVox typename VolumeType::VoxelType m_tBorderValue; }; - template - Mesh > extractCubicMesh(VolumeType* volData, Region region, WrapMode eWrapMode, typename VolumeType::VoxelType tBorderValue, bool bMergeQuads, IsQuadNeeded isQuadNeeded) + template > + Mesh > extractCubicMesh(VolumeType* volData, Region region, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), bool bMergeQuads = true, IsQuadNeeded isQuadNeeded = IsQuadNeeded()) { typedef Mesh > MeshType; MeshType result; @@ -204,19 +197,6 @@ namespace PolyVox extractor.execute(); return result; } - - template - // This is a bit ugly - it seems that the C++03 syntax is different from the C++11 syntax? See this thread: http://stackoverflow.com/questions/6076015/typename-outside-of-template - // Long term we should probably come back to this and if the #ifdef is still needed then maybe it should check for C++11 mode instead of MSVC? -#if defined(_MSC_VER) - Mesh > extractCubicMesh(VolumeType* volData, Region region, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = VolumeType::VoxelType(), bool bMergeQuads = true) -#else - Mesh > extractCubicMesh(VolumeType* volData, Region region, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), bool bMergeQuads = true) -#endif - { - DefaultIsQuadNeeded isQuadNeeded; - return extractCubicMesh >(volData, region, eWrapMode, tBorderValue, bMergeQuads, isQuadNeeded); - } } #include "PolyVoxCore/CubicSurfaceExtractor.inl" diff --git a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h index 8f73a6cf..c09378eb 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h @@ -151,13 +151,7 @@ namespace PolyVox class MarchingCubesSurfaceExtractor { public: - // This is a bit ugly - it seems that the C++03 syntax is different from the C++11 syntax? See this thread: http://stackoverflow.com/questions/6076015/typename-outside-of-template - // Long term we should probably come back to this and if the #ifdef is still needed then maybe it should check for C++11 mode instead of MSVC? -#if defined(_MSC_VER) - MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, Mesh >* result, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = VolumeType::VoxelType(), Controller controller = Controller()); -#else MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, Mesh >* result, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), Controller controller = Controller()); -#endif void execute(); @@ -324,27 +318,14 @@ namespace PolyVox typename Controller::DensityType m_tThreshold; }; - template< typename VolumeType, typename Controller> - Mesh > extractMarchingCubesMesh(VolumeType* volData, Region region, WrapMode eWrapMode, typename VolumeType::VoxelType tBorderValue, Controller controller) + template< typename VolumeType, typename Controller = DefaultMarchingCubesController > + Mesh > extractMarchingCubesMesh(VolumeType* volData, Region region, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), Controller controller = Controller()) { Mesh > result; MarchingCubesSurfaceExtractor extractor(volData, region, &result, eWrapMode, tBorderValue, controller); extractor.execute(); return result; } - - template< typename VolumeType> - // This is a bit ugly - it seems that the C++03 syntax is different from the C++11 syntax? See this thread: http://stackoverflow.com/questions/6076015/typename-outside-of-template - // Long term we should probably come back to this and if the #ifdef is still needed then maybe it should check for C++11 mode instead of MSVC? -#if defined(_MSC_VER) - Mesh > extractMarchingCubesMesh(VolumeType* volData, Region region, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = VolumeType::VoxelType()) -#else - Mesh > extractMarchingCubesMesh(VolumeType* volData, Region region, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType()) -#endif - { - DefaultMarchingCubesController controller; - return extractMarchingCubesMesh(volData, region, eWrapMode, tBorderValue, controller); - } } #include "PolyVoxCore/MarchingCubesSurfaceExtractor.inl" diff --git a/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h b/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h index f72d5d37..1069d589 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h +++ b/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h @@ -142,7 +142,8 @@ namespace PolyVox //////////////////////////////////////////////////////////////////////////////// // Mesh //////////////////////////////////////////////////////////////////////////////// - template class Mesh; + typedef uint32_t DefaultIndexType; + template class Mesh; //////////////////////////////////////////////////////////////////////////////// // Pager From c5cf71b161b684e96e760a2ec1a050cdad424bf7 Mon Sep 17 00:00:00 2001 From: David Williams Date: Thu, 7 Aug 2014 23:39:56 +0200 Subject: [PATCH 02/36] Extractors now templatised on index type. --- .../PolyVoxCore/CubicSurfaceExtractor.h | 4 ++-- .../MarchingCubesSurfaceExtractor.h | 10 ++++---- .../MarchingCubesSurfaceExtractor.inl | 24 +++++++++---------- .../PolyVoxCore/PolyVoxForwardDeclarations.h | 2 +- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h index 02fe92c4..69fb80f2 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h @@ -188,10 +188,10 @@ namespace PolyVox typename VolumeType::VoxelType m_tBorderValue; }; - template > + template, typename IndexType = DefaultIndexType > Mesh > extractCubicMesh(VolumeType* volData, Region region, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), bool bMergeQuads = true, IsQuadNeeded isQuadNeeded = IsQuadNeeded()) { - typedef Mesh > MeshType; + typedef Mesh, IndexType > MeshType; MeshType result; CubicSurfaceExtractor extractor(volData, region, &result, eWrapMode, tBorderValue, bMergeQuads, isQuadNeeded); extractor.execute(); diff --git a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h index c09378eb..b153f721 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h @@ -147,11 +147,11 @@ namespace PolyVox return result; } - template< typename VolumeType, typename Controller = DefaultMarchingCubesController > + template< typename VolumeType, typename Controller = DefaultMarchingCubesController, typename IndexType = DefaultIndexType > class MarchingCubesSurfaceExtractor { public: - MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, Mesh >* result, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), Controller controller = Controller()); + MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, Mesh, IndexType >* result, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), Controller controller = Controller()); void execute(); @@ -318,11 +318,11 @@ namespace PolyVox typename Controller::DensityType m_tThreshold; }; - template< typename VolumeType, typename Controller = DefaultMarchingCubesController > + template< typename VolumeType, typename Controller = DefaultMarchingCubesController, typename IndexType = DefaultIndexType > Mesh > extractMarchingCubesMesh(VolumeType* volData, Region region, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), Controller controller = Controller()) { - Mesh > result; - MarchingCubesSurfaceExtractor extractor(volData, region, &result, eWrapMode, tBorderValue, controller); + Mesh, IndexType > result; + MarchingCubesSurfaceExtractor extractor(volData, region, &result, eWrapMode, tBorderValue, controller); extractor.execute(); return result; } diff --git a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl index cbbcd9e8..44bd544f 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl @@ -25,8 +25,8 @@ freely, subject to the following restrictions: namespace PolyVox { - template - MarchingCubesSurfaceExtractor::MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, Mesh >* result, WrapMode eWrapMode, typename VolumeType::VoxelType tBorderValue, Controller controller) + template + MarchingCubesSurfaceExtractor::MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, Mesh, IndexType >* result, WrapMode eWrapMode, typename VolumeType::VoxelType tBorderValue, Controller controller) :m_volData(volData) ,m_sampVolume(volData) ,m_meshCurrent(result) @@ -41,8 +41,8 @@ namespace PolyVox m_sampVolume.setWrapMode(eWrapMode, tBorderValue); } - template - void MarchingCubesSurfaceExtractor::execute() + template + void MarchingCubesSurfaceExtractor::execute() { Timer timer; m_meshCurrent->clear(); @@ -129,9 +129,9 @@ namespace PolyVox << "x" << m_regSizeInVoxels.getDepthInVoxels() << ")"); } - template + template template - uint32_t MarchingCubesSurfaceExtractor::computeBitmaskForSlice(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask) + uint32_t MarchingCubesSurfaceExtractor::computeBitmaskForSlice(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask) { m_uNoOfOccupiedCells = 0; @@ -195,9 +195,9 @@ namespace PolyVox return m_uNoOfOccupiedCells; } - template + template template - void MarchingCubesSurfaceExtractor::computeBitmaskForCell(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask, uint32_t uXRegSpace, uint32_t uYRegSpace) + void MarchingCubesSurfaceExtractor::computeBitmaskForCell(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask, uint32_t uXRegSpace, uint32_t uYRegSpace) { uint8_t iCubeIndex = 0; @@ -395,8 +395,8 @@ namespace PolyVox } } - template - void MarchingCubesSurfaceExtractor::generateVerticesForSlice(const Array2DUint8& pCurrentBitmask, + template + void MarchingCubesSurfaceExtractor::generateVerticesForSlice(const Array2DUint8& pCurrentBitmask, Array2DInt32& m_pCurrentVertexIndicesX, Array2DInt32& m_pCurrentVertexIndicesY, Array2DInt32& m_pCurrentVertexIndicesZ) @@ -535,8 +535,8 @@ namespace PolyVox } } - template - void MarchingCubesSurfaceExtractor::generateIndicesForSlice(const Array2DUint8& pPreviousBitmask, + template + void MarchingCubesSurfaceExtractor::generateIndicesForSlice(const Array2DUint8& pPreviousBitmask, const Array2DInt32& m_pPreviousVertexIndicesX, const Array2DInt32& m_pPreviousVertexIndicesY, const Array2DInt32& m_pPreviousVertexIndicesZ, diff --git a/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h b/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h index 1069d589..05c2ecea 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h +++ b/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h @@ -111,7 +111,7 @@ namespace PolyVox //////////////////////////////////////////////////////////////////////////////// // MarchingCubesSurfaceExtractor //////////////////////////////////////////////////////////////////////////////// - template class MarchingCubesSurfaceExtractor; + template class MarchingCubesSurfaceExtractor; //////////////////////////////////////////////////////////////////////////////// // MarchingCubesVertex From 94d1b57b81468a66b9585a14a1f2c6cfd1d07e2c Mon Sep 17 00:00:00 2001 From: David Williams Date: Sat, 9 Aug 2014 09:18:30 +0200 Subject: [PATCH 03/36] Tidying mesh class. --- .../PolyVoxCore/include/PolyVoxCore/Mesh.h | 37 ++++++++++++------- .../PolyVoxCore/include/PolyVoxCore/Mesh.inl | 24 +++++++++--- 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/Mesh.h b/library/PolyVoxCore/include/PolyVoxCore/Mesh.h index 64356785..22eab2aa 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Mesh.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Mesh.h @@ -50,43 +50,54 @@ namespace PolyVox Mesh(); ~Mesh(); - const std::vector& getIndices(void) const; - uint32_t getNoOfIndices(void) const; IndexType getNoOfVertices(void) const; - const std::vector& getVertices(void) const; + const VertexType& getVertex(IndexType index) const; + const std::vector& getVertices(void) const; //Should probably deprecate + + uint32_t getNoOfIndices(void) const; + IndexType getIndex(uint32_t index) const; + const std::vector& getIndices(void) const; //Should probably deprecate + const Vector3DInt32& getOffset(void) const; + //void setNoOfVertices(IndexType noOfVertices); + //void setVertex() + void setOffset(const Vector3DInt32& offset); - void addTriangle(IndexType index0, IndexType index1, IndexType index2); IndexType addVertex(const VertexType& vertex); + void addTriangle(IndexType index0, IndexType index1, IndexType index2); + void clear(void); bool isEmpty(void) const; void removeUnusedVertices(void); Vector3DInt32 m_offset; - public: + private: std::vector m_vecTriangleIndices; std::vector m_vecVertices; }; template - Mesh< Vertex< typename MeshType::VertexType::DataType >, typename MeshType::IndexType > decodeMesh(const MeshType& mesh) + Mesh< Vertex< typename MeshType::VertexType::DataType >, typename MeshType::IndexType > decodeMesh(const MeshType& encodedMesh) { - Mesh< Vertex< typename MeshType::VertexType::DataType >, typename MeshType::IndexType > result; - result.m_vecVertices.resize(mesh.m_vecVertices.size()); + Mesh< Vertex< typename MeshType::VertexType::DataType >, typename MeshType::IndexType > decodedMesh; - for(typename MeshType::IndexType ct = 0; ct < mesh.m_vecVertices.size(); ct++) + for (auto ct = 0; ct < encodedMesh.getNoOfVertices(); ct++) { - result.m_vecVertices[ct] = decodeVertex(mesh.m_vecVertices[ct]); + decodedMesh.addVertex(decodeVertex(encodedMesh.getVertex(ct))); } - result.m_vecTriangleIndices = mesh.m_vecTriangleIndices; + POLYVOX_ASSERT(encodedMesh.getNoOfIndices() % 3 == 0, "The number of indices must always be a multiple of three."); + for (auto ct = 0; ct < encodedMesh.getNoOfIndices(); ct += 3) + { + decodedMesh.addTriangle(encodedMesh.getIndex(ct), encodedMesh.getIndex(ct + 1), encodedMesh.getIndex(ct + 2)); + } - result.m_offset = mesh.m_offset; + decodedMesh.setOffset(encodedMesh.getOffset()); - return result; + return decodedMesh; } } diff --git a/library/PolyVoxCore/include/PolyVoxCore/Mesh.inl b/library/PolyVoxCore/include/PolyVoxCore/Mesh.inl index e34f51ff..afecb00f 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Mesh.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/Mesh.inl @@ -34,9 +34,21 @@ namespace PolyVox } template - const std::vector& Mesh::getIndices(void) const + IndexType Mesh::getNoOfVertices(void) const { - return m_vecTriangleIndices; + return m_vecVertices.size(); + } + + template + const VertexType& Mesh::getVertex(IndexType index) const + { + return m_vecVertices[index]; + } + + template + const std::vector& Mesh::getVertices(void) const + { + return m_vecVertices; } template @@ -46,15 +58,15 @@ namespace PolyVox } template - IndexType Mesh::getNoOfVertices(void) const + IndexType Mesh::getIndex(uint32_t index) const { - return m_vecVertices.size(); + return m_vecTriangleIndices[index]; } template - const std::vector& Mesh::getVertices(void) const + const std::vector& Mesh::getIndices(void) const { - return m_vecVertices; + return m_vecTriangleIndices; } template From 29baac9d6ddea8da312edd5e1c77c87280db53f6 Mon Sep 17 00:00:00 2001 From: David Williams Date: Sat, 9 Aug 2014 09:31:45 +0200 Subject: [PATCH 04/36] Tidying mesh class. --- .../PolyVoxCore/include/PolyVoxCore/Mesh.h | 17 ++++------ .../PolyVoxCore/include/PolyVoxCore/Mesh.inl | 34 +++++++++++++------ 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/Mesh.h b/library/PolyVoxCore/include/PolyVoxCore/Mesh.h index 22eab2aa..2143133f 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Mesh.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Mesh.h @@ -52,17 +52,15 @@ namespace PolyVox IndexType getNoOfVertices(void) const; const VertexType& getVertex(IndexType index) const; - const std::vector& getVertices(void) const; //Should probably deprecate + const VertexType* getRawVertexData(void) const; + POLYVOX_DEPRECATED const std::vector& getVertices(void) const; uint32_t getNoOfIndices(void) const; IndexType getIndex(uint32_t index) const; - const std::vector& getIndices(void) const; //Should probably deprecate + const IndexType* getRawIndexData(void); + POLYVOX_DEPRECATED const std::vector& getIndices(void) const; const Vector3DInt32& getOffset(void) const; - - //void setNoOfVertices(IndexType noOfVertices); - //void setVertex() - void setOffset(const Vector3DInt32& offset); IndexType addVertex(const VertexType& vertex); @@ -70,13 +68,12 @@ namespace PolyVox void clear(void); bool isEmpty(void) const; - void removeUnusedVertices(void); - - Vector3DInt32 m_offset; + void removeUnusedVertices(void); private: - std::vector m_vecTriangleIndices; + std::vector m_vecIndices; std::vector m_vecVertices; + Vector3DInt32 m_offset; }; template diff --git a/library/PolyVoxCore/include/PolyVoxCore/Mesh.inl b/library/PolyVoxCore/include/PolyVoxCore/Mesh.inl index afecb00f..c574687f 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Mesh.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/Mesh.inl @@ -45,6 +45,12 @@ namespace PolyVox return m_vecVertices[index]; } + template + const VertexType* Mesh::getRawVertexData(void) const + { + return &(m_vecVertices[0]); + } + template const std::vector& Mesh::getVertices(void) const { @@ -54,19 +60,25 @@ namespace PolyVox template uint32_t Mesh::getNoOfIndices(void) const { - return m_vecTriangleIndices.size(); + return m_vecIndices.size(); } template IndexType Mesh::getIndex(uint32_t index) const { - return m_vecTriangleIndices[index]; + return m_vecIndices[index]; + } + + template + const IndexType* Mesh::getRawIndexData(void) + { + return &(m_vecIndices[0]); } template const std::vector& Mesh::getIndices(void) const { - return m_vecTriangleIndices; + return m_vecIndices; } template @@ -89,9 +101,9 @@ namespace PolyVox POLYVOX_ASSERT(index1 < m_vecVertices.size(), "Index points at an invalid vertex."); POLYVOX_ASSERT(index2 < m_vecVertices.size(), "Index points at an invalid vertex."); - m_vecTriangleIndices.push_back(index0); - m_vecTriangleIndices.push_back(index1); - m_vecTriangleIndices.push_back(index2); + m_vecIndices.push_back(index0); + m_vecIndices.push_back(index1); + m_vecIndices.push_back(index2); } template @@ -108,7 +120,7 @@ namespace PolyVox void Mesh::clear(void) { m_vecVertices.clear(); - m_vecTriangleIndices.clear(); + m_vecIndices.clear(); } template @@ -123,9 +135,9 @@ namespace PolyVox std::vector isVertexUsed(m_vecVertices.size()); std::fill(isVertexUsed.begin(), isVertexUsed.end(), false); - for(uint32_t triCt = 0; triCt < m_vecTriangleIndices.size(); triCt++) + for(uint32_t triCt = 0; triCt < m_vecIndices.size(); triCt++) { - int v = m_vecTriangleIndices[triCt]; + int v = m_vecIndices[triCt]; isVertexUsed[v] = true; } @@ -143,9 +155,9 @@ namespace PolyVox m_vecVertices.resize(noOfUsedVertices); - for(uint32_t triCt = 0; triCt < m_vecTriangleIndices.size(); triCt++) + for(uint32_t triCt = 0; triCt < m_vecIndices.size(); triCt++) { - m_vecTriangleIndices[triCt] = newPos[m_vecTriangleIndices[triCt]]; + m_vecIndices[triCt] = newPos[m_vecIndices[triCt]]; } } } From eaea74b7f80de5cfc3f925fb911b2918b9d2b29f Mon Sep 17 00:00:00 2001 From: David Williams Date: Sun, 10 Aug 2014 09:18:27 +0200 Subject: [PATCH 05/36] Templatizing marching cubes extractor on mesh type. --- .../MarchingCubesSurfaceExtractor.h | 14 +++++++---- .../MarchingCubesSurfaceExtractor.inl | 24 +++++++++---------- .../PolyVoxCore/PolyVoxForwardDeclarations.h | 2 +- tests/TestSurfaceExtractor.cpp | 3 +-- 4 files changed, 24 insertions(+), 19 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h index b153f721..51f2e485 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h @@ -147,11 +147,11 @@ namespace PolyVox return result; } - template< typename VolumeType, typename Controller = DefaultMarchingCubesController, typename IndexType = DefaultIndexType > + template< typename VolumeType, typename MeshType, typename Controller = DefaultMarchingCubesController > class MarchingCubesSurfaceExtractor { public: - MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, Mesh, IndexType >* result, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), Controller controller = Controller()); + MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, MeshType* result, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), Controller controller = Controller()); void execute(); @@ -318,12 +318,18 @@ namespace PolyVox typename Controller::DensityType m_tThreshold; }; + template< typename VolumeType, typename MeshType, typename Controller = DefaultMarchingCubesController > + void extractMarchingCubesMesh(VolumeType* volData, Region region, MeshType* result, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), Controller controller = Controller()) + { + MarchingCubesSurfaceExtractor extractor(volData, region, result, eWrapMode, tBorderValue, controller); + extractor.execute(); + } + template< typename VolumeType, typename Controller = DefaultMarchingCubesController, typename IndexType = DefaultIndexType > Mesh > extractMarchingCubesMesh(VolumeType* volData, Region region, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), Controller controller = Controller()) { Mesh, IndexType > result; - MarchingCubesSurfaceExtractor extractor(volData, region, &result, eWrapMode, tBorderValue, controller); - extractor.execute(); + extractMarchingCubesMesh(volData, region, &result, eWrapMode, tBorderValue, controller); return result; } } diff --git a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl index 44bd544f..49f8ca0e 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl @@ -25,8 +25,8 @@ freely, subject to the following restrictions: namespace PolyVox { - template - MarchingCubesSurfaceExtractor::MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, Mesh, IndexType >* result, WrapMode eWrapMode, typename VolumeType::VoxelType tBorderValue, Controller controller) + template + MarchingCubesSurfaceExtractor::MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, MeshType* result, WrapMode eWrapMode, typename VolumeType::VoxelType tBorderValue, Controller controller) :m_volData(volData) ,m_sampVolume(volData) ,m_meshCurrent(result) @@ -41,8 +41,8 @@ namespace PolyVox m_sampVolume.setWrapMode(eWrapMode, tBorderValue); } - template - void MarchingCubesSurfaceExtractor::execute() + template + void MarchingCubesSurfaceExtractor::execute() { Timer timer; m_meshCurrent->clear(); @@ -129,9 +129,9 @@ namespace PolyVox << "x" << m_regSizeInVoxels.getDepthInVoxels() << ")"); } - template + template template - uint32_t MarchingCubesSurfaceExtractor::computeBitmaskForSlice(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask) + uint32_t MarchingCubesSurfaceExtractor::computeBitmaskForSlice(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask) { m_uNoOfOccupiedCells = 0; @@ -195,9 +195,9 @@ namespace PolyVox return m_uNoOfOccupiedCells; } - template + template template - void MarchingCubesSurfaceExtractor::computeBitmaskForCell(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask, uint32_t uXRegSpace, uint32_t uYRegSpace) + void MarchingCubesSurfaceExtractor::computeBitmaskForCell(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask, uint32_t uXRegSpace, uint32_t uYRegSpace) { uint8_t iCubeIndex = 0; @@ -395,8 +395,8 @@ namespace PolyVox } } - template - void MarchingCubesSurfaceExtractor::generateVerticesForSlice(const Array2DUint8& pCurrentBitmask, + template + void MarchingCubesSurfaceExtractor::generateVerticesForSlice(const Array2DUint8& pCurrentBitmask, Array2DInt32& m_pCurrentVertexIndicesX, Array2DInt32& m_pCurrentVertexIndicesY, Array2DInt32& m_pCurrentVertexIndicesZ) @@ -535,8 +535,8 @@ namespace PolyVox } } - template - void MarchingCubesSurfaceExtractor::generateIndicesForSlice(const Array2DUint8& pPreviousBitmask, + template + void MarchingCubesSurfaceExtractor::generateIndicesForSlice(const Array2DUint8& pPreviousBitmask, const Array2DInt32& m_pPreviousVertexIndicesX, const Array2DInt32& m_pPreviousVertexIndicesY, const Array2DInt32& m_pPreviousVertexIndicesZ, diff --git a/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h b/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h index 05c2ecea..6139bb90 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h +++ b/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h @@ -111,7 +111,7 @@ namespace PolyVox //////////////////////////////////////////////////////////////////////////////// // MarchingCubesSurfaceExtractor //////////////////////////////////////////////////////////////////////////////// - template class MarchingCubesSurfaceExtractor; + template class MarchingCubesSurfaceExtractor; //////////////////////////////////////////////////////////////////////////////// // MarchingCubesVertex diff --git a/tests/TestSurfaceExtractor.cpp b/tests/TestSurfaceExtractor.cpp index d7be8937..081f6cfc 100644 --- a/tests/TestSurfaceExtractor.cpp +++ b/tests/TestSurfaceExtractor.cpp @@ -153,8 +153,7 @@ void testCustomController(Mesh >& result) } CustomMarchingCubesController controller; - MarchingCubesSurfaceExtractor< SimpleVolume, CustomMarchingCubesController > extractor(&volData, volData.getEnclosingRegion(), &result, WrapModes::Border, 0, controller); - extractor.execute(); + extractMarchingCubesMesh(&volData, volData.getEnclosingRegion(), &result, WrapModes::Border, 0, controller); } void TestSurfaceExtractor::testExecute() From 4b3c8a1648df18aa27d6058ea31810b54b906c1f Mon Sep 17 00:00:00 2001 From: David Williams Date: Mon, 11 Aug 2014 08:46:06 +0200 Subject: [PATCH 06/36] Committing half-finished work on test so that I can work on it from elsewhere. --- .../MarchingCubesSurfaceExtractor.h | 2 ++ tests/TestSurfaceExtractor.cpp | 26 +++++++++++++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h index 51f2e485..c482abdf 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h @@ -318,6 +318,8 @@ namespace PolyVox typename Controller::DensityType m_tThreshold; }; + // We don't provide a default MeshType here. If the user doesn't want to provide a MeshType then it probably makes + // more sense to use the other variaent of this function where the mesh is a return value rather than a parameter. template< typename VolumeType, typename MeshType, typename Controller = DefaultMarchingCubesController > void extractMarchingCubesMesh(VolumeType* volData, Region region, MeshType* result, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), Controller controller = Controller()) { diff --git a/tests/TestSurfaceExtractor.cpp b/tests/TestSurfaceExtractor.cpp index 081f6cfc..4a4e95bc 100644 --- a/tests/TestSurfaceExtractor.cpp +++ b/tests/TestSurfaceExtractor.cpp @@ -100,14 +100,13 @@ void writeMaterialValueToVoxel(int valueToWrite, MaterialDensityPair88& voxel) voxel.setMaterial(valueToWrite); } -// Runs the surface extractor for a given type. template -Mesh > testForType(void) //I think we could avoid specifying this return type by using auto/decltype? +SimpleVolume* createAndFillVolume(void) { const int32_t uVolumeSideLength = 32; //Create empty volume - SimpleVolume volData(Region(Vector3DInt32(0,0,0), Vector3DInt32(uVolumeSideLength-1, uVolumeSideLength-1, uVolumeSideLength-1))); + SimpleVolume* volData = new SimpleVolume(Region(Vector3DInt32(0, 0, 0), Vector3DInt32(uVolumeSideLength - 1, uVolumeSideLength - 1, uVolumeSideLength - 1))); for (int32_t z = 0; z < uVolumeSideLength; z++) { @@ -120,15 +119,25 @@ Mesh > testForType(void) //I think we could avoid writeDensityValueToVoxel(x + y + z, voxelValue); //Two different materials in two halves of the volume writeMaterialValueToVoxel(z > uVolumeSideLength / 2 ? 42 : 79, voxelValue); - volData.setVoxelAt(x, y, z, voxelValue); + volData->setVoxelAt(x, y, z, voxelValue); } } } + return volData; +} + +// Runs the surface extractor for a given type. +template +Mesh > testForType(void) //I think we could avoid specifying this return type by using auto/decltype? +{ + //Create empty volume + SimpleVolume* volData = createAndFillVolume(); + DefaultMarchingCubesController controller; controller.setThreshold(50); - auto result = extractMarchingCubesMesh(&volData, volData.getEnclosingRegion(), WrapModes::Border, VoxelType(), controller); + auto result = extractMarchingCubesMesh(volData, volData->getEnclosingRegion(), WrapModes::Border, VoxelType(), controller); return result; } @@ -223,6 +232,13 @@ void TestSurfaceExtractor::testExecute() QCOMPARE(floatMesh.getNoOfVertices(), uExpectedVertices); QCOMPARE(floatMesh.getNoOfIndices(), uExpectedIndices); QCOMPARE(floatMesh.getVertices()[uMaterialToCheck].data, fExpectedData);*/ + + auto uintVol = createAndFillVolume(); + auto uintMesh = extractMarchingCubesMesh(uintVol, uintVol->getEnclosingRegion()); + QCOMPARE(uintMesh.getNoOfVertices(), uExpectedVertices); + QCOMPARE(uintMesh.getNoOfIndices(), uExpectedIndices); + //QCOMPARE(uintMesh.getVertices()[uMaterialToCheck].data, static_cast(fExpectedData)); + std::cout << uintMesh.getVertices()[uMaterialToCheck].data << std::endl; } QTEST_MAIN(TestSurfaceExtractor) From 11845c1d316a30089174b08a360b945e911e2649 Mon Sep 17 00:00:00 2001 From: David Williams Date: Mon, 11 Aug 2014 15:53:25 +0200 Subject: [PATCH 07/36] Work on tests. --- tests/TestSurfaceExtractor.cpp | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/tests/TestSurfaceExtractor.cpp b/tests/TestSurfaceExtractor.cpp index 4a4e95bc..398e720d 100644 --- a/tests/TestSurfaceExtractor.cpp +++ b/tests/TestSurfaceExtractor.cpp @@ -100,7 +100,7 @@ void writeMaterialValueToVoxel(int valueToWrite, MaterialDensityPair88& voxel) voxel.setMaterial(valueToWrite); } -template +/*template SimpleVolume* createAndFillVolume(void) { const int32_t uVolumeSideLength = 32; @@ -125,19 +125,37 @@ SimpleVolume* createAndFillVolume(void) } return volData; -} +}*/ // Runs the surface extractor for a given type. template Mesh > testForType(void) //I think we could avoid specifying this return type by using auto/decltype? { + const int32_t uVolumeSideLength = 32; + //Create empty volume - SimpleVolume* volData = createAndFillVolume(); + SimpleVolume volData(Region(Vector3DInt32(0, 0, 0), Vector3DInt32(uVolumeSideLength - 1, uVolumeSideLength - 1, uVolumeSideLength - 1))); + + for (int32_t z = 0; z < uVolumeSideLength; z++) + { + for (int32_t y = 0; y < uVolumeSideLength; y++) + { + for (int32_t x = 0; x < uVolumeSideLength; x++) + { + VoxelType voxelValue; + //Create a density field which changes throughout the volume. + writeDensityValueToVoxel(x + y + z, voxelValue); + //Two different materials in two halves of the volume + writeMaterialValueToVoxel(z > uVolumeSideLength / 2 ? 42 : 79, voxelValue); + volData.setVoxelAt(x, y, z, voxelValue); + } + } + } DefaultMarchingCubesController controller; controller.setThreshold(50); - auto result = extractMarchingCubesMesh(volData, volData->getEnclosingRegion(), WrapModes::Border, VoxelType(), controller); + auto result = extractMarchingCubesMesh(&volData, volData.getEnclosingRegion(), WrapModes::Border, VoxelType(), controller); return result; } @@ -233,12 +251,12 @@ void TestSurfaceExtractor::testExecute() QCOMPARE(floatMesh.getNoOfIndices(), uExpectedIndices); QCOMPARE(floatMesh.getVertices()[uMaterialToCheck].data, fExpectedData);*/ - auto uintVol = createAndFillVolume(); + /*auto uintVol = createAndFillVolume(); auto uintMesh = extractMarchingCubesMesh(uintVol, uintVol->getEnclosingRegion()); QCOMPARE(uintMesh.getNoOfVertices(), uExpectedVertices); - QCOMPARE(uintMesh.getNoOfIndices(), uExpectedIndices); + QCOMPARE(uintMesh.getNoOfIndices(), uExpectedIndices);*/ //QCOMPARE(uintMesh.getVertices()[uMaterialToCheck].data, static_cast(fExpectedData)); - std::cout << uintMesh.getVertices()[uMaterialToCheck].data << std::endl; + //std::cout << uintMesh.getVertices()[uMaterialToCheck].data << std::endl; } QTEST_MAIN(TestSurfaceExtractor) From 64ba3f20f84986321d3701d3201ab05c06487acd Mon Sep 17 00:00:00 2001 From: David Williams Date: Mon, 11 Aug 2014 16:20:11 +0200 Subject: [PATCH 08/36] Work on extractor tests. --- tests/TestSurfaceExtractor.cpp | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/tests/TestSurfaceExtractor.cpp b/tests/TestSurfaceExtractor.cpp index 398e720d..c8a88e83 100644 --- a/tests/TestSurfaceExtractor.cpp +++ b/tests/TestSurfaceExtractor.cpp @@ -100,32 +100,30 @@ void writeMaterialValueToVoxel(int valueToWrite, MaterialDensityPair88& voxel) voxel.setMaterial(valueToWrite); } -/*template +template SimpleVolume* createAndFillVolume(void) { - const int32_t uVolumeSideLength = 32; + const int32_t uVolumeSideLength = 64; //Create empty volume SimpleVolume* volData = new SimpleVolume(Region(Vector3DInt32(0, 0, 0), Vector3DInt32(uVolumeSideLength - 1, uVolumeSideLength - 1, uVolumeSideLength - 1))); + // Fill for (int32_t z = 0; z < uVolumeSideLength; z++) { for (int32_t y = 0; y < uVolumeSideLength; y++) { for (int32_t x = 0; x < uVolumeSideLength; x++) { - VoxelType voxelValue; - //Create a density field which changes throughout the volume. - writeDensityValueToVoxel(x + y + z, voxelValue); - //Two different materials in two halves of the volume - writeMaterialValueToVoxel(z > uVolumeSideLength / 2 ? 42 : 79, voxelValue); - volData->setVoxelAt(x, y, z, voxelValue); + // Create a density field which changes throughout the volume. It's + // zero in the lower corner and increasing as the coordinates increase. + volData->setVoxelAt(x, y, z, x + y + z); } } } return volData; -}*/ +} // Runs the surface extractor for a given type. template @@ -251,12 +249,18 @@ void TestSurfaceExtractor::testExecute() QCOMPARE(floatMesh.getNoOfIndices(), uExpectedIndices); QCOMPARE(floatMesh.getVertices()[uMaterialToCheck].data, fExpectedData);*/ - /*auto uintVol = createAndFillVolume(); + auto uintVol = createAndFillVolume(); auto uintMesh = extractMarchingCubesMesh(uintVol, uintVol->getEnclosingRegion()); - QCOMPARE(uintMesh.getNoOfVertices(), uExpectedVertices); - QCOMPARE(uintMesh.getNoOfIndices(), uExpectedIndices);*/ - //QCOMPARE(uintMesh.getVertices()[uMaterialToCheck].data, static_cast(fExpectedData)); - //std::cout << uintMesh.getVertices()[uMaterialToCheck].data << std::endl; + QCOMPARE(uintMesh.getNoOfVertices(), 12096u); // Verifies size of mesh + QCOMPARE(uintMesh.getNoOfIndices(), 35157u); // Verifies size of mesh + QCOMPARE(uintMesh.getIndex(100), uint32_t(44)); // Verifies that we have 32-bit index buffer + + auto intVol = createAndFillVolume(); + CustomMarchingCubesController intCustomController; + auto intMesh = extractMarchingCubesMesh(uintVol, uintVol->getEnclosingRegion(), WrapModes::Border, int8_t(0), intCustomController); + QCOMPARE(intMesh.getNoOfVertices(), 16113u); // Verifies size of mesh + QCOMPARE(intMesh.getNoOfIndices(), 22053u); // Verifies size of mesh + QCOMPARE(intMesh.getIndex(100), uint32_t(26)); // Verifies that we have 32-bit index buffer } QTEST_MAIN(TestSurfaceExtractor) From e2f43ebc5d8583e351f09908a511539cc22f70ee Mon Sep 17 00:00:00 2001 From: David Williams Date: Mon, 11 Aug 2014 23:46:01 +0200 Subject: [PATCH 09/36] More work on tests and surface extraction functions. --- .../MarchingCubesSurfaceExtractor.h | 8 ++++---- .../MarchingCubesSurfaceExtractor.inl | 3 ++- tests/TestSurfaceExtractor.cpp | 18 ++++++++++++------ 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h index c482abdf..3ecf78fb 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h @@ -151,7 +151,7 @@ namespace PolyVox class MarchingCubesSurfaceExtractor { public: - MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, MeshType* result, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), Controller controller = Controller()); + MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), Controller controller = Controller(), MeshType* result = nullptr); void execute(); @@ -321,9 +321,9 @@ namespace PolyVox // We don't provide a default MeshType here. If the user doesn't want to provide a MeshType then it probably makes // more sense to use the other variaent of this function where the mesh is a return value rather than a parameter. template< typename VolumeType, typename MeshType, typename Controller = DefaultMarchingCubesController > - void extractMarchingCubesMesh(VolumeType* volData, Region region, MeshType* result, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), Controller controller = Controller()) + void extractMarchingCubesMesh(VolumeType* volData, Region region, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), Controller controller = Controller(), MeshType* result = nullptr) { - MarchingCubesSurfaceExtractor extractor(volData, region, result, eWrapMode, tBorderValue, controller); + MarchingCubesSurfaceExtractor extractor(volData, region, eWrapMode, tBorderValue, controller, result); extractor.execute(); } @@ -331,7 +331,7 @@ namespace PolyVox Mesh > extractMarchingCubesMesh(VolumeType* volData, Region region, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), Controller controller = Controller()) { Mesh, IndexType > result; - extractMarchingCubesMesh(volData, region, &result, eWrapMode, tBorderValue, controller); + extractMarchingCubesMesh(volData, region, eWrapMode, tBorderValue, controller, &result); return result; } } diff --git a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl index 49f8ca0e..bc212cdc 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl @@ -26,7 +26,7 @@ freely, subject to the following restrictions: namespace PolyVox { template - MarchingCubesSurfaceExtractor::MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, MeshType* result, WrapMode eWrapMode, typename VolumeType::VoxelType tBorderValue, Controller controller) + MarchingCubesSurfaceExtractor::MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, WrapMode eWrapMode, typename VolumeType::VoxelType tBorderValue, Controller controller, MeshType* result = nullptr) :m_volData(volData) ,m_sampVolume(volData) ,m_meshCurrent(result) @@ -34,6 +34,7 @@ namespace PolyVox ,m_controller(controller) ,m_tThreshold(m_controller.getThreshold()) { + POLYVOX_THROW_IF(m_meshCurrent == nullptr, std::invalid_argument, "Provided mesh cannot be null"); //m_regSizeInVoxels.cropTo(m_volData->getEnclosingRegion()); m_regSizeInCells = m_regSizeInVoxels; m_regSizeInCells.setUpperCorner(m_regSizeInCells.getUpperCorner() - Vector3DInt32(1,1,1)); diff --git a/tests/TestSurfaceExtractor.cpp b/tests/TestSurfaceExtractor.cpp index c8a88e83..d15e8fed 100644 --- a/tests/TestSurfaceExtractor.cpp +++ b/tests/TestSurfaceExtractor.cpp @@ -178,7 +178,7 @@ void testCustomController(Mesh >& result) } CustomMarchingCubesController controller; - extractMarchingCubesMesh(&volData, volData.getEnclosingRegion(), &result, WrapModes::Border, 0, controller); + extractMarchingCubesMesh(&volData, volData.getEnclosingRegion(), WrapModes::Border, 0, controller, &result); } void TestSurfaceExtractor::testExecute() @@ -255,12 +255,18 @@ void TestSurfaceExtractor::testExecute() QCOMPARE(uintMesh.getNoOfIndices(), 35157u); // Verifies size of mesh QCOMPARE(uintMesh.getIndex(100), uint32_t(44)); // Verifies that we have 32-bit index buffer + auto floatVol = createAndFillVolume(); + CustomMarchingCubesController floatCustomController; + auto floatMesh = extractMarchingCubesMesh(floatVol, floatVol->getEnclosingRegion(), WrapModes::Border, float(0), floatCustomController); + QCOMPARE(floatMesh.getNoOfVertices(), 16113u); // Verifies size of mesh + QCOMPARE(floatMesh.getNoOfIndices(), 22053u); // Verifies size of mesh + QCOMPARE(floatMesh.getIndex(100), uint32_t(26)); // Verifies that we have 32-bit index buffer + auto intVol = createAndFillVolume(); - CustomMarchingCubesController intCustomController; - auto intMesh = extractMarchingCubesMesh(uintVol, uintVol->getEnclosingRegion(), WrapModes::Border, int8_t(0), intCustomController); - QCOMPARE(intMesh.getNoOfVertices(), 16113u); // Verifies size of mesh - QCOMPARE(intMesh.getNoOfIndices(), 22053u); // Verifies size of mesh - QCOMPARE(intMesh.getIndex(100), uint32_t(26)); // Verifies that we have 32-bit index buffer + auto intMesh = extractMarchingCubesMesh(intVol, intVol->getEnclosingRegion(), WrapModes::Border, int8_t(0), DefaultMarchingCubesController()); + QCOMPARE(intMesh.getNoOfVertices(), 11718u); // Verifies size of mesh + QCOMPARE(intMesh.getNoOfIndices(), 34041u); // Verifies size of mesh + QCOMPARE(intMesh.getIndex(100), uint32_t(29)); // Verifies that we have 32-bit index buffer } QTEST_MAIN(TestSurfaceExtractor) From ccc9a3c158ab45f9c70942db7a057e71dab84935 Mon Sep 17 00:00:00 2001 From: David Williams Date: Tue, 12 Aug 2014 15:53:30 +0200 Subject: [PATCH 10/36] More work on surface extraction tests. --- .../MarchingCubesSurfaceExtractor.h | 10 ++++----- .../PolyVoxCore/include/PolyVoxCore/Mesh.h | 4 ++-- tests/TestSurfaceExtractor.cpp | 21 ++++++++++--------- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h index 3ecf78fb..3c370d5a 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h @@ -90,8 +90,8 @@ namespace PolyVox // floats into two bytes and store them in a single uint16_t // Move from range [-1.0f, 1.0f] to [0.0f, 255.0f] - px = (px + 1.0) * 127.5f; - py = (py + 1.0) * 127.5f; + px = (px + 1.0f) * 127.5f; + py = (py + 1.0f) * 127.5f; // Convert to uints uint16_t resultX = static_cast(px + 0.5f); @@ -300,7 +300,7 @@ namespace PolyVox uint32_t m_uNoOfOccupiedCells; //The surface patch we are currently filling. - Mesh >* m_meshCurrent; + MeshType* m_meshCurrent; //Information about the region we are currently processing Region m_regSizeInVoxels; @@ -327,10 +327,10 @@ namespace PolyVox extractor.execute(); } - template< typename VolumeType, typename Controller = DefaultMarchingCubesController, typename IndexType = DefaultIndexType > + template< typename VolumeType, typename Controller = DefaultMarchingCubesController > Mesh > extractMarchingCubesMesh(VolumeType* volData, Region region, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), Controller controller = Controller()) { - Mesh, IndexType > result; + Mesh, DefaultIndexType > result; extractMarchingCubesMesh(volData, region, eWrapMode, tBorderValue, controller, &result); return result; } diff --git a/library/PolyVoxCore/include/PolyVoxCore/Mesh.h b/library/PolyVoxCore/include/PolyVoxCore/Mesh.h index 2143133f..802b0656 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Mesh.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Mesh.h @@ -81,13 +81,13 @@ namespace PolyVox { Mesh< Vertex< typename MeshType::VertexType::DataType >, typename MeshType::IndexType > decodedMesh; - for (auto ct = 0; ct < encodedMesh.getNoOfVertices(); ct++) + for (MeshType::IndexType ct = 0; ct < encodedMesh.getNoOfVertices(); ct++) { decodedMesh.addVertex(decodeVertex(encodedMesh.getVertex(ct))); } POLYVOX_ASSERT(encodedMesh.getNoOfIndices() % 3 == 0, "The number of indices must always be a multiple of three."); - for (auto ct = 0; ct < encodedMesh.getNoOfIndices(); ct += 3) + for (uint32_t ct = 0; ct < encodedMesh.getNoOfIndices(); ct += 3) { decodedMesh.addTriangle(encodedMesh.getIndex(ct), encodedMesh.getIndex(ct + 1), encodedMesh.getIndex(ct + 2)); } diff --git a/tests/TestSurfaceExtractor.cpp b/tests/TestSurfaceExtractor.cpp index d15e8fed..0c494d93 100644 --- a/tests/TestSurfaceExtractor.cpp +++ b/tests/TestSurfaceExtractor.cpp @@ -251,22 +251,23 @@ void TestSurfaceExtractor::testExecute() auto uintVol = createAndFillVolume(); auto uintMesh = extractMarchingCubesMesh(uintVol, uintVol->getEnclosingRegion()); - QCOMPARE(uintMesh.getNoOfVertices(), 12096u); // Verifies size of mesh - QCOMPARE(uintMesh.getNoOfIndices(), 35157u); // Verifies size of mesh - QCOMPARE(uintMesh.getIndex(100), uint32_t(44)); // Verifies that we have 32-bit index buffer + QCOMPARE(uintMesh.getNoOfVertices(), uint32_t(12096)); // Verifies size of mesh and that we have 32-bit indices + QCOMPARE(uintMesh.getNoOfIndices(), uint32_t(35157)); // Verifies size of mesh + QCOMPARE(uintMesh.getIndex(100), uint32_t(44)); // Verifies that we have 32-bit indices auto floatVol = createAndFillVolume(); CustomMarchingCubesController floatCustomController; auto floatMesh = extractMarchingCubesMesh(floatVol, floatVol->getEnclosingRegion(), WrapModes::Border, float(0), floatCustomController); - QCOMPARE(floatMesh.getNoOfVertices(), 16113u); // Verifies size of mesh - QCOMPARE(floatMesh.getNoOfIndices(), 22053u); // Verifies size of mesh - QCOMPARE(floatMesh.getIndex(100), uint32_t(26)); // Verifies that we have 32-bit index buffer + QCOMPARE(floatMesh.getNoOfVertices(), uint32_t(16113)); // Verifies size of mesh and that we have 32-bit indices + QCOMPARE(floatMesh.getNoOfIndices(), uint32_t(22053)); // Verifies size of mesh + QCOMPARE(floatMesh.getIndex(100), uint32_t(26)); // Verifies that we have 32-bit indices auto intVol = createAndFillVolume(); - auto intMesh = extractMarchingCubesMesh(intVol, intVol->getEnclosingRegion(), WrapModes::Border, int8_t(0), DefaultMarchingCubesController()); - QCOMPARE(intMesh.getNoOfVertices(), 11718u); // Verifies size of mesh - QCOMPARE(intMesh.getNoOfIndices(), 34041u); // Verifies size of mesh - QCOMPARE(intMesh.getIndex(100), uint32_t(29)); // Verifies that we have 32-bit index buffer + Mesh< MarchingCubesVertex< int8_t >, uint16_t > intMesh; + extractMarchingCubesMesh(intVol, intVol->getEnclosingRegion(), WrapModes::Border, int8_t(0), DefaultMarchingCubesController(), &intMesh); + QCOMPARE(intMesh.getNoOfVertices(), uint16_t(11718)); // Verifies size of mesh and that we have 16-bit indices + QCOMPARE(intMesh.getNoOfIndices(), uint32_t(34041)); // Verifies size of mesh + QCOMPARE(intMesh.getIndex(100), uint16_t(29)); // Verifies that we have 16-bit indices } QTEST_MAIN(TestSurfaceExtractor) From 0b5e7071aeb27816f9466c72d3c434c1a47c510b Mon Sep 17 00:00:00 2001 From: David Williams Date: Tue, 12 Aug 2014 15:57:52 +0200 Subject: [PATCH 11/36] Added double test. --- tests/TestSurfaceExtractor.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/TestSurfaceExtractor.cpp b/tests/TestSurfaceExtractor.cpp index 0c494d93..e80f6b78 100644 --- a/tests/TestSurfaceExtractor.cpp +++ b/tests/TestSurfaceExtractor.cpp @@ -268,6 +268,14 @@ void TestSurfaceExtractor::testExecute() QCOMPARE(intMesh.getNoOfVertices(), uint16_t(11718)); // Verifies size of mesh and that we have 16-bit indices QCOMPARE(intMesh.getNoOfIndices(), uint32_t(34041)); // Verifies size of mesh QCOMPARE(intMesh.getIndex(100), uint16_t(29)); // Verifies that we have 16-bit indices + + auto doubleVol = createAndFillVolume(); + CustomMarchingCubesController doubleCustomController; + Mesh< MarchingCubesVertex< double >, uint16_t > doubleMesh; + extractMarchingCubesMesh(doubleVol, doubleVol->getEnclosingRegion(), WrapModes::Border, double(0), doubleCustomController, &doubleMesh); + QCOMPARE(doubleMesh.getNoOfVertices(), uint16_t(16113)); // Verifies size of mesh and that we have 32-bit indices + QCOMPARE(doubleMesh.getNoOfIndices(), uint32_t(22053)); // Verifies size of mesh + QCOMPARE(doubleMesh.getIndex(100), uint16_t(26)); // Verifies that we have 32-bit indices } QTEST_MAIN(TestSurfaceExtractor) From ed4bdf08c47a2e12c0ecbaaa0b66e1ebe30a00f9 Mon Sep 17 00:00:00 2001 From: David Williams Date: Tue, 12 Aug 2014 16:09:56 +0200 Subject: [PATCH 12/36] Added extraction test for MaterialDensityPair. --- tests/TestSurfaceExtractor.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/TestSurfaceExtractor.cpp b/tests/TestSurfaceExtractor.cpp index e80f6b78..db9ac0b2 100644 --- a/tests/TestSurfaceExtractor.cpp +++ b/tests/TestSurfaceExtractor.cpp @@ -117,7 +117,10 @@ SimpleVolume* createAndFillVolume(void) { // Create a density field which changes throughout the volume. It's // zero in the lower corner and increasing as the coordinates increase. - volData->setVoxelAt(x, y, z, x + y + z); + VoxelType voxelValue; + writeDensityValueToVoxel(x + y + z, voxelValue); + writeMaterialValueToVoxel(z > uVolumeSideLength / 2 ? 42 : 79, voxelValue); + volData->setVoxelAt(x, y, z, voxelValue); } } } @@ -276,6 +279,13 @@ void TestSurfaceExtractor::testExecute() QCOMPARE(doubleMesh.getNoOfVertices(), uint16_t(16113)); // Verifies size of mesh and that we have 32-bit indices QCOMPARE(doubleMesh.getNoOfIndices(), uint32_t(22053)); // Verifies size of mesh QCOMPARE(doubleMesh.getIndex(100), uint16_t(26)); // Verifies that we have 32-bit indices + + auto materialVol = createAndFillVolume(); + auto materialMesh = extractMarchingCubesMesh(materialVol, materialVol->getEnclosingRegion()); + QCOMPARE(materialMesh.getNoOfVertices(), uint32_t(12096)); // Verifies size of mesh and that we have 32-bit indices + QCOMPARE(materialMesh.getNoOfIndices(), uint32_t(35157)); // Verifies size of mesh + QCOMPARE(materialMesh.getIndex(100), uint32_t(44)); // Verifies that we have 32-bit indices + QCOMPARE(materialMesh.getVertex(100).data.getMaterial(), uint16_t(79)); } QTEST_MAIN(TestSurfaceExtractor) From d4118a20528af40299d6f3b3a4e9605f548aad50 Mon Sep 17 00:00:00 2001 From: David Williams Date: Tue, 12 Aug 2014 16:53:47 +0200 Subject: [PATCH 13/36] More work on tests. --- .../MarchingCubesSurfaceExtractor.h | 7 + tests/TestSurfaceExtractor.cpp | 149 +++--------------- 2 files changed, 26 insertions(+), 130 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h index 3c370d5a..3d8cba5b 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h @@ -318,6 +318,13 @@ namespace PolyVox typename Controller::DensityType m_tThreshold; }; + // This version of the function performs the extraction into a user-provided mesh rather than allocating a mesh automatically. + // There are a few reasons why this might be useful to more advanced users: + // + // 1. It leaves the user in control of memory allocation and would allow them to implement e.g. a mesh pooling system. + // 2. The user-provided mesh could have a different index type (e.g. 16-bit indices) to reduce memory usage. + // 3. The user could provide a custom mesh class, e.g a thin wrapper around an openGL VBO to allow direct writing into this structure. + // // We don't provide a default MeshType here. If the user doesn't want to provide a MeshType then it probably makes // more sense to use the other variaent of this function where the mesh is a return value rather than a parameter. template< typename VolumeType, typename MeshType, typename Controller = DefaultMarchingCubesController > diff --git a/tests/TestSurfaceExtractor.cpp b/tests/TestSurfaceExtractor.cpp index db9ac0b2..9a5acb21 100644 --- a/tests/TestSurfaceExtractor.cpp +++ b/tests/TestSurfaceExtractor.cpp @@ -52,12 +52,12 @@ public: float convertToMaterial(float /*voxel*/) { - return 1; + return 1.0f; } float blendMaterials(float /*a*/, float /*b*/, float /*weight*/) { - return 1; + return 1.0f; } float getThreshold(void) @@ -75,12 +75,6 @@ void writeDensityValueToVoxel(int valueToWrite, VoxelType& voxel) voxel = valueToWrite; } -template<> -void writeDensityValueToVoxel(int valueToWrite, Density8& voxel) -{ - voxel.setDensity(valueToWrite); -} - template<> void writeDensityValueToVoxel(int valueToWrite, MaterialDensityPair88& voxel) { @@ -128,150 +122,43 @@ SimpleVolume* createAndFillVolume(void) return volData; } -// Runs the surface extractor for a given type. -template -Mesh > testForType(void) //I think we could avoid specifying this return type by using auto/decltype? -{ - const int32_t uVolumeSideLength = 32; - - //Create empty volume - SimpleVolume volData(Region(Vector3DInt32(0, 0, 0), Vector3DInt32(uVolumeSideLength - 1, uVolumeSideLength - 1, uVolumeSideLength - 1))); - - for (int32_t z = 0; z < uVolumeSideLength; z++) - { - for (int32_t y = 0; y < uVolumeSideLength; y++) - { - for (int32_t x = 0; x < uVolumeSideLength; x++) - { - VoxelType voxelValue; - //Create a density field which changes throughout the volume. - writeDensityValueToVoxel(x + y + z, voxelValue); - //Two different materials in two halves of the volume - writeMaterialValueToVoxel(z > uVolumeSideLength / 2 ? 42 : 79, voxelValue); - volData.setVoxelAt(x, y, z, voxelValue); - } - } - } - - DefaultMarchingCubesController controller; - controller.setThreshold(50); - - auto result = extractMarchingCubesMesh(&volData, volData.getEnclosingRegion(), WrapModes::Border, VoxelType(), controller); - - return result; -} - -void testCustomController(Mesh >& result) -{ - const int32_t uVolumeSideLength = 32; - - //Create empty volume - SimpleVolume volData(Region(Vector3DInt32(0,0,0), Vector3DInt32(uVolumeSideLength-1, uVolumeSideLength-1, uVolumeSideLength-1))); - - for (int32_t z = 0; z < uVolumeSideLength; z++) - { - for (int32_t y = 0; y < uVolumeSideLength; y++) - { - for (int32_t x = 0; x < uVolumeSideLength; x++) - { - float voxelValue = x + y + z; - volData.setVoxelAt(x, y, z, voxelValue); - } - } - } - - CustomMarchingCubesController controller; - extractMarchingCubesMesh(&volData, volData.getEnclosingRegion(), WrapModes::Border, 0, controller, &result); -} - void TestSurfaceExtractor::testExecute() { - const static uint32_t uExpectedVertices = 4731; - const static uint32_t uExpectedIndices = 12810; - const static uint32_t uMaterialToCheck = 3000; - const static float fExpectedData = 1.0f; - const static float fNoMaterial = 1.0f; - - Mesh > mesh; - //Run the test for various voxel types. - QBENCHMARK { - mesh = testForType(); - } - QCOMPARE(mesh.getNoOfVertices(), uExpectedVertices); - QCOMPARE(mesh.getNoOfIndices(), uExpectedIndices); - QCOMPARE(mesh.getVertices()[uMaterialToCheck].data, static_cast(fExpectedData)); - - auto mesh1 = testForType(); - QCOMPARE(mesh1.getNoOfVertices(), uExpectedVertices); - QCOMPARE(mesh1.getNoOfIndices(), uExpectedIndices); - QCOMPARE(mesh1.getVertices()[uMaterialToCheck].data, static_cast(fExpectedData)); - - auto mesh2 = testForType(); - QCOMPARE(mesh2.getNoOfVertices(), uExpectedVertices); - QCOMPARE(mesh2.getNoOfIndices(), uExpectedIndices); - QCOMPARE(mesh2.getVertices()[uMaterialToCheck].data, static_cast(fExpectedData)); - - auto mesh3 = testForType(); - QCOMPARE(mesh3.getNoOfVertices(), uExpectedVertices); - QCOMPARE(mesh3.getNoOfIndices(), uExpectedIndices); - QCOMPARE(mesh3.getVertices()[uMaterialToCheck].data, static_cast(fExpectedData)); - - auto mesh4 = testForType(); - QCOMPARE(mesh4.getNoOfVertices(), uExpectedVertices); - QCOMPARE(mesh4.getNoOfIndices(), uExpectedIndices); - QCOMPARE(mesh4.getVertices()[uMaterialToCheck].data, static_cast(fExpectedData)); - - auto mesh5 = testForType(); - QCOMPARE(mesh5.getNoOfVertices(), uExpectedVertices); - QCOMPARE(mesh5.getNoOfIndices(), uExpectedIndices); - QCOMPARE(mesh5.getVertices()[uMaterialToCheck].data, static_cast(fExpectedData)); - - auto mesh6 = testForType(); - QCOMPARE(mesh6.getNoOfVertices(), uExpectedVertices); - QCOMPARE(mesh6.getNoOfIndices(), uExpectedIndices); - QCOMPARE(mesh6.getVertices()[uMaterialToCheck].data, static_cast(fExpectedData)); - - auto mesh7 = testForType(); - QCOMPARE(mesh7.getNoOfVertices(), uExpectedVertices); - QCOMPARE(mesh7.getNoOfIndices(), uExpectedIndices); - QCOMPARE(mesh7.getVertices()[uMaterialToCheck].data, static_cast(fExpectedData)); - - auto mesh8 = testForType(); - QCOMPARE(mesh8.getNoOfVertices(), uExpectedVertices); - QCOMPARE(mesh8.getNoOfIndices(), uExpectedIndices); - QCOMPARE(mesh8.getVertices()[uMaterialToCheck].data, static_cast(fExpectedData)); - - auto mesh9 = testForType(); - QCOMPARE(mesh9.getNoOfVertices(), uExpectedVertices); - QCOMPARE(mesh9.getNoOfIndices(), uExpectedIndices); - //QCOMPARE(mesh9.getVertices()[uMaterialToCheck].data, fExpectedMaterial); - - //Test whether the CustomSurfaceExtractor works. - /*testCustomController(floatMesh); - QCOMPARE(floatMesh.getNoOfVertices(), uExpectedVertices); - QCOMPARE(floatMesh.getNoOfIndices(), uExpectedIndices); - QCOMPARE(floatMesh.getVertices()[uMaterialToCheck].data, fExpectedData);*/ + // These tests apply the Marching Cubes surface extractor to volumes of various voxel types. In addition we sometimes make use of custom controllers + // and user-provided meshes to make sure these various combinations work as expected. + // + // It is also noted that the number of indices and vertices is varying quite significantly based on the voxel type. This seems unexpected, but could + // be explained if some overflow is occuring when writing data into the volume, causing volumes of different voxel types to have different distributions. + // Of course, the use of a custom controller will also make a significant diference, but this probably does need investigating further in the future. + // This basic test just uses the default controller and automatically generates a mesh of the appropriate type. auto uintVol = createAndFillVolume(); auto uintMesh = extractMarchingCubesMesh(uintVol, uintVol->getEnclosingRegion()); QCOMPARE(uintMesh.getNoOfVertices(), uint32_t(12096)); // Verifies size of mesh and that we have 32-bit indices QCOMPARE(uintMesh.getNoOfIndices(), uint32_t(35157)); // Verifies size of mesh QCOMPARE(uintMesh.getIndex(100), uint32_t(44)); // Verifies that we have 32-bit indices + QCOMPARE(uintMesh.getVertex(100).data, uint8_t(1)); // Not really meaningful for a primative type + // This test makes use of a custom controller auto floatVol = createAndFillVolume(); CustomMarchingCubesController floatCustomController; auto floatMesh = extractMarchingCubesMesh(floatVol, floatVol->getEnclosingRegion(), WrapModes::Border, float(0), floatCustomController); QCOMPARE(floatMesh.getNoOfVertices(), uint32_t(16113)); // Verifies size of mesh and that we have 32-bit indices QCOMPARE(floatMesh.getNoOfIndices(), uint32_t(22053)); // Verifies size of mesh QCOMPARE(floatMesh.getIndex(100), uint32_t(26)); // Verifies that we have 32-bit indices + QCOMPARE(floatMesh.getVertex(100).data, float(1.0f)); // Not really meaningful for a primative type + // This test makes use of a user provided mesh. It uses the default controller, but we have to explicitly provide this because C++ won't let us + // use a default for the second-to-last parameter but noot use a default for the last parameter. auto intVol = createAndFillVolume(); Mesh< MarchingCubesVertex< int8_t >, uint16_t > intMesh; extractMarchingCubesMesh(intVol, intVol->getEnclosingRegion(), WrapModes::Border, int8_t(0), DefaultMarchingCubesController(), &intMesh); QCOMPARE(intMesh.getNoOfVertices(), uint16_t(11718)); // Verifies size of mesh and that we have 16-bit indices QCOMPARE(intMesh.getNoOfIndices(), uint32_t(34041)); // Verifies size of mesh QCOMPARE(intMesh.getIndex(100), uint16_t(29)); // Verifies that we have 16-bit indices + QCOMPARE(intMesh.getVertex(100).data, int8_t(1)); // Not really meaningful for a primative type + // This test makes use of a user-provided mesh and also a custom controller. auto doubleVol = createAndFillVolume(); CustomMarchingCubesController doubleCustomController; Mesh< MarchingCubesVertex< double >, uint16_t > doubleMesh; @@ -279,13 +166,15 @@ void TestSurfaceExtractor::testExecute() QCOMPARE(doubleMesh.getNoOfVertices(), uint16_t(16113)); // Verifies size of mesh and that we have 32-bit indices QCOMPARE(doubleMesh.getNoOfIndices(), uint32_t(22053)); // Verifies size of mesh QCOMPARE(doubleMesh.getIndex(100), uint16_t(26)); // Verifies that we have 32-bit indices + QCOMPARE(doubleMesh.getVertex(100).data, double(1.0f)); // Not really meaningful for a primative type + // This test ensures the extractor works on a non-primitive voxel type. auto materialVol = createAndFillVolume(); auto materialMesh = extractMarchingCubesMesh(materialVol, materialVol->getEnclosingRegion()); QCOMPARE(materialMesh.getNoOfVertices(), uint32_t(12096)); // Verifies size of mesh and that we have 32-bit indices QCOMPARE(materialMesh.getNoOfIndices(), uint32_t(35157)); // Verifies size of mesh QCOMPARE(materialMesh.getIndex(100), uint32_t(44)); // Verifies that we have 32-bit indices - QCOMPARE(materialMesh.getVertex(100).data.getMaterial(), uint16_t(79)); + QCOMPARE(materialMesh.getVertex(100).data.getMaterial(), uint16_t(79)); // Verify the data attached to the vertex } QTEST_MAIN(TestSurfaceExtractor) From f1ed0c1be20bfcb5be7a232eaa99351e9b1fc321 Mon Sep 17 00:00:00 2001 From: David Williams Date: Wed, 13 Aug 2014 15:59:13 +0200 Subject: [PATCH 14/36] Examples now support both 16 and 32 bit indices. --- examples/common/OpenGLWidget.cpp | 2 +- examples/common/OpenGLWidget.h | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/examples/common/OpenGLWidget.cpp b/examples/common/OpenGLWidget.cpp index 439264bf..44747f2b 100644 --- a/examples/common/OpenGLWidget.cpp +++ b/examples/common/OpenGLWidget.cpp @@ -161,7 +161,7 @@ void OpenGLWidget::paintGL() // Bind the vertex array for the current mesh glBindVertexArray(meshData.vertexArrayObject); // Draw the mesh - glDrawElements(GL_TRIANGLES, meshData.noOfIndices, GL_UNSIGNED_INT, 0); + glDrawElements(GL_TRIANGLES, meshData.noOfIndices, meshData.indexType, 0); // Unbind the vertex array. glBindVertexArray(0); } diff --git a/examples/common/OpenGLWidget.h b/examples/common/OpenGLWidget.h index 1bbbc1e4..fb9a7459 100644 --- a/examples/common/OpenGLWidget.h +++ b/examples/common/OpenGLWidget.h @@ -36,6 +36,7 @@ distribution. struct OpenGLMeshData { GLuint noOfIndices; + GLenum indexType; GLuint indexBuffer; GLuint vertexBuffer; GLuint vertexArrayObject; @@ -53,8 +54,8 @@ public: OpenGLWidget(QWidget *parent); // Convert a PolyVox mesh to OpenGL index/vertex buffers. Inlined because it's templatised. - template - void addMesh(const PolyVox::Mesh< PolyVox::Vertex< DataType > >& surfaceMesh, const PolyVox::Vector3DInt32& translation = PolyVox::Vector3DInt32(0, 0, 0), float scale = 1.0f) + template + void addMesh(const MeshType& surfaceMesh, const PolyVox::Vector3DInt32& translation = PolyVox::Vector3DInt32(0, 0, 0), float scale = 1.0f) { // Convienient access to the vertices and indices const auto& vecIndices = surfaceMesh.getIndices(); @@ -71,29 +72,29 @@ public: // The GL_ARRAY_BUFFER will contain the list of vertex positions glGenBuffers(1, &(meshData.vertexBuffer)); glBindBuffer(GL_ARRAY_BUFFER, meshData.vertexBuffer); - glBufferData(GL_ARRAY_BUFFER, vecVertices.size() * sizeof(PolyVox::Vertex< DataType >), vecVertices.data(), GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, vecVertices.size() * sizeof(MeshType::VertexType), vecVertices.data(), GL_STATIC_DRAW); // and GL_ELEMENT_ARRAY_BUFFER will contain the indices glGenBuffers(1, &(meshData.indexBuffer)); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, meshData.indexBuffer); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, vecIndices.size() * sizeof(uint32_t), vecIndices.data(), GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, vecIndices.size() * sizeof(MeshType::IndexType), vecIndices.data(), GL_STATIC_DRAW); // Every surface extractor outputs valid positions for the vertices, so tell OpenGL how these are laid out glEnableVertexAttribArray(0); // Attrib '0' is the vertex positions - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(PolyVox::Vertex< DataType >), (GLvoid*)(offsetof(PolyVox::Vertex< DataType >, position))); //take the first 3 floats from every sizeof(decltype(vecVertices)::value_type) + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(MeshType::VertexType), (GLvoid*)(offsetof(MeshType::VertexType, position))); //take the first 3 floats from every sizeof(decltype(vecVertices)::value_type) // Some surface extractors also generate normals, so tell OpenGL how these are laid out. If a surface extractor // does not generate normals then nonsense values are written into the buffer here and sghould be ignored by the // shader. This is mostly just to simplify this example code - in a real application you will know whether your // chosen surface extractor generates normals and can skip uploading them if not. glEnableVertexAttribArray(1); // Attrib '1' is the vertex normals. - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(PolyVox::Vertex< DataType >), (GLvoid*)(offsetof(PolyVox::Vertex< DataType >, normal))); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(MeshType::VertexType), (GLvoid*)(offsetof(MeshType::VertexType, normal))); // Finally a surface extractor will probably output additional data. This is highly application dependant. For this example code // we're just uploading it as a set of bytes which we can read individually, but real code will want to do something specialised here. glEnableVertexAttribArray(2); //We're talking about shader attribute '2' - GLint size = (std::min)(sizeof(DataType), size_t(4)); // Can't upload more that 4 components (vec4 is GLSL's biggest type) - glVertexAttribIPointer(2, size, GL_UNSIGNED_BYTE, sizeof(PolyVox::Vertex< DataType >), (GLvoid*)(offsetof(PolyVox::Vertex< DataType >, data))); + GLint size = (std::min)(sizeof(MeshType::VertexType::DataType), size_t(4)); // Can't upload more that 4 components (vec4 is GLSL's biggest type) + glVertexAttribIPointer(2, size, GL_UNSIGNED_BYTE, sizeof(MeshType::VertexType), (GLvoid*)(offsetof(MeshType::VertexType, data))); // We're done uploading and can now unbind. glBindVertexArray(0); @@ -103,6 +104,9 @@ public: meshData.translation = QVector3D(translation.getX(), translation.getY(), translation.getZ()); meshData.scale = scale; + // Set 16 or 32-bit index buffer size. + meshData.indexType = sizeof(MeshType::IndexType) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; + // Now add the mesh to the list of meshes to render. addMeshData(meshData); } From 93fdd36f8fa3560d50ec8381a8aa16f8419895b4 Mon Sep 17 00:00:00 2001 From: David Williams Date: Thu, 14 Aug 2014 15:57:10 +0200 Subject: [PATCH 15/36] Work on interface to 'extractMarchingCubesMesh' and tests. --- .../PolyVoxCore/MarchingCubesSurfaceExtractor.h | 13 +++++++------ .../PolyVoxCore/MarchingCubesSurfaceExtractor.inl | 2 +- tests/TestSurfaceExtractor.cpp | 6 +++--- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h index 3d8cba5b..7a5ddccd 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h @@ -147,11 +147,11 @@ namespace PolyVox return result; } - template< typename VolumeType, typename MeshType, typename Controller = DefaultMarchingCubesController > + template< typename VolumeType, typename MeshType, typename Controller> class MarchingCubesSurfaceExtractor { public: - MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), Controller controller = Controller(), MeshType* result = nullptr); + MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, MeshType* result, Controller controller, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType()); void execute(); @@ -328,17 +328,18 @@ namespace PolyVox // We don't provide a default MeshType here. If the user doesn't want to provide a MeshType then it probably makes // more sense to use the other variaent of this function where the mesh is a return value rather than a parameter. template< typename VolumeType, typename MeshType, typename Controller = DefaultMarchingCubesController > - void extractMarchingCubesMesh(VolumeType* volData, Region region, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), Controller controller = Controller(), MeshType* result = nullptr) + void extractMarchingCubesCustomMesh(VolumeType* volData, Region region, MeshType* result, Controller controller = Controller(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType()) { - MarchingCubesSurfaceExtractor extractor(volData, region, eWrapMode, tBorderValue, controller, result); + result->getNoOfVertices(); + MarchingCubesSurfaceExtractor extractor(volData, region, result, controller, eWrapMode, tBorderValue); extractor.execute(); } template< typename VolumeType, typename Controller = DefaultMarchingCubesController > - Mesh > extractMarchingCubesMesh(VolumeType* volData, Region region, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), Controller controller = Controller()) + Mesh > extractMarchingCubesMesh(VolumeType* volData, Region region, Controller controller = Controller(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType()) { Mesh, DefaultIndexType > result; - extractMarchingCubesMesh(volData, region, eWrapMode, tBorderValue, controller, &result); + extractMarchingCubesCustomMesh, DefaultIndexType > >(volData, region, &result, controller, eWrapMode, tBorderValue); return result; } } diff --git a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl index bc212cdc..bf24116e 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl @@ -26,7 +26,7 @@ freely, subject to the following restrictions: namespace PolyVox { template - MarchingCubesSurfaceExtractor::MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, WrapMode eWrapMode, typename VolumeType::VoxelType tBorderValue, Controller controller, MeshType* result = nullptr) + MarchingCubesSurfaceExtractor::MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, MeshType* result, Controller controller, WrapMode eWrapMode, typename VolumeType::VoxelType tBorderValue) :m_volData(volData) ,m_sampVolume(volData) ,m_meshCurrent(result) diff --git a/tests/TestSurfaceExtractor.cpp b/tests/TestSurfaceExtractor.cpp index 9a5acb21..8ab93f35 100644 --- a/tests/TestSurfaceExtractor.cpp +++ b/tests/TestSurfaceExtractor.cpp @@ -142,7 +142,7 @@ void TestSurfaceExtractor::testExecute() // This test makes use of a custom controller auto floatVol = createAndFillVolume(); CustomMarchingCubesController floatCustomController; - auto floatMesh = extractMarchingCubesMesh(floatVol, floatVol->getEnclosingRegion(), WrapModes::Border, float(0), floatCustomController); + auto floatMesh = extractMarchingCubesMesh(floatVol, floatVol->getEnclosingRegion(), floatCustomController, WrapModes::Border, float(0)); QCOMPARE(floatMesh.getNoOfVertices(), uint32_t(16113)); // Verifies size of mesh and that we have 32-bit indices QCOMPARE(floatMesh.getNoOfIndices(), uint32_t(22053)); // Verifies size of mesh QCOMPARE(floatMesh.getIndex(100), uint32_t(26)); // Verifies that we have 32-bit indices @@ -152,7 +152,7 @@ void TestSurfaceExtractor::testExecute() // use a default for the second-to-last parameter but noot use a default for the last parameter. auto intVol = createAndFillVolume(); Mesh< MarchingCubesVertex< int8_t >, uint16_t > intMesh; - extractMarchingCubesMesh(intVol, intVol->getEnclosingRegion(), WrapModes::Border, int8_t(0), DefaultMarchingCubesController(), &intMesh); + extractMarchingCubesCustomMesh(intVol, intVol->getEnclosingRegion(), &intMesh); QCOMPARE(intMesh.getNoOfVertices(), uint16_t(11718)); // Verifies size of mesh and that we have 16-bit indices QCOMPARE(intMesh.getNoOfIndices(), uint32_t(34041)); // Verifies size of mesh QCOMPARE(intMesh.getIndex(100), uint16_t(29)); // Verifies that we have 16-bit indices @@ -162,7 +162,7 @@ void TestSurfaceExtractor::testExecute() auto doubleVol = createAndFillVolume(); CustomMarchingCubesController doubleCustomController; Mesh< MarchingCubesVertex< double >, uint16_t > doubleMesh; - extractMarchingCubesMesh(doubleVol, doubleVol->getEnclosingRegion(), WrapModes::Border, double(0), doubleCustomController, &doubleMesh); + extractMarchingCubesCustomMesh(doubleVol, doubleVol->getEnclosingRegion(), &doubleMesh, doubleCustomController, WrapModes::Border, double(0)); QCOMPARE(doubleMesh.getNoOfVertices(), uint16_t(16113)); // Verifies size of mesh and that we have 32-bit indices QCOMPARE(doubleMesh.getNoOfIndices(), uint32_t(22053)); // Verifies size of mesh QCOMPARE(doubleMesh.getIndex(100), uint16_t(26)); // Verifies that we have 32-bit indices From aa4591d206035e6312169dc55f0ff9d4c3ef4768 Mon Sep 17 00:00:00 2001 From: David Williams Date: Thu, 14 Aug 2014 16:34:29 +0200 Subject: [PATCH 16/36] Added comments. --- .../include/PolyVoxCore/MarchingCubesSurfaceExtractor.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h index 7a5ddccd..5e820c5c 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h @@ -326,15 +326,19 @@ namespace PolyVox // 3. The user could provide a custom mesh class, e.g a thin wrapper around an openGL VBO to allow direct writing into this structure. // // We don't provide a default MeshType here. If the user doesn't want to provide a MeshType then it probably makes - // more sense to use the other variaent of this function where the mesh is a return value rather than a parameter. + // more sense to use the other variant of this function where the mesh is a return value rather than a parameter. + // + // Note: This function is called 'extractMarchingCubesCustomMesh' rather than 'extractMarchingCubesMesh' to avoid ambiguity when only three parameters + // are provided (would the third parameter be a controller or a mesh?). It seems this can be fixed by using enable_if/static_assert to emulate concepts, + // but this is relatively complex and I haven't done it yet. Could always add it later as another overload. template< typename VolumeType, typename MeshType, typename Controller = DefaultMarchingCubesController > void extractMarchingCubesCustomMesh(VolumeType* volData, Region region, MeshType* result, Controller controller = Controller(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType()) { - result->getNoOfVertices(); MarchingCubesSurfaceExtractor extractor(volData, region, result, controller, eWrapMode, tBorderValue); extractor.execute(); } + // This version of the function is easier to use - it automatically creates and returns a mesh of the appropriate type so the user doesn't have to worry about it. template< typename VolumeType, typename Controller = DefaultMarchingCubesController > Mesh > extractMarchingCubesMesh(VolumeType* volData, Region region, Controller controller = Controller(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType()) { From 0ccc268b1618af41738c43e1b0349fafd50181d2 Mon Sep 17 00:00:00 2001 From: David Williams Date: Sat, 16 Aug 2014 09:55:08 +0200 Subject: [PATCH 17/36] Avoided ambiguous functions through the (possibly too clever) use of SFINAE. --- .../include/PolyVoxCore/MarchingCubesSurfaceExtractor.h | 9 ++++++--- tests/TestSurfaceExtractor.cpp | 6 +++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h index 5e820c5c..4db8218c 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h @@ -331,19 +331,22 @@ namespace PolyVox // Note: This function is called 'extractMarchingCubesCustomMesh' rather than 'extractMarchingCubesMesh' to avoid ambiguity when only three parameters // are provided (would the third parameter be a controller or a mesh?). It seems this can be fixed by using enable_if/static_assert to emulate concepts, // but this is relatively complex and I haven't done it yet. Could always add it later as another overload. + // + // Note: the last parameter is a dummy, which is used to avoid ambiguities between which version of the function to call by using SFINAE. template< typename VolumeType, typename MeshType, typename Controller = DefaultMarchingCubesController > - void extractMarchingCubesCustomMesh(VolumeType* volData, Region region, MeshType* result, Controller controller = Controller(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType()) + void extractMarchingCubesMesh(VolumeType* volData, Region region, MeshType* result, Controller controller = Controller(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), typename MeshType::VertexType doNotUseThisParameter = typename MeshType::VertexType()) { MarchingCubesSurfaceExtractor extractor(volData, region, result, controller, eWrapMode, tBorderValue); extractor.execute(); } // This version of the function is easier to use - it automatically creates and returns a mesh of the appropriate type so the user doesn't have to worry about it. + // Note: the last parameter is a dummy, which is used to avoid ambiguities between which version of the function to call by using SFINAE. template< typename VolumeType, typename Controller = DefaultMarchingCubesController > - Mesh > extractMarchingCubesMesh(VolumeType* volData, Region region, Controller controller = Controller(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType()) + Mesh > extractMarchingCubesMesh(VolumeType* volData, Region region, Controller controller = Controller(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), typename Controller::DensityType doNotUseThisParameter = typename Controller::DensityType()) { Mesh, DefaultIndexType > result; - extractMarchingCubesCustomMesh, DefaultIndexType > >(volData, region, &result, controller, eWrapMode, tBorderValue); + extractMarchingCubesMesh, DefaultIndexType > >(volData, region, &result, controller, eWrapMode, tBorderValue); return result; } } diff --git a/tests/TestSurfaceExtractor.cpp b/tests/TestSurfaceExtractor.cpp index 8ab93f35..6c665372 100644 --- a/tests/TestSurfaceExtractor.cpp +++ b/tests/TestSurfaceExtractor.cpp @@ -142,7 +142,7 @@ void TestSurfaceExtractor::testExecute() // This test makes use of a custom controller auto floatVol = createAndFillVolume(); CustomMarchingCubesController floatCustomController; - auto floatMesh = extractMarchingCubesMesh(floatVol, floatVol->getEnclosingRegion(), floatCustomController, WrapModes::Border, float(0)); + auto floatMesh = extractMarchingCubesMesh(floatVol, floatVol->getEnclosingRegion(), floatCustomController); QCOMPARE(floatMesh.getNoOfVertices(), uint32_t(16113)); // Verifies size of mesh and that we have 32-bit indices QCOMPARE(floatMesh.getNoOfIndices(), uint32_t(22053)); // Verifies size of mesh QCOMPARE(floatMesh.getIndex(100), uint32_t(26)); // Verifies that we have 32-bit indices @@ -152,7 +152,7 @@ void TestSurfaceExtractor::testExecute() // use a default for the second-to-last parameter but noot use a default for the last parameter. auto intVol = createAndFillVolume(); Mesh< MarchingCubesVertex< int8_t >, uint16_t > intMesh; - extractMarchingCubesCustomMesh(intVol, intVol->getEnclosingRegion(), &intMesh); + extractMarchingCubesMesh(intVol, intVol->getEnclosingRegion(), &intMesh); QCOMPARE(intMesh.getNoOfVertices(), uint16_t(11718)); // Verifies size of mesh and that we have 16-bit indices QCOMPARE(intMesh.getNoOfIndices(), uint32_t(34041)); // Verifies size of mesh QCOMPARE(intMesh.getIndex(100), uint16_t(29)); // Verifies that we have 16-bit indices @@ -162,7 +162,7 @@ void TestSurfaceExtractor::testExecute() auto doubleVol = createAndFillVolume(); CustomMarchingCubesController doubleCustomController; Mesh< MarchingCubesVertex< double >, uint16_t > doubleMesh; - extractMarchingCubesCustomMesh(doubleVol, doubleVol->getEnclosingRegion(), &doubleMesh, doubleCustomController, WrapModes::Border, double(0)); + extractMarchingCubesMesh(doubleVol, doubleVol->getEnclosingRegion(), &doubleMesh, doubleCustomController); QCOMPARE(doubleMesh.getNoOfVertices(), uint16_t(16113)); // Verifies size of mesh and that we have 32-bit indices QCOMPARE(doubleMesh.getNoOfIndices(), uint32_t(22053)); // Verifies size of mesh QCOMPARE(doubleMesh.getIndex(100), uint16_t(26)); // Verifies that we have 32-bit indices From 6c30f34089d20a95ef6513160bda5b41063bce2c Mon Sep 17 00:00:00 2001 From: David Williams Date: Sat, 16 Aug 2014 16:24:52 +0200 Subject: [PATCH 18/36] Renamed 'Controller' to 'ControllerType' when used as a template parameter. --- .../MarchingCubesSurfaceExtractor.h | 18 +++++++------- .../MarchingCubesSurfaceExtractor.inl | 24 +++++++++---------- .../PolyVoxCore/PolyVoxForwardDeclarations.h | 2 +- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h index 4db8218c..61909a41 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h @@ -147,11 +147,11 @@ namespace PolyVox return result; } - template< typename VolumeType, typename MeshType, typename Controller> + template< typename VolumeType, typename MeshType, typename ControllerType> class MarchingCubesSurfaceExtractor { public: - MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, MeshType* result, Controller controller, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType()); + MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, MeshType* result, ControllerType controller, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType()); void execute(); @@ -312,10 +312,10 @@ namespace PolyVox Region m_regSliceCurrent; //Used to convert arbitrary voxel types in densities and materials. - Controller m_controller; + ControllerType m_controller; //Our threshold value - typename Controller::DensityType m_tThreshold; + typename ControllerType::DensityType m_tThreshold; }; // This version of the function performs the extraction into a user-provided mesh rather than allocating a mesh automatically. @@ -333,17 +333,17 @@ namespace PolyVox // but this is relatively complex and I haven't done it yet. Could always add it later as another overload. // // Note: the last parameter is a dummy, which is used to avoid ambiguities between which version of the function to call by using SFINAE. - template< typename VolumeType, typename MeshType, typename Controller = DefaultMarchingCubesController > - void extractMarchingCubesMesh(VolumeType* volData, Region region, MeshType* result, Controller controller = Controller(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), typename MeshType::VertexType doNotUseThisParameter = typename MeshType::VertexType()) + template< typename VolumeType, typename MeshType, typename ControllerType = DefaultMarchingCubesController > + void extractMarchingCubesMesh(VolumeType* volData, Region region, MeshType* result, ControllerType controller = ControllerType(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), typename MeshType::VertexType doNotUseThisParameter = typename MeshType::VertexType()) { - MarchingCubesSurfaceExtractor extractor(volData, region, result, controller, eWrapMode, tBorderValue); + MarchingCubesSurfaceExtractor extractor(volData, region, result, controller, eWrapMode, tBorderValue); extractor.execute(); } // This version of the function is easier to use - it automatically creates and returns a mesh of the appropriate type so the user doesn't have to worry about it. // Note: the last parameter is a dummy, which is used to avoid ambiguities between which version of the function to call by using SFINAE. - template< typename VolumeType, typename Controller = DefaultMarchingCubesController > - Mesh > extractMarchingCubesMesh(VolumeType* volData, Region region, Controller controller = Controller(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), typename Controller::DensityType doNotUseThisParameter = typename Controller::DensityType()) + template< typename VolumeType, typename ControllerType = DefaultMarchingCubesController > + Mesh > extractMarchingCubesMesh(VolumeType* volData, Region region, ControllerType controller = ControllerType(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), typename ControllerType::DensityType doNotUseThisParameter = typename ControllerType::DensityType()) { Mesh, DefaultIndexType > result; extractMarchingCubesMesh, DefaultIndexType > >(volData, region, &result, controller, eWrapMode, tBorderValue); diff --git a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl index bf24116e..fd226560 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl @@ -25,8 +25,8 @@ freely, subject to the following restrictions: namespace PolyVox { - template - MarchingCubesSurfaceExtractor::MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, MeshType* result, Controller controller, WrapMode eWrapMode, typename VolumeType::VoxelType tBorderValue) + template + MarchingCubesSurfaceExtractor::MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, MeshType* result, ControllerType controller, WrapMode eWrapMode, typename VolumeType::VoxelType tBorderValue) :m_volData(volData) ,m_sampVolume(volData) ,m_meshCurrent(result) @@ -42,8 +42,8 @@ namespace PolyVox m_sampVolume.setWrapMode(eWrapMode, tBorderValue); } - template - void MarchingCubesSurfaceExtractor::execute() + template + void MarchingCubesSurfaceExtractor::execute() { Timer timer; m_meshCurrent->clear(); @@ -130,9 +130,9 @@ namespace PolyVox << "x" << m_regSizeInVoxels.getDepthInVoxels() << ")"); } - template + template template - uint32_t MarchingCubesSurfaceExtractor::computeBitmaskForSlice(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask) + uint32_t MarchingCubesSurfaceExtractor::computeBitmaskForSlice(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask) { m_uNoOfOccupiedCells = 0; @@ -196,9 +196,9 @@ namespace PolyVox return m_uNoOfOccupiedCells; } - template + template template - void MarchingCubesSurfaceExtractor::computeBitmaskForCell(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask, uint32_t uXRegSpace, uint32_t uYRegSpace) + void MarchingCubesSurfaceExtractor::computeBitmaskForCell(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask, uint32_t uXRegSpace, uint32_t uYRegSpace) { uint8_t iCubeIndex = 0; @@ -396,8 +396,8 @@ namespace PolyVox } } - template - void MarchingCubesSurfaceExtractor::generateVerticesForSlice(const Array2DUint8& pCurrentBitmask, + template + void MarchingCubesSurfaceExtractor::generateVerticesForSlice(const Array2DUint8& pCurrentBitmask, Array2DInt32& m_pCurrentVertexIndicesX, Array2DInt32& m_pCurrentVertexIndicesY, Array2DInt32& m_pCurrentVertexIndicesZ) @@ -536,8 +536,8 @@ namespace PolyVox } } - template - void MarchingCubesSurfaceExtractor::generateIndicesForSlice(const Array2DUint8& pPreviousBitmask, + template + void MarchingCubesSurfaceExtractor::generateIndicesForSlice(const Array2DUint8& pPreviousBitmask, const Array2DInt32& m_pPreviousVertexIndicesX, const Array2DInt32& m_pPreviousVertexIndicesY, const Array2DInt32& m_pPreviousVertexIndicesZ, diff --git a/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h b/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h index 6139bb90..0b401a98 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h +++ b/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h @@ -111,7 +111,7 @@ namespace PolyVox //////////////////////////////////////////////////////////////////////////////// // MarchingCubesSurfaceExtractor //////////////////////////////////////////////////////////////////////////////// - template class MarchingCubesSurfaceExtractor; + template class MarchingCubesSurfaceExtractor; //////////////////////////////////////////////////////////////////////////////// // MarchingCubesVertex From 1f5c0743643df8526f4d0b5a9eed103f052d6185 Mon Sep 17 00:00:00 2001 From: David Williams Date: Sat, 16 Aug 2014 16:38:32 +0200 Subject: [PATCH 19/36] Bringing extractCubicMesh into line with extractMarchingCubesMesh. --- .../include/PolyVoxCore/CubicSurfaceExtractor.h | 15 ++++++++++----- .../PolyVoxCore/MarchingCubesSurfaceExtractor.h | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h index 69fb80f2..4996d79d 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h @@ -188,13 +188,18 @@ namespace PolyVox typename VolumeType::VoxelType m_tBorderValue; }; - template, typename IndexType = DefaultIndexType > + template > + void extractCubicMesh(VolumeType* volData, Region region, MeshType* result, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), bool bMergeQuads = true, IsQuadNeeded isQuadNeeded = IsQuadNeeded()) + { + CubicSurfaceExtractor extractor(volData, region, result, eWrapMode, tBorderValue, bMergeQuads, isQuadNeeded); + extractor.execute(); + } + + template > Mesh > extractCubicMesh(VolumeType* volData, Region region, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), bool bMergeQuads = true, IsQuadNeeded isQuadNeeded = IsQuadNeeded()) { - typedef Mesh, IndexType > MeshType; - MeshType result; - CubicSurfaceExtractor extractor(volData, region, &result, eWrapMode, tBorderValue, bMergeQuads, isQuadNeeded); - extractor.execute(); + Mesh< CubicVertex > result; + extractCubicMesh(volData, region, &result, eWrapMode, tBorderValue, bMergeQuads, isQuadNeeded); return result; } } diff --git a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h index 61909a41..3439e003 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h @@ -345,7 +345,7 @@ namespace PolyVox template< typename VolumeType, typename ControllerType = DefaultMarchingCubesController > Mesh > extractMarchingCubesMesh(VolumeType* volData, Region region, ControllerType controller = ControllerType(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), typename ControllerType::DensityType doNotUseThisParameter = typename ControllerType::DensityType()) { - Mesh, DefaultIndexType > result; + Mesh > result; extractMarchingCubesMesh, DefaultIndexType > >(volData, region, &result, controller, eWrapMode, tBorderValue); return result; } From 5c30aa20f90db3d7b75ffc3c1c5798c4059293c3 Mon Sep 17 00:00:00 2001 From: David Williams Date: Sun, 17 Aug 2014 15:45:23 +0200 Subject: [PATCH 20/36] Working on new tests for extractCubicSurface(). --- tests/TestCubicSurfaceExtractor.cpp | 43 +++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/TestCubicSurfaceExtractor.cpp b/tests/TestCubicSurfaceExtractor.cpp index 8476b7f8..77fffbb1 100644 --- a/tests/TestCubicSurfaceExtractor.cpp +++ b/tests/TestCubicSurfaceExtractor.cpp @@ -121,6 +121,43 @@ uint32_t testForType(void) return uTotalVertices + uTotalIndices; } +// Runs the surface extractor for a given type. +template +SimpleVolume* createAndFillVolumeWithNoise(VoxelType maxVoxelValue) +{ + const int32_t uVolumeSideLength = 64; + + //Create empty volume + SimpleVolume* volData = new SimpleVolume(Region(Vector3DInt32(0, 0, 0), Vector3DInt32(uVolumeSideLength - 1, uVolumeSideLength - 1, uVolumeSideLength - 1)), 32); + + srand(12345); + + //Fill the volume with data + for (int32_t z = 0; z < uVolumeSideLength; z++) + { + for (int32_t y = 0; y < uVolumeSideLength; y++) + { + for (int32_t x = 0; x < uVolumeSideLength; x++) + { + if (maxVoxelValue == 0) + { + // This test case is currently only dealing with unsigned voxel, so if + // the max requested voxel value is zero then every voxel should be zero + volData->setVoxelAt(x, y, z, VoxelType(0)); + } + else + { + // Otherwise we write random voxel values between zero and the requested maximum + int voxelValue = rand() % (maxVoxelValue + 1); + volData->setVoxelAt(x, y, z, static_cast(voxelValue)); + } + } + } + } + + return volData; +} + void TestCubicSurfaceExtractor::testExecute() { /*const static uint32_t uExpectedVertices = 6624; @@ -184,6 +221,12 @@ void TestCubicSurfaceExtractor::testExecute() result = testForType(); } QCOMPARE(result, uExpectedSumOfVerticesAndIndices); + + auto volData = createAndFillVolumeWithNoise(2); + auto mesh = extractCubicMesh(volData, volData->getEnclosingRegion()); + QCOMPARE(mesh.getNoOfVertices(), uint32_t(451651)); + QCOMPARE(mesh.getNoOfIndices(), uint32_t(1715934)); + } QTEST_MAIN(TestCubicSurfaceExtractor) From ba6685289de8a14373995599099269a93c8f59c2 Mon Sep 17 00:00:00 2001 From: David Williams Date: Sun, 17 Aug 2014 23:43:59 +0200 Subject: [PATCH 21/36] Work on cubic extractor interface. --- .../PolyVoxCore/CubicSurfaceExtractor.h | 10 ++-- .../PolyVoxCore/CubicSurfaceExtractor.inl | 2 +- tests/TestCubicSurfaceExtractor.cpp | 60 +++++++++++++++---- 3 files changed, 54 insertions(+), 18 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h index 4996d79d..586f85d3 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h @@ -147,7 +147,7 @@ namespace PolyVox }; public: - CubicSurfaceExtractor(VolumeType* volData, Region region, MeshType* result, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), bool bMergeQuads = true, IsQuadNeeded isQuadNeeded = IsQuadNeeded()); + CubicSurfaceExtractor(VolumeType* volData, Region region, MeshType* result, IsQuadNeeded isQuadNeeded = IsQuadNeeded(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), bool bMergeQuads = true); void execute(); @@ -189,17 +189,17 @@ namespace PolyVox }; template > - void extractCubicMesh(VolumeType* volData, Region region, MeshType* result, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), bool bMergeQuads = true, IsQuadNeeded isQuadNeeded = IsQuadNeeded()) + void extractCubicProvidedMesh(VolumeType* volData, Region region, MeshType* result, IsQuadNeeded isQuadNeeded = IsQuadNeeded(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), bool bMergeQuads = true) { - CubicSurfaceExtractor extractor(volData, region, result, eWrapMode, tBorderValue, bMergeQuads, isQuadNeeded); + CubicSurfaceExtractor extractor(volData, region, result, isQuadNeeded, eWrapMode, tBorderValue, bMergeQuads); extractor.execute(); } template > - Mesh > extractCubicMesh(VolumeType* volData, Region region, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), bool bMergeQuads = true, IsQuadNeeded isQuadNeeded = IsQuadNeeded()) + Mesh > extractCubicMesh(VolumeType* volData, Region region, IsQuadNeeded isQuadNeeded = IsQuadNeeded(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), bool bMergeQuads = true) { Mesh< CubicVertex > result; - extractCubicMesh(volData, region, &result, eWrapMode, tBorderValue, bMergeQuads, isQuadNeeded); + extractCubicProvidedMesh(volData, region, &result, isQuadNeeded, eWrapMode, tBorderValue, bMergeQuads); return result; } } diff --git a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.inl b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.inl index 6ebec5ef..edf8ea7a 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.inl @@ -36,7 +36,7 @@ namespace PolyVox const uint32_t CubicSurfaceExtractor::MaxVerticesPerPosition = 8; template - CubicSurfaceExtractor::CubicSurfaceExtractor(VolumeType* volData, Region region, MeshType* result, WrapMode eWrapMode, typename VolumeType::VoxelType tBorderValue, bool bMergeQuads, IsQuadNeeded isQuadNeeded) + CubicSurfaceExtractor::CubicSurfaceExtractor(VolumeType* volData, Region region, MeshType* result, IsQuadNeeded isQuadNeeded, WrapMode eWrapMode, typename VolumeType::VoxelType tBorderValue, bool bMergeQuads) :m_volData(volData) ,m_regSizeInVoxels(region) ,m_meshCurrent(result) diff --git a/tests/TestCubicSurfaceExtractor.cpp b/tests/TestCubicSurfaceExtractor.cpp index 77fffbb1..9f8027bf 100644 --- a/tests/TestCubicSurfaceExtractor.cpp +++ b/tests/TestCubicSurfaceExtractor.cpp @@ -33,6 +33,26 @@ freely, subject to the following restrictions: using namespace PolyVox; +template +class CustomIsQuadNeeded +{ +public: + typedef _VoxelType VoxelType; + + bool operator()(VoxelType back, VoxelType front, VoxelType& materialToUse) + { + if ((back > 0) && (front == 0)) + { + materialToUse = static_cast(back); + return true; + } + else + { + return false; + } + } +}; + // These 'writeDensityValueToVoxel' functions provide a unified interface for writting densities to primative and class voxel types. // They are conceptually the inverse of the 'convertToDensity' function used by the MarchingCubesSurfaceExtractor. They probably shouldn't be part @@ -123,12 +143,12 @@ uint32_t testForType(void) // Runs the surface extractor for a given type. template -SimpleVolume* createAndFillVolumeWithNoise(VoxelType maxVoxelValue) +SimpleVolume* createAndFillVolumeWithNoise(VoxelType minValue, VoxelType maxValue) { - const int32_t uVolumeSideLength = 64; + const int32_t uVolumeSideLength = 32; //Create empty volume - SimpleVolume* volData = new SimpleVolume(Region(Vector3DInt32(0, 0, 0), Vector3DInt32(uVolumeSideLength - 1, uVolumeSideLength - 1, uVolumeSideLength - 1)), 32); + SimpleVolume* volData = new SimpleVolume(Region(Vector3DInt32(0, 0, 0), Vector3DInt32(uVolumeSideLength - 1, uVolumeSideLength - 1, uVolumeSideLength - 1)), 16); srand(12345); @@ -139,16 +159,15 @@ SimpleVolume* createAndFillVolumeWithNoise(VoxelType maxVoxelValue) { for (int32_t x = 0; x < uVolumeSideLength; x++) { - if (maxVoxelValue == 0) + if (minValue == maxValue) { - // This test case is currently only dealing with unsigned voxel, so if - // the max requested voxel value is zero then every voxel should be zero - volData->setVoxelAt(x, y, z, VoxelType(0)); + // In this case we are filling the whole volume with a single value. + volData->setVoxelAt(x, y, z, minValue); } else { // Otherwise we write random voxel values between zero and the requested maximum - int voxelValue = rand() % (maxVoxelValue + 1); + int voxelValue = (rand() % (maxValue - minValue + 1)) + minValue; volData->setVoxelAt(x, y, z, static_cast(voxelValue)); } } @@ -222,10 +241,27 @@ void TestCubicSurfaceExtractor::testExecute() } QCOMPARE(result, uExpectedSumOfVerticesAndIndices); - auto volData = createAndFillVolumeWithNoise(2); - auto mesh = extractCubicMesh(volData, volData->getEnclosingRegion()); - QCOMPARE(mesh.getNoOfVertices(), uint32_t(451651)); - QCOMPARE(mesh.getNoOfIndices(), uint32_t(1715934)); + auto uint8Vol = createAndFillVolumeWithNoise(0, 2); + auto uint8Mesh = extractCubicMesh(uint8Vol, uint8Vol->getEnclosingRegion()); + QCOMPARE(uint8Mesh.getNoOfVertices(), uint32_t(57687)); + QCOMPARE(uint8Mesh.getNoOfIndices(), uint32_t(216234)); + + auto int8Vol = createAndFillVolumeWithNoise(0, 2); + auto int8Mesh = extractCubicMesh(int8Vol, int8Vol->getEnclosingRegion(), DefaultIsQuadNeeded()); + QCOMPARE(int8Mesh.getNoOfVertices(), uint32_t(57687)); + QCOMPARE(int8Mesh.getNoOfIndices(), uint32_t(216234)); + + auto uint32Vol = createAndFillVolumeWithNoise(0, 2); + Mesh< CubicVertex< uint32_t >, uint16_t > uint32Mesh; + extractCubicProvidedMesh(uint32Vol, uint32Vol->getEnclosingRegion(), &uint32Mesh); + QCOMPARE(uint32Mesh.getNoOfVertices(), uint16_t(57687)); + QCOMPARE(uint32Mesh.getNoOfIndices(), uint32_t(216234)); + + auto int32Vol = createAndFillVolumeWithNoise(0, 2); + Mesh< CubicVertex< int32_t >, uint16_t > int32Mesh; + extractCubicProvidedMesh(int32Vol, int32Vol->getEnclosingRegion(), &int32Mesh, DefaultIsQuadNeeded()); + QCOMPARE(int32Mesh.getNoOfVertices(), uint16_t(57687)); + QCOMPARE(int32Mesh.getNoOfIndices(), uint32_t(216234)); } From 3c82652f46be56f4a52b9d0d8c3cf7a6c7b1a370 Mon Sep 17 00:00:00 2001 From: David Williams Date: Mon, 18 Aug 2014 15:38:18 +0200 Subject: [PATCH 22/36] Removed the use of clever SFINAE tricks to resolve ambiguous functions. It was proving complex with the cubic surface extractor, so we remove it from the marching cubes version as well. For now, we just rename the ambiguous functions differently to avoid the ambiguity but could come back to this in the future. --- .../PolyVoxCore/MarchingCubesSurfaceExtractor.h | 10 +++------- tests/TestSurfaceExtractor.cpp | 4 ++-- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h index 3439e003..5faab2f3 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h @@ -331,22 +331,18 @@ namespace PolyVox // Note: This function is called 'extractMarchingCubesCustomMesh' rather than 'extractMarchingCubesMesh' to avoid ambiguity when only three parameters // are provided (would the third parameter be a controller or a mesh?). It seems this can be fixed by using enable_if/static_assert to emulate concepts, // but this is relatively complex and I haven't done it yet. Could always add it later as another overload. - // - // Note: the last parameter is a dummy, which is used to avoid ambiguities between which version of the function to call by using SFINAE. template< typename VolumeType, typename MeshType, typename ControllerType = DefaultMarchingCubesController > - void extractMarchingCubesMesh(VolumeType* volData, Region region, MeshType* result, ControllerType controller = ControllerType(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), typename MeshType::VertexType doNotUseThisParameter = typename MeshType::VertexType()) + void extractMarchingCubesMeshCustom(VolumeType* volData, Region region, MeshType* result, ControllerType controller = ControllerType(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType()) { MarchingCubesSurfaceExtractor extractor(volData, region, result, controller, eWrapMode, tBorderValue); extractor.execute(); } - // This version of the function is easier to use - it automatically creates and returns a mesh of the appropriate type so the user doesn't have to worry about it. - // Note: the last parameter is a dummy, which is used to avoid ambiguities between which version of the function to call by using SFINAE. template< typename VolumeType, typename ControllerType = DefaultMarchingCubesController > - Mesh > extractMarchingCubesMesh(VolumeType* volData, Region region, ControllerType controller = ControllerType(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), typename ControllerType::DensityType doNotUseThisParameter = typename ControllerType::DensityType()) + Mesh > extractMarchingCubesMesh(VolumeType* volData, Region region, ControllerType controller = ControllerType(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType()) { Mesh > result; - extractMarchingCubesMesh, DefaultIndexType > >(volData, region, &result, controller, eWrapMode, tBorderValue); + extractMarchingCubesMeshCustom, DefaultIndexType > >(volData, region, &result, controller, eWrapMode, tBorderValue); return result; } } diff --git a/tests/TestSurfaceExtractor.cpp b/tests/TestSurfaceExtractor.cpp index 6c665372..7c0e6ddb 100644 --- a/tests/TestSurfaceExtractor.cpp +++ b/tests/TestSurfaceExtractor.cpp @@ -152,7 +152,7 @@ void TestSurfaceExtractor::testExecute() // use a default for the second-to-last parameter but noot use a default for the last parameter. auto intVol = createAndFillVolume(); Mesh< MarchingCubesVertex< int8_t >, uint16_t > intMesh; - extractMarchingCubesMesh(intVol, intVol->getEnclosingRegion(), &intMesh); + extractMarchingCubesMeshCustom(intVol, intVol->getEnclosingRegion(), &intMesh); QCOMPARE(intMesh.getNoOfVertices(), uint16_t(11718)); // Verifies size of mesh and that we have 16-bit indices QCOMPARE(intMesh.getNoOfIndices(), uint32_t(34041)); // Verifies size of mesh QCOMPARE(intMesh.getIndex(100), uint16_t(29)); // Verifies that we have 16-bit indices @@ -162,7 +162,7 @@ void TestSurfaceExtractor::testExecute() auto doubleVol = createAndFillVolume(); CustomMarchingCubesController doubleCustomController; Mesh< MarchingCubesVertex< double >, uint16_t > doubleMesh; - extractMarchingCubesMesh(doubleVol, doubleVol->getEnclosingRegion(), &doubleMesh, doubleCustomController); + extractMarchingCubesMeshCustom(doubleVol, doubleVol->getEnclosingRegion(), &doubleMesh, doubleCustomController); QCOMPARE(doubleMesh.getNoOfVertices(), uint16_t(16113)); // Verifies size of mesh and that we have 32-bit indices QCOMPARE(doubleMesh.getNoOfIndices(), uint32_t(22053)); // Verifies size of mesh QCOMPARE(doubleMesh.getIndex(100), uint16_t(26)); // Verifies that we have 32-bit indices From be47aec7f76e6a0446ce358faa7d066ba9f05ca6 Mon Sep 17 00:00:00 2001 From: David Williams Date: Mon, 18 Aug 2014 15:47:00 +0200 Subject: [PATCH 23/36] Work on cubic extractor tests. --- .../PolyVoxCore/CubicSurfaceExtractor.h | 4 ++-- tests/TestCubicSurfaceExtractor.cpp | 22 ++++++++++++------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h index 586f85d3..45035247 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h @@ -189,7 +189,7 @@ namespace PolyVox }; template > - void extractCubicProvidedMesh(VolumeType* volData, Region region, MeshType* result, IsQuadNeeded isQuadNeeded = IsQuadNeeded(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), bool bMergeQuads = true) + void extractCubicMeshCustom(VolumeType* volData, Region region, MeshType* result, IsQuadNeeded isQuadNeeded = IsQuadNeeded(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), bool bMergeQuads = true) { CubicSurfaceExtractor extractor(volData, region, result, isQuadNeeded, eWrapMode, tBorderValue, bMergeQuads); extractor.execute(); @@ -199,7 +199,7 @@ namespace PolyVox Mesh > extractCubicMesh(VolumeType* volData, Region region, IsQuadNeeded isQuadNeeded = IsQuadNeeded(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), bool bMergeQuads = true) { Mesh< CubicVertex > result; - extractCubicProvidedMesh(volData, region, &result, isQuadNeeded, eWrapMode, tBorderValue, bMergeQuads); + extractCubicMeshCustom(volData, region, &result, isQuadNeeded, eWrapMode, tBorderValue, bMergeQuads); return result; } } diff --git a/tests/TestCubicSurfaceExtractor.cpp b/tests/TestCubicSurfaceExtractor.cpp index 9f8027bf..cbff3e62 100644 --- a/tests/TestCubicSurfaceExtractor.cpp +++ b/tests/TestCubicSurfaceExtractor.cpp @@ -41,7 +41,9 @@ public: bool operator()(VoxelType back, VoxelType front, VoxelType& materialToUse) { - if ((back > 0) && (front == 0)) + // Not a useful test - it just does something different + // to the DefaultIsQuadNeeded so we can check it compiles. + if ((back > 1) && (front <= 1)) { materialToUse = static_cast(back); return true; @@ -241,27 +243,31 @@ void TestCubicSurfaceExtractor::testExecute() } QCOMPARE(result, uExpectedSumOfVerticesAndIndices); + // Test with default mesh and contoller types. auto uint8Vol = createAndFillVolumeWithNoise(0, 2); auto uint8Mesh = extractCubicMesh(uint8Vol, uint8Vol->getEnclosingRegion()); QCOMPARE(uint8Mesh.getNoOfVertices(), uint32_t(57687)); QCOMPARE(uint8Mesh.getNoOfIndices(), uint32_t(216234)); + // Test with default mesh type but user-provided controller. auto int8Vol = createAndFillVolumeWithNoise(0, 2); - auto int8Mesh = extractCubicMesh(int8Vol, int8Vol->getEnclosingRegion(), DefaultIsQuadNeeded()); - QCOMPARE(int8Mesh.getNoOfVertices(), uint32_t(57687)); - QCOMPARE(int8Mesh.getNoOfIndices(), uint32_t(216234)); + auto int8Mesh = extractCubicMesh(int8Vol, int8Vol->getEnclosingRegion(), CustomIsQuadNeeded()); + QCOMPARE(int8Mesh.getNoOfVertices(), uint32_t(29027)); + QCOMPARE(int8Mesh.getNoOfIndices(), uint32_t(178356)); + // Test with default controller but user-provided mesh. auto uint32Vol = createAndFillVolumeWithNoise(0, 2); Mesh< CubicVertex< uint32_t >, uint16_t > uint32Mesh; - extractCubicProvidedMesh(uint32Vol, uint32Vol->getEnclosingRegion(), &uint32Mesh); + extractCubicMeshCustom(uint32Vol, uint32Vol->getEnclosingRegion(), &uint32Mesh); QCOMPARE(uint32Mesh.getNoOfVertices(), uint16_t(57687)); QCOMPARE(uint32Mesh.getNoOfIndices(), uint32_t(216234)); + // Test with both mesh and controller being provided by the user. auto int32Vol = createAndFillVolumeWithNoise(0, 2); Mesh< CubicVertex< int32_t >, uint16_t > int32Mesh; - extractCubicProvidedMesh(int32Vol, int32Vol->getEnclosingRegion(), &int32Mesh, DefaultIsQuadNeeded()); - QCOMPARE(int32Mesh.getNoOfVertices(), uint16_t(57687)); - QCOMPARE(int32Mesh.getNoOfIndices(), uint32_t(216234)); + extractCubicMeshCustom(int32Vol, int32Vol->getEnclosingRegion(), &int32Mesh, CustomIsQuadNeeded()); + QCOMPARE(int32Mesh.getNoOfVertices(), uint16_t(29027)); + QCOMPARE(int32Mesh.getNoOfIndices(), uint32_t(178356)); } From 5e7c4fbc1229672d968b5b204d1e187a39658d86 Mon Sep 17 00:00:00 2001 From: David Williams Date: Mon, 18 Aug 2014 16:01:31 +0200 Subject: [PATCH 24/36] Added handy type aliases for meshes. --- .../include/PolyVoxCore/CubicSurfaceExtractor.h | 8 ++++++-- .../PolyVoxCore/MarchingCubesSurfaceExtractor.h | 10 +++++++--- tests/TestCubicSurfaceExtractor.cpp | 4 ++-- tests/TestSurfaceExtractor.cpp | 7 +++---- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h index 45035247..b24ff159 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h @@ -53,6 +53,10 @@ namespace PolyVox DataType data; }; + // Convienient shorthand for declaring a mesh of 'cubic' vertices + template + using CubicMesh = Mesh< CubicVertex, IndexType >; + /// Decodes a position from a CubicVertex inline Vector3DFloat decodePosition(const Vector3DUint8& encodedPosition) { @@ -196,9 +200,9 @@ namespace PolyVox } template > - Mesh > extractCubicMesh(VolumeType* volData, Region region, IsQuadNeeded isQuadNeeded = IsQuadNeeded(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), bool bMergeQuads = true) + CubicMesh extractCubicMesh(VolumeType* volData, Region region, IsQuadNeeded isQuadNeeded = IsQuadNeeded(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), bool bMergeQuads = true) { - Mesh< CubicVertex > result; + CubicMesh result; extractCubicMeshCustom(volData, region, &result, isQuadNeeded, eWrapMode, tBorderValue, bMergeQuads); return result; } diff --git a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h index 5faab2f3..9f722098 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h @@ -55,6 +55,10 @@ namespace PolyVox DataType data; }; + // Convienient shorthand for declaring a mesh of marching cubes vertices + template + using MarchingCubesMesh = Mesh< MarchingCubesVertex, IndexType >; + /// Decodes a position from a MarchingCubesVertex inline Vector3DFloat decodePosition(const Vector3DUint16& encodedPosition) { @@ -339,10 +343,10 @@ namespace PolyVox } template< typename VolumeType, typename ControllerType = DefaultMarchingCubesController > - Mesh > extractMarchingCubesMesh(VolumeType* volData, Region region, ControllerType controller = ControllerType(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType()) + MarchingCubesMesh extractMarchingCubesMesh(VolumeType* volData, Region region, ControllerType controller = ControllerType(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType()) { - Mesh > result; - extractMarchingCubesMeshCustom, DefaultIndexType > >(volData, region, &result, controller, eWrapMode, tBorderValue); + MarchingCubesMesh result; + extractMarchingCubesMeshCustom(volData, region, &result, controller, eWrapMode, tBorderValue); return result; } } diff --git a/tests/TestCubicSurfaceExtractor.cpp b/tests/TestCubicSurfaceExtractor.cpp index cbff3e62..4bafb89c 100644 --- a/tests/TestCubicSurfaceExtractor.cpp +++ b/tests/TestCubicSurfaceExtractor.cpp @@ -257,14 +257,14 @@ void TestCubicSurfaceExtractor::testExecute() // Test with default controller but user-provided mesh. auto uint32Vol = createAndFillVolumeWithNoise(0, 2); - Mesh< CubicVertex< uint32_t >, uint16_t > uint32Mesh; + CubicMesh< uint32_t, uint16_t > uint32Mesh; extractCubicMeshCustom(uint32Vol, uint32Vol->getEnclosingRegion(), &uint32Mesh); QCOMPARE(uint32Mesh.getNoOfVertices(), uint16_t(57687)); QCOMPARE(uint32Mesh.getNoOfIndices(), uint32_t(216234)); // Test with both mesh and controller being provided by the user. auto int32Vol = createAndFillVolumeWithNoise(0, 2); - Mesh< CubicVertex< int32_t >, uint16_t > int32Mesh; + CubicMesh< int32_t, uint16_t > int32Mesh; extractCubicMeshCustom(int32Vol, int32Vol->getEnclosingRegion(), &int32Mesh, CustomIsQuadNeeded()); QCOMPARE(int32Mesh.getNoOfVertices(), uint16_t(29027)); QCOMPARE(int32Mesh.getNoOfIndices(), uint32_t(178356)); diff --git a/tests/TestSurfaceExtractor.cpp b/tests/TestSurfaceExtractor.cpp index 7c0e6ddb..8434be94 100644 --- a/tests/TestSurfaceExtractor.cpp +++ b/tests/TestSurfaceExtractor.cpp @@ -148,10 +148,9 @@ void TestSurfaceExtractor::testExecute() QCOMPARE(floatMesh.getIndex(100), uint32_t(26)); // Verifies that we have 32-bit indices QCOMPARE(floatMesh.getVertex(100).data, float(1.0f)); // Not really meaningful for a primative type - // This test makes use of a user provided mesh. It uses the default controller, but we have to explicitly provide this because C++ won't let us - // use a default for the second-to-last parameter but noot use a default for the last parameter. + // This test makes use of a user provided mesh, while stil using the default controller. auto intVol = createAndFillVolume(); - Mesh< MarchingCubesVertex< int8_t >, uint16_t > intMesh; + MarchingCubesMesh< int8_t, uint16_t > intMesh; extractMarchingCubesMeshCustom(intVol, intVol->getEnclosingRegion(), &intMesh); QCOMPARE(intMesh.getNoOfVertices(), uint16_t(11718)); // Verifies size of mesh and that we have 16-bit indices QCOMPARE(intMesh.getNoOfIndices(), uint32_t(34041)); // Verifies size of mesh @@ -161,7 +160,7 @@ void TestSurfaceExtractor::testExecute() // This test makes use of a user-provided mesh and also a custom controller. auto doubleVol = createAndFillVolume(); CustomMarchingCubesController doubleCustomController; - Mesh< MarchingCubesVertex< double >, uint16_t > doubleMesh; + MarchingCubesMesh< double, uint16_t > doubleMesh; extractMarchingCubesMeshCustom(doubleVol, doubleVol->getEnclosingRegion(), &doubleMesh, doubleCustomController); QCOMPARE(doubleMesh.getNoOfVertices(), uint16_t(16113)); // Verifies size of mesh and that we have 32-bit indices QCOMPARE(doubleMesh.getNoOfIndices(), uint32_t(22053)); // Verifies size of mesh From 502b21c3bd8ef958288e37776b27ce1c6d703e3b Mon Sep 17 00:00:00 2001 From: David Williams Date: Mon, 18 Aug 2014 16:13:59 +0200 Subject: [PATCH 25/36] Moved comments around. --- .../PolyVoxCore/CubicSurfaceExtractor.h | 98 +++++++++++-------- .../MarchingCubesSurfaceExtractor.h | 3 +- 2 files changed, 58 insertions(+), 43 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h index b24ff159..ee100097 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h @@ -75,48 +75,8 @@ namespace PolyVox result.data = cubicVertex.data; // Data is not encoded return result; } - - /// The CubicSurfaceExtractor creates a mesh in which each voxel appears to be rendered as a cube - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /// Introduction - /// ------------ - /// Games such as Minecraft and Voxatron have a unique graphical style in which each voxel in the world appears to be rendered as a single cube. Actually rendering a cube for each voxel would be very expensive, but in practice the only faces which need to be drawn are those which lie on the boundary between solid and empty voxels. The CubicSurfaceExtractor can be used to create such a mesh from PolyVox volume data. As an example, images from Minecraft and Voxatron are shown below: - /// - /// \image html MinecraftAndVoxatron.jpg - /// - /// Before we get into the specifics of the CubicSurfaceExtractor, it is useful to understand the principles which apply to *all* PolyVox surface extractors and which are described in the Surface Extraction document (ADD LINK). From here on, it is assumed that you are familier with PolyVox regions and how they are used to limit surface extraction to a particular part of the volume. The principles of allowing dynamic terrain are also common to all surface extractors and are described here (ADD LINK). - /// - /// Basic Operation - /// --------------- - /// At its core, the CubicSurfaceExtractor works by by looking at pairs of adjacent voxels and determining whether a quad should be placed between then. The most simple situation to imagine is a binary volume where every voxel is either solid or empty. In this case a quad should be generated whenever a solid voxel is next to an empty voxel as this represents part of the surface of the solid object. There is no need to generate a quad between two solid voxels (this quad would never be seen as it is inside the object) and there is no need to generate a quad between two empty voxels (there is no object here). PolyVox allows the principle to be extended far beyond such simple binary volumes but they provide a useful starting point for understanding how the algorithm works. - /// - /// As an example, lets consider the part of a volume shown below. We are going to explain the principles in only two dimensions as this makes it much simpler to illustrate, so you will need to mentally extend the process into the third dimension. Hopefully you will find this intuitive. The diagram below shows a small part of a larger volume (as indicated by the voxel coordinates on the axes) which contains only solid and empty voxels represented by solid and hollow circles respectively. The region on which we are running the surface extractor is marked in pink, and for the purpose of this example it corresponds to the whole of the diagram. - /// - /// \image html CubicSurfaceExtractor1.png - /// - /// The output of the surface extractor is the mesh marked in red. As you can see, this forms a closed object which corrsponds to the shape of the underlying voxel data. We won't describe the rendering of such meshes here - for details of this please see (SOME LINK HERE). - /// - /// Working with Regions - /// -------------------- - /// So far the behaviour is easy to understand, but let's look at what happens when the extraction is limited to a particular region of the volume. The figure below shows the same data set as the previous figure, but the extraction region (still marked in pink) has been limited to 13 to 16 in x and 47 to 51 in y: - /// - /// \image html CubicSurfaceExtractor2.png - /// - /// As you can see, the extractor continues to generate a number of quads as indicated by the solid red lines. However, you can also see that the shape is no longer closed. This is because the solid voxels actually extend outside the region which is being processed, and so the extractor does not encounter a boundary between solid and empty voxels. Although this may initially appear problematic, the hole in the mesh does not actually matter because it will be hidden by the mesh corresponding to the region adjacent to it (see next diagram). - /// - /// More interestingly, the diagram also contains a couple of dotted red lines lying on the bottom and right hand side of the extracted region. These are present to illustrate a common point of confusion, which is that *no quads are generated at this position even though it is a boundary between solid and empty voxels*. This is indeed somewhat counter intuitive but there is a rational reasaoning behind it. - /// If you consider the dashed line on the righthand side of the extracted region, then it is clear that this lies on a boundary between solid and empty voxels and so we do need to create quads here. But what is not so clear is whether these quads should be assigned to the mesh which corresponds to the region in pink, or whether they should be assigned to the region to the right of it which is marked in blue in the diagram below: - /// - /// \image html CubicSurfaceExtractor3.png - /// - /// We could choose to add the quads to *both* regions, but this can cause confusion when one of the region is modified (causing the face to disappear or a new one to be created) as *both* regions need to have their mesh regenerated to correctly represent the new state of the volume data. Such pairs of coplanar quads can also cause problems with physics engines, and may prevent transparent block from rendering correctly. Therefore we choose to instead only add the quad to one of the the regions and we always choose the one with the greater coordinate value in the direction in which they differ. In the above example the regions differ by the 'x' component of their position, and so the quad is added to the region with the greater 'x' value (the one marked in blue). - /// - /// **Note:** *This behaviour has changed recently (September 2012). Earlier versions of PolyVox tried to be smart about this problem by looking beyond the region which was being processed, but this complicated the code and didn't work very well. Ultimatly we decided to simply stick with the convention outlined above.* - /// - /// One of the practical implications of this is that when you modify a voxel *you may have to re-extract the mesh for regions other than region which actually contains the voxel you modified.* This happens when the voxel lies on the upper x,y or z face of a region. Assuming that you have some management code which can mark a region as needing re-extraction when a voxel changes, you should probably extend this to mark the regions of neighbouring voxels as invalid (this will have no effect when the voxel is well within a region, but will mark the neighbouring region as needing an update if the voxel lies on a region face). - /// - /// Another scenario which sometimes results in confusion is when you wish to extract a region which corresponds to the whole volume, partcularly when solid voxels extend right to the edge of the volume. - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /// Do not use this class directly. Use the 'extractCubicSurface' function instead (see examples). template class CubicSurfaceExtractor { @@ -192,6 +152,19 @@ namespace PolyVox typename VolumeType::VoxelType m_tBorderValue; }; + // This version of the function performs the extraction into a user-provided mesh rather than allocating a mesh automatically. + // There are a few reasons why this might be useful to more advanced users: + // + // 1. It leaves the user in control of memory allocation and would allow them to implement e.g. a mesh pooling system. + // 2. The user-provided mesh could have a different index type (e.g. 16-bit indices) to reduce memory usage. + // 3. The user could provide a custom mesh class, e.g a thin wrapper around an openGL VBO to allow direct writing into this structure. + // + // We don't provide a default MeshType here. If the user doesn't want to provide a MeshType then it probably makes + // more sense to use the other variant of this function where the mesh is a return value rather than a parameter. + // + // Note: This function is called 'extractCubicMeshCustom' rather than 'extractCubicMesh' to avoid ambiguity when only three parameters + // are provided (would the third parameter be a controller or a mesh?). It seems this can be fixed by using enable_if/static_assert to emulate concepts, + // but this is relatively complex and I haven't done it yet. Could always add it later as another overload. template > void extractCubicMeshCustom(VolumeType* volData, Region region, MeshType* result, IsQuadNeeded isQuadNeeded = IsQuadNeeded(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), bool bMergeQuads = true) { @@ -199,6 +172,47 @@ namespace PolyVox extractor.execute(); } + /// The CubicSurfaceExtractor creates a mesh in which each voxel appears to be rendered as a cube + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /// Introduction + /// ------------ + /// Games such as Minecraft and Voxatron have a unique graphical style in which each voxel in the world appears to be rendered as a single cube. Actually rendering a cube for each voxel would be very expensive, but in practice the only faces which need to be drawn are those which lie on the boundary between solid and empty voxels. The CubicSurfaceExtractor can be used to create such a mesh from PolyVox volume data. As an example, images from Minecraft and Voxatron are shown below: + /// + /// \image html MinecraftAndVoxatron.jpg + /// + /// Before we get into the specifics of the CubicSurfaceExtractor, it is useful to understand the principles which apply to *all* PolyVox surface extractors and which are described in the Surface Extraction document (ADD LINK). From here on, it is assumed that you are familier with PolyVox regions and how they are used to limit surface extraction to a particular part of the volume. The principles of allowing dynamic terrain are also common to all surface extractors and are described here (ADD LINK). + /// + /// Basic Operation + /// --------------- + /// At its core, the CubicSurfaceExtractor works by by looking at pairs of adjacent voxels and determining whether a quad should be placed between then. The most simple situation to imagine is a binary volume where every voxel is either solid or empty. In this case a quad should be generated whenever a solid voxel is next to an empty voxel as this represents part of the surface of the solid object. There is no need to generate a quad between two solid voxels (this quad would never be seen as it is inside the object) and there is no need to generate a quad between two empty voxels (there is no object here). PolyVox allows the principle to be extended far beyond such simple binary volumes but they provide a useful starting point for understanding how the algorithm works. + /// + /// As an example, lets consider the part of a volume shown below. We are going to explain the principles in only two dimensions as this makes it much simpler to illustrate, so you will need to mentally extend the process into the third dimension. Hopefully you will find this intuitive. The diagram below shows a small part of a larger volume (as indicated by the voxel coordinates on the axes) which contains only solid and empty voxels represented by solid and hollow circles respectively. The region on which we are running the surface extractor is marked in pink, and for the purpose of this example it corresponds to the whole of the diagram. + /// + /// \image html CubicSurfaceExtractor1.png + /// + /// The output of the surface extractor is the mesh marked in red. As you can see, this forms a closed object which corrsponds to the shape of the underlying voxel data. We won't describe the rendering of such meshes here - for details of this please see (SOME LINK HERE). + /// + /// Working with Regions + /// -------------------- + /// So far the behaviour is easy to understand, but let's look at what happens when the extraction is limited to a particular region of the volume. The figure below shows the same data set as the previous figure, but the extraction region (still marked in pink) has been limited to 13 to 16 in x and 47 to 51 in y: + /// + /// \image html CubicSurfaceExtractor2.png + /// + /// As you can see, the extractor continues to generate a number of quads as indicated by the solid red lines. However, you can also see that the shape is no longer closed. This is because the solid voxels actually extend outside the region which is being processed, and so the extractor does not encounter a boundary between solid and empty voxels. Although this may initially appear problematic, the hole in the mesh does not actually matter because it will be hidden by the mesh corresponding to the region adjacent to it (see next diagram). + /// + /// More interestingly, the diagram also contains a couple of dotted red lines lying on the bottom and right hand side of the extracted region. These are present to illustrate a common point of confusion, which is that *no quads are generated at this position even though it is a boundary between solid and empty voxels*. This is indeed somewhat counter intuitive but there is a rational reasaoning behind it. + /// If you consider the dashed line on the righthand side of the extracted region, then it is clear that this lies on a boundary between solid and empty voxels and so we do need to create quads here. But what is not so clear is whether these quads should be assigned to the mesh which corresponds to the region in pink, or whether they should be assigned to the region to the right of it which is marked in blue in the diagram below: + /// + /// \image html CubicSurfaceExtractor3.png + /// + /// We could choose to add the quads to *both* regions, but this can cause confusion when one of the region is modified (causing the face to disappear or a new one to be created) as *both* regions need to have their mesh regenerated to correctly represent the new state of the volume data. Such pairs of coplanar quads can also cause problems with physics engines, and may prevent transparent block from rendering correctly. Therefore we choose to instead only add the quad to one of the the regions and we always choose the one with the greater coordinate value in the direction in which they differ. In the above example the regions differ by the 'x' component of their position, and so the quad is added to the region with the greater 'x' value (the one marked in blue). + /// + /// **Note:** *This behaviour has changed recently (September 2012). Earlier versions of PolyVox tried to be smart about this problem by looking beyond the region which was being processed, but this complicated the code and didn't work very well. Ultimatly we decided to simply stick with the convention outlined above.* + /// + /// One of the practical implications of this is that when you modify a voxel *you may have to re-extract the mesh for regions other than region which actually contains the voxel you modified.* This happens when the voxel lies on the upper x,y or z face of a region. Assuming that you have some management code which can mark a region as needing re-extraction when a voxel changes, you should probably extend this to mark the regions of neighbouring voxels as invalid (this will have no effect when the voxel is well within a region, but will mark the neighbouring region as needing an update if the voxel lies on a region face). + /// + /// Another scenario which sometimes results in confusion is when you wish to extract a region which corresponds to the whole volume, partcularly when solid voxels extend right to the edge of the volume. + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template > CubicMesh extractCubicMesh(VolumeType* volData, Region region, IsQuadNeeded isQuadNeeded = IsQuadNeeded(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), bool bMergeQuads = true) { diff --git a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h index 9f722098..0612a33f 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h @@ -151,6 +151,7 @@ namespace PolyVox return result; } + /// Do not use this class directly. Use the 'extractMarchingCubesSurface' function instead (see examples). template< typename VolumeType, typename MeshType, typename ControllerType> class MarchingCubesSurfaceExtractor { @@ -332,7 +333,7 @@ namespace PolyVox // We don't provide a default MeshType here. If the user doesn't want to provide a MeshType then it probably makes // more sense to use the other variant of this function where the mesh is a return value rather than a parameter. // - // Note: This function is called 'extractMarchingCubesCustomMesh' rather than 'extractMarchingCubesMesh' to avoid ambiguity when only three parameters + // Note: This function is called 'extractMarchingCubesMeshCustom' rather than 'extractMarchingCubesMesh' to avoid ambiguity when only three parameters // are provided (would the third parameter be a controller or a mesh?). It seems this can be fixed by using enable_if/static_assert to emulate concepts, // but this is relatively complex and I haven't done it yet. Could always add it later as another overload. template< typename VolumeType, typename MeshType, typename ControllerType = DefaultMarchingCubesController > From 0863d820685a5f110761e77da00d617517782592 Mon Sep 17 00:00:00 2001 From: David Williams Date: Mon, 18 Aug 2014 16:20:43 +0200 Subject: [PATCH 26/36] Added check to alert the user if their version of Visual Studio is too old. --- library/PolyVoxCore/include/PolyVoxCore/Impl/TypeDef.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/TypeDef.h b/library/PolyVoxCore/include/PolyVoxCore/Impl/TypeDef.h index 1069a791..f9ac641e 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Impl/TypeDef.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/TypeDef.h @@ -24,6 +24,10 @@ freely, subject to the following restrictions: #ifndef __PolyVox_TypeDef_H__ #define __PolyVox_TypeDef_H__ +#if defined(_MSC_VER) && (_MSC_VER < 1800) +#error "Your version of Visual Studio is too old to build PolyVox. You need at least version Visual Stusio 2013" +#endif + //Definitions needed to make library functions accessable // See http://gcc.gnu.org/wiki/Visibility for more info. #if defined _WIN32 || defined __CYGWIN__ From 4b3b940b910d4e37d4698cf577d1417ed1164a0a Mon Sep 17 00:00:00 2001 From: David Williams Date: Mon, 18 Aug 2014 16:29:08 +0200 Subject: [PATCH 27/36] Removed old testing code. --- tests/TestCubicSurfaceExtractor.cpp | 157 ++-------------------------- 1 file changed, 8 insertions(+), 149 deletions(-) diff --git a/tests/TestCubicSurfaceExtractor.cpp b/tests/TestCubicSurfaceExtractor.cpp index 4bafb89c..c8283af4 100644 --- a/tests/TestCubicSurfaceExtractor.cpp +++ b/tests/TestCubicSurfaceExtractor.cpp @@ -55,94 +55,6 @@ public: } }; - -// These 'writeDensityValueToVoxel' functions provide a unified interface for writting densities to primative and class voxel types. -// They are conceptually the inverse of the 'convertToDensity' function used by the MarchingCubesSurfaceExtractor. They probably shouldn't be part -// of PolyVox, but they might be useful to other tests so we cold move them into a 'Tests.h' or something in the future. -template -void writeDensityValueToVoxel(int valueToWrite, VoxelType& voxel) -{ - voxel = valueToWrite; -} - -template<> -void writeDensityValueToVoxel(int valueToWrite, Density8& voxel) -{ - voxel.setDensity(valueToWrite); -} - -template<> -void writeDensityValueToVoxel(int valueToWrite, MaterialDensityPair88& voxel) -{ - voxel.setDensity(valueToWrite); -} - -template -void writeMaterialValueToVoxel(int valueToWrite, VoxelType& voxel) -{ - //Most types don't have a material - return; -} - -template<> -void writeMaterialValueToVoxel(int valueToWrite, MaterialDensityPair88& voxel) -{ - voxel.setMaterial(valueToWrite); -} - -// Runs the surface extractor for a given type. -template -uint32_t testForType(void) -{ - const int32_t uVolumeSideLength = 256; - - //Create empty volume - SimpleVolume volData(Region(Vector3DInt32(0,0,0), Vector3DInt32(uVolumeSideLength-1, uVolumeSideLength-1, uVolumeSideLength-1)), 128); - - //Fill the volume with data - for (int32_t z = 0; z < uVolumeSideLength; z++) - { - for (int32_t y = 0; y < uVolumeSideLength; y++) - { - for (int32_t x = 0; x < uVolumeSideLength; x++) - { - if(x + y + z > uVolumeSideLength) - { - VoxelType voxelValue; - writeDensityValueToVoxel(100, voxelValue); - writeMaterialValueToVoxel(42, voxelValue); - volData.setVoxelAt(x, y, z, voxelValue); - } - } - } - } - - uint32_t uTotalVertices = 0; - uint32_t uTotalIndices = 0; - - //Run the surface extractor a number of times over differnt regions of the volume. - const int32_t uRegionSideLength = 64; - for (int32_t z = 0; z < uVolumeSideLength; z += uRegionSideLength) - { - for (int32_t y = 0; y < uVolumeSideLength; y += uRegionSideLength) - { - for (int32_t x = 0; x < uVolumeSideLength; x += uRegionSideLength) - { - Region regionToExtract(x, y, z, x + uRegionSideLength - 1, y + uRegionSideLength - 1, z + uRegionSideLength - 1); - - auto result = extractCubicMesh(&volData, regionToExtract); - - uTotalVertices += result.getNoOfVertices(); - uTotalIndices += result.getNoOfIndices(); - } - } - } - - // Just some value which is representative of the work we've done. It doesn't - // matter what it is, just that it should be the same every time we run the test. - return uTotalVertices + uTotalIndices; -} - // Runs the surface extractor for a given type. template SimpleVolume* createAndFillVolumeWithNoise(VoxelType minValue, VoxelType maxValue) @@ -181,67 +93,7 @@ SimpleVolume* createAndFillVolumeWithNoise(VoxelType minValue, VoxelT void TestCubicSurfaceExtractor::testExecute() { - /*const static uint32_t uExpectedVertices = 6624; - const static uint32_t uExpectedIndices = 9936; - const static uint32_t uMaterialToCheck = 3000; - const static float fExpectedMaterial = 42.0f; - const static uint32_t uIndexToCheck = 2000; - const static uint32_t uExpectedIndex = 1334; - - Mesh mesh;*/ - - /*testForType(mesh); - QCOMPARE(mesh.getNoOfVertices(), uExpectedVertices); - QCOMPARE(mesh.getNoOfIndices(), uExpectedIndices); - QCOMPARE(mesh.getVertices()[uMaterialToCheck].getMaterial(), fNoMaterial); - - testForType(mesh); - QCOMPARE(mesh.getNoOfVertices(), uExpectedVertices); - QCOMPARE(mesh.getNoOfIndices(), uExpectedIndices); - QCOMPARE(mesh.getVertices()[uMaterialToCheck].getMaterial(), fNoMaterial); - - testForType(mesh); - QCOMPARE(mesh.getNoOfVertices(), uExpectedVertices); - QCOMPARE(mesh.getNoOfIndices(), uExpectedIndices); - QCOMPARE(mesh.getVertices()[uMaterialToCheck].getMaterial(), fNoMaterial); - - testForType(mesh); - QCOMPARE(mesh.getNoOfVertices(), uExpectedVertices); - QCOMPARE(mesh.getNoOfIndices(), uExpectedIndices); - QCOMPARE(mesh.getVertices()[uMaterialToCheck].getMaterial(), fNoMaterial); - - testForType(mesh); - QCOMPARE(mesh.getNoOfVertices(), uExpectedVertices); - QCOMPARE(mesh.getNoOfIndices(), uExpectedIndices); - QCOMPARE(mesh.getVertices()[uMaterialToCheck].getMaterial(), fNoMaterial); - - testForType(mesh); - QCOMPARE(mesh.getNoOfVertices(), uExpectedVertices); - QCOMPARE(mesh.getNoOfIndices(), uExpectedIndices); - QCOMPARE(mesh.getVertices()[uMaterialToCheck].getMaterial(), fNoMaterial); - - testForType(mesh); - QCOMPARE(mesh.getNoOfVertices(), uExpectedVertices); - QCOMPARE(mesh.getNoOfIndices(), uExpectedIndices); - QCOMPARE(mesh.getVertices()[uMaterialToCheck].getMaterial(), fNoMaterial); - - testForType(mesh); - QCOMPARE(mesh.getNoOfVertices(), uExpectedVertices); - QCOMPARE(mesh.getNoOfIndices(), uExpectedIndices); - QCOMPARE(mesh.getVertices()[uMaterialToCheck].getMaterial(), fNoMaterial);*/ - - /*testForType(mesh); - QCOMPARE(mesh.getNoOfVertices(), uExpectedVertices); - QCOMPARE(mesh.getNoOfIndices(), uExpectedIndices); - QCOMPARE(mesh.getVertices()[uMaterialToCheck].getMaterial(), fNoMaterial);*/ - - const static uint32_t uExpectedSumOfVerticesAndIndices = 704668; - //const static uint32_t uExpectedSumOfVerticesAndIndices = 2792332; - uint32_t result = 0; - QBENCHMARK { - result = testForType(); - } - QCOMPARE(result, uExpectedSumOfVerticesAndIndices); + // Behavioural tests // Test with default mesh and contoller types. auto uint8Vol = createAndFillVolumeWithNoise(0, 2); @@ -269,6 +121,13 @@ void TestCubicSurfaceExtractor::testExecute() QCOMPARE(int32Mesh.getNoOfVertices(), uint16_t(29027)); QCOMPARE(int32Mesh.getNoOfIndices(), uint32_t(178356)); + // Performance tests + + auto emptyVol = createAndFillVolumeWithNoise(0, 0); + CubicMesh< uint32_t, uint16_t > emptyMesh; + QBENCHMARK{ extractCubicMeshCustom(emptyVol, emptyVol->getEnclosingRegion(), &emptyMesh); } + QCOMPARE(emptyMesh.getNoOfVertices(), uint16_t(0)); + } QTEST_MAIN(TestCubicSurfaceExtractor) From ec9b06ef0f8460208dbf5b565f320319c77f95c3 Mon Sep 17 00:00:00 2001 From: David Williams Date: Mon, 18 Aug 2014 16:40:36 +0200 Subject: [PATCH 28/36] Work on cubic surface extractor tests. --- tests/TestCubicSurfaceExtractor.cpp | 38 ++++++++++++++++------------- tests/TestCubicSurfaceExtractor.h | 4 ++- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/tests/TestCubicSurfaceExtractor.cpp b/tests/TestCubicSurfaceExtractor.cpp index c8283af4..6acd3222 100644 --- a/tests/TestCubicSurfaceExtractor.cpp +++ b/tests/TestCubicSurfaceExtractor.cpp @@ -57,21 +57,19 @@ public: // Runs the surface extractor for a given type. template -SimpleVolume* createAndFillVolumeWithNoise(VoxelType minValue, VoxelType maxValue) +SimpleVolume* createAndFillVolumeWithNoise(int32_t iVolumeSideLength, VoxelType minValue, VoxelType maxValue) { - const int32_t uVolumeSideLength = 32; - //Create empty volume - SimpleVolume* volData = new SimpleVolume(Region(Vector3DInt32(0, 0, 0), Vector3DInt32(uVolumeSideLength - 1, uVolumeSideLength - 1, uVolumeSideLength - 1)), 16); + SimpleVolume* volData = new SimpleVolume(Region(Vector3DInt32(0, 0, 0), Vector3DInt32(iVolumeSideLength - 1, iVolumeSideLength - 1, iVolumeSideLength - 1)), 32); srand(12345); //Fill the volume with data - for (int32_t z = 0; z < uVolumeSideLength; z++) + for (int32_t z = 0; z < iVolumeSideLength; z++) { - for (int32_t y = 0; y < uVolumeSideLength; y++) + for (int32_t y = 0; y < iVolumeSideLength; y++) { - for (int32_t x = 0; x < uVolumeSideLength; x++) + for (int32_t x = 0; x < iVolumeSideLength; x++) { if (minValue == maxValue) { @@ -91,43 +89,49 @@ SimpleVolume* createAndFillVolumeWithNoise(VoxelType minValue, VoxelT return volData; } -void TestCubicSurfaceExtractor::testExecute() +void TestCubicSurfaceExtractor::testBehaviour() { - // Behavioural tests - // Test with default mesh and contoller types. - auto uint8Vol = createAndFillVolumeWithNoise(0, 2); + auto uint8Vol = createAndFillVolumeWithNoise(32, 0, 2); auto uint8Mesh = extractCubicMesh(uint8Vol, uint8Vol->getEnclosingRegion()); QCOMPARE(uint8Mesh.getNoOfVertices(), uint32_t(57687)); QCOMPARE(uint8Mesh.getNoOfIndices(), uint32_t(216234)); // Test with default mesh type but user-provided controller. - auto int8Vol = createAndFillVolumeWithNoise(0, 2); + auto int8Vol = createAndFillVolumeWithNoise(32, 0, 2); auto int8Mesh = extractCubicMesh(int8Vol, int8Vol->getEnclosingRegion(), CustomIsQuadNeeded()); QCOMPARE(int8Mesh.getNoOfVertices(), uint32_t(29027)); QCOMPARE(int8Mesh.getNoOfIndices(), uint32_t(178356)); // Test with default controller but user-provided mesh. - auto uint32Vol = createAndFillVolumeWithNoise(0, 2); + auto uint32Vol = createAndFillVolumeWithNoise(32, 0, 2); CubicMesh< uint32_t, uint16_t > uint32Mesh; extractCubicMeshCustom(uint32Vol, uint32Vol->getEnclosingRegion(), &uint32Mesh); QCOMPARE(uint32Mesh.getNoOfVertices(), uint16_t(57687)); QCOMPARE(uint32Mesh.getNoOfIndices(), uint32_t(216234)); // Test with both mesh and controller being provided by the user. - auto int32Vol = createAndFillVolumeWithNoise(0, 2); + auto int32Vol = createAndFillVolumeWithNoise(32, 0, 2); CubicMesh< int32_t, uint16_t > int32Mesh; extractCubicMeshCustom(int32Vol, int32Vol->getEnclosingRegion(), &int32Mesh, CustomIsQuadNeeded()); QCOMPARE(int32Mesh.getNoOfVertices(), uint16_t(29027)); QCOMPARE(int32Mesh.getNoOfIndices(), uint32_t(178356)); +} - // Performance tests - - auto emptyVol = createAndFillVolumeWithNoise(0, 0); +void TestCubicSurfaceExtractor::testEmptyVolumePerformance() +{ + auto emptyVol = createAndFillVolumeWithNoise(32, 0, 0); CubicMesh< uint32_t, uint16_t > emptyMesh; QBENCHMARK{ extractCubicMeshCustom(emptyVol, emptyVol->getEnclosingRegion(), &emptyMesh); } QCOMPARE(emptyMesh.getNoOfVertices(), uint16_t(0)); +} +void TestCubicSurfaceExtractor::testNoiseVolumePerformance() +{ + auto noiseVol = createAndFillVolumeWithNoise(32, 0, 1); + CubicMesh< uint32_t, uint16_t > noiseMesh; + QBENCHMARK{ extractCubicMeshCustom(noiseVol, noiseVol->getEnclosingRegion(), &noiseMesh); } + QCOMPARE(noiseMesh.getNoOfVertices(), uint16_t(28429)); } QTEST_MAIN(TestCubicSurfaceExtractor) diff --git a/tests/TestCubicSurfaceExtractor.h b/tests/TestCubicSurfaceExtractor.h index 1ea815d8..15e0da5f 100644 --- a/tests/TestCubicSurfaceExtractor.h +++ b/tests/TestCubicSurfaceExtractor.h @@ -31,7 +31,9 @@ class TestCubicSurfaceExtractor: public QObject Q_OBJECT private slots: - void testExecute(); + void testBehaviour(); + void testEmptyVolumePerformance(); + void testNoiseVolumePerformance(); }; #endif From c0added385021d1a2f31f45372fee04a73d27fe2 Mon Sep 17 00:00:00 2001 From: David Williams Date: Mon, 18 Aug 2014 16:48:32 +0200 Subject: [PATCH 29/36] Work on cubic extractor tests. --- tests/TestCubicSurfaceExtractor.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/tests/TestCubicSurfaceExtractor.cpp b/tests/TestCubicSurfaceExtractor.cpp index 6acd3222..33d477e7 100644 --- a/tests/TestCubicSurfaceExtractor.cpp +++ b/tests/TestCubicSurfaceExtractor.cpp @@ -26,6 +26,7 @@ freely, subject to the following restrictions: #include "PolyVoxCore/Density.h" #include "PolyVoxCore/Material.h" #include "PolyVoxCore/MaterialDensityPair.h" +#include "PolyVoxCore/RawVolume.h" #include "PolyVoxCore/SimpleVolume.h" #include "PolyVoxCore/CubicSurfaceExtractor.h" @@ -56,11 +57,11 @@ public: }; // Runs the surface extractor for a given type. -template -SimpleVolume* createAndFillVolumeWithNoise(int32_t iVolumeSideLength, VoxelType minValue, VoxelType maxValue) +template +VolumeType* createAndFillVolumeWithNoise(int32_t iVolumeSideLength, typename VolumeType::VoxelType minValue, typename VolumeType::VoxelType maxValue) { //Create empty volume - SimpleVolume* volData = new SimpleVolume(Region(Vector3DInt32(0, 0, 0), Vector3DInt32(iVolumeSideLength - 1, iVolumeSideLength - 1, iVolumeSideLength - 1)), 32); + VolumeType* volData = new VolumeType(Region(Vector3DInt32(0, 0, 0), Vector3DInt32(iVolumeSideLength - 1, iVolumeSideLength - 1, iVolumeSideLength - 1))); srand(12345); @@ -80,7 +81,7 @@ SimpleVolume* createAndFillVolumeWithNoise(int32_t iVolumeSideLength, { // Otherwise we write random voxel values between zero and the requested maximum int voxelValue = (rand() % (maxValue - minValue + 1)) + minValue; - volData->setVoxelAt(x, y, z, static_cast(voxelValue)); + volData->setVoxelAt(x, y, z, static_cast(voxelValue)); } } } @@ -92,26 +93,26 @@ SimpleVolume* createAndFillVolumeWithNoise(int32_t iVolumeSideLength, void TestCubicSurfaceExtractor::testBehaviour() { // Test with default mesh and contoller types. - auto uint8Vol = createAndFillVolumeWithNoise(32, 0, 2); + auto uint8Vol = createAndFillVolumeWithNoise< SimpleVolume >(32, 0, 2); auto uint8Mesh = extractCubicMesh(uint8Vol, uint8Vol->getEnclosingRegion()); QCOMPARE(uint8Mesh.getNoOfVertices(), uint32_t(57687)); QCOMPARE(uint8Mesh.getNoOfIndices(), uint32_t(216234)); // Test with default mesh type but user-provided controller. - auto int8Vol = createAndFillVolumeWithNoise(32, 0, 2); + auto int8Vol = createAndFillVolumeWithNoise< SimpleVolume >(32, 0, 2); auto int8Mesh = extractCubicMesh(int8Vol, int8Vol->getEnclosingRegion(), CustomIsQuadNeeded()); QCOMPARE(int8Mesh.getNoOfVertices(), uint32_t(29027)); QCOMPARE(int8Mesh.getNoOfIndices(), uint32_t(178356)); // Test with default controller but user-provided mesh. - auto uint32Vol = createAndFillVolumeWithNoise(32, 0, 2); + auto uint32Vol = createAndFillVolumeWithNoise< SimpleVolume >(32, 0, 2); CubicMesh< uint32_t, uint16_t > uint32Mesh; extractCubicMeshCustom(uint32Vol, uint32Vol->getEnclosingRegion(), &uint32Mesh); QCOMPARE(uint32Mesh.getNoOfVertices(), uint16_t(57687)); QCOMPARE(uint32Mesh.getNoOfIndices(), uint32_t(216234)); // Test with both mesh and controller being provided by the user. - auto int32Vol = createAndFillVolumeWithNoise(32, 0, 2); + auto int32Vol = createAndFillVolumeWithNoise< SimpleVolume >(32, 0, 2); CubicMesh< int32_t, uint16_t > int32Mesh; extractCubicMeshCustom(int32Vol, int32Vol->getEnclosingRegion(), &int32Mesh, CustomIsQuadNeeded()); QCOMPARE(int32Mesh.getNoOfVertices(), uint16_t(29027)); @@ -120,7 +121,7 @@ void TestCubicSurfaceExtractor::testBehaviour() void TestCubicSurfaceExtractor::testEmptyVolumePerformance() { - auto emptyVol = createAndFillVolumeWithNoise(32, 0, 0); + auto emptyVol = createAndFillVolumeWithNoise< RawVolume >(32, 0, 0); CubicMesh< uint32_t, uint16_t > emptyMesh; QBENCHMARK{ extractCubicMeshCustom(emptyVol, emptyVol->getEnclosingRegion(), &emptyMesh); } QCOMPARE(emptyMesh.getNoOfVertices(), uint16_t(0)); @@ -128,7 +129,7 @@ void TestCubicSurfaceExtractor::testEmptyVolumePerformance() void TestCubicSurfaceExtractor::testNoiseVolumePerformance() { - auto noiseVol = createAndFillVolumeWithNoise(32, 0, 1); + auto noiseVol = createAndFillVolumeWithNoise< RawVolume >(32, 0, 1); CubicMesh< uint32_t, uint16_t > noiseMesh; QBENCHMARK{ extractCubicMeshCustom(noiseVol, noiseVol->getEnclosingRegion(), &noiseMesh); } QCOMPARE(noiseMesh.getNoOfVertices(), uint16_t(28429)); From e2051ed71314368a7f3e141dd565f3dd42b2a9c2 Mon Sep 17 00:00:00 2001 From: David Williams Date: Mon, 18 Aug 2014 21:25:29 +0200 Subject: [PATCH 30/36] Tweaks to cubic surface tests. --- tests/TestCubicSurfaceExtractor.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/TestCubicSurfaceExtractor.cpp b/tests/TestCubicSurfaceExtractor.cpp index 33d477e7..ccc47a22 100644 --- a/tests/TestCubicSurfaceExtractor.cpp +++ b/tests/TestCubicSurfaceExtractor.cpp @@ -121,18 +121,18 @@ void TestCubicSurfaceExtractor::testBehaviour() void TestCubicSurfaceExtractor::testEmptyVolumePerformance() { - auto emptyVol = createAndFillVolumeWithNoise< RawVolume >(32, 0, 0); + auto emptyVol = createAndFillVolumeWithNoise< SimpleVolume >(128, 0, 0); CubicMesh< uint32_t, uint16_t > emptyMesh; - QBENCHMARK{ extractCubicMeshCustom(emptyVol, emptyVol->getEnclosingRegion(), &emptyMesh); } + QBENCHMARK{ extractCubicMeshCustom(emptyVol, Region(32, 32, 32, 63, 63, 63), &emptyMesh); } QCOMPARE(emptyMesh.getNoOfVertices(), uint16_t(0)); } void TestCubicSurfaceExtractor::testNoiseVolumePerformance() { - auto noiseVol = createAndFillVolumeWithNoise< RawVolume >(32, 0, 1); + auto noiseVol = createAndFillVolumeWithNoise< SimpleVolume >(128, 0, 2); CubicMesh< uint32_t, uint16_t > noiseMesh; - QBENCHMARK{ extractCubicMeshCustom(noiseVol, noiseVol->getEnclosingRegion(), &noiseMesh); } - QCOMPARE(noiseMesh.getNoOfVertices(), uint16_t(28429)); + QBENCHMARK{ extractCubicMeshCustom(noiseVol, Region(32, 32, 32, 63, 63, 63), &noiseMesh); } + QCOMPARE(noiseMesh.getNoOfVertices(), uint16_t(57729)); } QTEST_MAIN(TestCubicSurfaceExtractor) From c75b0d58ce96e310ca10435d251cc4ed2a657ff0 Mon Sep 17 00:00:00 2001 From: David Williams Date: Mon, 18 Aug 2014 22:06:44 +0200 Subject: [PATCH 31/36] Fixed incorrect threshold calculation for floats. --- .../PolyVoxCore/DefaultMarchingCubesController.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/DefaultMarchingCubesController.h b/library/PolyVoxCore/include/PolyVoxCore/DefaultMarchingCubesController.h index 6a7aaa22..2d1b6144 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/DefaultMarchingCubesController.h +++ b/library/PolyVoxCore/include/PolyVoxCore/DefaultMarchingCubesController.h @@ -75,8 +75,15 @@ namespace PolyVox * if the voxel type is 'float' then the representable range is -FLT_MAX to FLT_MAX and the threshold will be set to zero. */ DefaultMarchingCubesController(void) - :m_tThreshold(((std::numeric_limits::min)() + (std::numeric_limits::max)()) / 2) - { + { + if (std::is_signed()) + { + m_tThreshold = DensityType(0); + } + else + { + m_tThreshold = (((std::numeric_limits::min)() + (std::numeric_limits::max)()) / 2); + } } /** From 72f696652a32d4a016f8aba092e8a05d569368d2 Mon Sep 17 00:00:00 2001 From: David Williams Date: Mon, 18 Aug 2014 22:10:35 +0200 Subject: [PATCH 32/36] Added marching cubes performance tests. --- tests/TestCubicSurfaceExtractor.cpp | 1 + tests/TestSurfaceExtractor.cpp | 52 ++++++++++++++++++++++++++++- tests/TestSurfaceExtractor.h | 4 ++- 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/tests/TestCubicSurfaceExtractor.cpp b/tests/TestCubicSurfaceExtractor.cpp index ccc47a22..fe4da9e1 100644 --- a/tests/TestCubicSurfaceExtractor.cpp +++ b/tests/TestCubicSurfaceExtractor.cpp @@ -63,6 +63,7 @@ VolumeType* createAndFillVolumeWithNoise(int32_t iVolumeSideLength, typename Vol //Create empty volume VolumeType* volData = new VolumeType(Region(Vector3DInt32(0, 0, 0), Vector3DInt32(iVolumeSideLength - 1, iVolumeSideLength - 1, iVolumeSideLength - 1))); + // Seed generator for consistency between runs. srand(12345); //Fill the volume with data diff --git a/tests/TestSurfaceExtractor.cpp b/tests/TestSurfaceExtractor.cpp index 8434be94..f1005ae5 100644 --- a/tests/TestSurfaceExtractor.cpp +++ b/tests/TestSurfaceExtractor.cpp @@ -122,7 +122,41 @@ SimpleVolume* createAndFillVolume(void) return volData; } -void TestSurfaceExtractor::testExecute() +// From http://stackoverflow.com/a/5289624 +float randomFloat(float a, float b) +{ + float random = ((float)rand()) / (float)RAND_MAX; + float diff = b - a; + float r = random * diff; + return a + r; +} + +template +VolumeType* createAndFillVolumeWithNoise(int32_t iVolumeSideLength, float minValue, float maxValue) +{ + //Create empty volume + VolumeType* volData = new VolumeType(Region(Vector3DInt32(0, 0, 0), Vector3DInt32(iVolumeSideLength - 1, iVolumeSideLength - 1, iVolumeSideLength - 1))); + + // Seed generator for consistency between runs. + srand(12345); + + // Fill + for (int32_t z = 0; z < iVolumeSideLength; z++) + { + for (int32_t y = 0; y < iVolumeSideLength; y++) + { + for (int32_t x = 0; x < iVolumeSideLength; x++) + { + float voxelValue = randomFloat(minValue, maxValue); + volData->setVoxelAt(x, y, z, voxelValue); + } + } + } + + return volData; +} + +void TestSurfaceExtractor::testBehaviour() { // These tests apply the Marching Cubes surface extractor to volumes of various voxel types. In addition we sometimes make use of custom controllers // and user-provided meshes to make sure these various combinations work as expected. @@ -176,4 +210,20 @@ void TestSurfaceExtractor::testExecute() QCOMPARE(materialMesh.getVertex(100).data.getMaterial(), uint16_t(79)); // Verify the data attached to the vertex } +void TestSurfaceExtractor::testEmptyVolumePerformance() +{ + auto emptyVol = createAndFillVolumeWithNoise< SimpleVolume >(128, -2.0f, -1.0f); + MarchingCubesMesh< float, uint16_t > emptyMesh; + QBENCHMARK{ extractMarchingCubesMeshCustom(emptyVol, Region(32, 32, 32, 63, 63, 63), &emptyMesh); } + QCOMPARE(emptyMesh.getNoOfVertices(), uint16_t(0)); +} + +void TestSurfaceExtractor::testNoiseVolumePerformance() +{ + auto noiseVol = createAndFillVolumeWithNoise< SimpleVolume >(128, -1.0f, 1.0f); + MarchingCubesMesh< float, uint16_t > noiseMesh; + QBENCHMARK{ extractMarchingCubesMeshCustom(noiseVol, Region(32, 32, 32, 63, 63, 63), &noiseMesh); } + QCOMPARE(noiseMesh.getNoOfVertices(), uint16_t(48967)); +} + QTEST_MAIN(TestSurfaceExtractor) diff --git a/tests/TestSurfaceExtractor.h b/tests/TestSurfaceExtractor.h index 367d798e..20e5ee45 100644 --- a/tests/TestSurfaceExtractor.h +++ b/tests/TestSurfaceExtractor.h @@ -31,7 +31,9 @@ class TestSurfaceExtractor: public QObject Q_OBJECT private slots: - void testExecute(); + void testBehaviour(); + void testEmptyVolumePerformance(); + void testNoiseVolumePerformance(); }; #endif From 349009c67eec8047461ba5f3e8d3c469d65b76ae Mon Sep 17 00:00:00 2001 From: David Williams Date: Tue, 19 Aug 2014 21:02:06 +0200 Subject: [PATCH 33/36] Removed template aliases as they push GCC version up to 4.7, and we don't really *need* them. --- .../include/PolyVoxCore/CubicSurfaceExtractor.h | 9 +++++---- .../PolyVoxCore/MarchingCubesSurfaceExtractor.h | 11 ++++++----- tests/TestCubicSurfaceExtractor.cpp | 8 ++++---- tests/TestSurfaceExtractor.cpp | 11 ++++++----- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h index ee100097..409ed096 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h @@ -54,8 +54,9 @@ namespace PolyVox }; // Convienient shorthand for declaring a mesh of 'cubic' vertices - template - using CubicMesh = Mesh< CubicVertex, IndexType >; + // Currently disabled because it requires GCC 4.7 + //template + //using CubicMesh = Mesh< CubicVertex, IndexType >; /// Decodes a position from a CubicVertex inline Vector3DFloat decodePosition(const Vector3DUint8& encodedPosition) @@ -214,9 +215,9 @@ namespace PolyVox /// Another scenario which sometimes results in confusion is when you wish to extract a region which corresponds to the whole volume, partcularly when solid voxels extend right to the edge of the volume. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template > - CubicMesh extractCubicMesh(VolumeType* volData, Region region, IsQuadNeeded isQuadNeeded = IsQuadNeeded(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), bool bMergeQuads = true) + Mesh > extractCubicMesh(VolumeType* volData, Region region, IsQuadNeeded isQuadNeeded = IsQuadNeeded(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), bool bMergeQuads = true) { - CubicMesh result; + Mesh< CubicVertex > result; extractCubicMeshCustom(volData, region, &result, isQuadNeeded, eWrapMode, tBorderValue, bMergeQuads); return result; } diff --git a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h index 0612a33f..cf42800e 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h @@ -56,8 +56,9 @@ namespace PolyVox }; // Convienient shorthand for declaring a mesh of marching cubes vertices - template - using MarchingCubesMesh = Mesh< MarchingCubesVertex, IndexType >; + // Currently disabled because it requires GCC 4.7 + //template + //using MarchingCubesMesh = Mesh< MarchingCubesVertex, IndexType >; /// Decodes a position from a MarchingCubesVertex inline Vector3DFloat decodePosition(const Vector3DUint16& encodedPosition) @@ -344,10 +345,10 @@ namespace PolyVox } template< typename VolumeType, typename ControllerType = DefaultMarchingCubesController > - MarchingCubesMesh extractMarchingCubesMesh(VolumeType* volData, Region region, ControllerType controller = ControllerType(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType()) + Mesh > extractMarchingCubesMesh(VolumeType* volData, Region region, ControllerType controller = ControllerType(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType()) { - MarchingCubesMesh result; - extractMarchingCubesMeshCustom(volData, region, &result, controller, eWrapMode, tBorderValue); + Mesh > result; + extractMarchingCubesMeshCustom, DefaultIndexType > >(volData, region, &result, controller, eWrapMode, tBorderValue); return result; } } diff --git a/tests/TestCubicSurfaceExtractor.cpp b/tests/TestCubicSurfaceExtractor.cpp index fe4da9e1..ac7ac1b3 100644 --- a/tests/TestCubicSurfaceExtractor.cpp +++ b/tests/TestCubicSurfaceExtractor.cpp @@ -107,14 +107,14 @@ void TestCubicSurfaceExtractor::testBehaviour() // Test with default controller but user-provided mesh. auto uint32Vol = createAndFillVolumeWithNoise< SimpleVolume >(32, 0, 2); - CubicMesh< uint32_t, uint16_t > uint32Mesh; + Mesh< CubicVertex< uint32_t >, uint16_t > uint32Mesh; extractCubicMeshCustom(uint32Vol, uint32Vol->getEnclosingRegion(), &uint32Mesh); QCOMPARE(uint32Mesh.getNoOfVertices(), uint16_t(57687)); QCOMPARE(uint32Mesh.getNoOfIndices(), uint32_t(216234)); // Test with both mesh and controller being provided by the user. auto int32Vol = createAndFillVolumeWithNoise< SimpleVolume >(32, 0, 2); - CubicMesh< int32_t, uint16_t > int32Mesh; + Mesh< CubicVertex< int32_t >, uint16_t > int32Mesh; extractCubicMeshCustom(int32Vol, int32Vol->getEnclosingRegion(), &int32Mesh, CustomIsQuadNeeded()); QCOMPARE(int32Mesh.getNoOfVertices(), uint16_t(29027)); QCOMPARE(int32Mesh.getNoOfIndices(), uint32_t(178356)); @@ -123,7 +123,7 @@ void TestCubicSurfaceExtractor::testBehaviour() void TestCubicSurfaceExtractor::testEmptyVolumePerformance() { auto emptyVol = createAndFillVolumeWithNoise< SimpleVolume >(128, 0, 0); - CubicMesh< uint32_t, uint16_t > emptyMesh; + Mesh< CubicVertex< uint32_t >, uint16_t > emptyMesh; QBENCHMARK{ extractCubicMeshCustom(emptyVol, Region(32, 32, 32, 63, 63, 63), &emptyMesh); } QCOMPARE(emptyMesh.getNoOfVertices(), uint16_t(0)); } @@ -131,7 +131,7 @@ void TestCubicSurfaceExtractor::testEmptyVolumePerformance() void TestCubicSurfaceExtractor::testNoiseVolumePerformance() { auto noiseVol = createAndFillVolumeWithNoise< SimpleVolume >(128, 0, 2); - CubicMesh< uint32_t, uint16_t > noiseMesh; + Mesh< CubicVertex< uint32_t >, uint16_t > noiseMesh; QBENCHMARK{ extractCubicMeshCustom(noiseVol, Region(32, 32, 32, 63, 63, 63), &noiseMesh); } QCOMPARE(noiseMesh.getNoOfVertices(), uint16_t(57729)); } diff --git a/tests/TestSurfaceExtractor.cpp b/tests/TestSurfaceExtractor.cpp index f1005ae5..95ebb299 100644 --- a/tests/TestSurfaceExtractor.cpp +++ b/tests/TestSurfaceExtractor.cpp @@ -182,9 +182,10 @@ void TestSurfaceExtractor::testBehaviour() QCOMPARE(floatMesh.getIndex(100), uint32_t(26)); // Verifies that we have 32-bit indices QCOMPARE(floatMesh.getVertex(100).data, float(1.0f)); // Not really meaningful for a primative type - // This test makes use of a user provided mesh, while stil using the default controller. + // This test makes use of a user provided mesh. It uses the default controller, but we have to explicitly provide this because C++ won't let us + // use a default for the second-to-last parameter but noot use a default for the last parameter. auto intVol = createAndFillVolume(); - MarchingCubesMesh< int8_t, uint16_t > intMesh; + Mesh< MarchingCubesVertex< int8_t >, uint16_t > intMesh; extractMarchingCubesMeshCustom(intVol, intVol->getEnclosingRegion(), &intMesh); QCOMPARE(intMesh.getNoOfVertices(), uint16_t(11718)); // Verifies size of mesh and that we have 16-bit indices QCOMPARE(intMesh.getNoOfIndices(), uint32_t(34041)); // Verifies size of mesh @@ -194,7 +195,7 @@ void TestSurfaceExtractor::testBehaviour() // This test makes use of a user-provided mesh and also a custom controller. auto doubleVol = createAndFillVolume(); CustomMarchingCubesController doubleCustomController; - MarchingCubesMesh< double, uint16_t > doubleMesh; + Mesh< MarchingCubesVertex< double >, uint16_t > doubleMesh; extractMarchingCubesMeshCustom(doubleVol, doubleVol->getEnclosingRegion(), &doubleMesh, doubleCustomController); QCOMPARE(doubleMesh.getNoOfVertices(), uint16_t(16113)); // Verifies size of mesh and that we have 32-bit indices QCOMPARE(doubleMesh.getNoOfIndices(), uint32_t(22053)); // Verifies size of mesh @@ -213,7 +214,7 @@ void TestSurfaceExtractor::testBehaviour() void TestSurfaceExtractor::testEmptyVolumePerformance() { auto emptyVol = createAndFillVolumeWithNoise< SimpleVolume >(128, -2.0f, -1.0f); - MarchingCubesMesh< float, uint16_t > emptyMesh; + Mesh< MarchingCubesVertex< float >, uint16_t > emptyMesh; QBENCHMARK{ extractMarchingCubesMeshCustom(emptyVol, Region(32, 32, 32, 63, 63, 63), &emptyMesh); } QCOMPARE(emptyMesh.getNoOfVertices(), uint16_t(0)); } @@ -221,7 +222,7 @@ void TestSurfaceExtractor::testEmptyVolumePerformance() void TestSurfaceExtractor::testNoiseVolumePerformance() { auto noiseVol = createAndFillVolumeWithNoise< SimpleVolume >(128, -1.0f, 1.0f); - MarchingCubesMesh< float, uint16_t > noiseMesh; + Mesh< MarchingCubesVertex< float >, uint16_t > noiseMesh; QBENCHMARK{ extractMarchingCubesMeshCustom(noiseVol, Region(32, 32, 32, 63, 63, 63), &noiseMesh); } QCOMPARE(noiseMesh.getNoOfVertices(), uint16_t(48967)); } From 9b88e975995ad86f772f27264c76c4d393573627 Mon Sep 17 00:00:00 2001 From: David Williams Date: Tue, 19 Aug 2014 21:02:52 +0200 Subject: [PATCH 34/36] Linux/GCC fix. --- library/PolyVoxCore/include/PolyVoxCore/Mesh.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/Mesh.h b/library/PolyVoxCore/include/PolyVoxCore/Mesh.h index 802b0656..4c1b3582 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Mesh.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Mesh.h @@ -81,7 +81,7 @@ namespace PolyVox { Mesh< Vertex< typename MeshType::VertexType::DataType >, typename MeshType::IndexType > decodedMesh; - for (MeshType::IndexType ct = 0; ct < encodedMesh.getNoOfVertices(); ct++) + for (typename MeshType::IndexType ct = 0; ct < encodedMesh.getNoOfVertices(); ct++) { decodedMesh.addVertex(decodeVertex(encodedMesh.getVertex(ct))); } From 17ee7982d56dc641f9131654dbfb42c5d7fc760d Mon Sep 17 00:00:00 2001 From: DavidWilliams Date: Tue, 19 Aug 2014 21:30:37 +0200 Subject: [PATCH 35/36] Added typename for GCC. --- examples/common/OpenGLWidget.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/common/OpenGLWidget.h b/examples/common/OpenGLWidget.h index fb9a7459..5ceb28ae 100644 --- a/examples/common/OpenGLWidget.h +++ b/examples/common/OpenGLWidget.h @@ -72,29 +72,29 @@ public: // The GL_ARRAY_BUFFER will contain the list of vertex positions glGenBuffers(1, &(meshData.vertexBuffer)); glBindBuffer(GL_ARRAY_BUFFER, meshData.vertexBuffer); - glBufferData(GL_ARRAY_BUFFER, vecVertices.size() * sizeof(MeshType::VertexType), vecVertices.data(), GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, vecVertices.size() * sizeof(typename MeshType::VertexType), vecVertices.data(), GL_STATIC_DRAW); // and GL_ELEMENT_ARRAY_BUFFER will contain the indices glGenBuffers(1, &(meshData.indexBuffer)); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, meshData.indexBuffer); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, vecIndices.size() * sizeof(MeshType::IndexType), vecIndices.data(), GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, vecIndices.size() * sizeof(typename MeshType::IndexType), vecIndices.data(), GL_STATIC_DRAW); // Every surface extractor outputs valid positions for the vertices, so tell OpenGL how these are laid out glEnableVertexAttribArray(0); // Attrib '0' is the vertex positions - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(MeshType::VertexType), (GLvoid*)(offsetof(MeshType::VertexType, position))); //take the first 3 floats from every sizeof(decltype(vecVertices)::value_type) + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(typename MeshType::VertexType), (GLvoid*)(offsetof(typename MeshType::VertexType, position))); //take the first 3 floats from every sizeof(decltype(vecVertices)::value_type) // Some surface extractors also generate normals, so tell OpenGL how these are laid out. If a surface extractor // does not generate normals then nonsense values are written into the buffer here and sghould be ignored by the // shader. This is mostly just to simplify this example code - in a real application you will know whether your // chosen surface extractor generates normals and can skip uploading them if not. glEnableVertexAttribArray(1); // Attrib '1' is the vertex normals. - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(MeshType::VertexType), (GLvoid*)(offsetof(MeshType::VertexType, normal))); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(typename MeshType::VertexType), (GLvoid*)(offsetof(typename MeshType::VertexType, normal))); // Finally a surface extractor will probably output additional data. This is highly application dependant. For this example code // we're just uploading it as a set of bytes which we can read individually, but real code will want to do something specialised here. glEnableVertexAttribArray(2); //We're talking about shader attribute '2' - GLint size = (std::min)(sizeof(MeshType::VertexType::DataType), size_t(4)); // Can't upload more that 4 components (vec4 is GLSL's biggest type) - glVertexAttribIPointer(2, size, GL_UNSIGNED_BYTE, sizeof(MeshType::VertexType), (GLvoid*)(offsetof(MeshType::VertexType, data))); + GLint size = (std::min)(sizeof(typename MeshType::VertexType::DataType), size_t(4)); // Can't upload more that 4 components (vec4 is GLSL's biggest type) + glVertexAttribIPointer(2, size, GL_UNSIGNED_BYTE, sizeof(typename MeshType::VertexType), (GLvoid*)(offsetof(typename MeshType::VertexType, data))); // We're done uploading and can now unbind. glBindVertexArray(0); @@ -105,7 +105,7 @@ public: meshData.scale = scale; // Set 16 or 32-bit index buffer size. - meshData.indexType = sizeof(MeshType::IndexType) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; + meshData.indexType = sizeof(typename MeshType::IndexType) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; // Now add the mesh to the list of meshes to render. addMeshData(meshData); From 33c5fe752634709b6d94c5265df653ae1c201750 Mon Sep 17 00:00:00 2001 From: David Williams Date: Tue, 19 Aug 2014 21:36:08 +0200 Subject: [PATCH 36/36] More cubic surface extractor tests. --- tests/TestCubicSurfaceExtractor.cpp | 42 +++++++++++++++++++++++++++++ tests/TestCubicSurfaceExtractor.h | 1 + 2 files changed, 43 insertions(+) diff --git a/tests/TestCubicSurfaceExtractor.cpp b/tests/TestCubicSurfaceExtractor.cpp index ac7ac1b3..906a2e80 100644 --- a/tests/TestCubicSurfaceExtractor.cpp +++ b/tests/TestCubicSurfaceExtractor.cpp @@ -91,6 +91,40 @@ VolumeType* createAndFillVolumeWithNoise(int32_t iVolumeSideLength, typename Vol return volData; } +// Runs the surface extractor for a given type. +template +VolumeType* createAndFillVolumeRealistic(int32_t iVolumeSideLength) +{ + //Create empty volume + VolumeType* volData = new VolumeType(Region(Vector3DInt32(0, 0, 0), Vector3DInt32(iVolumeSideLength - 1, iVolumeSideLength - 1, iVolumeSideLength - 1))); + + // Seed generator for consistency between runs. + srand(12345); + + //Fill the volume with data + for (int32_t z = 0; z < iVolumeSideLength; z++) + { + for (int32_t y = 0; y < iVolumeSideLength; y++) + { + for (int32_t x = 0; x < iVolumeSideLength; x++) + { + // Should create a checker board pattern stretched along z? This is 'realistic' in the sense + // that it's not empty/random data, and should allow significant decimation to be performed. + if ((x ^ y) & 0x01) + { + volData->setVoxelAt(x, y, z, 0); + } + else + { + volData->setVoxelAt(x, y, z, 1); + } + } + } + } + + return volData; +} + void TestCubicSurfaceExtractor::testBehaviour() { // Test with default mesh and contoller types. @@ -128,6 +162,14 @@ void TestCubicSurfaceExtractor::testEmptyVolumePerformance() QCOMPARE(emptyMesh.getNoOfVertices(), uint16_t(0)); } +void TestCubicSurfaceExtractor::testRealisticVolumePerformance() +{ + auto realisticVol = createAndFillVolumeRealistic< SimpleVolume >(128); + Mesh< CubicVertex< uint32_t >, uint16_t > realisticMesh; + QBENCHMARK{ extractCubicMeshCustom(realisticVol, Region(32, 32, 32, 63, 63, 63), &realisticMesh); } + QCOMPARE(realisticMesh.getNoOfVertices(), uint16_t(2176)); +} + void TestCubicSurfaceExtractor::testNoiseVolumePerformance() { auto noiseVol = createAndFillVolumeWithNoise< SimpleVolume >(128, 0, 2); diff --git a/tests/TestCubicSurfaceExtractor.h b/tests/TestCubicSurfaceExtractor.h index 15e0da5f..31fb1cc4 100644 --- a/tests/TestCubicSurfaceExtractor.h +++ b/tests/TestCubicSurfaceExtractor.h @@ -33,6 +33,7 @@ class TestCubicSurfaceExtractor: public QObject private slots: void testBehaviour(); void testEmptyVolumePerformance(); + void testRealisticVolumePerformance(); void testNoiseVolumePerformance(); };