Initial changes required for Cubiquity.

This commit is contained in:
David Williams 2012-12-15 17:49:43 +01:00
parent c0d4b2a36f
commit a1ac75022c
15 changed files with 734 additions and 753 deletions

View File

@ -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<PositionMaterial>* result, bool bMergeQuads = true, IsQuadNeeded isQuadNeeded = IsQuadNeeded());
CubicSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh<PositionMaterial<typename VolumeType::VoxelType> >* 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<Quad>& quads);
bool mergeQuads(Quad& q1, Quad& q2);
@ -128,7 +127,7 @@ namespace PolyVox
Region m_regSizeInVoxels;
//The surface patch we are currently filling.
SurfaceMesh<PositionMaterial>* m_meshCurrent;
SurfaceMesh<PositionMaterial<typename VolumeType::VoxelType> >* m_meshCurrent;
//Used to avoid creating duplicate vertices.
Array<3, IndexAndMaterial> m_previousSliceVertices;

View File

@ -35,7 +35,7 @@ namespace PolyVox
const uint32_t CubicSurfaceExtractor<VolumeType, IsQuadNeeded>::MaxVerticesPerPosition = 6;
template<typename VolumeType, typename IsQuadNeeded>
CubicSurfaceExtractor<VolumeType, IsQuadNeeded>::CubicSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh<PositionMaterial>* result, bool bMergeQuads, IsQuadNeeded isQuadNeeded)
CubicSurfaceExtractor<VolumeType, IsQuadNeeded>::CubicSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh<PositionMaterial<typename VolumeType::VoxelType> >* 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<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
uint32_t v1 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) + 0.5f, material, m_currentSliceVertices);
uint32_t v2 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) + 0.5f, static_cast<float>(regZ) + 0.5f, material, m_currentSliceVertices);
uint32_t v3 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) + 0.5f, static_cast<float>(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<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
uint32_t v1 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) + 0.5f, material, m_currentSliceVertices);
uint32_t v2 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) + 0.5f, static_cast<float>(regZ) + 0.5f, material, m_currentSliceVertices);
uint32_t v3 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) + 0.5f, static_cast<float>(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<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
uint32_t v1 = addVertex(static_cast<float>(regX) + 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
uint32_t v2 = addVertex(static_cast<float>(regX) + 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) + 0.5f, material, m_currentSliceVertices);
uint32_t v3 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(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<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
uint32_t v1 = addVertex(static_cast<float>(regX) + 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
uint32_t v2 = addVertex(static_cast<float>(regX) + 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) + 0.5f, material, m_currentSliceVertices);
uint32_t v3 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(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<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
uint32_t v1 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) + 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
uint32_t v2 = addVertex(static_cast<float>(regX) + 0.5f, static_cast<float>(regY) + 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
uint32_t v3 = addVertex(static_cast<float>(regX) + 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(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<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
uint32_t v1 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) + 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
uint32_t v2 = addVertex(static_cast<float>(regX) + 0.5f, static_cast<float>(regY) + 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
uint32_t v3 = addVertex(static_cast<float>(regX) + 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(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<typename VolumeType, typename IsQuadNeeded>
int32_t CubicSurfaceExtractor<VolumeType, IsQuadNeeded>::addVertex(float fX, float fY, float fZ, uint32_t uMaterialIn, Array<3, IndexAndMaterial>& existingVertices)
int32_t CubicSurfaceExtractor<VolumeType, IsQuadNeeded>::addVertex(float fX, float fY, float fZ, typename VolumeType::VoxelType uMaterialIn, Array<3, IndexAndMaterial>& existingVertices)
{
uint32_t uX = static_cast<uint32_t>(fX + 0.75f);
uint32_t uY = static_cast<uint32_t>(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<typename VolumeType::VoxelType> (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<int32_t>(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

View File

@ -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<typename VoxelType>
class DefaultIsQuadNeeded
{
public:
bool operator()(VoxelType back, VoxelType front, uint32_t& materialToUse)
{
if((back > 0) && (front == 0))
{
materialToUse = static_cast<uint32_t>(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<typename VoxelType>
class DefaultIsQuadNeeded
{
public:
bool operator()(VoxelType back, VoxelType front, float& materialToUse)
{
if((back > 0) && (front == 0))
{
materialToUse = static_cast<float>(back);
return true;
}
else
{
return false;
}
}
};
}
#endif //__PolyVox_DefaultIsQuadNeeded_H__

View File

@ -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 <limits>
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 <i>density field</i>. 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 <i>any</i> voxel type, we use a <i>Marching Cubes controller</i> (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<typename VoxelType>
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<DensityType>::min)() + (std::numeric_limits<DensityType>::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 <limits>
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 <i>density field</i>. 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 <i>any</i> voxel type, we use a <i>Marching Cubes controller</i> (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<typename VoxelType>
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<DensityType>::min)() + (std::numeric_limits<DensityType>::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

View File

@ -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 <cassert>
namespace PolyVox
{
template <typename Type>
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 <typename Type>
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 <typename Type>
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 <cassert>
namespace PolyVox
{
template <typename Type>
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 <typename Type>
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 <typename Type>
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__

View File

@ -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<typename VolumeType::VoxelType> >
class MarchingCubesSurfaceExtractor
{
public:
MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh<PositionMaterialNormal>* result, Controller controller = Controller());
void execute();
private:
//Compute the cell bitmask for a particular slice in z.
template<bool isPrevZAvail>
uint32_t computeBitmaskForSlice(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask);
//Compute the cell bitmask for a given cell.
template<bool isPrevXAvail, bool isPrevYAvail, bool isPrevZAvail>
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<float>(m_controller.convertToDensity(volIter.peekVoxel1nx0py0pz()));
float voxel1px = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1px0py0pz()));
float voxel1ny = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel0px1ny0pz()));
float voxel1py = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel0px1py0pz()));
float voxel1nz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel0px0py1nz()));
float voxel1pz = static_cast<float>(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<float>(m_controller.convertToDensity(volIter.peekVoxel1nx1ny1nz()));
const float pVoxel1nx1ny0pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1nx1ny0pz()));
const float pVoxel1nx1ny1pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1nx1ny1pz()));
const float pVoxel1nx0py1nz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1nx0py1nz()));
const float pVoxel1nx0py0pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1nx0py0pz()));
const float pVoxel1nx0py1pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1nx0py1pz()));
const float pVoxel1nx1py1nz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1nx1py1nz()));
const float pVoxel1nx1py0pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1nx1py0pz()));
const float pVoxel1nx1py1pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1nx1py1pz()));
const float pVoxel0px1ny1nz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel0px1ny1nz()));
const float pVoxel0px1ny0pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel0px1ny0pz()));
const float pVoxel0px1ny1pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel0px1ny1pz()));
const float pVoxel0px0py1nz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel0px0py1nz()));
//const float pVoxel0px0py0pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel0px0py0pz()));
const float pVoxel0px0py1pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel0px0py1pz()));
const float pVoxel0px1py1nz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel0px1py1nz()));
const float pVoxel0px1py0pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel0px1py0pz()));
const float pVoxel0px1py1pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel0px1py1pz()));
const float pVoxel1px1ny1nz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1px1ny1nz()));
const float pVoxel1px1ny0pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1px1ny0pz()));
const float pVoxel1px1ny1pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1px1ny1pz()));
const float pVoxel1px0py1nz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1px0py1nz()));
const float pVoxel1px0py0pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1px0py0pz()));
const float pVoxel1px0py1pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1px0py1pz()));
const float pVoxel1px1py1nz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1px1py1nz()));
const float pVoxel1px1py0pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1px1py0pz()));
const float pVoxel1px1py1pz = static_cast<float>(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<PositionMaterialNormal>* 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<typename VolumeType::VoxelType> >
class MarchingCubesSurfaceExtractor
{
public:
MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh<PositionMaterialNormal<typename Controller::MaterialType> >* result, Controller controller = Controller());
void execute();
private:
//Compute the cell bitmask for a particular slice in z.
template<bool isPrevZAvail>
uint32_t computeBitmaskForSlice(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask);
//Compute the cell bitmask for a given cell.
template<bool isPrevXAvail, bool isPrevYAvail, bool isPrevZAvail>
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<float>(m_controller.convertToDensity(volIter.peekVoxel1nx0py0pz()));
float voxel1px = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1px0py0pz()));
float voxel1ny = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel0px1ny0pz()));
float voxel1py = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel0px1py0pz()));
float voxel1nz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel0px0py1nz()));
float voxel1pz = static_cast<float>(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<float>(m_controller.convertToDensity(volIter.peekVoxel1nx1ny1nz()));
const float pVoxel1nx1ny0pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1nx1ny0pz()));
const float pVoxel1nx1ny1pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1nx1ny1pz()));
const float pVoxel1nx0py1nz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1nx0py1nz()));
const float pVoxel1nx0py0pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1nx0py0pz()));
const float pVoxel1nx0py1pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1nx0py1pz()));
const float pVoxel1nx1py1nz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1nx1py1nz()));
const float pVoxel1nx1py0pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1nx1py0pz()));
const float pVoxel1nx1py1pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1nx1py1pz()));
const float pVoxel0px1ny1nz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel0px1ny1nz()));
const float pVoxel0px1ny0pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel0px1ny0pz()));
const float pVoxel0px1ny1pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel0px1ny1pz()));
const float pVoxel0px0py1nz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel0px0py1nz()));
//const float pVoxel0px0py0pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel0px0py0pz()));
const float pVoxel0px0py1pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel0px0py1pz()));
const float pVoxel0px1py1nz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel0px1py1nz()));
const float pVoxel0px1py0pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel0px1py0pz()));
const float pVoxel0px1py1pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel0px1py1pz()));
const float pVoxel1px1ny1nz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1px1ny1nz()));
const float pVoxel1px1ny0pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1px1ny0pz()));
const float pVoxel1px1ny1pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1px1ny1pz()));
const float pVoxel1px0py1nz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1px0py1nz()));
const float pVoxel1px0py0pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1px0py0pz()));
const float pVoxel1px0py1pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1px0py1pz()));
const float pVoxel1px1py1nz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1px1py1nz()));
const float pVoxel1px1py0pz = static_cast<float>(m_controller.convertToDensity(volIter.peekVoxel1px1py0pz()));
const float pVoxel1px1py1pz = static_cast<float>(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<PositionMaterialNormal<typename Controller::MaterialType> >* 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

View File

@ -24,19 +24,18 @@ freely, subject to the following restrictions:
namespace PolyVox
{
template<typename VolumeType, typename Controller>
MarchingCubesSurfaceExtractor<VolumeType, Controller>::MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh<PositionMaterialNormal>* result, Controller controller)
MarchingCubesSurfaceExtractor<VolumeType, Controller>::MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh<PositionMaterialNormal<typename Controller::MaterialType> >* 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<typename VolumeType, typename Controller>
@ -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<false, false, isPrevZAvail>(pPreviousBitmask, pCurrentBitmask, uXRegSpace, uYRegSpace);
computeBitmaskForCell<false, false, isPrevZAvail>(pPreviousBitmask, pCurrentBitmask);
//Process the edge where x is minimal.
iXVolSpace = m_regSliceCurrent.getLowerCorner().getX();
@ -160,7 +159,7 @@ namespace PolyVox
m_sampVolume.movePositiveY();
computeBitmaskForCell<false, true, isPrevZAvail>(pPreviousBitmask, pCurrentBitmask, uXRegSpace, uYRegSpace);
computeBitmaskForCell<false, true, isPrevZAvail>(pPreviousBitmask, pCurrentBitmask);
}
//Process the edge where y is minimal.
@ -173,7 +172,7 @@ namespace PolyVox
m_sampVolume.movePositiveX();
computeBitmaskForCell<true, false, isPrevZAvail>(pPreviousBitmask, pCurrentBitmask, uXRegSpace, uYRegSpace);
computeBitmaskForCell<true, false, isPrevZAvail>(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<true, true, isPrevZAvail>(pPreviousBitmask, pCurrentBitmask, uXRegSpace, uYRegSpace);
computeBitmaskForCell<true, true, isPrevZAvail>(pPreviousBitmask, pCurrentBitmask);
}
}
@ -196,7 +195,7 @@ namespace PolyVox
template<typename VolumeType, typename Controller>
template<bool isPrevXAvail, bool isPrevYAvail, bool isPrevZAvail>
void MarchingCubesSurfaceExtractor<VolumeType, Controller>::computeBitmaskForCell(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask, uint32_t uXRegSpace, uint32_t uYRegSpace)
void MarchingCubesSurfaceExtractor<VolumeType, Controller>::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<float>(m_tThreshold - m_controller.convertToDensity(v000)) / static_cast<float>(m_controller.convertToDensity(v100) - m_controller.convertToDensity(v000));
float fInterp = static_cast<float>(m_tThreshold - m_controller.convertToDensity(v000)) / static_cast<float>(m_controller.convertToDensity(v100) - m_controller.convertToDensity(v000));
const Vector3DFloat v3dPosition(static_cast<float>(iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX()) + fInterp, static_cast<float>(iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY()), static_cast<float>(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<float>(uMaterial));
const uint32_t uLastVertexIndex = m_meshCurrent->addVertex(surfaceVertex);
PositionMaterialNormal<typename Controller::MaterialType> 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<float>(m_tThreshold - m_controller.convertToDensity(v000)) / static_cast<float>(m_controller.convertToDensity(v010) - m_controller.convertToDensity(v000));
float fInterp = static_cast<float>(m_tThreshold - m_controller.convertToDensity(v000)) / static_cast<float>(m_controller.convertToDensity(v010) - m_controller.convertToDensity(v000));
const Vector3DFloat v3dPosition(static_cast<float>(iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX()), static_cast<float>(iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY()) + fInterp, static_cast<float>(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<float>(uMaterial));
const uint32_t uLastVertexIndex = m_meshCurrent->addVertex(surfaceVertex);
PositionMaterialNormal<typename Controller::MaterialType> 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<float>(m_tThreshold - m_controller.convertToDensity(v000)) / static_cast<float>(m_controller.convertToDensity(v001) - m_controller.convertToDensity(v000));
float fInterp = static_cast<float>(m_tThreshold - m_controller.convertToDensity(v000)) / static_cast<float>(m_controller.convertToDensity(v001) - m_controller.convertToDensity(v000));
const Vector3DFloat v3dPosition(static_cast<float>(iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX()), static_cast<float>(iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY()), static_cast<float>(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<float>(uMaterial));
const uint32_t uLastVertexIndex = m_meshCurrent->addVertex(surfaceVertex);
PositionMaterialNormal<typename Controller::MaterialType> 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))
{

View File

@ -110,12 +110,12 @@ namespace PolyVox
////////////////////////////////////////////////////////////////////////////////
// PositionMaterial
////////////////////////////////////////////////////////////////////////////////
class PositionMaterial;
template<typename MaterialType> class PositionMaterial;
////////////////////////////////////////////////////////////////////////////////
// PositionMaterialNormal
////////////////////////////////////////////////////////////////////////////////
class PositionMaterialNormal;
template<typename MaterialType> class PositionMaterialNormal;
////////////////////////////////////////////////////////////////////////////////
// RawVolume

View File

@ -95,8 +95,8 @@ namespace PolyVox
std::vector<LodRecord> m_vecLodRecords;
};
template <typename VertexType>
polyvox_shared_ptr< SurfaceMesh<VertexType> > extractSubset(SurfaceMesh<VertexType>& inputMesh, std::set<uint8_t> setMaterials);
/*template <typename VertexType>
polyvox_shared_ptr< SurfaceMesh<VertexType> > extractSubset(SurfaceMesh<VertexType>& inputMesh, std::set<uint8_t> setMaterials);*/
}
#include "PolyVoxCore/SurfaceMesh.inl"

