From a1ac75022c01b104b1e8f86f1e8bac5e3f458221 Mon Sep 17 00:00:00 2001 From: David Williams Date: Sat, 15 Dec 2012 17:49:43 +0100 Subject: [PATCH] Initial changes required for Cubiquity. --- .../PolyVoxCore/CubicSurfaceExtractor.h | 9 +- .../PolyVoxCore/CubicSurfaceExtractor.inl | 66 +-- .../include/PolyVoxCore/DefaultIsQuadNeeded.h | 98 ++-- .../DefaultMarchingCubesController.h | 282 ++++++------ .../include/PolyVoxCore/Interpolation.h | 164 +++---- .../MarchingCubesSurfaceExtractor.h | 430 +++++++++--------- .../MarchingCubesSurfaceExtractor.inl | 101 ++-- .../PolyVoxCore/PolyVoxForwardDeclarations.h | 4 +- .../include/PolyVoxCore/SurfaceMesh.h | 4 +- .../include/PolyVoxCore/SurfaceMesh.inl | 6 +- .../include/PolyVoxCore/Vector.inl | 20 +- .../include/PolyVoxCore/VertexTypes.h | 92 +++- .../include/PolyVoxCore/VolumeResampler.inl | 2 +- library/PolyVoxCore/source/Impl/Utility.cpp | 130 +++--- library/PolyVoxCore/source/VertexTypes.cpp | 79 +--- 15 files changed, 734 insertions(+), 753 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h index 76f53f89..1c9f6cdd 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h @@ -81,7 +81,7 @@ namespace PolyVox struct IndexAndMaterial { int32_t iIndex; - int32_t uMaterial; //Should actually use the material type here, but this is ok for now. + typename VolumeType::VoxelType uMaterial; }; enum FaceNames @@ -109,13 +109,12 @@ namespace PolyVox }; public: - CubicSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh* result, bool bMergeQuads = true, IsQuadNeeded isQuadNeeded = IsQuadNeeded()); - + CubicSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh >* result, bool bMergeQuads = true, IsQuadNeeded isQuadNeeded = IsQuadNeeded()); void execute(); private: - int32_t addVertex(float fX, float fY, float fZ, uint32_t uMaterial, Array<3, IndexAndMaterial>& existingVertices); + int32_t addVertex(float fX, float fY, float fZ, typename VolumeType::VoxelType uMaterial, Array<3, IndexAndMaterial>& existingVertices); bool performQuadMerging(std::list& quads); bool mergeQuads(Quad& q1, Quad& q2); @@ -128,7 +127,7 @@ namespace PolyVox Region m_regSizeInVoxels; //The surface patch we are currently filling. - SurfaceMesh* m_meshCurrent; + SurfaceMesh >* m_meshCurrent; //Used to avoid creating duplicate vertices. Array<3, IndexAndMaterial> m_previousSliceVertices; diff --git a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.inl b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.inl index 211498c9..8dbe3c4a 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.inl @@ -35,7 +35,7 @@ namespace PolyVox const uint32_t CubicSurfaceExtractor::MaxVerticesPerPosition = 6; template - CubicSurfaceExtractor::CubicSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh* result, bool bMergeQuads, IsQuadNeeded isQuadNeeded) + CubicSurfaceExtractor::CubicSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh >* result, bool bMergeQuads, IsQuadNeeded isQuadNeeded) :m_volData(volData) ,m_regSizeInVoxels(region) ,m_meshCurrent(result) @@ -58,6 +58,10 @@ namespace PolyVox memset(m_previousSliceVertices.getRawData(), 0xff, m_previousSliceVertices.getNoOfElements() * sizeof(IndexAndMaterial)); memset(m_currentSliceVertices.getRawData(), 0xff, m_currentSliceVertices.getNoOfElements() * sizeof(IndexAndMaterial)); + uint32_t uRegionWidth = m_regSizeInVoxels.getUpperCorner().getX() - m_regSizeInVoxels.getLowerCorner().getX() + 1; + uint32_t uRegionHeight = m_regSizeInVoxels.getUpperCorner().getY() - m_regSizeInVoxels.getLowerCorner().getY() + 1; + uint32_t uRegionDepth = m_regSizeInVoxels.getUpperCorner().getZ() - m_regSizeInVoxels.getLowerCorner().getZ() + 1; + m_vecQuads[NegativeX].resize(m_regSizeInVoxels.getUpperCorner().getX() - m_regSizeInVoxels.getLowerCorner().getX() + 2); m_vecQuads[PositiveX].resize(m_regSizeInVoxels.getUpperCorner().getX() - m_regSizeInVoxels.getLowerCorner().getX() + 2); @@ -83,7 +87,7 @@ namespace PolyVox volumeSampler.setPosition(x,y,z); - uint32_t material; //Filled in by callback + typename VolumeType::VoxelType material; //Filled in by callback typename VolumeType::VoxelType currentVoxel = volumeSampler.getVoxel(); typename VolumeType::VoxelType negXVoxel = volumeSampler.peekVoxel1nx0py0pz(); typename VolumeType::VoxelType negYVoxel = volumeSampler.peekVoxel0px1ny0pz(); @@ -92,20 +96,20 @@ namespace PolyVox // X if(m_funcIsQuadNeededCallback(currentVoxel, negXVoxel, material)) { - uint32_t v0 = addVertex(static_cast(regX) - 0.5f, static_cast(regY) - 0.5f, static_cast(regZ) - 0.5f, material, m_previousSliceVertices); - uint32_t v1 = addVertex(static_cast(regX) - 0.5f, static_cast(regY) - 0.5f, static_cast(regZ) + 0.5f, material, m_currentSliceVertices); - uint32_t v2 = addVertex(static_cast(regX) - 0.5f, static_cast(regY) + 0.5f, static_cast(regZ) + 0.5f, material, m_currentSliceVertices); - uint32_t v3 = addVertex(static_cast(regX) - 0.5f, static_cast(regY) + 0.5f, static_cast(regZ) - 0.5f, material, m_previousSliceVertices); + uint32_t v0 = addVertex(regX - 0.5f, regY - 0.5f, regZ - 0.5f, material, m_previousSliceVertices); + uint32_t v1 = addVertex(regX - 0.5f, regY - 0.5f, regZ + 0.5f, material, m_currentSliceVertices); + uint32_t v2 = addVertex(regX - 0.5f, regY + 0.5f, regZ + 0.5f, material, m_currentSliceVertices); + uint32_t v3 = addVertex(regX - 0.5f, regY + 0.5f, regZ - 0.5f, material, m_previousSliceVertices); m_vecQuads[NegativeX][regX].push_back(Quad(v0, v1, v2, v3)); } if(m_funcIsQuadNeededCallback(negXVoxel, currentVoxel, material)) { - uint32_t v0 = addVertex(static_cast(regX) - 0.5f, static_cast(regY) - 0.5f, static_cast(regZ) - 0.5f, material, m_previousSliceVertices); - uint32_t v1 = addVertex(static_cast(regX) - 0.5f, static_cast(regY) - 0.5f, static_cast(regZ) + 0.5f, material, m_currentSliceVertices); - uint32_t v2 = addVertex(static_cast(regX) - 0.5f, static_cast(regY) + 0.5f, static_cast(regZ) + 0.5f, material, m_currentSliceVertices); - uint32_t v3 = addVertex(static_cast(regX) - 0.5f, static_cast(regY) + 0.5f, static_cast(regZ) - 0.5f, material, m_previousSliceVertices); + uint32_t v0 = addVertex(regX - 0.5f, regY - 0.5f, regZ - 0.5f, material, m_previousSliceVertices); + uint32_t v1 = addVertex(regX - 0.5f, regY - 0.5f, regZ + 0.5f, material, m_currentSliceVertices); + uint32_t v2 = addVertex(regX - 0.5f, regY + 0.5f, regZ + 0.5f, material, m_currentSliceVertices); + uint32_t v3 = addVertex(regX - 0.5f, regY + 0.5f, regZ - 0.5f, material, m_previousSliceVertices); m_vecQuads[PositiveX][regX].push_back(Quad(v0, v3, v2, v1)); } @@ -113,20 +117,20 @@ namespace PolyVox // Y if(m_funcIsQuadNeededCallback(currentVoxel, negYVoxel, material)) { - uint32_t v0 = addVertex(static_cast(regX) - 0.5f, static_cast(regY) - 0.5f, static_cast(regZ) - 0.5f, material, m_previousSliceVertices); - uint32_t v1 = addVertex(static_cast(regX) + 0.5f, static_cast(regY) - 0.5f, static_cast(regZ) - 0.5f, material, m_previousSliceVertices); - uint32_t v2 = addVertex(static_cast(regX) + 0.5f, static_cast(regY) - 0.5f, static_cast(regZ) + 0.5f, material, m_currentSliceVertices); - uint32_t v3 = addVertex(static_cast(regX) - 0.5f, static_cast(regY) - 0.5f, static_cast(regZ) + 0.5f, material, m_currentSliceVertices); + uint32_t v0 = addVertex(regX - 0.5f, regY - 0.5f, regZ - 0.5f, material, m_previousSliceVertices); + uint32_t v1 = addVertex(regX + 0.5f, regY - 0.5f, regZ - 0.5f, material, m_previousSliceVertices); + uint32_t v2 = addVertex(regX + 0.5f, regY - 0.5f, regZ + 0.5f, material, m_currentSliceVertices); + uint32_t v3 = addVertex(regX - 0.5f, regY - 0.5f, regZ + 0.5f, material, m_currentSliceVertices); m_vecQuads[NegativeY][regY].push_back(Quad(v0, v1, v2, v3)); } if(m_funcIsQuadNeededCallback(negYVoxel, currentVoxel, material)) { - uint32_t v0 = addVertex(static_cast(regX) - 0.5f, static_cast(regY) - 0.5f, static_cast(regZ) - 0.5f, material, m_previousSliceVertices); - uint32_t v1 = addVertex(static_cast(regX) + 0.5f, static_cast(regY) - 0.5f, static_cast(regZ) - 0.5f, material, m_previousSliceVertices); - uint32_t v2 = addVertex(static_cast(regX) + 0.5f, static_cast(regY) - 0.5f, static_cast(regZ) + 0.5f, material, m_currentSliceVertices); - uint32_t v3 = addVertex(static_cast(regX) - 0.5f, static_cast(regY) - 0.5f, static_cast(regZ) + 0.5f, material, m_currentSliceVertices); + uint32_t v0 = addVertex(regX - 0.5f, regY - 0.5f, regZ - 0.5f, material, m_previousSliceVertices); + uint32_t v1 = addVertex(regX + 0.5f, regY - 0.5f, regZ - 0.5f, material, m_previousSliceVertices); + uint32_t v2 = addVertex(regX + 0.5f, regY - 0.5f, regZ + 0.5f, material, m_currentSliceVertices); + uint32_t v3 = addVertex(regX - 0.5f, regY - 0.5f, regZ + 0.5f, material, m_currentSliceVertices); m_vecQuads[PositiveY][regY].push_back(Quad(v0, v3, v2, v1)); } @@ -134,20 +138,20 @@ namespace PolyVox // Z if(m_funcIsQuadNeededCallback(currentVoxel, negZVoxel, material)) { - uint32_t v0 = addVertex(static_cast(regX) - 0.5f, static_cast(regY) - 0.5f, static_cast(regZ) - 0.5f, material, m_previousSliceVertices); - uint32_t v1 = addVertex(static_cast(regX) - 0.5f, static_cast(regY) + 0.5f, static_cast(regZ) - 0.5f, material, m_previousSliceVertices); - uint32_t v2 = addVertex(static_cast(regX) + 0.5f, static_cast(regY) + 0.5f, static_cast(regZ) - 0.5f, material, m_previousSliceVertices); - uint32_t v3 = addVertex(static_cast(regX) + 0.5f, static_cast(regY) - 0.5f, static_cast(regZ) - 0.5f, material, m_previousSliceVertices); + uint32_t v0 = addVertex(regX - 0.5f, regY - 0.5f, regZ - 0.5f, material, m_previousSliceVertices); + uint32_t v1 = addVertex(regX - 0.5f, regY + 0.5f, regZ - 0.5f, material, m_previousSliceVertices); + uint32_t v2 = addVertex(regX + 0.5f, regY + 0.5f, regZ - 0.5f, material, m_previousSliceVertices); + uint32_t v3 = addVertex(regX + 0.5f, regY - 0.5f, regZ - 0.5f, material, m_previousSliceVertices); m_vecQuads[NegativeZ][regZ].push_back(Quad(v0, v1, v2, v3)); } if(m_funcIsQuadNeededCallback(negZVoxel, currentVoxel, material)) { - uint32_t v0 = addVertex(static_cast(regX) - 0.5f, static_cast(regY) - 0.5f, static_cast(regZ) - 0.5f, material, m_previousSliceVertices); - uint32_t v1 = addVertex(static_cast(regX) - 0.5f, static_cast(regY) + 0.5f, static_cast(regZ) - 0.5f, material, m_previousSliceVertices); - uint32_t v2 = addVertex(static_cast(regX) + 0.5f, static_cast(regY) + 0.5f, static_cast(regZ) - 0.5f, material, m_previousSliceVertices); - uint32_t v3 = addVertex(static_cast(regX) + 0.5f, static_cast(regY) - 0.5f, static_cast(regZ) - 0.5f, material, m_previousSliceVertices); + uint32_t v0 = addVertex(regX - 0.5f, regY - 0.5f, regZ - 0.5f, material, m_previousSliceVertices); + uint32_t v1 = addVertex(regX - 0.5f, regY + 0.5f, regZ - 0.5f, material, m_previousSliceVertices); + uint32_t v2 = addVertex(regX + 0.5f, regY + 0.5f, regZ - 0.5f, material, m_previousSliceVertices); + uint32_t v3 = addVertex(regX + 0.5f, regY - 0.5f, regZ - 0.5f, material, m_previousSliceVertices); m_vecQuads[PositiveZ][regZ].push_back(Quad(v0, v3, v2, v1)); } @@ -184,7 +188,7 @@ namespace PolyVox } m_meshCurrent->m_Region = m_regSizeInVoxels; - m_meshCurrent->removeUnusedVertices(); + //m_meshCurrent->removeUnusedVertices(); m_meshCurrent->m_vecLodRecords.clear(); LodRecord lodRecord; @@ -194,7 +198,7 @@ namespace PolyVox } template - int32_t CubicSurfaceExtractor::addVertex(float fX, float fY, float fZ, uint32_t uMaterialIn, Array<3, IndexAndMaterial>& existingVertices) + int32_t CubicSurfaceExtractor::addVertex(float fX, float fY, float fZ, typename VolumeType::VoxelType uMaterialIn, Array<3, IndexAndMaterial>& existingVertices) { uint32_t uX = static_cast(fX + 0.75f); uint32_t uY = static_cast(fY + 0.75f); @@ -206,14 +210,14 @@ namespace PolyVox if(rEntry.iIndex == -1) { //No vertices matched and we've now hit an empty space. Fill it by creating a vertex. - rEntry.iIndex = m_meshCurrent->addVertex(PositionMaterial(Vector3DFloat(fX, fY, fZ), uMaterialIn)); + rEntry.iIndex = m_meshCurrent->addVertex(PositionMaterial (Vector3DFloat(fX, fY, fZ), uMaterialIn)); rEntry.uMaterial = uMaterialIn; return rEntry.iIndex; } //If we have an existing vertex and the material matches then we can return it. - if(rEntry.uMaterial == static_cast(uMaterialIn)) + if(rEntry.uMaterial == uMaterialIn) { return rEntry.iIndex; } @@ -260,7 +264,7 @@ namespace PolyVox { //All four vertices of a given quad have the same material, //so just check that the first pair of vertices match. - if(std::abs(m_meshCurrent->getVertices()[q1.vertices[0]].getMaterial() - m_meshCurrent->getVertices()[q2.vertices[0]].getMaterial()) < 0.001) + if(m_meshCurrent->getVertices()[q1.vertices[0]].getMaterial() == m_meshCurrent->getVertices()[q2.vertices[0]].getMaterial()) { //Now check whether quad 2 is adjacent to quad one by comparing vertices. //Adjacent quads must share two vertices, and the second quad could be to the diff --git a/library/PolyVoxCore/include/PolyVoxCore/DefaultIsQuadNeeded.h b/library/PolyVoxCore/include/PolyVoxCore/DefaultIsQuadNeeded.h index 81ddb9e5..ea6cf673 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/DefaultIsQuadNeeded.h +++ b/library/PolyVoxCore/include/PolyVoxCore/DefaultIsQuadNeeded.h @@ -1,50 +1,50 @@ -/******************************************************************************* -Copyright (c) 2005-2009 David Williams - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*******************************************************************************/ - -#ifndef __PolyVox_DefaultIsQuadNeeded_H__ -#define __PolyVox_DefaultIsQuadNeeded_H__ - -#include "PolyVoxCore/Impl/TypeDef.h" - -namespace PolyVox -{ - template - class DefaultIsQuadNeeded - { - public: - bool operator()(VoxelType back, VoxelType front, uint32_t& materialToUse) - { - if((back > 0) && (front == 0)) - { - materialToUse = static_cast(back); - return true; - } - else - { - return false; - } - } - }; -} - +/******************************************************************************* +Copyright (c) 2005-2009 David Williams + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*******************************************************************************/ + +#ifndef __PolyVox_DefaultIsQuadNeeded_H__ +#define __PolyVox_DefaultIsQuadNeeded_H__ + +#include "PolyVoxCore/Impl/TypeDef.h" + +namespace PolyVox +{ + template + class DefaultIsQuadNeeded + { + public: + bool operator()(VoxelType back, VoxelType front, float& materialToUse) + { + if((back > 0) && (front == 0)) + { + materialToUse = static_cast(back); + return true; + } + else + { + return false; + } + } + }; +} + #endif //__PolyVox_DefaultIsQuadNeeded_H__ \ No newline at end of file diff --git a/library/PolyVoxCore/include/PolyVoxCore/DefaultMarchingCubesController.h b/library/PolyVoxCore/include/PolyVoxCore/DefaultMarchingCubesController.h index 8d5359e9..43aa0956 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/DefaultMarchingCubesController.h +++ b/library/PolyVoxCore/include/PolyVoxCore/DefaultMarchingCubesController.h @@ -1,148 +1,134 @@ -/******************************************************************************* -Copyright (c) 2005-2009 David Williams - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*******************************************************************************/ - -#ifndef __PolyVox_MarchingCubesController_H__ -#define __PolyVox_MarchingCubesController_H__ - -#include "PolyVoxCore/BaseVolume.h" - -#include - -namespace PolyVox -{ - /** - * This class provides a default implementation of a controller for the MarchingCubesSurfaceExtractor. It controls the behaviour of the - * MarchingCubesSurfaceExtractor and provides the required properties from the underlying voxel type. - * - * PolyVox does not enforce any requirements regarding what data must be present in a voxel, and instead allows any primitive or user-defined - * type to be used. However, the Marching Cubes algorithm does have some requirents about the underlying data in that conceptually it operates - * on a density field. In addition, the PolyVox implementation of the Marching Cubes algorithm also understands the idea of each voxel - * having a material which is copied into the vertex data. - * - * Because we want the MarchingCubesSurfaceExtractor to work on any voxel type, we use a Marching Cubes controller (passed as - * a parameter of the MarchingCubesSurfaceExtractor) to expose the required properties. This parameter defaults to the DefaultMarchingCubesController. - * The main implementation of this class is designed to work with primitives data types, and the class is also specialised for the Material, - * Density and MaterialdensityPair classes. - * - * If you create a custom class for your voxel data then you probably want to include a specialisation of DefaultMarchingCubesController, - * though you don't have to if you don't want to use the Marching Cubes algorithm or if you prefer to define a seperate Marching Cubes controller - * and pass it as an explicit parameter (rather than relying on the default). - * - * For primitive types, the DefaultMarchingCubesController considers the value of the voxel to represent it's density and just returns a constant - * for the material. So you can, for example, run the MarchingCubesSurfaceExtractor on a volume of floats or ints. - * - * It is possible to customise the behaviour of the controller by providing a threshold value through the constructor. The extracted surface - * will pass through the density value specified by the threshold, and so you should make sure that the threshold value you choose is between - * the minimum and maximum values found in your volume data. By default it is in the middle of the representable range of the underlying type. - * - * \sa MarchingCubesSurfaceExtractor - * - */ - template - class DefaultMarchingCubesController - { - public: - /// Used to inform the MarchingCubesSurfaceExtractor about which type it should use for representing densities. - typedef VoxelType DensityType; - /// Used to inform the MarchingCubesSurfaceExtractor about which type it should use for representing materials. We're using a float here - /// because this implementation always returns a constant value off 1.0f. PolyVox also uses floats to store the materials in the mesh vertices - /// but this is not really desirable on modern hardware. We'll probably come back to material representation in the future. - typedef float MaterialType; - - /** - * Constructor - * - * This version of the constructor takes no parameters and sets the threshold to the middle of the representable range of the underlying type. - * For example, if the voxel type is 'uint8_t' then the representable range is 0-255, and the threshold will be set to 127. On the other hand, - * 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) - ,m_eWrapMode(WrapModes::Border) - ,m_tBorder(VoxelType(0)) - { - } - - /** - * Converts the underlying voxel type into a density value. - * - * The default implementation of this function just returns the voxel type directly and is suitable for primitives types. Specialisations of - * this class can modify this behaviour. - */ - DensityType convertToDensity(VoxelType voxel) - { - return voxel; - } - - /** - * Converts the underlying voxel type into a material value. - * - * The default implementation of this function just returns the constant '1'. There's not much else it can do, as it needs to work with primitive - * types and the actual value of the type is already being considered to be the density. Specialisations of this class can modify this behaviour. - */ - MaterialType convertToMaterial(VoxelType /*voxel*/) - { - return 1; - } - - VoxelType getBorderValue(void) - { - return m_tBorder; - } - - /** - * Returns the density value which was passed to the constructor. - * - * As mentioned in the class description, the extracted surface will pass through the density value specified by the threshold, and so you - * should make sure that the threshold value you choose is between the minimum and maximum values found in your volume data. By default it - * is in the middle of the representable range of the underlying type. - */ - DensityType getThreshold(void) - { - return m_tThreshold; - } - - WrapMode getWrapMode(void) - { - return m_eWrapMode; - } - - void setThreshold(DensityType tThreshold) - { - m_tThreshold = tThreshold; - } - - void setWrapMode(WrapMode eWrapMode, VoxelType tBorder = VoxelType(0)) - { - m_eWrapMode = eWrapMode; - m_tBorder = tBorder; - } - - private: - DensityType m_tThreshold; - WrapMode m_eWrapMode; - VoxelType m_tBorder; - }; -} - -#endif +/******************************************************************************* +Copyright (c) 2005-2009 David Williams + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*******************************************************************************/ + +#ifndef __PolyVox_MarchingCubesController_H__ +#define __PolyVox_MarchingCubesController_H__ + +#include "PolyVoxCore/BaseVolume.h" + +#include + +namespace PolyVox +{ + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /// This class provides a default implementation of a controller for the MarchingCubesSurfaceExtractor. It controls the behaviour of the + /// MarchingCubesSurfaceExtractor and provides the required properties from the underlying voxel type. + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /// PolyVox does not enforce any requirements regarding what data must be present in a voxel, and instead allows any primitive or user-defined + /// type to be used. However, the Marching Cubes algorithm does have some requirents about the underlying data in that conceptually it operates + /// on a density field. In addition, the PolyVox implementation of the Marching Cubes algorithm also understands the idea of each voxel + /// having a material which is copied into the vertex data. + /// + /// Because we want the MarchingCubesSurfaceExtractor to work on any voxel type, we use a Marching Cubes controller (passed as + /// a parameter of the MarchingCubesSurfaceExtractor) to expose the required properties. This parameter defaults to the DefaultMarchingCubesController. + /// The main implementation of this class is designed to work with primitives data types, and the class is also specialised for the Material, + /// Density and MaterialdensityPair classes. + /// + /// If you create a custom class for your voxel data then you probably want to include a specialisation of DefaultMarchingCubesController, + /// though you don't have to if you don't want to use the Marching Cubes algorithm or if you prefer to define a seperate Marching Cubes controller + /// and pass it as an explicit parameter (rather than relying on the default). + /// + /// For primitive types, the DefaultMarchingCubesController considers the value of the voxel to represent it's density and just returns a constant + /// for the material. So you can, for example, run the MarchingCubesSurfaceExtractor on a volume of floats or ints. + /// + /// It is possible to customise the behaviour of the controller by providing a threshold value through the constructor. The extracted surface + /// will pass through the density value specified by the threshold, and so you should make sure that the threshold value you choose is between + /// the minimum and maximum values found in your volume data. By default it is in the middle of the representable range of the underlying type. + /// + /// \sa MarchingCubesSurfaceExtractor + /// + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + template + class DefaultMarchingCubesController + { + public: + /// Used to inform the MarchingCubesSurfaceExtractor about which type it should use for representing densities. + typedef VoxelType DensityType; + /// Used to inform the MarchingCubesSurfaceExtractor about which type it should use for representing materials. We're using a float here + /// because this implementation always returns a constant value off 1.0f. PolyVox also uses floats to store the materials in the mesh vertices + /// but this is not really desirable on modern hardware. We'll probably come back to material representation in the future. + typedef float MaterialType; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /// Constructor + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /// This version of the constructor takes no parameters and sets the threshold to the middle of the representable range of the underlying type. + /// For example, if the voxel type is 'uint8_t' then the representable range is 0-255, and the threshold will be set to 127. On the other hand, + /// 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; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /// Constructor + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /// This version of the constructor allows you to set a custom threshold. + /// \param tThreshold The threshold to use. + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + DefaultMarchingCubesController(DensityType tThreshold) + { + m_tThreshold = tThreshold; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /// Converts the underlying voxel type into a density value. + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /// The default implementation of this function just returns the voxel type directly and is suitable for primitives types. Specialisations of + /// this class can modify this behaviour. + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + DensityType convertToDensity(VoxelType voxel) + { + return voxel; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /// Converts the underlying voxel type into a material value. + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /// The default implementation of this function just returns the constant '1'. There's not much else it can do, as it needs to work with primitive + /// types and the actual value of the type is already being considered to be the density. Specialisations of this class can modify this behaviour. + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + MaterialType convertToMaterial(VoxelType voxel) + { + return 1; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /// Returns the density value which was passed to the constructor. + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /// As mentioned in the class description, the extracted surface will pass through the density value specified by the threshold, and so you + /// should make sure that the threshold value you choose is between the minimum and maximum values found in your volume data. By default it + ///is in the middle of the representable range of the underlying type. + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + DensityType getThreshold(void) + { + return m_tThreshold; + } + + private: + DensityType m_tThreshold; + }; +} + +#endif diff --git a/library/PolyVoxCore/include/PolyVoxCore/Interpolation.h b/library/PolyVoxCore/include/PolyVoxCore/Interpolation.h index 5c76fe47..e52b321f 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Interpolation.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Interpolation.h @@ -1,82 +1,82 @@ -/******************************************************************************* -Copyright (c) 2005-2009 David Williams - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*******************************************************************************/ - -#ifndef __PolyVox_Interpolation_H__ -#define __PolyVox_Interpolation_H__ - -#include - -namespace PolyVox -{ - template - Type lerp( - const Type& v0,const Type& v1, - const float x) - { - assert((x >= 0.0f) && (x <= 1.0f)); - - //Interpolate along X - Type v0_1 = v0 + x * (v1 - v0); - - return v0_1; - } - - template - Type bilerp( - const Type& v00,const Type& v10,const Type& v01,const Type& v11, - const float x, const float y) - { - assert((x >= 0.0f) && (y >= 0.0f) && - (x <= 1.0f) && (y <= 1.0f)); - - // Linearly interpolate along x - Type v00_10 = lerp(v00, v10, x); - Type v01_11 = lerp(v01, v11, x); - - // And linearly interpolate the results along y - Type v00_10__v01_11 = lerp(v00_10, v01_11, y); - - return v00_10__v01_11; - } - - template - Type trilerp( - const Type& v000,const Type& v100,const Type& v010,const Type& v110, - const Type& v001,const Type& v101,const Type& v011,const Type& v111, - const float x, const float y, const float z) - { - assert((x >= 0.0f) && (y >= 0.0f) && (z >= 0.0f) && - (x <= 1.0f) && (y <= 1.0f) && (z <= 1.0f)); - - // Bilinearly interpolate along Y - Type v000_v100__v010_v110 = bilerp(v000, v100, v010, v110, x, y); - Type v001_v101__v011_v111 = bilerp(v001, v101, v011, v111, x, y); - - // And linearly interpolate the results along z - Type v000_v100__v010_v110____v001_v101__v011_v111 = lerp(v000_v100__v010_v110, v001_v101__v011_v111, z); - - return v000_v100__v010_v110____v001_v101__v011_v111; - } -} - -#endif //__PolyVox_Interpolation_H__ +/******************************************************************************* +Copyright (c) 2005-2009 David Williams + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*******************************************************************************/ + +#ifndef __PolyVox_Interpolation_H__ +#define __PolyVox_Interpolation_H__ + +#include + +namespace PolyVox +{ + template + Type lerp( + const Type& v0,const Type& v1, + const float x) + { + assert((x >= 0.0f) && (x <= 1.0f)); + + //Interpolate along X + Type v0_1 = (v1 - v0) * x + v0; + + return v0_1; + } + + template + Type bilerp( + const Type& v00,const Type& v10,const Type& v01,const Type& v11, + const float x, const float y) + { + assert((x >= 0.0f) && (y >= 0.0f) && + (x <= 1.0f) && (y <= 1.0f)); + + // Linearly interpolate along x + Type v00_10 = lerp(v00, v10, x); + Type v01_11 = lerp(v01, v11, x); + + // And linearly interpolate the results along y + Type v00_10__v01_11 = lerp(v00_10, v01_11, y); + + return v00_10__v01_11; + } + + template + Type trilerp( + const Type& v000,const Type& v100,const Type& v010,const Type& v110, + const Type& v001,const Type& v101,const Type& v011,const Type& v111, + const float x, const float y, const float z) + { + assert((x >= 0.0f) && (y >= 0.0f) && (z >= 0.0f) && + (x <= 1.0f) && (y <= 1.0f) && (z <= 1.0f)); + + // Bilinearly interpolate along Y + Type v000_v100__v010_v110 = bilerp(v000, v100, v010, v110, x, y); + Type v001_v101__v011_v111 = bilerp(v001, v101, v011, v111, x, y); + + // And linearly interpolate the results along z + Type v000_v100__v010_v110____v001_v101__v011_v111 = lerp(v000_v100__v010_v110, v001_v101__v011_v111, z); + + return v000_v100__v010_v110____v001_v101__v011_v111; + } +} + +#endif //__PolyVox_Interpolation_H__ diff --git a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h index b0d7c694..6fa98986 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h @@ -1,210 +1,220 @@ -/******************************************************************************* -Copyright (c) 2005-2009 David Williams - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*******************************************************************************/ - -#ifndef __PolyVox_SurfaceExtractor_H__ -#define __PolyVox_SurfaceExtractor_H__ - -#include "Impl/MarchingCubesTables.h" -#include "Impl/TypeDef.h" - -#include "PolyVoxCore/Array.h" -#include "PolyVoxCore/SurfaceMesh.h" -#include "PolyVoxCore/DefaultMarchingCubesController.h" - -namespace PolyVox -{ - template< typename VolumeType, typename Controller = DefaultMarchingCubesController > - class MarchingCubesSurfaceExtractor - { - public: - MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh* result, Controller controller = Controller()); - - void execute(); - - private: - //Compute the cell bitmask for a particular slice in z. - template - uint32_t computeBitmaskForSlice(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask); - - //Compute the cell bitmask for a given cell. - template - void computeBitmaskForCell(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask, uint32_t uXRegSpace, uint32_t uYRegSpace); - - //Use the cell bitmasks to generate all the vertices needed for that slice - void generateVerticesForSlice(const Array2DUint8& pCurrentBitmask, - Array2DInt32& m_pCurrentVertexIndicesX, - Array2DInt32& m_pCurrentVertexIndicesY, - Array2DInt32& m_pCurrentVertexIndicesZ); - - //////////////////////////////////////////////////////////////////////////////// - // NOTE: These two functions are in the .h file rather than the .inl due to an apparent bug in VC2010. - //See http://stackoverflow.com/questions/1484885/strange-vc-compile-error-c2244 for details. - //////////////////////////////////////////////////////////////////////////////// - Vector3DFloat computeCentralDifferenceGradient(const typename VolumeType::Sampler& volIter) - { - //FIXME - Should actually use DensityType here, both in principle and because the maths may be - //faster (and to reduce casts). So it would be good to add a way to get DensityType from a voxel. - //But watch out for when the DensityType is unsigned and the difference could be negative. - float voxel1nx = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx0py0pz())); - float voxel1px = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px0py0pz())); - - float voxel1ny = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1ny0pz())); - float voxel1py = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1py0pz())); - - float voxel1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px0py1nz())); - float voxel1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px0py1pz())); - - return Vector3DFloat - ( - voxel1nx - voxel1px, - voxel1ny - voxel1py, - voxel1nz - voxel1pz - ); - } - - Vector3DFloat computeSobelGradient(const typename VolumeType::Sampler& volIter) - { - static const int weights[3][3][3] = { { {2,3,2}, {3,6,3}, {2,3,2} }, { - {3,6,3}, {6,0,6}, {3,6,3} }, { {2,3,2}, {3,6,3}, {2,3,2} } }; - - //FIXME - Should actually use DensityType here, both in principle and because the maths may be - //faster (and to reduce casts). So it would be good to add a way to get DensityType from a voxel. - //But watch out for when the DensityType is unsigned and the difference could be negative. - const float pVoxel1nx1ny1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx1ny1nz())); - const float pVoxel1nx1ny0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx1ny0pz())); - const float pVoxel1nx1ny1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx1ny1pz())); - const float pVoxel1nx0py1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx0py1nz())); - const float pVoxel1nx0py0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx0py0pz())); - const float pVoxel1nx0py1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx0py1pz())); - const float pVoxel1nx1py1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx1py1nz())); - const float pVoxel1nx1py0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx1py0pz())); - const float pVoxel1nx1py1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx1py1pz())); - - const float pVoxel0px1ny1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1ny1nz())); - const float pVoxel0px1ny0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1ny0pz())); - const float pVoxel0px1ny1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1ny1pz())); - const float pVoxel0px0py1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px0py1nz())); - //const float pVoxel0px0py0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px0py0pz())); - const float pVoxel0px0py1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px0py1pz())); - const float pVoxel0px1py1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1py1nz())); - const float pVoxel0px1py0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1py0pz())); - const float pVoxel0px1py1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1py1pz())); - - const float pVoxel1px1ny1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px1ny1nz())); - const float pVoxel1px1ny0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px1ny0pz())); - const float pVoxel1px1ny1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px1ny1pz())); - const float pVoxel1px0py1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px0py1nz())); - const float pVoxel1px0py0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px0py0pz())); - const float pVoxel1px0py1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px0py1pz())); - const float pVoxel1px1py1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px1py1nz())); - const float pVoxel1px1py0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px1py0pz())); - const float pVoxel1px1py1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px1py1pz())); - - const float xGrad(- weights[0][0][0] * pVoxel1nx1ny1nz - - weights[1][0][0] * pVoxel1nx1ny0pz - weights[2][0][0] * - pVoxel1nx1ny1pz - weights[0][1][0] * pVoxel1nx0py1nz - - weights[1][1][0] * pVoxel1nx0py0pz - weights[2][1][0] * - pVoxel1nx0py1pz - weights[0][2][0] * pVoxel1nx1py1nz - - weights[1][2][0] * pVoxel1nx1py0pz - weights[2][2][0] * - pVoxel1nx1py1pz + weights[0][0][2] * pVoxel1px1ny1nz + - weights[1][0][2] * pVoxel1px1ny0pz + weights[2][0][2] * - pVoxel1px1ny1pz + weights[0][1][2] * pVoxel1px0py1nz + - weights[1][1][2] * pVoxel1px0py0pz + weights[2][1][2] * - pVoxel1px0py1pz + weights[0][2][2] * pVoxel1px1py1nz + - weights[1][2][2] * pVoxel1px1py0pz + weights[2][2][2] * - pVoxel1px1py1pz); - - const float yGrad(- weights[0][0][0] * pVoxel1nx1ny1nz - - weights[1][0][0] * pVoxel1nx1ny0pz - weights[2][0][0] * - pVoxel1nx1ny1pz + weights[0][2][0] * pVoxel1nx1py1nz + - weights[1][2][0] * pVoxel1nx1py0pz + weights[2][2][0] * - pVoxel1nx1py1pz - weights[0][0][1] * pVoxel0px1ny1nz - - weights[1][0][1] * pVoxel0px1ny0pz - weights[2][0][1] * - pVoxel0px1ny1pz + weights[0][2][1] * pVoxel0px1py1nz + - weights[1][2][1] * pVoxel0px1py0pz + weights[2][2][1] * - pVoxel0px1py1pz - weights[0][0][2] * pVoxel1px1ny1nz - - weights[1][0][2] * pVoxel1px1ny0pz - weights[2][0][2] * - pVoxel1px1ny1pz + weights[0][2][2] * pVoxel1px1py1nz + - weights[1][2][2] * pVoxel1px1py0pz + weights[2][2][2] * - pVoxel1px1py1pz); - - const float zGrad(- weights[0][0][0] * pVoxel1nx1ny1nz + - weights[2][0][0] * pVoxel1nx1ny1pz - weights[0][1][0] * - pVoxel1nx0py1nz + weights[2][1][0] * pVoxel1nx0py1pz - - weights[0][2][0] * pVoxel1nx1py1nz + weights[2][2][0] * - pVoxel1nx1py1pz - weights[0][0][1] * pVoxel0px1ny1nz + - weights[2][0][1] * pVoxel0px1ny1pz - weights[0][1][1] * - pVoxel0px0py1nz + weights[2][1][1] * pVoxel0px0py1pz - - weights[0][2][1] * pVoxel0px1py1nz + weights[2][2][1] * - pVoxel0px1py1pz - weights[0][0][2] * pVoxel1px1ny1nz + - weights[2][0][2] * pVoxel1px1ny1pz - weights[0][1][2] * - pVoxel1px0py1nz + weights[2][1][2] * pVoxel1px0py1pz - - weights[0][2][2] * pVoxel1px1py1nz + weights[2][2][2] * - pVoxel1px1py1pz); - - //Note: The above actually give gradients going from low density to high density. - //For our normals we want the the other way around, so we switch the components as we return them. - return Vector3DFloat(-xGrad,-yGrad,-zGrad); - } - //////////////////////////////////////////////////////////////////////////////// - // End of compiler bug workaroumd. - //////////////////////////////////////////////////////////////////////////////// - - //Use the cell bitmasks to generate all the indices needed for that slice - void generateIndicesForSlice(const Array2DUint8& pPreviousBitmask, - const Array2DInt32& m_pPreviousVertexIndicesX, - const Array2DInt32& m_pPreviousVertexIndicesY, - const Array2DInt32& m_pPreviousVertexIndicesZ, - const Array2DInt32& m_pCurrentVertexIndicesX, - const Array2DInt32& m_pCurrentVertexIndicesY); - - //The volume data and a sampler to access it. - VolumeType* m_volData; - typename VolumeType::Sampler m_sampVolume; - - //Used to return the number of cells in a slice which contain triangles. - uint32_t m_uNoOfOccupiedCells; - - //The surface patch we are currently filling. - SurfaceMesh* m_meshCurrent; - - //Information about the region we are currently processing - Region m_regSizeInVoxels; - Region m_regSizeInCells; - /*Region m_regSizeInVoxelsCropped; - Region m_regSizeInVoxelsUncropped; - Region m_regVolumeCropped;*/ - Region m_regSlicePrevious; - Region m_regSliceCurrent; - - //Used to convert arbitrary voxel types in densities and materials. - Controller m_controller; - - //Our threshold value - typename Controller::DensityType m_tThreshold; - }; -} - -#include "PolyVoxCore/MarchingCubesSurfaceExtractor.inl" - -#endif +/******************************************************************************* +Copyright (c) 2005-2009 David Williams + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*******************************************************************************/ + +#ifndef __PolyVox_SurfaceExtractor_H__ +#define __PolyVox_SurfaceExtractor_H__ + +#include "Impl/MarchingCubesTables.h" +#include "Impl/TypeDef.h" + +#include "PolyVoxCore/Array.h" +#include "PolyVoxCore/SurfaceMesh.h" +#include "PolyVoxCore/DefaultMarchingCubesController.h" + +namespace PolyVox +{ + template< typename VolumeType, typename Controller = DefaultMarchingCubesController > + class MarchingCubesSurfaceExtractor + { + public: + MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh >* result, Controller controller = Controller()); + + void execute(); + + private: + //Compute the cell bitmask for a particular slice in z. + template + uint32_t computeBitmaskForSlice(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask); + + //Compute the cell bitmask for a given cell. + template + void computeBitmaskForCell(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask); + + //Use the cell bitmasks to generate all the vertices needed for that slice + void generateVerticesForSlice(const Array2DUint8& pCurrentBitmask, + Array2DInt32& m_pCurrentVertexIndicesX, + Array2DInt32& m_pCurrentVertexIndicesY, + Array2DInt32& m_pCurrentVertexIndicesZ); + + //////////////////////////////////////////////////////////////////////////////// + // NOTE: These two functions are in the .h file rather than the .inl due to an apparent bug in VC2010. + //See http://stackoverflow.com/questions/1484885/strange-vc-compile-error-c2244 for details. + //////////////////////////////////////////////////////////////////////////////// + Vector3DFloat computeCentralDifferenceGradient(const typename VolumeType::Sampler& volIter) + { + //FIXME - Should actually use DensityType here, both in principle and because the maths may be + //faster (and to reduce casts). So it would be good to add a way to get DensityType from a voxel. + //But watch out for when the DensityType is unsigned and the difference could be negative. + float voxel1nx = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx0py0pz())); + float voxel1px = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px0py0pz())); + + float voxel1ny = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1ny0pz())); + float voxel1py = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1py0pz())); + + float voxel1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px0py1nz())); + float voxel1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px0py1pz())); + + return Vector3DFloat + ( + voxel1nx - voxel1px, + voxel1ny - voxel1py, + voxel1nz - voxel1pz + ); + } + + Vector3DFloat computeSobelGradient(const typename VolumeType::Sampler& volIter) + { + static const int weights[3][3][3] = { { {2,3,2}, {3,6,3}, {2,3,2} }, { + {3,6,3}, {6,0,6}, {3,6,3} }, { {2,3,2}, {3,6,3}, {2,3,2} } }; + + //FIXME - Should actually use DensityType here, both in principle and because the maths may be + //faster (and to reduce casts). So it would be good to add a way to get DensityType from a voxel. + //But watch out for when the DensityType is unsigned and the difference could be negative. + const float pVoxel1nx1ny1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx1ny1nz())); + const float pVoxel1nx1ny0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx1ny0pz())); + const float pVoxel1nx1ny1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx1ny1pz())); + const float pVoxel1nx0py1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx0py1nz())); + const float pVoxel1nx0py0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx0py0pz())); + const float pVoxel1nx0py1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx0py1pz())); + const float pVoxel1nx1py1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx1py1nz())); + const float pVoxel1nx1py0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx1py0pz())); + const float pVoxel1nx1py1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx1py1pz())); + + const float pVoxel0px1ny1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1ny1nz())); + const float pVoxel0px1ny0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1ny0pz())); + const float pVoxel0px1ny1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1ny1pz())); + const float pVoxel0px0py1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px0py1nz())); + //const float pVoxel0px0py0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px0py0pz())); + const float pVoxel0px0py1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px0py1pz())); + const float pVoxel0px1py1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1py1nz())); + const float pVoxel0px1py0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1py0pz())); + const float pVoxel0px1py1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1py1pz())); + + const float pVoxel1px1ny1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px1ny1nz())); + const float pVoxel1px1ny0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px1ny0pz())); + const float pVoxel1px1ny1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px1ny1pz())); + const float pVoxel1px0py1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px0py1nz())); + const float pVoxel1px0py0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px0py0pz())); + const float pVoxel1px0py1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px0py1pz())); + const float pVoxel1px1py1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px1py1nz())); + const float pVoxel1px1py0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px1py0pz())); + const float pVoxel1px1py1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px1py1pz())); + + const float xGrad(- weights[0][0][0] * pVoxel1nx1ny1nz - + weights[1][0][0] * pVoxel1nx1ny0pz - weights[2][0][0] * + pVoxel1nx1ny1pz - weights[0][1][0] * pVoxel1nx0py1nz - + weights[1][1][0] * pVoxel1nx0py0pz - weights[2][1][0] * + pVoxel1nx0py1pz - weights[0][2][0] * pVoxel1nx1py1nz - + weights[1][2][0] * pVoxel1nx1py0pz - weights[2][2][0] * + pVoxel1nx1py1pz + weights[0][0][2] * pVoxel1px1ny1nz + + weights[1][0][2] * pVoxel1px1ny0pz + weights[2][0][2] * + pVoxel1px1ny1pz + weights[0][1][2] * pVoxel1px0py1nz + + weights[1][1][2] * pVoxel1px0py0pz + weights[2][1][2] * + pVoxel1px0py1pz + weights[0][2][2] * pVoxel1px1py1nz + + weights[1][2][2] * pVoxel1px1py0pz + weights[2][2][2] * + pVoxel1px1py1pz); + + const float yGrad(- weights[0][0][0] * pVoxel1nx1ny1nz - + weights[1][0][0] * pVoxel1nx1ny0pz - weights[2][0][0] * + pVoxel1nx1ny1pz + weights[0][2][0] * pVoxel1nx1py1nz + + weights[1][2][0] * pVoxel1nx1py0pz + weights[2][2][0] * + pVoxel1nx1py1pz - weights[0][0][1] * pVoxel0px1ny1nz - + weights[1][0][1] * pVoxel0px1ny0pz - weights[2][0][1] * + pVoxel0px1ny1pz + weights[0][2][1] * pVoxel0px1py1nz + + weights[1][2][1] * pVoxel0px1py0pz + weights[2][2][1] * + pVoxel0px1py1pz - weights[0][0][2] * pVoxel1px1ny1nz - + weights[1][0][2] * pVoxel1px1ny0pz - weights[2][0][2] * + pVoxel1px1ny1pz + weights[0][2][2] * pVoxel1px1py1nz + + weights[1][2][2] * pVoxel1px1py0pz + weights[2][2][2] * + pVoxel1px1py1pz); + + const float zGrad(- weights[0][0][0] * pVoxel1nx1ny1nz + + weights[2][0][0] * pVoxel1nx1ny1pz - weights[0][1][0] * + pVoxel1nx0py1nz + weights[2][1][0] * pVoxel1nx0py1pz - + weights[0][2][0] * pVoxel1nx1py1nz + weights[2][2][0] * + pVoxel1nx1py1pz - weights[0][0][1] * pVoxel0px1ny1nz + + weights[2][0][1] * pVoxel0px1ny1pz - weights[0][1][1] * + pVoxel0px0py1nz + weights[2][1][1] * pVoxel0px0py1pz - + weights[0][2][1] * pVoxel0px1py1nz + weights[2][2][1] * + pVoxel0px1py1pz - weights[0][0][2] * pVoxel1px1ny1nz + + weights[2][0][2] * pVoxel1px1ny1pz - weights[0][1][2] * + pVoxel1px0py1nz + weights[2][1][2] * pVoxel1px0py1pz - + weights[0][2][2] * pVoxel1px1py1nz + weights[2][2][2] * + pVoxel1px1py1pz); + + //Note: The above actually give gradients going from low density to high density. + //For our normals we want the the other way around, so we switch the components as we return them. + return Vector3DFloat(-xGrad,-yGrad,-zGrad); + } + //////////////////////////////////////////////////////////////////////////////// + // End of compiler bug workaroumd. + //////////////////////////////////////////////////////////////////////////////// + + //Use the cell bitmasks to generate all the indices needed for that slice + void generateIndicesForSlice(const Array2DUint8& pPreviousBitmask, + const Array2DInt32& m_pPreviousVertexIndicesX, + const Array2DInt32& m_pPreviousVertexIndicesY, + const Array2DInt32& m_pPreviousVertexIndicesZ, + const Array2DInt32& m_pCurrentVertexIndicesX, + const Array2DInt32& m_pCurrentVertexIndicesY); + + //The volume data and a sampler to access it. + VolumeType* m_volData; + typename VolumeType::Sampler m_sampVolume; + + //Holds a position in volume space. + int32_t iXVolSpace; + int32_t iYVolSpace; + int32_t iZVolSpace; + + //Holds a position in region space. + uint32_t uXRegSpace; + uint32_t uYRegSpace; + uint32_t uZRegSpace; + + //Used to return the number of cells in a slice which contain triangles. + uint32_t m_uNoOfOccupiedCells; + + //The surface patch we are currently filling. + SurfaceMesh >* m_meshCurrent; + + //Information about the region we are currently processing + Region m_regSizeInVoxels; + Region m_regSizeInCells; + /*Region m_regSizeInVoxelsCropped; + Region m_regSizeInVoxelsUncropped; + Region m_regVolumeCropped;*/ + Region m_regSlicePrevious; + Region m_regSliceCurrent; + + //Our threshold value + typename Controller::DensityType m_tThreshold; + + //Used to convert arbitrary voxel types in densities and materials. + Controller m_controller; + }; +} + +#include "PolyVoxCore/MarchingCubesSurfaceExtractor.inl" + +#endif diff --git a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl index 23617603..e9761d1e 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl @@ -24,19 +24,18 @@ freely, subject to the following restrictions: namespace PolyVox { template - MarchingCubesSurfaceExtractor::MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh* result, Controller controller) + MarchingCubesSurfaceExtractor::MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh >* result, Controller controller) :m_volData(volData) ,m_sampVolume(volData) ,m_meshCurrent(result) ,m_regSizeInVoxels(region) - ,m_controller(controller) - ,m_tThreshold(m_controller.getThreshold()) { //m_regSizeInVoxels.cropTo(m_volData->getEnclosingRegion()); m_regSizeInCells = m_regSizeInVoxels; m_regSizeInCells.setUpperCorner(m_regSizeInCells.getUpperCorner() - Vector3DInt32(1,1,1)); - m_sampVolume.setWrapMode(m_controller.getWrapMode(), m_controller.getBorderValue()); + m_controller = controller; + m_tThreshold = m_controller.getThreshold(); } template @@ -44,9 +43,9 @@ namespace PolyVox { m_meshCurrent->clear(); - const uint32_t uArrayWidth = m_regSizeInVoxels.getUpperCorner().getX() - m_regSizeInVoxels.getLowerCorner().getX() + 1; - const uint32_t uArrayHeight = m_regSizeInVoxels.getUpperCorner().getY() - m_regSizeInVoxels.getLowerCorner().getY() + 1; - const uint32_t arraySizes[2]= {uArrayWidth, uArrayHeight}; // Array dimensions + uint32_t uArrayWidth = m_regSizeInVoxels.getUpperCorner().getX() - m_regSizeInVoxels.getLowerCorner().getX() + 1; + uint32_t uArrayHeight = m_regSizeInVoxels.getUpperCorner().getY() - m_regSizeInVoxels.getLowerCorner().getY() + 1; + uint32_t arraySizes[2]= {uArrayWidth, uArrayHeight}; // Array dimensions //For edge indices Array2DInt32 m_pPreviousVertexIndicesX(arraySizes); @@ -137,18 +136,18 @@ namespace PolyVox const int32_t iMaxXVolSpace = m_regSliceCurrent.getUpperCorner().getX(); const int32_t iMaxYVolSpace = m_regSliceCurrent.getUpperCorner().getY(); - const int32_t iZVolSpace = m_regSliceCurrent.getLowerCorner().getZ(); + iZVolSpace = m_regSliceCurrent.getLowerCorner().getZ(); + uZRegSpace = iZVolSpace - m_regSizeInVoxels.getLowerCorner().getZ(); //Process the lower left corner - int32_t iYVolSpace = m_regSliceCurrent.getLowerCorner().getY(); - int32_t iXVolSpace = m_regSliceCurrent.getLowerCorner().getX(); + iYVolSpace = m_regSliceCurrent.getLowerCorner().getY(); + iXVolSpace = m_regSliceCurrent.getLowerCorner().getX(); - uint32_t uXRegSpace = iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX(); - uint32_t uYRegSpace = iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY(); + uXRegSpace = iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX(); + uYRegSpace = iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY(); - m_sampVolume.setPosition(iXVolSpace,iYVolSpace,iZVolSpace); - computeBitmaskForCell(pPreviousBitmask, pCurrentBitmask, uXRegSpace, uYRegSpace); + computeBitmaskForCell(pPreviousBitmask, pCurrentBitmask); //Process the edge where x is minimal. iXVolSpace = m_regSliceCurrent.getLowerCorner().getX(); @@ -160,7 +159,7 @@ namespace PolyVox m_sampVolume.movePositiveY(); - computeBitmaskForCell(pPreviousBitmask, pCurrentBitmask, uXRegSpace, uYRegSpace); + computeBitmaskForCell(pPreviousBitmask, pCurrentBitmask); } //Process the edge where y is minimal. @@ -173,7 +172,7 @@ namespace PolyVox m_sampVolume.movePositiveX(); - computeBitmaskForCell(pPreviousBitmask, pCurrentBitmask, uXRegSpace, uYRegSpace); + computeBitmaskForCell(pPreviousBitmask, pCurrentBitmask); } //Process all remaining elemnents of the slice. In this case, previous x and y values are always available @@ -187,7 +186,7 @@ namespace PolyVox m_sampVolume.movePositiveX(); - computeBitmaskForCell(pPreviousBitmask, pCurrentBitmask, uXRegSpace, uYRegSpace); + computeBitmaskForCell(pPreviousBitmask, pCurrentBitmask); } } @@ -196,7 +195,7 @@ namespace PolyVox template template - void MarchingCubesSurfaceExtractor::computeBitmaskForCell(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask, uint32_t uXRegSpace, uint32_t uYRegSpace) + void MarchingCubesSurfaceExtractor::computeBitmaskForCell(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask) { uint8_t iCubeIndex = 0; @@ -386,7 +385,7 @@ namespace PolyVox } //Save the bitmask - pCurrentBitmask[uXRegSpace][uYRegSpace] = iCubeIndex; + pCurrentBitmask[uXRegSpace][iYVolSpace- m_regSizeInVoxels.getLowerCorner().getY()] = iCubeIndex; if(edgeTable[iCubeIndex] != 0) { @@ -400,7 +399,7 @@ namespace PolyVox Array2DInt32& m_pCurrentVertexIndicesY, Array2DInt32& m_pCurrentVertexIndicesZ) { - const int32_t iZVolSpace = m_regSliceCurrent.getLowerCorner().getZ(); + int32_t iZVolSpace = m_regSliceCurrent.getLowerCorner().getZ(); //Iterate over each cell in the region for(int32_t iYVolSpace = m_regSliceCurrent.getLowerCorner().getY(); iYVolSpace <= m_regSliceCurrent.getUpperCorner().getY(); iYVolSpace++) @@ -413,7 +412,7 @@ namespace PolyVox const uint32_t uXRegSpace = iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX(); //Determine the index into the edge table which tells us which vertices are inside of the surface - const uint8_t iCubeIndex = pCurrentBitmask[uXRegSpace][uYRegSpace]; + uint8_t iCubeIndex = pCurrentBitmask[uXRegSpace][uYRegSpace]; /* Cube is entirely in/out of the surface */ if (edgeTable[iCubeIndex] == 0) @@ -426,16 +425,16 @@ namespace PolyVox m_sampVolume.setPosition(iXVolSpace,iYVolSpace,iZVolSpace); const typename VolumeType::VoxelType v000 = m_sampVolume.getVoxel(); - const Vector3DFloat n000 = computeCentralDifferenceGradient(m_sampVolume); + const Vector3DFloat n000 = computeSobelGradient(m_sampVolume); /* Find the vertices where the surface intersects the cube */ if (edgeTable[iCubeIndex] & 1) { m_sampVolume.movePositiveX(); const typename VolumeType::VoxelType v100 = m_sampVolume.getVoxel(); - const Vector3DFloat n100 = computeCentralDifferenceGradient(m_sampVolume); + const Vector3DFloat n100 = computeSobelGradient(m_sampVolume); - const float fInterp = static_cast(m_tThreshold - m_controller.convertToDensity(v000)) / static_cast(m_controller.convertToDensity(v100) - m_controller.convertToDensity(v000)); + float fInterp = static_cast(m_tThreshold - m_controller.convertToDensity(v000)) / static_cast(m_controller.convertToDensity(v100) - m_controller.convertToDensity(v000)); const Vector3DFloat v3dPosition(static_cast(iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX()) + fInterp, static_cast(iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY()), static_cast(iZVolSpace - m_regSizeInCells.getLowerCorner().getZ())); @@ -445,12 +444,13 @@ namespace PolyVox //Choose one of the two materials to use for the vertex (we don't interpolate as interpolation of //material IDs does not make sense). We take the largest, so that if we are working on a material-only //volume we get the one which is non-zero. Both materials can be non-zero if our volume has a density component. - const typename Controller::MaterialType uMaterial000 = m_controller.convertToMaterial(v000); - const typename Controller::MaterialType uMaterial100 = m_controller.convertToMaterial(v100); - const typename Controller::MaterialType uMaterial = (std::max)(uMaterial000, uMaterial100); + typename Controller::MaterialType uMaterial000 = m_controller.convertToMaterial(v000); + typename Controller::MaterialType uMaterial100 = m_controller.convertToMaterial(v100); + //typename Controller::MaterialType uMaterial = (std::max)(uMaterial000, uMaterial100); + typename Controller::MaterialType uMaterial = m_controller.blendMaterials(uMaterial000, uMaterial100, fInterp); - const PositionMaterialNormal surfaceVertex(v3dPosition, v3dNormal, static_cast(uMaterial)); - const uint32_t uLastVertexIndex = m_meshCurrent->addVertex(surfaceVertex); + PositionMaterialNormal surfaceVertex(v3dPosition, v3dNormal, uMaterial); + uint32_t uLastVertexIndex = m_meshCurrent->addVertex(surfaceVertex); m_pCurrentVertexIndicesX[iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX()][iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY()] = uLastVertexIndex; m_sampVolume.moveNegativeX(); @@ -459,9 +459,9 @@ namespace PolyVox { m_sampVolume.movePositiveY(); const typename VolumeType::VoxelType v010 = m_sampVolume.getVoxel(); - const Vector3DFloat n010 = computeCentralDifferenceGradient(m_sampVolume); + const Vector3DFloat n010 = computeSobelGradient(m_sampVolume); - const float fInterp = static_cast(m_tThreshold - m_controller.convertToDensity(v000)) / static_cast(m_controller.convertToDensity(v010) - m_controller.convertToDensity(v000)); + float fInterp = static_cast(m_tThreshold - m_controller.convertToDensity(v000)) / static_cast(m_controller.convertToDensity(v010) - m_controller.convertToDensity(v000)); const Vector3DFloat v3dPosition(static_cast(iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX()), static_cast(iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY()) + fInterp, static_cast(iZVolSpace - m_regSizeInVoxels.getLowerCorner().getZ())); @@ -471,12 +471,13 @@ namespace PolyVox //Choose one of the two materials to use for the vertex (we don't interpolate as interpolation of //material IDs does not make sense). We take the largest, so that if we are working on a material-only //volume we get the one which is non-zero. Both materials can be non-zero if our volume has a density component. - const typename Controller::MaterialType uMaterial000 = m_controller.convertToMaterial(v000); - const typename Controller::MaterialType uMaterial010 = m_controller.convertToMaterial(v010); - const typename Controller::MaterialType uMaterial = (std::max)(uMaterial000, uMaterial010); + typename Controller::MaterialType uMaterial000 = m_controller.convertToMaterial(v000); + typename Controller::MaterialType uMaterial010 = m_controller.convertToMaterial(v010); + //typename Controller::MaterialType uMaterial = (std::max)(uMaterial000, uMaterial010); + typename Controller::MaterialType uMaterial = m_controller.blendMaterials(uMaterial000, uMaterial010, fInterp); - const PositionMaterialNormal surfaceVertex(v3dPosition, v3dNormal, static_cast(uMaterial)); - const uint32_t uLastVertexIndex = m_meshCurrent->addVertex(surfaceVertex); + PositionMaterialNormal surfaceVertex(v3dPosition, v3dNormal, uMaterial); + uint32_t uLastVertexIndex = m_meshCurrent->addVertex(surfaceVertex); m_pCurrentVertexIndicesY[iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX()][iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY()] = uLastVertexIndex; m_sampVolume.moveNegativeY(); @@ -485,9 +486,9 @@ namespace PolyVox { m_sampVolume.movePositiveZ(); const typename VolumeType::VoxelType v001 = m_sampVolume.getVoxel(); - const Vector3DFloat n001 = computeCentralDifferenceGradient(m_sampVolume); + const Vector3DFloat n001 = computeSobelGradient(m_sampVolume); - const float fInterp = static_cast(m_tThreshold - m_controller.convertToDensity(v000)) / static_cast(m_controller.convertToDensity(v001) - m_controller.convertToDensity(v000)); + float fInterp = static_cast(m_tThreshold - m_controller.convertToDensity(v000)) / static_cast(m_controller.convertToDensity(v001) - m_controller.convertToDensity(v000)); const Vector3DFloat v3dPosition(static_cast(iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX()), static_cast(iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY()), static_cast(iZVolSpace - m_regSizeInVoxels.getLowerCorner().getZ()) + fInterp); @@ -497,12 +498,13 @@ namespace PolyVox //Choose one of the two materials to use for the vertex (we don't interpolate as interpolation of //material IDs does not make sense). We take the largest, so that if we are working on a material-only //volume we get the one which is non-zero. Both materials can be non-zero if our volume has a density component. - const typename Controller::MaterialType uMaterial000 = m_controller.convertToMaterial(v000); - const typename Controller::MaterialType uMaterial001 = m_controller.convertToMaterial(v001); - const typename Controller::MaterialType uMaterial = (std::max)(uMaterial000, uMaterial001); + typename Controller::MaterialType uMaterial000 = m_controller.convertToMaterial(v000); + typename Controller::MaterialType uMaterial001 = m_controller.convertToMaterial(v001); + //typename Controller::MaterialType uMaterial = (std::max)(uMaterial000, uMaterial001); + typename Controller::MaterialType uMaterial = m_controller.blendMaterials(uMaterial000, uMaterial001, fInterp); - const PositionMaterialNormal surfaceVertex(v3dPosition, v3dNormal, static_cast(uMaterial)); - const uint32_t uLastVertexIndex = m_meshCurrent->addVertex(surfaceVertex); + PositionMaterialNormal surfaceVertex(v3dPosition, v3dNormal, uMaterial); + uint32_t uLastVertexIndex = m_meshCurrent->addVertex(surfaceVertex); m_pCurrentVertexIndicesZ[iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX()][iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY()] = uLastVertexIndex; m_sampVolume.moveNegativeZ(); @@ -525,12 +527,11 @@ namespace PolyVox indlist[i] = -1; } - const int32_t iZVolSpace = m_regSlicePrevious.getLowerCorner().getZ(); - for(int32_t iYVolSpace = m_regSlicePrevious.getLowerCorner().getY(); iYVolSpace <= m_regSizeInCells.getUpperCorner().getY(); iYVolSpace++) { for(int32_t iXVolSpace = m_regSlicePrevious.getLowerCorner().getX(); iXVolSpace <= m_regSizeInCells.getUpperCorner().getX(); iXVolSpace++) - { + { + int32_t iZVolSpace = m_regSlicePrevious.getLowerCorner().getZ(); m_sampVolume.setPosition(iXVolSpace,iYVolSpace,iZVolSpace); //Current position @@ -538,7 +539,7 @@ namespace PolyVox const uint32_t uYRegSpace = m_sampVolume.getPosition().getY() - m_regSizeInVoxels.getLowerCorner().getY(); //Determine the index into the edge table which tells us which vertices are inside of the surface - const uint8_t iCubeIndex = pPreviousBitmask[uXRegSpace][uYRegSpace]; + uint8_t iCubeIndex = pPreviousBitmask[uXRegSpace][uYRegSpace]; /* Cube is entirely in/out of the surface */ if (edgeTable[iCubeIndex] == 0) @@ -610,9 +611,9 @@ namespace PolyVox for (int i=0;triTable[iCubeIndex][i]!=-1;i+=3) { - const int32_t ind0 = indlist[triTable[iCubeIndex][i ]]; - const int32_t ind1 = indlist[triTable[iCubeIndex][i+1]]; - const int32_t ind2 = indlist[triTable[iCubeIndex][i+2]]; + int32_t ind0 = indlist[triTable[iCubeIndex][i ]]; + int32_t ind1 = indlist[triTable[iCubeIndex][i+1]]; + int32_t ind2 = indlist[triTable[iCubeIndex][i+2]]; if((ind0 != -1) && (ind1 != -1) && (ind2 != -1)) { diff --git a/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h b/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h index addaa962..b8d6b995 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h +++ b/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h @@ -110,12 +110,12 @@ namespace PolyVox //////////////////////////////////////////////////////////////////////////////// // PositionMaterial //////////////////////////////////////////////////////////////////////////////// - class PositionMaterial; + template class PositionMaterial; //////////////////////////////////////////////////////////////////////////////// // PositionMaterialNormal //////////////////////////////////////////////////////////////////////////////// - class PositionMaterialNormal; + template class PositionMaterialNormal; //////////////////////////////////////////////////////////////////////////////// // RawVolume diff --git a/library/PolyVoxCore/include/PolyVoxCore/SurfaceMesh.h b/library/PolyVoxCore/include/PolyVoxCore/SurfaceMesh.h index 9ccc58fb..ce96c227 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/SurfaceMesh.h +++ b/library/PolyVoxCore/include/PolyVoxCore/SurfaceMesh.h @@ -95,8 +95,8 @@ namespace PolyVox std::vector m_vecLodRecords; }; - template - polyvox_shared_ptr< SurfaceMesh > extractSubset(SurfaceMesh& inputMesh, std::set setMaterials); + /*template + polyvox_shared_ptr< SurfaceMesh > extractSubset(SurfaceMesh& inputMesh, std::set setMaterials);*/ } #include "PolyVoxCore/SurfaceMesh.inl" diff --git a/library/PolyVoxCore/include/PolyVoxCore/SurfaceMesh.inl b/library/PolyVoxCore/include/PolyVoxCore/SurfaceMesh.inl index 45d24ada..af9f852b 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/SurfaceMesh.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/SurfaceMesh.inl @@ -365,7 +365,7 @@ namespace PolyVox void SurfaceMesh::removeUnusedVertices(void) { std::vector isVertexUsed(m_vecVertices.size()); - fill(isVertexUsed.begin(), isVertexUsed.end(), false); + std::fill(isVertexUsed.begin(), isVertexUsed.end(), false); for(uint32_t triCt = 0; triCt < m_vecTriangleIndices.size(); triCt++) { @@ -394,7 +394,7 @@ namespace PolyVox } //Currently a free function - think where this needs to go. - template + /*template polyvox_shared_ptr< SurfaceMesh > extractSubset(SurfaceMesh& inputMesh, std::set setMaterials) { polyvox_shared_ptr< SurfaceMesh > result(new SurfaceMesh); @@ -460,7 +460,7 @@ namespace PolyVox result->m_vecLodRecords.push_back(lodRecord); return result; - } + }*/ template void SurfaceMesh::scaleVertices(float amount) diff --git a/library/PolyVoxCore/include/PolyVoxCore/Vector.inl b/library/PolyVoxCore/include/PolyVoxCore/Vector.inl index 09501f18..9e65543a 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Vector.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/Vector.inl @@ -54,7 +54,7 @@ namespace PolyVox Vector::Vector(StorageType x, StorageType y) { #ifndef SWIGPYTHON // SWIG instantiates all constructors, unless we can find a way around that. Should we use SWIGIMPORT here, and then %import this file rather then %include it? - static_assert(Size == 2, "This constructor should only be used for vectors with two elements."); + //static_assert(Size == 2, "This constructor should only be used for vectors with two elements."); #endif m_tElements[0] = x; @@ -71,7 +71,7 @@ namespace PolyVox Vector::Vector(StorageType x, StorageType y, StorageType z) { #ifndef SWIGPYTHON // SWIG instantiates all constructors, unless we can find a way around that. Should we use SWIGIMPORT here, and then %import this file rather then %include it? - static_assert(Size == 3, "This constructor should only be used for vectors with three elements."); + //static_assert(Size == 3, "This constructor should only be used for vectors with three elements."); #endif m_tElements[0] = x; @@ -91,7 +91,7 @@ namespace PolyVox Vector::Vector(StorageType x, StorageType y, StorageType z, StorageType w) { #ifndef SWIGPYTHON // SWIG instantiates all constructors, unless we can find a way around that. Should we use SWIGIMPORT here, and then %import this file rather then %include it? - static_assert(Size == 4, "This constructor should only be used for vectors with four elements."); + //static_assert(Size == 4, "This constructor should only be used for vectors with four elements."); #endif m_tElements[0] = x; @@ -142,7 +142,7 @@ namespace PolyVox // vector with one element, and supporting this would cause confusion over the // behaviour of the constructor taking a single value, as this fills all elements // to that value rather than just the first one. - static_assert(Size > 1, "Vector must have a length greater than one."); + //static_assert(Size > 1, "Vector must have a length greater than one."); } /** @@ -449,7 +449,7 @@ namespace PolyVox inline StorageType Vector::getZ(void) const { #ifndef SWIGPYTHON // SWIG instantiates all getters, unless we can find a way around that. Should we use SWIGIMPORT here, and then %import this file rather then %include it? - static_assert(Size >= 3, "You can only get the 'z' component from a vector with at least three elements."); + //static_assert(Size >= 3, "You can only get the 'z' component from a vector with at least three elements."); #endif return m_tElements[2]; @@ -462,7 +462,7 @@ namespace PolyVox inline StorageType Vector::getW(void) const { #ifndef SWIGPYTHON // SWIG instantiates all getters, unless we can find a way around that. Should we use SWIGIMPORT here, and then %import this file rather then %include it? - static_assert(Size >= 4, "You can only get the 'w' component from a vector with at least four elements."); + //static_assert(Size >= 4, "You can only get the 'w' component from a vector with at least four elements."); #endif return m_tElements[3]; @@ -502,7 +502,7 @@ namespace PolyVox inline void Vector::setElements(StorageType x, StorageType y, StorageType z) { #ifndef SWIGPYTHON // SWIG instantiates all setters, unless we can find a way around that. Should we use SWIGIMPORT here, and then %import this file rather then %include it? - static_assert(Size >= 3, "You can only use this version of setElements() on a vector with at least three elements."); + //static_assert(Size >= 3, "You can only use this version of setElements() on a vector with at least three elements."); #endif m_tElements[0] = x; m_tElements[1] = y; @@ -520,7 +520,7 @@ namespace PolyVox inline void Vector::setElements(StorageType x, StorageType y, StorageType z, StorageType w) { #ifndef SWIGPYTHON // SWIG instantiates all setters, unless we can find a way around that. Should we use SWIGIMPORT here, and then %import this file rather then %include it? - static_assert(Size >= 4, "You can only use this version of setElements() on a vector with at least four elements."); + //static_assert(Size >= 4, "You can only use this version of setElements() on a vector with at least four elements."); #endif m_tElements[0] = x; m_tElements[1] = y; @@ -553,7 +553,7 @@ namespace PolyVox inline void Vector::setZ(StorageType tZ) { #ifndef SWIGPYTHON // SWIG instantiates all setters, unless we can find a way around that. Should we use SWIGIMPORT here, and then %import this file rather then %include it? - static_assert(Size >= 3, "You can only set the 'w' component from a vector with at least three elements."); + //static_assert(Size >= 3, "You can only set the 'w' component from a vector with at least three elements."); #endif m_tElements[2] = tZ; } @@ -565,7 +565,7 @@ namespace PolyVox inline void Vector::setW(StorageType tW) { #ifndef SWIGPYTHON // SWIG instantiates all setters, unless we can find a way around that. Should we use SWIGIMPORT here, and then %import this file rather then %include it? - static_assert(Size >= 4, "You can only set the 'w' component from a vector with at least four elements."); + //static_assert(Size >= 4, "You can only set the 'w' component from a vector with at least four elements."); #endif m_tElements[3] = tW; } diff --git a/library/PolyVoxCore/include/PolyVoxCore/VertexTypes.h b/library/PolyVoxCore/include/PolyVoxCore/VertexTypes.h index 033fad6b..3eb18072 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/VertexTypes.h +++ b/library/PolyVoxCore/include/PolyVoxCore/VertexTypes.h @@ -36,49 +36,107 @@ namespace PolyVox #ifdef SWIG class PositionMaterial #else + template class POLYVOX_API PositionMaterial #endif { public: - PositionMaterial(); - PositionMaterial(Vector3DFloat positionToSet, float materialToSet); + PositionMaterial() + { + } - float getMaterial(void) const; - const Vector3DFloat& getPosition(void) const; + PositionMaterial(Vector3DFloat positionToSet, MaterialType materialToSet) + :position(positionToSet) + ,material(materialToSet) + { + } - void setMaterial(float materialToSet); - void setPosition(const Vector3DFloat& positionToSet); + MaterialType getMaterial(void) const + { + return material; + } + + const Vector3DFloat& getPosition(void) const + { + return position; + } + + void setMaterial(MaterialType materialToSet) + { + material = materialToSet; + } + + void setPosition(const Vector3DFloat& positionToSet) + { + position = positionToSet; + } public: //Nicely fits into four floats. Vector3DFloat position; - float material; + MaterialType material; }; #ifdef SWIG class PositionMaterialNormal #else + template class POLYVOX_API PositionMaterialNormal #endif { public: - PositionMaterialNormal(); - PositionMaterialNormal(Vector3DFloat positionToSet, float materialToSet); - PositionMaterialNormal(Vector3DFloat positionToSet, Vector3DFloat normalToSet, float materialToSet); + PositionMaterialNormal() + { + } - float getMaterial(void) const; - const Vector3DFloat& getNormal(void) const; - const Vector3DFloat& getPosition(void) const; + PositionMaterialNormal(Vector3DFloat positionToSet, MaterialType materialToSet) + :position(positionToSet) + ,material(materialToSet) + { + } - void setMaterial(float materialToSet); - void setNormal(const Vector3DFloat& normalToSet); - void setPosition(const Vector3DFloat& positionToSet); + PositionMaterialNormal(Vector3DFloat positionToSet, Vector3DFloat normalToSet, MaterialType materialToSet) + :position(positionToSet) + ,normal(normalToSet) + ,material(materialToSet) + { + } + + MaterialType getMaterial(void) const + { + return material; + } + + const Vector3DFloat& getNormal(void) const + { + return normal; + } + + const Vector3DFloat& getPosition(void) const + { + return position; + } + + void setMaterial(MaterialType materialToSet) + { + material = materialToSet; + } + + void setNormal(const Vector3DFloat& normalToSet) + { + normal = normalToSet; + } + + void setPosition(const Vector3DFloat& positionToSet) + { + position = positionToSet; + } public: //Nicely fits into seven floats, meaning we //can squeeze in one more for material blending. Vector3DFloat position; Vector3DFloat normal; - float material; //FIXME: This shouldn't be float on CPU? + MaterialType material; //FIXME: This shouldn't be float on CPU? }; } diff --git a/library/PolyVoxCore/include/PolyVoxCore/VolumeResampler.inl b/library/PolyVoxCore/include/PolyVoxCore/VolumeResampler.inl index b76281b9..72dcc573 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/VolumeResampler.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/VolumeResampler.inl @@ -127,7 +127,7 @@ namespace PolyVox sy = modf(sy, &dummy); sz = modf(sz, &dummy); - typename SrcVolumeType::VoxelType tInterpolatedValue = trilerp(voxel000,voxel100,voxel010,voxel110,voxel001,voxel101,voxel011,voxel111,sx,sy,sz); + typename SrcVolumeType::VoxelType tInterpolatedValue = trilerp(voxel000,voxel100,voxel010,voxel110,voxel001,voxel101,voxel011,voxel111,sx,sy,sz); typename DstVolumeType::VoxelType result = static_cast(tInterpolatedValue); m_pVolDst->setVoxelAt(dx,dy,dz,result); diff --git a/library/PolyVoxCore/source/Impl/Utility.cpp b/library/PolyVoxCore/source/Impl/Utility.cpp index 6e907ab9..a56d0210 100644 --- a/library/PolyVoxCore/source/Impl/Utility.cpp +++ b/library/PolyVoxCore/source/Impl/Utility.cpp @@ -1,65 +1,65 @@ -/******************************************************************************* -Copyright (c) 2005-2009 David Williams - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*******************************************************************************/ - -#include "PolyVoxCore/Impl/Utility.h" - -#include -#include - -namespace PolyVox -{ - //Note: this function only works for inputs which are a power of two and not zero - //If this is not the case then the output is undefined. - uint8_t logBase2(uint32_t uInput) - { - //Debug mode validation - assert(uInput != 0); - assert(isPowerOf2(uInput)); - - //Release mode validation - if(uInput == 0) - { - throw std::invalid_argument("Cannot compute the log of zero."); - } - if(!isPowerOf2(uInput)) - { - throw std::invalid_argument("Input must be a power of two in order to compute the log."); - } - - uint32_t uResult = 0; - while( (uInput >> uResult) != 0) - { - ++uResult; - } - return static_cast(uResult-1); - } - - - bool isPowerOf2(uint32_t uInput) - { - if(uInput == 0) - return false; - else - return ((uInput & (uInput-1)) == 0); - } -} +/******************************************************************************* +Copyright (c) 2005-2009 David Williams + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*******************************************************************************/ + +#include "PolyVoxCore/Impl/Utility.h" + +#include +#include + +namespace PolyVox +{ + //Note: this function only works for inputs which are a power of two and not zero + //If this is not the case then the output is undefined. + uint8_t logBase2(uint32_t uInput) + { + //Debug mode validation + assert(uInput != 0); + assert(isPowerOf2(uInput)); + + //Release mode validation + if(uInput == 0) + { + //throw std::invalid_argument("Cannot compute the log of zero."); + } + if(!isPowerOf2(uInput)) + { + //throw std::invalid_argument("Input must be a power of two in order to compute the log."); + } + + uint32_t uResult = 0; + while( (uInput >> uResult) != 0) + { + ++uResult; + } + return static_cast(uResult-1); + } + + + bool isPowerOf2(uint32_t uInput) + { + if(uInput == 0) + return false; + else + return ((uInput & (uInput-1)) == 0); + } +} diff --git a/library/PolyVoxCore/source/VertexTypes.cpp b/library/PolyVoxCore/source/VertexTypes.cpp index db41759e..b4dca237 100644 --- a/library/PolyVoxCore/source/VertexTypes.cpp +++ b/library/PolyVoxCore/source/VertexTypes.cpp @@ -25,86 +25,9 @@ freely, subject to the following restrictions: namespace PolyVox { - PositionMaterialNormal::PositionMaterialNormal() - { - } - - PositionMaterialNormal::PositionMaterialNormal(Vector3DFloat positionToSet, float materialToSet) - :position(positionToSet) - ,material(materialToSet) - { - - } - - PositionMaterialNormal::PositionMaterialNormal(Vector3DFloat positionToSet, Vector3DFloat normalToSet, float materialToSet) - :position(positionToSet) - ,normal(normalToSet) - ,material(materialToSet) - { - } - - float PositionMaterialNormal::getMaterial(void) const - { - return material; - } - - const Vector3DFloat& PositionMaterialNormal::getNormal(void) const - { - return normal; - } - - const Vector3DFloat& PositionMaterialNormal::getPosition(void) const - { - return position; - } - - void PositionMaterialNormal::setMaterial(float materialToSet) - { - material = materialToSet; - } - - void PositionMaterialNormal::setNormal(const Vector3DFloat& normalToSet) - { - normal = normalToSet; - } - - void PositionMaterialNormal::setPosition(const Vector3DFloat& positionToSet) - { - position = positionToSet; - } - //////////////////////////////////////////////////////////////////////////////// // PositionMaterial //////////////////////////////////////////////////////////////////////////////// - PositionMaterial::PositionMaterial() - { - } - - PositionMaterial::PositionMaterial(Vector3DFloat positionToSet, float materialToSet) - :position(positionToSet) - ,material(materialToSet) - { - - } - - float PositionMaterial::getMaterial(void) const - { - return material; - } - - const Vector3DFloat& PositionMaterial::getPosition(void) const - { - return position; - } - - void PositionMaterial::setMaterial(float materialToSet) - { - material = materialToSet; - } - - void PositionMaterial::setPosition(const Vector3DFloat& positionToSet) - { - position = positionToSet; - } + }