Merge branch 'feature/mesh-work' into develop
This commit is contained in:
commit
43e0d6f417
@ -161,7 +161,7 @@ void OpenGLWidget::paintGL()
|
||||
// Bind the vertex array for the current mesh
|
||||
glBindVertexArray(meshData.vertexArrayObject);
|
||||
// Draw the mesh
|
||||
glDrawElements(GL_TRIANGLES, meshData.noOfIndices, GL_UNSIGNED_INT, 0);
|
||||
glDrawElements(GL_TRIANGLES, meshData.noOfIndices, meshData.indexType, 0);
|
||||
// Unbind the vertex array.
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ distribution.
|
||||
struct OpenGLMeshData
|
||||
{
|
||||
GLuint noOfIndices;
|
||||
GLenum indexType;
|
||||
GLuint indexBuffer;
|
||||
GLuint vertexBuffer;
|
||||
GLuint vertexArrayObject;
|
||||
@ -53,8 +54,8 @@ public:
|
||||
OpenGLWidget(QWidget *parent);
|
||||
|
||||
// Convert a PolyVox mesh to OpenGL index/vertex buffers. Inlined because it's templatised.
|
||||
template <typename DataType>
|
||||
void addMesh(const PolyVox::Mesh< PolyVox::Vertex< DataType > >& surfaceMesh, const PolyVox::Vector3DInt32& translation = PolyVox::Vector3DInt32(0, 0, 0), float scale = 1.0f)
|
||||
template <typename MeshType>
|
||||
void addMesh(const MeshType& surfaceMesh, const PolyVox::Vector3DInt32& translation = PolyVox::Vector3DInt32(0, 0, 0), float scale = 1.0f)
|
||||
{
|
||||
// Convienient access to the vertices and indices
|
||||
const auto& vecIndices = surfaceMesh.getIndices();
|
||||
@ -71,29 +72,29 @@ public:
|
||||
// The GL_ARRAY_BUFFER will contain the list of vertex positions
|
||||
glGenBuffers(1, &(meshData.vertexBuffer));
|
||||
glBindBuffer(GL_ARRAY_BUFFER, meshData.vertexBuffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, vecVertices.size() * sizeof(PolyVox::Vertex< DataType >), vecVertices.data(), GL_STATIC_DRAW);
|
||||
glBufferData(GL_ARRAY_BUFFER, vecVertices.size() * sizeof(typename MeshType::VertexType), vecVertices.data(), GL_STATIC_DRAW);
|
||||
|
||||
// and GL_ELEMENT_ARRAY_BUFFER will contain the indices
|
||||
glGenBuffers(1, &(meshData.indexBuffer));
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, meshData.indexBuffer);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, vecIndices.size() * sizeof(uint32_t), vecIndices.data(), GL_STATIC_DRAW);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, vecIndices.size() * sizeof(typename MeshType::IndexType), vecIndices.data(), GL_STATIC_DRAW);
|
||||
|
||||
// Every surface extractor outputs valid positions for the vertices, so tell OpenGL how these are laid out
|
||||
glEnableVertexAttribArray(0); // Attrib '0' is the vertex positions
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(PolyVox::Vertex< DataType >), (GLvoid*)(offsetof(PolyVox::Vertex< DataType >, position))); //take the first 3 floats from every sizeof(decltype(vecVertices)::value_type)
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(typename MeshType::VertexType), (GLvoid*)(offsetof(typename MeshType::VertexType, position))); //take the first 3 floats from every sizeof(decltype(vecVertices)::value_type)
|
||||
|
||||
// Some surface extractors also generate normals, so tell OpenGL how these are laid out. If a surface extractor
|
||||
// does not generate normals then nonsense values are written into the buffer here and sghould be ignored by the
|
||||
// shader. This is mostly just to simplify this example code - in a real application you will know whether your
|
||||
// chosen surface extractor generates normals and can skip uploading them if not.
|
||||
glEnableVertexAttribArray(1); // Attrib '1' is the vertex normals.
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(PolyVox::Vertex< DataType >), (GLvoid*)(offsetof(PolyVox::Vertex< DataType >, normal)));
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(typename MeshType::VertexType), (GLvoid*)(offsetof(typename MeshType::VertexType, normal)));
|
||||
|
||||
// Finally a surface extractor will probably output additional data. This is highly application dependant. For this example code
|
||||
// we're just uploading it as a set of bytes which we can read individually, but real code will want to do something specialised here.
|
||||
glEnableVertexAttribArray(2); //We're talking about shader attribute '2'
|
||||
GLint size = (std::min)(sizeof(DataType), size_t(4)); // Can't upload more that 4 components (vec4 is GLSL's biggest type)
|
||||
glVertexAttribIPointer(2, size, GL_UNSIGNED_BYTE, sizeof(PolyVox::Vertex< DataType >), (GLvoid*)(offsetof(PolyVox::Vertex< DataType >, data)));
|
||||
GLint size = (std::min)(sizeof(typename MeshType::VertexType::DataType), size_t(4)); // Can't upload more that 4 components (vec4 is GLSL's biggest type)
|
||||
glVertexAttribIPointer(2, size, GL_UNSIGNED_BYTE, sizeof(typename MeshType::VertexType), (GLvoid*)(offsetof(typename MeshType::VertexType, data)));
|
||||
|
||||
// We're done uploading and can now unbind.
|
||||
glBindVertexArray(0);
|
||||
@ -103,6 +104,9 @@ public:
|
||||
meshData.translation = QVector3D(translation.getX(), translation.getY(), translation.getZ());
|
||||
meshData.scale = scale;
|
||||
|
||||
// Set 16 or 32-bit index buffer size.
|
||||
meshData.indexType = sizeof(typename MeshType::IndexType) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
|
||||
|
||||
// Now add the mesh to the list of meshes to render.
|
||||
addMeshData(meshData);
|
||||
}
|
||||
|
@ -53,6 +53,11 @@ namespace PolyVox
|
||||
DataType data;
|
||||
};
|
||||
|
||||
// Convienient shorthand for declaring a mesh of 'cubic' vertices
|
||||
// Currently disabled because it requires GCC 4.7
|
||||
//template <typename VertexDataType, typename IndexType = DefaultIndexType>
|
||||
//using CubicMesh = Mesh< CubicVertex<VertexDataType>, IndexType >;
|
||||
|
||||
/// Decodes a position from a CubicVertex
|
||||
inline Vector3DFloat decodePosition(const Vector3DUint8& encodedPosition)
|
||||
{
|
||||
@ -72,6 +77,102 @@ namespace PolyVox
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Do not use this class directly. Use the 'extractCubicSurface' function instead (see examples).
|
||||
template<typename VolumeType, typename MeshType, typename IsQuadNeeded>
|
||||
class CubicSurfaceExtractor
|
||||
{
|
||||
struct IndexAndMaterial
|
||||
{
|
||||
int32_t iIndex;
|
||||
typename VolumeType::VoxelType uMaterial;
|
||||
};
|
||||
|
||||
enum FaceNames
|
||||
{
|
||||
PositiveX,
|
||||
PositiveY,
|
||||
PositiveZ,
|
||||
NegativeX,
|
||||
NegativeY,
|
||||
NegativeZ,
|
||||
NoOfFaces
|
||||
};
|
||||
|
||||
struct Quad
|
||||
{
|
||||
Quad(uint32_t v0, uint32_t v1, uint32_t v2, uint32_t v3)
|
||||
{
|
||||
vertices[0] = v0;
|
||||
vertices[1] = v1;
|
||||
vertices[2] = v2;
|
||||
vertices[3] = v3;
|
||||
}
|
||||
|
||||
uint32_t vertices[4];
|
||||
};
|
||||
|
||||
public:
|
||||
CubicSurfaceExtractor(VolumeType* volData, Region region, MeshType* result, IsQuadNeeded isQuadNeeded = IsQuadNeeded(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), bool bMergeQuads = true);
|
||||
|
||||
void execute();
|
||||
|
||||
private:
|
||||
int32_t addVertex(uint32_t uX, uint32_t uY, uint32_t uZ, typename VolumeType::VoxelType uMaterial, Array<3, IndexAndMaterial>& existingVertices);
|
||||
bool performQuadMerging(std::list<Quad>& quads);
|
||||
bool mergeQuads(Quad& q1, Quad& q2);
|
||||
|
||||
IsQuadNeeded m_funcIsQuadNeededCallback;
|
||||
|
||||
//The volume data and a sampler to access it.
|
||||
VolumeType* m_volData;
|
||||
|
||||
//Information about the region we are currently processing
|
||||
Region m_regSizeInVoxels;
|
||||
|
||||
//The surface patch we are currently filling.
|
||||
MeshType* m_meshCurrent;
|
||||
|
||||
//Used to avoid creating duplicate vertices.
|
||||
Array<3, IndexAndMaterial> m_previousSliceVertices;
|
||||
Array<3, IndexAndMaterial> m_currentSliceVertices;
|
||||
|
||||
//During extraction we create a number of different lists of quads. All the
|
||||
//quads in a given list are in the same plane and facing in the same direction.
|
||||
std::vector< std::list<Quad> > m_vecQuads[NoOfFaces];
|
||||
|
||||
//Controls whether quad merging should be performed. This might be undesirable
|
||||
//is the user needs per-vertex attributes, or to perform per vertex lighting.
|
||||
bool m_bMergeQuads;
|
||||
|
||||
//This constant defines the maximum number of quads which can share a
|
||||
//vertex in a cubic style mesh. See the initialisation for more details.
|
||||
static const uint32_t MaxVerticesPerPosition;
|
||||
|
||||
//The wrap mode
|
||||
WrapMode m_eWrapMode;
|
||||
typename VolumeType::VoxelType m_tBorderValue;
|
||||
};
|
||||
|
||||
// This version of the function performs the extraction into a user-provided mesh rather than allocating a mesh automatically.
|
||||
// There are a few reasons why this might be useful to more advanced users:
|
||||
//
|
||||
// 1. It leaves the user in control of memory allocation and would allow them to implement e.g. a mesh pooling system.
|
||||
// 2. The user-provided mesh could have a different index type (e.g. 16-bit indices) to reduce memory usage.
|
||||
// 3. The user could provide a custom mesh class, e.g a thin wrapper around an openGL VBO to allow direct writing into this structure.
|
||||
//
|
||||
// We don't provide a default MeshType here. If the user doesn't want to provide a MeshType then it probably makes
|
||||
// more sense to use the other variant of this function where the mesh is a return value rather than a parameter.
|
||||
//
|
||||
// Note: This function is called 'extractCubicMeshCustom' rather than 'extractCubicMesh' to avoid ambiguity when only three parameters
|
||||
// are provided (would the third parameter be a controller or a mesh?). It seems this can be fixed by using enable_if/static_assert to emulate concepts,
|
||||
// but this is relatively complex and I haven't done it yet. Could always add it later as another overload.
|
||||
template<typename VolumeType, typename MeshType, typename IsQuadNeeded = DefaultIsQuadNeeded<typename VolumeType::VoxelType> >
|
||||
void extractCubicMeshCustom(VolumeType* volData, Region region, MeshType* result, IsQuadNeeded isQuadNeeded = IsQuadNeeded(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), bool bMergeQuads = true)
|
||||
{
|
||||
CubicSurfaceExtractor<VolumeType, MeshType, IsQuadNeeded> extractor(volData, region, result, isQuadNeeded, eWrapMode, tBorderValue, bMergeQuads);
|
||||
extractor.execute();
|
||||
}
|
||||
|
||||
/// The CubicSurfaceExtractor creates a mesh in which each voxel appears to be rendered as a cube
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// Introduction
|
||||
@ -113,110 +214,13 @@ namespace PolyVox
|
||||
///
|
||||
/// Another scenario which sometimes results in confusion is when you wish to extract a region which corresponds to the whole volume, partcularly when solid voxels extend right to the edge of the volume.
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
template<typename VolumeType, typename MeshType, typename IsQuadNeeded>
|
||||
class CubicSurfaceExtractor
|
||||
template<typename VolumeType, typename IsQuadNeeded = DefaultIsQuadNeeded<typename VolumeType::VoxelType> >
|
||||
Mesh<CubicVertex<typename VolumeType::VoxelType> > extractCubicMesh(VolumeType* volData, Region region, IsQuadNeeded isQuadNeeded = IsQuadNeeded(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), bool bMergeQuads = true)
|
||||
{
|
||||
struct IndexAndMaterial
|
||||
{
|
||||
int32_t iIndex;
|
||||
typename VolumeType::VoxelType uMaterial;
|
||||
};
|
||||
|
||||
enum FaceNames
|
||||
{
|
||||
PositiveX,
|
||||
PositiveY,
|
||||
PositiveZ,
|
||||
NegativeX,
|
||||
NegativeY,
|
||||
NegativeZ,
|
||||
NoOfFaces
|
||||
};
|
||||
|
||||
struct Quad
|
||||
{
|
||||
Quad(uint32_t v0, uint32_t v1, uint32_t v2, uint32_t v3)
|
||||
{
|
||||
vertices[0] = v0;
|
||||
vertices[1] = v1;
|
||||
vertices[2] = v2;
|
||||
vertices[3] = v3;
|
||||
}
|
||||
|
||||
uint32_t vertices[4];
|
||||
};
|
||||
|
||||
public:
|
||||
// This is a bit ugly - it seems that the C++03 syntax is different from the C++11 syntax? See this thread: http://stackoverflow.com/questions/6076015/typename-outside-of-template
|
||||
// Long term we should probably come back to this and if the #ifdef is still needed then maybe it should check for C++11 mode instead of MSVC?
|
||||
#if defined(_MSC_VER)
|
||||
CubicSurfaceExtractor(VolumeType* volData, Region region, MeshType* result, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = VolumeType::VoxelType(), bool bMergeQuads = true, IsQuadNeeded isQuadNeeded = IsQuadNeeded());
|
||||
#else
|
||||
CubicSurfaceExtractor(VolumeType* volData, Region region, MeshType* result, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), bool bMergeQuads = true, IsQuadNeeded isQuadNeeded = IsQuadNeeded());
|
||||
#endif
|
||||
|
||||
|
||||
void execute();
|
||||
|
||||
private:
|
||||
int32_t addVertex(uint32_t uX, uint32_t uY, uint32_t uZ, typename VolumeType::VoxelType uMaterial, Array<3, IndexAndMaterial>& existingVertices);
|
||||
bool performQuadMerging(std::list<Quad>& quads);
|
||||
bool mergeQuads(Quad& q1, Quad& q2);
|
||||
|
||||
IsQuadNeeded m_funcIsQuadNeededCallback;
|
||||
|
||||
//The volume data and a sampler to access it.
|
||||
VolumeType* m_volData;
|
||||
|
||||
//Information about the region we are currently processing
|
||||
Region m_regSizeInVoxels;
|
||||
|
||||
//The surface patch we are currently filling.
|
||||
MeshType* m_meshCurrent;
|
||||
|
||||
//Used to avoid creating duplicate vertices.
|
||||
Array<3, IndexAndMaterial> m_previousSliceVertices;
|
||||
Array<3, IndexAndMaterial> m_currentSliceVertices;
|
||||
|
||||
//During extraction we create a number of different lists of quads. All the
|
||||
//quads in a given list are in the same plane and facing in the same direction.
|
||||
std::vector< std::list<Quad> > m_vecQuads[NoOfFaces];
|
||||
|
||||
//Controls whether quad merging should be performed. This might be undesirable
|
||||
//is the user needs per-vertex attributes, or to perform per vertex lighting.
|
||||
bool m_bMergeQuads;
|
||||
|
||||
//This constant defines the maximum number of quads which can share a
|
||||
//vertex in a cubic style mesh. See the initialisation for more details.
|
||||
static const uint32_t MaxVerticesPerPosition;
|
||||
|
||||
//The wrap mode
|
||||
WrapMode m_eWrapMode;
|
||||
typename VolumeType::VoxelType m_tBorderValue;
|
||||
};
|
||||
|
||||
template<typename VolumeType, typename IsQuadNeeded>
|
||||
Mesh<CubicVertex<typename VolumeType::VoxelType> > extractCubicMesh(VolumeType* volData, Region region, WrapMode eWrapMode, typename VolumeType::VoxelType tBorderValue, bool bMergeQuads, IsQuadNeeded isQuadNeeded)
|
||||
{
|
||||
typedef Mesh<CubicVertex<typename VolumeType::VoxelType> > MeshType;
|
||||
MeshType result;
|
||||
CubicSurfaceExtractor<VolumeType, MeshType, IsQuadNeeded> extractor(volData, region, &result, eWrapMode, tBorderValue, bMergeQuads, isQuadNeeded);
|
||||
extractor.execute();
|
||||
Mesh< CubicVertex<typename VolumeType::VoxelType> > result;
|
||||
extractCubicMeshCustom(volData, region, &result, isQuadNeeded, eWrapMode, tBorderValue, bMergeQuads);
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename VolumeType>
|
||||
// This is a bit ugly - it seems that the C++03 syntax is different from the C++11 syntax? See this thread: http://stackoverflow.com/questions/6076015/typename-outside-of-template
|
||||
// Long term we should probably come back to this and if the #ifdef is still needed then maybe it should check for C++11 mode instead of MSVC?
|
||||
#if defined(_MSC_VER)
|
||||
Mesh<CubicVertex<typename VolumeType::VoxelType> > extractCubicMesh(VolumeType* volData, Region region, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = VolumeType::VoxelType(), bool bMergeQuads = true)
|
||||
#else
|
||||
Mesh<CubicVertex<typename VolumeType::VoxelType> > extractCubicMesh(VolumeType* volData, Region region, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), bool bMergeQuads = true)
|
||||
#endif
|
||||
{
|
||||
DefaultIsQuadNeeded<typename VolumeType::VoxelType> isQuadNeeded;
|
||||
return extractCubicMesh<VolumeType, DefaultIsQuadNeeded<typename VolumeType::VoxelType> >(volData, region, eWrapMode, tBorderValue, bMergeQuads, isQuadNeeded);
|
||||
}
|
||||
}
|
||||
|
||||
#include "PolyVoxCore/CubicSurfaceExtractor.inl"
|
||||
|
@ -36,7 +36,7 @@ namespace PolyVox
|
||||
const uint32_t CubicSurfaceExtractor<VolumeType, MeshType, IsQuadNeeded>::MaxVerticesPerPosition = 8;
|
||||
|
||||
template<typename VolumeType, typename MeshType, typename IsQuadNeeded>
|
||||
CubicSurfaceExtractor<VolumeType, MeshType, IsQuadNeeded>::CubicSurfaceExtractor(VolumeType* volData, Region region, MeshType* result, WrapMode eWrapMode, typename VolumeType::VoxelType tBorderValue, bool bMergeQuads, IsQuadNeeded isQuadNeeded)
|
||||
CubicSurfaceExtractor<VolumeType, MeshType, IsQuadNeeded>::CubicSurfaceExtractor(VolumeType* volData, Region region, MeshType* result, IsQuadNeeded isQuadNeeded, WrapMode eWrapMode, typename VolumeType::VoxelType tBorderValue, bool bMergeQuads)
|
||||
:m_volData(volData)
|
||||
,m_regSizeInVoxels(region)
|
||||
,m_meshCurrent(result)
|
||||
|
@ -75,8 +75,15 @@ namespace PolyVox
|
||||
* if the voxel type is 'float' then the representable range is -FLT_MAX to FLT_MAX and the threshold will be set to zero.
|
||||
*/
|
||||
DefaultMarchingCubesController(void)
|
||||
:m_tThreshold(((std::numeric_limits<DensityType>::min)() + (std::numeric_limits<DensityType>::max)()) / 2)
|
||||
{
|
||||
if (std::is_signed<DensityType>())
|
||||
{
|
||||
m_tThreshold = DensityType(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_tThreshold = (((std::numeric_limits<DensityType>::min)() + (std::numeric_limits<DensityType>::max)()) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -24,6 +24,10 @@ freely, subject to the following restrictions:
|
||||
#ifndef __PolyVox_TypeDef_H__
|
||||
#define __PolyVox_TypeDef_H__
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1800)
|
||||
#error "Your version of Visual Studio is too old to build PolyVox. You need at least version Visual Stusio 2013"
|
||||
#endif
|
||||
|
||||
//Definitions needed to make library functions accessable
|
||||
// See http://gcc.gnu.org/wiki/Visibility for more info.
|
||||
#if defined _WIN32 || defined __CYGWIN__
|
||||
|
@ -55,6 +55,11 @@ namespace PolyVox
|
||||
DataType data;
|
||||
};
|
||||
|
||||
// Convienient shorthand for declaring a mesh of marching cubes vertices
|
||||
// Currently disabled because it requires GCC 4.7
|
||||
//template <typename VertexDataType, typename IndexType = DefaultIndexType>
|
||||
//using MarchingCubesMesh = Mesh< MarchingCubesVertex<VertexDataType>, IndexType >;
|
||||
|
||||
/// Decodes a position from a MarchingCubesVertex
|
||||
inline Vector3DFloat decodePosition(const Vector3DUint16& encodedPosition)
|
||||
{
|
||||
@ -90,8 +95,8 @@ namespace PolyVox
|
||||
// floats into two bytes and store them in a single uint16_t
|
||||
|
||||
// Move from range [-1.0f, 1.0f] to [0.0f, 255.0f]
|
||||
px = (px + 1.0) * 127.5f;
|
||||
py = (py + 1.0) * 127.5f;
|
||||
px = (px + 1.0f) * 127.5f;
|
||||
py = (py + 1.0f) * 127.5f;
|
||||
|
||||
// Convert to uints
|
||||
uint16_t resultX = static_cast<uint16_t>(px + 0.5f);
|
||||
@ -147,17 +152,12 @@ namespace PolyVox
|
||||
return result;
|
||||
}
|
||||
|
||||
template< typename VolumeType, typename Controller = DefaultMarchingCubesController<typename VolumeType::VoxelType> >
|
||||
/// Do not use this class directly. Use the 'extractMarchingCubesSurface' function instead (see examples).
|
||||
template< typename VolumeType, typename MeshType, typename ControllerType>
|
||||
class MarchingCubesSurfaceExtractor
|
||||
{
|
||||
public:
|
||||
// This is a bit ugly - it seems that the C++03 syntax is different from the C++11 syntax? See this thread: http://stackoverflow.com/questions/6076015/typename-outside-of-template
|
||||
// Long term we should probably come back to this and if the #ifdef is still needed then maybe it should check for C++11 mode instead of MSVC?
|
||||
#if defined(_MSC_VER)
|
||||
MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, Mesh<MarchingCubesVertex<typename VolumeType::VoxelType> >* result, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = VolumeType::VoxelType(), Controller controller = Controller());
|
||||
#else
|
||||
MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, Mesh<MarchingCubesVertex<typename VolumeType::VoxelType> >* result, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(), Controller controller = Controller());
|
||||
#endif
|
||||
MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, MeshType* result, ControllerType controller, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType());
|
||||
|
||||
void execute();
|
||||
|
||||
@ -306,7 +306,7 @@ namespace PolyVox
|
||||
uint32_t m_uNoOfOccupiedCells;
|
||||
|
||||
//The surface patch we are currently filling.
|
||||
Mesh<MarchingCubesVertex<typename VolumeType::VoxelType> >* m_meshCurrent;
|
||||
MeshType* m_meshCurrent;
|
||||
|
||||
//Information about the region we are currently processing
|
||||
Region m_regSizeInVoxels;
|
||||
@ -318,32 +318,38 @@ namespace PolyVox
|
||||
Region m_regSliceCurrent;
|
||||
|
||||
//Used to convert arbitrary voxel types in densities and materials.
|
||||
Controller m_controller;
|
||||
ControllerType m_controller;
|
||||
|
||||
//Our threshold value
|
||||
typename Controller::DensityType m_tThreshold;
|
||||
typename ControllerType::DensityType m_tThreshold;
|
||||
};
|
||||
|
||||
template< typename VolumeType, typename Controller>
|
||||
Mesh<MarchingCubesVertex<typename VolumeType::VoxelType> > extractMarchingCubesMesh(VolumeType* volData, Region region, WrapMode eWrapMode, typename VolumeType::VoxelType tBorderValue, Controller controller)
|
||||
// This version of the function performs the extraction into a user-provided mesh rather than allocating a mesh automatically.
|
||||
// There are a few reasons why this might be useful to more advanced users:
|
||||
//
|
||||
// 1. It leaves the user in control of memory allocation and would allow them to implement e.g. a mesh pooling system.
|
||||
// 2. The user-provided mesh could have a different index type (e.g. 16-bit indices) to reduce memory usage.
|
||||
// 3. The user could provide a custom mesh class, e.g a thin wrapper around an openGL VBO to allow direct writing into this structure.
|
||||
//
|
||||
// We don't provide a default MeshType here. If the user doesn't want to provide a MeshType then it probably makes
|
||||
// more sense to use the other variant of this function where the mesh is a return value rather than a parameter.
|
||||
//
|
||||
// Note: This function is called 'extractMarchingCubesMeshCustom' rather than 'extractMarchingCubesMesh' to avoid ambiguity when only three parameters
|
||||
// are provided (would the third parameter be a controller or a mesh?). It seems this can be fixed by using enable_if/static_assert to emulate concepts,
|
||||
// but this is relatively complex and I haven't done it yet. Could always add it later as another overload.
|
||||
template< typename VolumeType, typename MeshType, typename ControllerType = DefaultMarchingCubesController<typename VolumeType::VoxelType> >
|
||||
void extractMarchingCubesMeshCustom(VolumeType* volData, Region region, MeshType* result, ControllerType controller = ControllerType(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType())
|
||||
{
|
||||
Mesh<MarchingCubesVertex<typename VolumeType::VoxelType> > result;
|
||||
MarchingCubesSurfaceExtractor<VolumeType, Controller> extractor(volData, region, &result, eWrapMode, tBorderValue, controller);
|
||||
MarchingCubesSurfaceExtractor<VolumeType, MeshType, ControllerType> extractor(volData, region, result, controller, eWrapMode, tBorderValue);
|
||||
extractor.execute();
|
||||
return result;
|
||||
}
|
||||
|
||||
template< typename VolumeType>
|
||||
// This is a bit ugly - it seems that the C++03 syntax is different from the C++11 syntax? See this thread: http://stackoverflow.com/questions/6076015/typename-outside-of-template
|
||||
// Long term we should probably come back to this and if the #ifdef is still needed then maybe it should check for C++11 mode instead of MSVC?
|
||||
#if defined(_MSC_VER)
|
||||
Mesh<MarchingCubesVertex<typename VolumeType::VoxelType> > extractMarchingCubesMesh(VolumeType* volData, Region region, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = VolumeType::VoxelType())
|
||||
#else
|
||||
Mesh<MarchingCubesVertex<typename VolumeType::VoxelType> > extractMarchingCubesMesh(VolumeType* volData, Region region, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType())
|
||||
#endif
|
||||
template< typename VolumeType, typename ControllerType = DefaultMarchingCubesController<typename VolumeType::VoxelType> >
|
||||
Mesh<MarchingCubesVertex<typename VolumeType::VoxelType> > extractMarchingCubesMesh(VolumeType* volData, Region region, ControllerType controller = ControllerType(), WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType())
|
||||
{
|
||||
DefaultMarchingCubesController<typename VolumeType::VoxelType> controller;
|
||||
return extractMarchingCubesMesh(volData, region, eWrapMode, tBorderValue, controller);
|
||||
Mesh<MarchingCubesVertex<typename VolumeType::VoxelType> > result;
|
||||
extractMarchingCubesMeshCustom<VolumeType, Mesh<MarchingCubesVertex<typename VolumeType::VoxelType>, DefaultIndexType > >(volData, region, &result, controller, eWrapMode, tBorderValue);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,8 +25,8 @@ freely, subject to the following restrictions:
|
||||
|
||||
namespace PolyVox
|
||||
{
|
||||
template<typename VolumeType, typename Controller>
|
||||
MarchingCubesSurfaceExtractor<VolumeType, Controller>::MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, Mesh<MarchingCubesVertex<typename VolumeType::VoxelType> >* result, WrapMode eWrapMode, typename VolumeType::VoxelType tBorderValue, Controller controller)
|
||||
template<typename VolumeType, typename MeshType, typename ControllerType>
|
||||
MarchingCubesSurfaceExtractor<VolumeType, MeshType, ControllerType>::MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, MeshType* result, ControllerType controller, WrapMode eWrapMode, typename VolumeType::VoxelType tBorderValue)
|
||||
:m_volData(volData)
|
||||
,m_sampVolume(volData)
|
||||
,m_meshCurrent(result)
|
||||
@ -34,6 +34,7 @@ namespace PolyVox
|
||||
,m_controller(controller)
|
||||
,m_tThreshold(m_controller.getThreshold())
|
||||
{
|
||||
POLYVOX_THROW_IF(m_meshCurrent == nullptr, std::invalid_argument, "Provided mesh cannot be null");
|
||||
//m_regSizeInVoxels.cropTo(m_volData->getEnclosingRegion());
|
||||
m_regSizeInCells = m_regSizeInVoxels;
|
||||
m_regSizeInCells.setUpperCorner(m_regSizeInCells.getUpperCorner() - Vector3DInt32(1,1,1));
|
||||
@ -41,8 +42,8 @@ namespace PolyVox
|
||||
m_sampVolume.setWrapMode(eWrapMode, tBorderValue);
|
||||
}
|
||||
|
||||
template<typename VolumeType, typename Controller>
|
||||
void MarchingCubesSurfaceExtractor<VolumeType, Controller>::execute()
|
||||
template<typename VolumeType, typename MeshType, typename ControllerType>
|
||||
void MarchingCubesSurfaceExtractor<VolumeType, MeshType, ControllerType>::execute()
|
||||
{
|
||||
Timer timer;
|
||||
m_meshCurrent->clear();
|
||||
@ -129,9 +130,9 @@ namespace PolyVox
|
||||
<< "x" << m_regSizeInVoxels.getDepthInVoxels() << ")");
|
||||
}
|
||||
|
||||
template<typename VolumeType, typename Controller>
|
||||
template<typename VolumeType, typename MeshType, typename ControllerType>
|
||||
template<bool isPrevZAvail>
|
||||
uint32_t MarchingCubesSurfaceExtractor<VolumeType, Controller>::computeBitmaskForSlice(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask)
|
||||
uint32_t MarchingCubesSurfaceExtractor<VolumeType, MeshType, ControllerType>::computeBitmaskForSlice(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask)
|
||||
{
|
||||
m_uNoOfOccupiedCells = 0;
|
||||
|
||||
@ -195,9 +196,9 @@ namespace PolyVox
|
||||
return m_uNoOfOccupiedCells;
|
||||
}
|
||||
|
||||
template<typename VolumeType, typename Controller>
|
||||
template<typename VolumeType, typename MeshType, typename ControllerType>
|
||||
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, MeshType, ControllerType>::computeBitmaskForCell(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask, uint32_t uXRegSpace, uint32_t uYRegSpace)
|
||||
{
|
||||
uint8_t iCubeIndex = 0;
|
||||
|
||||
@ -395,8 +396,8 @@ namespace PolyVox
|
||||
}
|
||||
}
|
||||
|
||||
template<typename VolumeType, typename Controller>
|
||||
void MarchingCubesSurfaceExtractor<VolumeType, Controller>::generateVerticesForSlice(const Array2DUint8& pCurrentBitmask,
|
||||
template<typename VolumeType, typename MeshType, typename ControllerType>
|
||||
void MarchingCubesSurfaceExtractor<VolumeType, MeshType, ControllerType>::generateVerticesForSlice(const Array2DUint8& pCurrentBitmask,
|
||||
Array2DInt32& m_pCurrentVertexIndicesX,
|
||||
Array2DInt32& m_pCurrentVertexIndicesY,
|
||||
Array2DInt32& m_pCurrentVertexIndicesZ)
|
||||
@ -535,8 +536,8 @@ namespace PolyVox
|
||||
}
|
||||
}
|
||||
|
||||
template<typename VolumeType, typename Controller>
|
||||
void MarchingCubesSurfaceExtractor<VolumeType, Controller>::generateIndicesForSlice(const Array2DUint8& pPreviousBitmask,
|
||||
template<typename VolumeType, typename MeshType, typename ControllerType>
|
||||
void MarchingCubesSurfaceExtractor<VolumeType, MeshType, ControllerType>::generateIndicesForSlice(const Array2DUint8& pPreviousBitmask,
|
||||
const Array2DInt32& m_pPreviousVertexIndicesX,
|
||||
const Array2DInt32& m_pPreviousVertexIndicesY,
|
||||
const Array2DInt32& m_pPreviousVertexIndicesZ,
|
||||
|
@ -50,43 +50,51 @@ namespace PolyVox
|
||||
Mesh();
|
||||
~Mesh();
|
||||
|
||||
const std::vector<IndexType>& getIndices(void) const;
|
||||
uint32_t getNoOfIndices(void) const;
|
||||
IndexType getNoOfVertices(void) const;
|
||||
const std::vector<VertexType>& getVertices(void) const;
|
||||
const Vector3DInt32& getOffset(void) const;
|
||||
const VertexType& getVertex(IndexType index) const;
|
||||
const VertexType* getRawVertexData(void) const;
|
||||
POLYVOX_DEPRECATED const std::vector<VertexType>& getVertices(void) const;
|
||||
|
||||
uint32_t getNoOfIndices(void) const;
|
||||
IndexType getIndex(uint32_t index) const;
|
||||
const IndexType* getRawIndexData(void);
|
||||
POLYVOX_DEPRECATED const std::vector<IndexType>& getIndices(void) const;
|
||||
|
||||
const Vector3DInt32& getOffset(void) const;
|
||||
void setOffset(const Vector3DInt32& offset);
|
||||
|
||||
void addTriangle(IndexType index0, IndexType index1, IndexType index2);
|
||||
IndexType addVertex(const VertexType& vertex);
|
||||
void addTriangle(IndexType index0, IndexType index1, IndexType index2);
|
||||
|
||||
void clear(void);
|
||||
bool isEmpty(void) const;
|
||||
void removeUnusedVertices(void);
|
||||
|
||||
Vector3DInt32 m_offset;
|
||||
|
||||
public:
|
||||
std::vector<IndexType> m_vecTriangleIndices;
|
||||
private:
|
||||
std::vector<IndexType> m_vecIndices;
|
||||
std::vector<VertexType> m_vecVertices;
|
||||
Vector3DInt32 m_offset;
|
||||
};
|
||||
|
||||
template <typename MeshType>
|
||||
Mesh< Vertex< typename MeshType::VertexType::DataType >, typename MeshType::IndexType > decodeMesh(const MeshType& mesh)
|
||||
Mesh< Vertex< typename MeshType::VertexType::DataType >, typename MeshType::IndexType > decodeMesh(const MeshType& encodedMesh)
|
||||
{
|
||||
Mesh< Vertex< typename MeshType::VertexType::DataType >, typename MeshType::IndexType > result;
|
||||
result.m_vecVertices.resize(mesh.m_vecVertices.size());
|
||||
Mesh< Vertex< typename MeshType::VertexType::DataType >, typename MeshType::IndexType > decodedMesh;
|
||||
|
||||
for(typename MeshType::IndexType ct = 0; ct < mesh.m_vecVertices.size(); ct++)
|
||||
for (typename MeshType::IndexType ct = 0; ct < encodedMesh.getNoOfVertices(); ct++)
|
||||
{
|
||||
result.m_vecVertices[ct] = decodeVertex(mesh.m_vecVertices[ct]);
|
||||
decodedMesh.addVertex(decodeVertex(encodedMesh.getVertex(ct)));
|
||||
}
|
||||
|
||||
result.m_vecTriangleIndices = mesh.m_vecTriangleIndices;
|
||||
POLYVOX_ASSERT(encodedMesh.getNoOfIndices() % 3 == 0, "The number of indices must always be a multiple of three.");
|
||||
for (uint32_t ct = 0; ct < encodedMesh.getNoOfIndices(); ct += 3)
|
||||
{
|
||||
decodedMesh.addTriangle(encodedMesh.getIndex(ct), encodedMesh.getIndex(ct + 1), encodedMesh.getIndex(ct + 2));
|
||||
}
|
||||
|
||||
result.m_offset = mesh.m_offset;
|
||||
decodedMesh.setOffset(encodedMesh.getOffset());
|
||||
|
||||
return result;
|
||||
return decodedMesh;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,30 +33,54 @@ namespace PolyVox
|
||||
{
|
||||
}
|
||||
|
||||
template <typename VertexType, typename IndexType>
|
||||
const std::vector<IndexType>& Mesh<VertexType, IndexType>::getIndices(void) const
|
||||
{
|
||||
return m_vecTriangleIndices;
|
||||
}
|
||||
|
||||
template <typename VertexType, typename IndexType>
|
||||
uint32_t Mesh<VertexType, IndexType>::getNoOfIndices(void) const
|
||||
{
|
||||
return m_vecTriangleIndices.size();
|
||||
}
|
||||
|
||||
template <typename VertexType, typename IndexType>
|
||||
IndexType Mesh<VertexType, IndexType>::getNoOfVertices(void) const
|
||||
{
|
||||
return m_vecVertices.size();
|
||||
}
|
||||
|
||||
template <typename VertexType, typename IndexType>
|
||||
const VertexType& Mesh<VertexType, IndexType>::getVertex(IndexType index) const
|
||||
{
|
||||
return m_vecVertices[index];
|
||||
}
|
||||
|
||||
template <typename VertexType, typename IndexType>
|
||||
const VertexType* Mesh<VertexType, IndexType>::getRawVertexData(void) const
|
||||
{
|
||||
return &(m_vecVertices[0]);
|
||||
}
|
||||
|
||||
template <typename VertexType, typename IndexType>
|
||||
const std::vector<VertexType>& Mesh<VertexType, IndexType>::getVertices(void) const
|
||||
{
|
||||
return m_vecVertices;
|
||||
}
|
||||
|
||||
template <typename VertexType, typename IndexType>
|
||||
uint32_t Mesh<VertexType, IndexType>::getNoOfIndices(void) const
|
||||
{
|
||||
return m_vecIndices.size();
|
||||
}
|
||||
|
||||
template <typename VertexType, typename IndexType>
|
||||
IndexType Mesh<VertexType, IndexType>::getIndex(uint32_t index) const
|
||||
{
|
||||
return m_vecIndices[index];
|
||||
}
|
||||
|
||||
template <typename VertexType, typename IndexType>
|
||||
const IndexType* Mesh<VertexType, IndexType>::getRawIndexData(void)
|
||||
{
|
||||
return &(m_vecIndices[0]);
|
||||
}
|
||||
|
||||
template <typename VertexType, typename IndexType>
|
||||
const std::vector<IndexType>& Mesh<VertexType, IndexType>::getIndices(void) const
|
||||
{
|
||||
return m_vecIndices;
|
||||
}
|
||||
|
||||
template <typename VertexType, typename IndexType>
|
||||
const Vector3DInt32& Mesh<VertexType, IndexType>::getOffset(void) const
|
||||
{
|
||||
@ -77,9 +101,9 @@ namespace PolyVox
|
||||
POLYVOX_ASSERT(index1 < m_vecVertices.size(), "Index points at an invalid vertex.");
|
||||
POLYVOX_ASSERT(index2 < m_vecVertices.size(), "Index points at an invalid vertex.");
|
||||
|
||||
m_vecTriangleIndices.push_back(index0);
|
||||
m_vecTriangleIndices.push_back(index1);
|
||||
m_vecTriangleIndices.push_back(index2);
|
||||
m_vecIndices.push_back(index0);
|
||||
m_vecIndices.push_back(index1);
|
||||
m_vecIndices.push_back(index2);
|
||||
}
|
||||
|
||||
template <typename VertexType, typename IndexType>
|
||||
@ -96,7 +120,7 @@ namespace PolyVox
|
||||
void Mesh<VertexType, IndexType>::clear(void)
|
||||
{
|
||||
m_vecVertices.clear();
|
||||
m_vecTriangleIndices.clear();
|
||||
m_vecIndices.clear();
|
||||
}
|
||||
|
||||
template <typename VertexType, typename IndexType>
|
||||
@ -111,9 +135,9 @@ namespace PolyVox
|
||||
std::vector<bool> isVertexUsed(m_vecVertices.size());
|
||||
std::fill(isVertexUsed.begin(), isVertexUsed.end(), false);
|
||||
|
||||
for(uint32_t triCt = 0; triCt < m_vecTriangleIndices.size(); triCt++)
|
||||
for(uint32_t triCt = 0; triCt < m_vecIndices.size(); triCt++)
|
||||
{
|
||||
int v = m_vecTriangleIndices[triCt];
|
||||
int v = m_vecIndices[triCt];
|
||||
isVertexUsed[v] = true;
|
||||
}
|
||||
|
||||
@ -131,9 +155,9 @@ namespace PolyVox
|
||||
|
||||
m_vecVertices.resize(noOfUsedVertices);
|
||||
|
||||
for(uint32_t triCt = 0; triCt < m_vecTriangleIndices.size(); triCt++)
|
||||
for(uint32_t triCt = 0; triCt < m_vecIndices.size(); triCt++)
|
||||
{
|
||||
m_vecTriangleIndices[triCt] = newPos[m_vecTriangleIndices[triCt]];
|
||||
m_vecIndices[triCt] = newPos[m_vecIndices[triCt]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ namespace PolyVox
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// MarchingCubesSurfaceExtractor
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template<typename VolumeType, typename Controller> class MarchingCubesSurfaceExtractor;
|
||||
template<typename VolumeType, typename MeshType, typename ControllerType> class MarchingCubesSurfaceExtractor;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// MarchingCubesVertex
|
||||
@ -142,7 +142,8 @@ namespace PolyVox
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Mesh
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename VertexType, typename IndexType = uint32_t> class Mesh;
|
||||
typedef uint32_t DefaultIndexType;
|
||||
template <typename VertexType, typename IndexType = DefaultIndexType> class Mesh;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Pager
|
||||
|
@ -26,6 +26,7 @@ freely, subject to the following restrictions:
|
||||
#include "PolyVoxCore/Density.h"
|
||||
#include "PolyVoxCore/Material.h"
|
||||
#include "PolyVoxCore/MaterialDensityPair.h"
|
||||
#include "PolyVoxCore/RawVolume.h"
|
||||
#include "PolyVoxCore/SimpleVolume.h"
|
||||
#include "PolyVoxCore/CubicSurfaceExtractor.h"
|
||||
|
||||
@ -33,157 +34,148 @@ freely, subject to the following restrictions:
|
||||
|
||||
using namespace PolyVox;
|
||||
|
||||
|
||||
// These 'writeDensityValueToVoxel' functions provide a unified interface for writting densities to primative and class voxel types.
|
||||
// They are conceptually the inverse of the 'convertToDensity' function used by the MarchingCubesSurfaceExtractor. They probably shouldn't be part
|
||||
// of PolyVox, but they might be useful to other tests so we cold move them into a 'Tests.h' or something in the future.
|
||||
template<typename VoxelType>
|
||||
void writeDensityValueToVoxel(int valueToWrite, VoxelType& voxel)
|
||||
template<typename _VoxelType>
|
||||
class CustomIsQuadNeeded
|
||||
{
|
||||
voxel = valueToWrite;
|
||||
}
|
||||
public:
|
||||
typedef _VoxelType VoxelType;
|
||||
|
||||
template<>
|
||||
void writeDensityValueToVoxel(int valueToWrite, Density8& voxel)
|
||||
{
|
||||
voxel.setDensity(valueToWrite);
|
||||
}
|
||||
bool operator()(VoxelType back, VoxelType front, VoxelType& materialToUse)
|
||||
{
|
||||
// Not a useful test - it just does something different
|
||||
// to the DefaultIsQuadNeeded so we can check it compiles.
|
||||
if ((back > 1) && (front <= 1))
|
||||
{
|
||||
materialToUse = static_cast<VoxelType>(back);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
void writeDensityValueToVoxel(int valueToWrite, MaterialDensityPair88& voxel)
|
||||
// Runs the surface extractor for a given type.
|
||||
template <typename VolumeType>
|
||||
VolumeType* createAndFillVolumeWithNoise(int32_t iVolumeSideLength, typename VolumeType::VoxelType minValue, typename VolumeType::VoxelType maxValue)
|
||||
{
|
||||
voxel.setDensity(valueToWrite);
|
||||
}
|
||||
//Create empty volume
|
||||
VolumeType* volData = new VolumeType(Region(Vector3DInt32(0, 0, 0), Vector3DInt32(iVolumeSideLength - 1, iVolumeSideLength - 1, iVolumeSideLength - 1)));
|
||||
|
||||
template<typename VoxelType>
|
||||
void writeMaterialValueToVoxel(int valueToWrite, VoxelType& voxel)
|
||||
{
|
||||
//Most types don't have a material
|
||||
return;
|
||||
}
|
||||
// Seed generator for consistency between runs.
|
||||
srand(12345);
|
||||
|
||||
template<>
|
||||
void writeMaterialValueToVoxel(int valueToWrite, MaterialDensityPair88& voxel)
|
||||
{
|
||||
voxel.setMaterial(valueToWrite);
|
||||
//Fill the volume with data
|
||||
for (int32_t z = 0; z < iVolumeSideLength; z++)
|
||||
{
|
||||
for (int32_t y = 0; y < iVolumeSideLength; y++)
|
||||
{
|
||||
for (int32_t x = 0; x < iVolumeSideLength; x++)
|
||||
{
|
||||
if (minValue == maxValue)
|
||||
{
|
||||
// In this case we are filling the whole volume with a single value.
|
||||
volData->setVoxelAt(x, y, z, minValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise we write random voxel values between zero and the requested maximum
|
||||
int voxelValue = (rand() % (maxValue - minValue + 1)) + minValue;
|
||||
volData->setVoxelAt(x, y, z, static_cast<typename VolumeType::VoxelType>(voxelValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return volData;
|
||||
}
|
||||
|
||||
// Runs the surface extractor for a given type.
|
||||
template <typename VoxelType>
|
||||
uint32_t testForType(void)
|
||||
template <typename VolumeType>
|
||||
VolumeType* createAndFillVolumeRealistic(int32_t iVolumeSideLength)
|
||||
{
|
||||
const int32_t uVolumeSideLength = 256;
|
||||
|
||||
//Create empty volume
|
||||
SimpleVolume<VoxelType> volData(Region(Vector3DInt32(0,0,0), Vector3DInt32(uVolumeSideLength-1, uVolumeSideLength-1, uVolumeSideLength-1)), 128);
|
||||
VolumeType* volData = new VolumeType(Region(Vector3DInt32(0, 0, 0), Vector3DInt32(iVolumeSideLength - 1, iVolumeSideLength - 1, iVolumeSideLength - 1)));
|
||||
|
||||
// Seed generator for consistency between runs.
|
||||
srand(12345);
|
||||
|
||||
//Fill the volume with data
|
||||
for (int32_t z = 0; z < uVolumeSideLength; z++)
|
||||
for (int32_t z = 0; z < iVolumeSideLength; z++)
|
||||
{
|
||||
for (int32_t y = 0; y < uVolumeSideLength; y++)
|
||||
for (int32_t y = 0; y < iVolumeSideLength; y++)
|
||||
{
|
||||
for (int32_t x = 0; x < uVolumeSideLength; x++)
|
||||
for (int32_t x = 0; x < iVolumeSideLength; x++)
|
||||
{
|
||||
if(x + y + z > uVolumeSideLength)
|
||||
// Should create a checker board pattern stretched along z? This is 'realistic' in the sense
|
||||
// that it's not empty/random data, and should allow significant decimation to be performed.
|
||||
if ((x ^ y) & 0x01)
|
||||
{
|
||||
VoxelType voxelValue;
|
||||
writeDensityValueToVoxel<VoxelType>(100, voxelValue);
|
||||
writeMaterialValueToVoxel<VoxelType>(42, voxelValue);
|
||||
volData.setVoxelAt(x, y, z, voxelValue);
|
||||
volData->setVoxelAt(x, y, z, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
volData->setVoxelAt(x, y, z, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t uTotalVertices = 0;
|
||||
uint32_t uTotalIndices = 0;
|
||||
|
||||
//Run the surface extractor a number of times over differnt regions of the volume.
|
||||
const int32_t uRegionSideLength = 64;
|
||||
for (int32_t z = 0; z < uVolumeSideLength; z += uRegionSideLength)
|
||||
{
|
||||
for (int32_t y = 0; y < uVolumeSideLength; y += uRegionSideLength)
|
||||
{
|
||||
for (int32_t x = 0; x < uVolumeSideLength; x += uRegionSideLength)
|
||||
{
|
||||
Region regionToExtract(x, y, z, x + uRegionSideLength - 1, y + uRegionSideLength - 1, z + uRegionSideLength - 1);
|
||||
|
||||
auto result = extractCubicMesh(&volData, regionToExtract);
|
||||
|
||||
uTotalVertices += result.getNoOfVertices();
|
||||
uTotalIndices += result.getNoOfIndices();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Just some value which is representative of the work we've done. It doesn't
|
||||
// matter what it is, just that it should be the same every time we run the test.
|
||||
return uTotalVertices + uTotalIndices;
|
||||
return volData;
|
||||
}
|
||||
|
||||
void TestCubicSurfaceExtractor::testExecute()
|
||||
void TestCubicSurfaceExtractor::testBehaviour()
|
||||
{
|
||||
/*const static uint32_t uExpectedVertices = 6624;
|
||||
const static uint32_t uExpectedIndices = 9936;
|
||||
const static uint32_t uMaterialToCheck = 3000;
|
||||
const static float fExpectedMaterial = 42.0f;
|
||||
const static uint32_t uIndexToCheck = 2000;
|
||||
const static uint32_t uExpectedIndex = 1334;
|
||||
// Test with default mesh and contoller types.
|
||||
auto uint8Vol = createAndFillVolumeWithNoise< SimpleVolume<uint8_t> >(32, 0, 2);
|
||||
auto uint8Mesh = extractCubicMesh(uint8Vol, uint8Vol->getEnclosingRegion());
|
||||
QCOMPARE(uint8Mesh.getNoOfVertices(), uint32_t(57687));
|
||||
QCOMPARE(uint8Mesh.getNoOfIndices(), uint32_t(216234));
|
||||
|
||||
Mesh<CubicVertex> mesh;*/
|
||||
// Test with default mesh type but user-provided controller.
|
||||
auto int8Vol = createAndFillVolumeWithNoise< SimpleVolume<int8_t> >(32, 0, 2);
|
||||
auto int8Mesh = extractCubicMesh(int8Vol, int8Vol->getEnclosingRegion(), CustomIsQuadNeeded<int8_t>());
|
||||
QCOMPARE(int8Mesh.getNoOfVertices(), uint32_t(29027));
|
||||
QCOMPARE(int8Mesh.getNoOfIndices(), uint32_t(178356));
|
||||
|
||||
/*testForType<int8_t>(mesh);
|
||||
QCOMPARE(mesh.getNoOfVertices(), uExpectedVertices);
|
||||
QCOMPARE(mesh.getNoOfIndices(), uExpectedIndices);
|
||||
QCOMPARE(mesh.getVertices()[uMaterialToCheck].getMaterial(), fNoMaterial);
|
||||
// Test with default controller but user-provided mesh.
|
||||
auto uint32Vol = createAndFillVolumeWithNoise< SimpleVolume<uint32_t> >(32, 0, 2);
|
||||
Mesh< CubicVertex< uint32_t >, uint16_t > uint32Mesh;
|
||||
extractCubicMeshCustom(uint32Vol, uint32Vol->getEnclosingRegion(), &uint32Mesh);
|
||||
QCOMPARE(uint32Mesh.getNoOfVertices(), uint16_t(57687));
|
||||
QCOMPARE(uint32Mesh.getNoOfIndices(), uint32_t(216234));
|
||||
|
||||
testForType<uint8_t>(mesh);
|
||||
QCOMPARE(mesh.getNoOfVertices(), uExpectedVertices);
|
||||
QCOMPARE(mesh.getNoOfIndices(), uExpectedIndices);
|
||||
QCOMPARE(mesh.getVertices()[uMaterialToCheck].getMaterial(), fNoMaterial);
|
||||
// Test with both mesh and controller being provided by the user.
|
||||
auto int32Vol = createAndFillVolumeWithNoise< SimpleVolume<int32_t> >(32, 0, 2);
|
||||
Mesh< CubicVertex< int32_t >, uint16_t > int32Mesh;
|
||||
extractCubicMeshCustom(int32Vol, int32Vol->getEnclosingRegion(), &int32Mesh, CustomIsQuadNeeded<int32_t>());
|
||||
QCOMPARE(int32Mesh.getNoOfVertices(), uint16_t(29027));
|
||||
QCOMPARE(int32Mesh.getNoOfIndices(), uint32_t(178356));
|
||||
}
|
||||
|
||||
testForType<int16_t>(mesh);
|
||||
QCOMPARE(mesh.getNoOfVertices(), uExpectedVertices);
|
||||
QCOMPARE(mesh.getNoOfIndices(), uExpectedIndices);
|
||||
QCOMPARE(mesh.getVertices()[uMaterialToCheck].getMaterial(), fNoMaterial);
|
||||
void TestCubicSurfaceExtractor::testEmptyVolumePerformance()
|
||||
{
|
||||
auto emptyVol = createAndFillVolumeWithNoise< SimpleVolume<uint32_t> >(128, 0, 0);
|
||||
Mesh< CubicVertex< uint32_t >, uint16_t > emptyMesh;
|
||||
QBENCHMARK{ extractCubicMeshCustom(emptyVol, Region(32, 32, 32, 63, 63, 63), &emptyMesh); }
|
||||
QCOMPARE(emptyMesh.getNoOfVertices(), uint16_t(0));
|
||||
}
|
||||
|
||||
testForType<uint16_t>(mesh);
|
||||
QCOMPARE(mesh.getNoOfVertices(), uExpectedVertices);
|
||||
QCOMPARE(mesh.getNoOfIndices(), uExpectedIndices);
|
||||
QCOMPARE(mesh.getVertices()[uMaterialToCheck].getMaterial(), fNoMaterial);
|
||||
void TestCubicSurfaceExtractor::testRealisticVolumePerformance()
|
||||
{
|
||||
auto realisticVol = createAndFillVolumeRealistic< SimpleVolume<uint32_t> >(128);
|
||||
Mesh< CubicVertex< uint32_t >, uint16_t > realisticMesh;
|
||||
QBENCHMARK{ extractCubicMeshCustom(realisticVol, Region(32, 32, 32, 63, 63, 63), &realisticMesh); }
|
||||
QCOMPARE(realisticMesh.getNoOfVertices(), uint16_t(2176));
|
||||
}
|
||||
|
||||
testForType<int32_t>(mesh);
|
||||
QCOMPARE(mesh.getNoOfVertices(), uExpectedVertices);
|
||||
QCOMPARE(mesh.getNoOfIndices(), uExpectedIndices);
|
||||
QCOMPARE(mesh.getVertices()[uMaterialToCheck].getMaterial(), fNoMaterial);
|
||||
|
||||
testForType<uint32_t>(mesh);
|
||||
QCOMPARE(mesh.getNoOfVertices(), uExpectedVertices);
|
||||
QCOMPARE(mesh.getNoOfIndices(), uExpectedIndices);
|
||||
QCOMPARE(mesh.getVertices()[uMaterialToCheck].getMaterial(), fNoMaterial);
|
||||
|
||||
testForType<float>(mesh);
|
||||
QCOMPARE(mesh.getNoOfVertices(), uExpectedVertices);
|
||||
QCOMPARE(mesh.getNoOfIndices(), uExpectedIndices);
|
||||
QCOMPARE(mesh.getVertices()[uMaterialToCheck].getMaterial(), fNoMaterial);
|
||||
|
||||
testForType<double>(mesh);
|
||||
QCOMPARE(mesh.getNoOfVertices(), uExpectedVertices);
|
||||
QCOMPARE(mesh.getNoOfIndices(), uExpectedIndices);
|
||||
QCOMPARE(mesh.getVertices()[uMaterialToCheck].getMaterial(), fNoMaterial);*/
|
||||
|
||||
/*testForType<Material8>(mesh);
|
||||
QCOMPARE(mesh.getNoOfVertices(), uExpectedVertices);
|
||||
QCOMPARE(mesh.getNoOfIndices(), uExpectedIndices);
|
||||
QCOMPARE(mesh.getVertices()[uMaterialToCheck].getMaterial(), fNoMaterial);*/
|
||||
|
||||
const static uint32_t uExpectedSumOfVerticesAndIndices = 704668;
|
||||
//const static uint32_t uExpectedSumOfVerticesAndIndices = 2792332;
|
||||
uint32_t result = 0;
|
||||
QBENCHMARK {
|
||||
result = testForType<MaterialDensityPair88>();
|
||||
}
|
||||
QCOMPARE(result, uExpectedSumOfVerticesAndIndices);
|
||||
void TestCubicSurfaceExtractor::testNoiseVolumePerformance()
|
||||
{
|
||||
auto noiseVol = createAndFillVolumeWithNoise< SimpleVolume<uint32_t> >(128, 0, 2);
|
||||
Mesh< CubicVertex< uint32_t >, uint16_t > noiseMesh;
|
||||
QBENCHMARK{ extractCubicMeshCustom(noiseVol, Region(32, 32, 32, 63, 63, 63), &noiseMesh); }
|
||||
QCOMPARE(noiseMesh.getNoOfVertices(), uint16_t(57729));
|
||||
}
|
||||
|
||||
QTEST_MAIN(TestCubicSurfaceExtractor)
|
||||
|
@ -31,7 +31,10 @@ class TestCubicSurfaceExtractor: public QObject
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void testExecute();
|
||||
void testBehaviour();
|
||||
void testEmptyVolumePerformance();
|
||||
void testRealisticVolumePerformance();
|
||||
void testNoiseVolumePerformance();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -52,12 +52,12 @@ public:
|
||||
|
||||
float convertToMaterial(float /*voxel*/)
|
||||
{
|
||||
return 1;
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float blendMaterials(float /*a*/, float /*b*/, float /*weight*/)
|
||||
{
|
||||
return 1;
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float getThreshold(void)
|
||||
@ -75,12 +75,6 @@ void writeDensityValueToVoxel(int valueToWrite, VoxelType& voxel)
|
||||
voxel = valueToWrite;
|
||||
}
|
||||
|
||||
template<>
|
||||
void writeDensityValueToVoxel(int valueToWrite, Density8& voxel)
|
||||
{
|
||||
voxel.setDensity(valueToWrite);
|
||||
}
|
||||
|
||||
template<>
|
||||
void writeDensityValueToVoxel(int valueToWrite, MaterialDensityPair88& voxel)
|
||||
{
|
||||
@ -100,130 +94,137 @@ void writeMaterialValueToVoxel(int valueToWrite, MaterialDensityPair88& voxel)
|
||||
voxel.setMaterial(valueToWrite);
|
||||
}
|
||||
|
||||
// Runs the surface extractor for a given type.
|
||||
template <typename VoxelType>
|
||||
Mesh<MarchingCubesVertex<VoxelType> > testForType(void) //I think we could avoid specifying this return type by using auto/decltype?
|
||||
SimpleVolume<VoxelType>* createAndFillVolume(void)
|
||||
{
|
||||
const int32_t uVolumeSideLength = 32;
|
||||
const int32_t uVolumeSideLength = 64;
|
||||
|
||||
//Create empty volume
|
||||
SimpleVolume<VoxelType> volData(Region(Vector3DInt32(0,0,0), Vector3DInt32(uVolumeSideLength-1, uVolumeSideLength-1, uVolumeSideLength-1)));
|
||||
SimpleVolume<VoxelType>* volData = new SimpleVolume<VoxelType>(Region(Vector3DInt32(0, 0, 0), Vector3DInt32(uVolumeSideLength - 1, uVolumeSideLength - 1, uVolumeSideLength - 1)));
|
||||
|
||||
// Fill
|
||||
for (int32_t z = 0; z < uVolumeSideLength; z++)
|
||||
{
|
||||
for (int32_t y = 0; y < uVolumeSideLength; y++)
|
||||
{
|
||||
for (int32_t x = 0; x < uVolumeSideLength; x++)
|
||||
{
|
||||
// Create a density field which changes throughout the volume. It's
|
||||
// zero in the lower corner and increasing as the coordinates increase.
|
||||
VoxelType voxelValue;
|
||||
//Create a density field which changes throughout the volume.
|
||||
writeDensityValueToVoxel<VoxelType>(x + y + z, voxelValue);
|
||||
//Two different materials in two halves of the volume
|
||||
writeMaterialValueToVoxel<VoxelType>(z > uVolumeSideLength / 2 ? 42 : 79, voxelValue);
|
||||
volData.setVoxelAt(x, y, z, voxelValue);
|
||||
volData->setVoxelAt(x, y, z, voxelValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DefaultMarchingCubesController<VoxelType> controller;
|
||||
controller.setThreshold(50);
|
||||
|
||||
auto result = extractMarchingCubesMesh(&volData, volData.getEnclosingRegion(), WrapModes::Border, VoxelType(), controller);
|
||||
|
||||
return result;
|
||||
return volData;
|
||||
}
|
||||
|
||||
void testCustomController(Mesh<MarchingCubesVertex<float> >& result)
|
||||
// From http://stackoverflow.com/a/5289624
|
||||
float randomFloat(float a, float b)
|
||||
{
|
||||
const int32_t uVolumeSideLength = 32;
|
||||
float random = ((float)rand()) / (float)RAND_MAX;
|
||||
float diff = b - a;
|
||||
float r = random * diff;
|
||||
return a + r;
|
||||
}
|
||||
|
||||
template <typename VolumeType>
|
||||
VolumeType* createAndFillVolumeWithNoise(int32_t iVolumeSideLength, float minValue, float maxValue)
|
||||
{
|
||||
//Create empty volume
|
||||
SimpleVolume<float> volData(Region(Vector3DInt32(0,0,0), Vector3DInt32(uVolumeSideLength-1, uVolumeSideLength-1, uVolumeSideLength-1)));
|
||||
VolumeType* volData = new VolumeType(Region(Vector3DInt32(0, 0, 0), Vector3DInt32(iVolumeSideLength - 1, iVolumeSideLength - 1, iVolumeSideLength - 1)));
|
||||
|
||||
for (int32_t z = 0; z < uVolumeSideLength; z++)
|
||||
// Seed generator for consistency between runs.
|
||||
srand(12345);
|
||||
|
||||
// Fill
|
||||
for (int32_t z = 0; z < iVolumeSideLength; z++)
|
||||
{
|
||||
for (int32_t y = 0; y < uVolumeSideLength; y++)
|
||||
for (int32_t y = 0; y < iVolumeSideLength; y++)
|
||||
{
|
||||
for (int32_t x = 0; x < uVolumeSideLength; x++)
|
||||
for (int32_t x = 0; x < iVolumeSideLength; x++)
|
||||
{
|
||||
float voxelValue = x + y + z;
|
||||
volData.setVoxelAt(x, y, z, voxelValue);
|
||||
float voxelValue = randomFloat(minValue, maxValue);
|
||||
volData->setVoxelAt(x, y, z, voxelValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomMarchingCubesController controller;
|
||||
MarchingCubesSurfaceExtractor< SimpleVolume<float>, CustomMarchingCubesController > extractor(&volData, volData.getEnclosingRegion(), &result, WrapModes::Border, 0, controller);
|
||||
extractor.execute();
|
||||
return volData;
|
||||
}
|
||||
|
||||
void TestSurfaceExtractor::testExecute()
|
||||
void TestSurfaceExtractor::testBehaviour()
|
||||
{
|
||||
const static uint32_t uExpectedVertices = 4731;
|
||||
const static uint32_t uExpectedIndices = 12810;
|
||||
const static uint32_t uMaterialToCheck = 3000;
|
||||
const static float fExpectedData = 1.0f;
|
||||
const static float fNoMaterial = 1.0f;
|
||||
// These tests apply the Marching Cubes surface extractor to volumes of various voxel types. In addition we sometimes make use of custom controllers
|
||||
// and user-provided meshes to make sure these various combinations work as expected.
|
||||
//
|
||||
// It is also noted that the number of indices and vertices is varying quite significantly based on the voxel type. This seems unexpected, but could
|
||||
// be explained if some overflow is occuring when writing data into the volume, causing volumes of different voxel types to have different distributions.
|
||||
// Of course, the use of a custom controller will also make a significant diference, but this probably does need investigating further in the future.
|
||||
|
||||
Mesh<MarchingCubesVertex<int8_t> > mesh;
|
||||
//Run the test for various voxel types.
|
||||
QBENCHMARK {
|
||||
mesh = testForType<int8_t>();
|
||||
}
|
||||
QCOMPARE(mesh.getNoOfVertices(), uExpectedVertices);
|
||||
QCOMPARE(mesh.getNoOfIndices(), uExpectedIndices);
|
||||
QCOMPARE(mesh.getVertices()[uMaterialToCheck].data, static_cast<int8_t>(fExpectedData));
|
||||
// This basic test just uses the default controller and automatically generates a mesh of the appropriate type.
|
||||
auto uintVol = createAndFillVolume<uint8_t>();
|
||||
auto uintMesh = extractMarchingCubesMesh(uintVol, uintVol->getEnclosingRegion());
|
||||
QCOMPARE(uintMesh.getNoOfVertices(), uint32_t(12096)); // Verifies size of mesh and that we have 32-bit indices
|
||||
QCOMPARE(uintMesh.getNoOfIndices(), uint32_t(35157)); // Verifies size of mesh
|
||||
QCOMPARE(uintMesh.getIndex(100), uint32_t(44)); // Verifies that we have 32-bit indices
|
||||
QCOMPARE(uintMesh.getVertex(100).data, uint8_t(1)); // Not really meaningful for a primative type
|
||||
|
||||
auto mesh1 = testForType<uint8_t>();
|
||||
QCOMPARE(mesh1.getNoOfVertices(), uExpectedVertices);
|
||||
QCOMPARE(mesh1.getNoOfIndices(), uExpectedIndices);
|
||||
QCOMPARE(mesh1.getVertices()[uMaterialToCheck].data, static_cast<uint8_t>(fExpectedData));
|
||||
// This test makes use of a custom controller
|
||||
auto floatVol = createAndFillVolume<float>();
|
||||
CustomMarchingCubesController floatCustomController;
|
||||
auto floatMesh = extractMarchingCubesMesh(floatVol, floatVol->getEnclosingRegion(), floatCustomController);
|
||||
QCOMPARE(floatMesh.getNoOfVertices(), uint32_t(16113)); // Verifies size of mesh and that we have 32-bit indices
|
||||
QCOMPARE(floatMesh.getNoOfIndices(), uint32_t(22053)); // Verifies size of mesh
|
||||
QCOMPARE(floatMesh.getIndex(100), uint32_t(26)); // Verifies that we have 32-bit indices
|
||||
QCOMPARE(floatMesh.getVertex(100).data, float(1.0f)); // Not really meaningful for a primative type
|
||||
|
||||
auto mesh2 = testForType<int16_t>();
|
||||
QCOMPARE(mesh2.getNoOfVertices(), uExpectedVertices);
|
||||
QCOMPARE(mesh2.getNoOfIndices(), uExpectedIndices);
|
||||
QCOMPARE(mesh2.getVertices()[uMaterialToCheck].data, static_cast<int16_t>(fExpectedData));
|
||||
// This test makes use of a user provided mesh. It uses the default controller, but we have to explicitly provide this because C++ won't let us
|
||||
// use a default for the second-to-last parameter but noot use a default for the last parameter.
|
||||
auto intVol = createAndFillVolume<int8_t>();
|
||||
Mesh< MarchingCubesVertex< int8_t >, uint16_t > intMesh;
|
||||
extractMarchingCubesMeshCustom(intVol, intVol->getEnclosingRegion(), &intMesh);
|
||||
QCOMPARE(intMesh.getNoOfVertices(), uint16_t(11718)); // Verifies size of mesh and that we have 16-bit indices
|
||||
QCOMPARE(intMesh.getNoOfIndices(), uint32_t(34041)); // Verifies size of mesh
|
||||
QCOMPARE(intMesh.getIndex(100), uint16_t(29)); // Verifies that we have 16-bit indices
|
||||
QCOMPARE(intMesh.getVertex(100).data, int8_t(1)); // Not really meaningful for a primative type
|
||||
|
||||
auto mesh3 = testForType<uint16_t>();
|
||||
QCOMPARE(mesh3.getNoOfVertices(), uExpectedVertices);
|
||||
QCOMPARE(mesh3.getNoOfIndices(), uExpectedIndices);
|
||||
QCOMPARE(mesh3.getVertices()[uMaterialToCheck].data, static_cast<uint16_t>(fExpectedData));
|
||||
// This test makes use of a user-provided mesh and also a custom controller.
|
||||
auto doubleVol = createAndFillVolume<double>();
|
||||
CustomMarchingCubesController doubleCustomController;
|
||||
Mesh< MarchingCubesVertex< double >, uint16_t > doubleMesh;
|
||||
extractMarchingCubesMeshCustom(doubleVol, doubleVol->getEnclosingRegion(), &doubleMesh, doubleCustomController);
|
||||
QCOMPARE(doubleMesh.getNoOfVertices(), uint16_t(16113)); // Verifies size of mesh and that we have 32-bit indices
|
||||
QCOMPARE(doubleMesh.getNoOfIndices(), uint32_t(22053)); // Verifies size of mesh
|
||||
QCOMPARE(doubleMesh.getIndex(100), uint16_t(26)); // Verifies that we have 32-bit indices
|
||||
QCOMPARE(doubleMesh.getVertex(100).data, double(1.0f)); // Not really meaningful for a primative type
|
||||
|
||||
auto mesh4 = testForType<int32_t>();
|
||||
QCOMPARE(mesh4.getNoOfVertices(), uExpectedVertices);
|
||||
QCOMPARE(mesh4.getNoOfIndices(), uExpectedIndices);
|
||||
QCOMPARE(mesh4.getVertices()[uMaterialToCheck].data, static_cast<int32_t>(fExpectedData));
|
||||
// This test ensures the extractor works on a non-primitive voxel type.
|
||||
auto materialVol = createAndFillVolume<MaterialDensityPair88>();
|
||||
auto materialMesh = extractMarchingCubesMesh(materialVol, materialVol->getEnclosingRegion());
|
||||
QCOMPARE(materialMesh.getNoOfVertices(), uint32_t(12096)); // Verifies size of mesh and that we have 32-bit indices
|
||||
QCOMPARE(materialMesh.getNoOfIndices(), uint32_t(35157)); // Verifies size of mesh
|
||||
QCOMPARE(materialMesh.getIndex(100), uint32_t(44)); // Verifies that we have 32-bit indices
|
||||
QCOMPARE(materialMesh.getVertex(100).data.getMaterial(), uint16_t(79)); // Verify the data attached to the vertex
|
||||
}
|
||||
|
||||
auto mesh5 = testForType<uint32_t>();
|
||||
QCOMPARE(mesh5.getNoOfVertices(), uExpectedVertices);
|
||||
QCOMPARE(mesh5.getNoOfIndices(), uExpectedIndices);
|
||||
QCOMPARE(mesh5.getVertices()[uMaterialToCheck].data, static_cast<uint32_t>(fExpectedData));
|
||||
void TestSurfaceExtractor::testEmptyVolumePerformance()
|
||||
{
|
||||
auto emptyVol = createAndFillVolumeWithNoise< SimpleVolume<float> >(128, -2.0f, -1.0f);
|
||||
Mesh< MarchingCubesVertex< float >, uint16_t > emptyMesh;
|
||||
QBENCHMARK{ extractMarchingCubesMeshCustom(emptyVol, Region(32, 32, 32, 63, 63, 63), &emptyMesh); }
|
||||
QCOMPARE(emptyMesh.getNoOfVertices(), uint16_t(0));
|
||||
}
|
||||
|
||||
auto mesh6 = testForType<float>();
|
||||
QCOMPARE(mesh6.getNoOfVertices(), uExpectedVertices);
|
||||
QCOMPARE(mesh6.getNoOfIndices(), uExpectedIndices);
|
||||
QCOMPARE(mesh6.getVertices()[uMaterialToCheck].data, static_cast<float>(fExpectedData));
|
||||
|
||||
auto mesh7 = testForType<double>();
|
||||
QCOMPARE(mesh7.getNoOfVertices(), uExpectedVertices);
|
||||
QCOMPARE(mesh7.getNoOfIndices(), uExpectedIndices);
|
||||
QCOMPARE(mesh7.getVertices()[uMaterialToCheck].data, static_cast<double>(fExpectedData));
|
||||
|
||||
auto mesh8 = testForType<Density8>();
|
||||
QCOMPARE(mesh8.getNoOfVertices(), uExpectedVertices);
|
||||
QCOMPARE(mesh8.getNoOfIndices(), uExpectedIndices);
|
||||
QCOMPARE(mesh8.getVertices()[uMaterialToCheck].data, static_cast<Density8>(fExpectedData));
|
||||
|
||||
auto mesh9 = testForType<MaterialDensityPair88>();
|
||||
QCOMPARE(mesh9.getNoOfVertices(), uExpectedVertices);
|
||||
QCOMPARE(mesh9.getNoOfIndices(), uExpectedIndices);
|
||||
//QCOMPARE(mesh9.getVertices()[uMaterialToCheck].data, fExpectedMaterial);
|
||||
|
||||
//Test whether the CustomSurfaceExtractor works.
|
||||
/*testCustomController(floatMesh);
|
||||
QCOMPARE(floatMesh.getNoOfVertices(), uExpectedVertices);
|
||||
QCOMPARE(floatMesh.getNoOfIndices(), uExpectedIndices);
|
||||
QCOMPARE(floatMesh.getVertices()[uMaterialToCheck].data, fExpectedData);*/
|
||||
void TestSurfaceExtractor::testNoiseVolumePerformance()
|
||||
{
|
||||
auto noiseVol = createAndFillVolumeWithNoise< SimpleVolume<float> >(128, -1.0f, 1.0f);
|
||||
Mesh< MarchingCubesVertex< float >, uint16_t > noiseMesh;
|
||||
QBENCHMARK{ extractMarchingCubesMeshCustom(noiseVol, Region(32, 32, 32, 63, 63, 63), &noiseMesh); }
|
||||
QCOMPARE(noiseMesh.getNoOfVertices(), uint16_t(48967));
|
||||
}
|
||||
|
||||
QTEST_MAIN(TestSurfaceExtractor)
|
||||
|
@ -31,7 +31,9 @@ class TestSurfaceExtractor: public QObject
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void testExecute();
|
||||
void testBehaviour();
|
||||
void testEmptyVolumePerformance();
|
||||
void testNoiseVolumePerformance();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user