View File

@ -365,7 +365,7 @@ namespace PolyVox
void SurfaceMesh<VertexType>::removeUnusedVertices(void)
{
std::vector<bool> 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 <typename VertexType>
/*template <typename VertexType>
polyvox_shared_ptr< SurfaceMesh<VertexType> > extractSubset(SurfaceMesh<VertexType>& inputMesh, std::set<uint8_t> setMaterials)
{
polyvox_shared_ptr< SurfaceMesh<VertexType> > result(new SurfaceMesh<VertexType>);
@ -460,7 +460,7 @@ namespace PolyVox
result->m_vecLodRecords.push_back(lodRecord);
return result;
}
}*/
template <typename VertexType>
void SurfaceMesh<VertexType>::scaleVertices(float amount)

View File

@ -54,7 +54,7 @@ namespace PolyVox
Vector<Size,StorageType,OperationType>::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<Size,StorageType,OperationType>::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<Size,StorageType,OperationType>::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<Size, StorageType, OperationType>::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<Size, StorageType, OperationType>::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<Size,StorageType,OperationType>::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<Size,StorageType,OperationType>::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<Size, StorageType, OperationType>::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<Size, StorageType, OperationType>::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;
}

View File

@ -36,49 +36,107 @@ namespace PolyVox
#ifdef SWIG
class PositionMaterial
#else
template<typename MaterialType>
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<typename MaterialType>
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?
};
}

View File

@ -127,7 +127,7 @@ namespace PolyVox
sy = modf(sy, &dummy);
sz = modf(sz, &dummy);
typename SrcVolumeType::VoxelType tInterpolatedValue = trilerp<float>(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<typename DstVolumeType::VoxelType>(tInterpolatedValue);
m_pVolDst->setVoxelAt(dx,dy,dz,result);

View File

@ -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 <cassert>
#include <stdexcept>
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<uint8_t>(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 <cassert>
#include <stdexcept>
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<uint8_t>(uResult-1);
}
bool isPowerOf2(uint32_t uInput)
{
if(uInput == 0)
return false;
else
return ((uInput & (uInput-1)) == 0);
}
}

View File

@ -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;
}
}