From d4118a20528af40299d6f3b3a4e9605f548aad50 Mon Sep 17 00:00:00 2001 From: David Williams Date: Tue, 12 Aug 2014 16:53:47 +0200 Subject: [PATCH] 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)