Merge branch 'develop' into feature/wrap-modes

Conflicts:
	library/PolyVoxCore/include/PolyVoxCore/RawVolumeSampler.inl
This commit is contained in:
David Williams 2012-12-03 21:30:51 +01:00
commit 9a05f04f85
19 changed files with 28 additions and 1640 deletions

View File

@ -7,6 +7,10 @@ Removed functionality
Functionality deprecated for the previous release has now been removed. This includes: Functionality deprecated for the previous release has now been removed. This includes:
- Region::getWidth() and related functions. You should now use Region::getWidthInVoxels() or Region::getWidthInCells. - Region::getWidth() and related functions. You should now use Region::getWidthInVoxels() or Region::getWidthInCells.
- The MeshDecimator. We don't have a direct replacement for this so you should consider an alternative such as downsampling the volume or using an external mesh processing library.
- The SimpleInterface. This was primarily for the bindings, and we are making other efforts to get those working.
- Serialisation. You should implement ay required serialisation yourself.
- This had a number of problems and was a little too high-level for PolyVox. You should implement change tracking yourself.
The Region class has been tidied up and enhanced with new functionality. It now contains functions for growing and shrinking regions, as well as 'accumulate()' functions which ensure the Region contains a given point. The concept of an invalid region has also been introduced - this is one whose lower corner is greater than it's upper corner. The Region class has been tidied up and enhanced with new functionality. It now contains functions for growing and shrinking regions, as well as 'accumulate()' functions which ensure the Region contains a given point. The concept of an invalid region has also been introduced - this is one whose lower corner is greater than it's upper corner.

View File

@ -29,9 +29,7 @@ SET(CORE_SRC_FILES
source/ArraySizes.cpp source/ArraySizes.cpp
source/AStarPathfinder.cpp source/AStarPathfinder.cpp
source/Log.cpp source/Log.cpp
source/MeshDecimator.cpp
source/Region.cpp source/Region.cpp
source/SimpleInterface.cpp
source/VertexTypes.cpp source/VertexTypes.cpp
) )
@ -70,8 +68,6 @@ SET(CORE_INC_FILES
include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl
include/PolyVoxCore/Material.h include/PolyVoxCore/Material.h
include/PolyVoxCore/MaterialDensityPair.h include/PolyVoxCore/MaterialDensityPair.h
include/PolyVoxCore/MeshDecimator.h
include/PolyVoxCore/MeshDecimator.inl
include/PolyVoxCore/PolyVoxForwardDeclarations.h include/PolyVoxCore/PolyVoxForwardDeclarations.h
include/PolyVoxCore/RawVolume.h include/PolyVoxCore/RawVolume.h
include/PolyVoxCore/RawVolume.inl include/PolyVoxCore/RawVolume.inl
@ -79,7 +75,6 @@ SET(CORE_INC_FILES
include/PolyVoxCore/Raycast.h include/PolyVoxCore/Raycast.h
include/PolyVoxCore/Raycast.inl include/PolyVoxCore/Raycast.inl
include/PolyVoxCore/Region.h include/PolyVoxCore/Region.h
include/PolyVoxCore/SimpleInterface.h
include/PolyVoxCore/SimpleVolume.h include/PolyVoxCore/SimpleVolume.h
include/PolyVoxCore/SimpleVolume.inl include/PolyVoxCore/SimpleVolume.inl
include/PolyVoxCore/SimpleVolumeBlock.inl include/PolyVoxCore/SimpleVolumeBlock.inl

View File

@ -24,7 +24,7 @@ freely, subject to the following restrictions:
#ifndef __PolyVox_DefaultIsQuadNeeded_H__ #ifndef __PolyVox_DefaultIsQuadNeeded_H__
#define __PolyVox_DefaultIsQuadNeeded_H__ #define __PolyVox_DefaultIsQuadNeeded_H__
#include <cstdint> #include "PolyVoxCore/Impl/TypeDef.h"
namespace PolyVox namespace PolyVox
{ {

View File

@ -622,8 +622,8 @@ namespace PolyVox
template <typename VoxelType> template <typename VoxelType>
float LargeVolume<VoxelType>::calculateCompressionRatio(void) float LargeVolume<VoxelType>::calculateCompressionRatio(void)
{ {
float fRawSize = m_pBlocks.size() * m_uBlockSideLength * m_uBlockSideLength* m_uBlockSideLength * sizeof(VoxelType); float fRawSize = static_cast<float>(m_pBlocks.size() * m_uBlockSideLength * m_uBlockSideLength* m_uBlockSideLength * sizeof(VoxelType));
float fCompressedSize = calculateSizeInBytes(); float fCompressedSize = static_cast<float>(calculateSizeInBytes());
return fCompressedSize/fRawSize; return fCompressedSize/fRawSize;
} }

View File

@ -1,187 +0,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.
*******************************************************************************/
#ifndef __PolyVox_MeshDecimator_H__
#define __PolyVox_MeshDecimator_H__
#include "PolyVoxCore/SurfaceMesh.h"
#include "PolyVoxCore/Vector.h"
#include "PolyVoxCore/VertexTypes.h"
#include <bitset>
#include <vector>
namespace PolyVox
{
/// The MeshDecimator reduces the number of triangles in a mesh.
////////////////////////////////////////////////////////////////////////////////
/// Meshes generated by the PolyVox surface extractors typically have a very high
/// number of triangles in them. This can pose difficulties both for the rendering
/// storage of such meshes. The MeshDecimator provides a way of reducing the triangle
/// count with minimal visual effect.
///
/// The MeshDecimator is based on the principle of edge collapse, and currently works
/// with meshes generated by the MarchingCubesSurfaceExtractor or CubicSurfaceExtractor. It does
/// not work with meshes generated by the CubicSurfaceExtractorWithNormals, although
/// this may be addressed in the future. The algorithm iterates over each pair of
/// connected vertices in the mesh and attemps to determine if they can be collapsed
/// into a single vertex.
///
/// The main criteria used in deciding whether two vertices can collapse is whether
/// they have the same normal. In the case of the cubic surfaces the normals must be
/// exactly the same, whereas in the case of the Marching Cubes surfaces a threshold
/// is used to determine whether two normals are 'close enough'. Additional constraints
/// apply to vertices which lie on the edges of regions or on the boundary between two
/// regions - these vertices are much less likely to be collapsed.
///
/// Given a mesh called 'mesh', you can create a decimated version as follows:
/// \code
/// SurfaceMesh<PositionMaterial> decimatedMesh;
/// MeshDecimator<PositionMaterial> decimator(&mesh, &decimatedMesh);
/// decimator.execute();
/// \endcode
///
/// The above applies for a cubic mesh, for a Marching Cubes mesh you need to parametise
/// the MeshDecimator and resulting SurfaceMesh on the 'PositionMaterialNormal' type
/// instead of the 'PositionMaterial' type.
///
/// \deprecated
template <typename VertexType>
class MeshDecimator
{
//Used to keep track of when a vertex is
//on one or more faces of the region
enum RegionFaceFlags
{
RFF_ON_REGION_FACE_NEG_X,
RFF_ON_REGION_FACE_POS_X ,
RFF_ON_REGION_FACE_NEG_Y ,
RFF_ON_REGION_FACE_POS_Y ,
RFF_ON_REGION_FACE_NEG_Z ,
RFF_ON_REGION_FACE_POS_Z,
RFF_NO_OF_REGION_FACE_FLAGS
};
//Data about the initial mesh - this
//will be fill in once at the start
struct InitialVertexMetadata
{
Vector3DFloat normal;
bool isOnMaterialEdge;
std::bitset<RFF_NO_OF_REGION_FACE_FLAGS> isOnRegionFace;
};
//Representing a triangle for decimation purposes.
struct Triangle
{
uint32_t v0;
uint32_t v1;
uint32_t v2;
Vector3DFloat normal;
};
struct IntVertex
{
int32_t x;
int32_t y;
int32_t z;
uint32_t index;
IntVertex(int32_t xVal, int32_t yVal, int32_t zVal, uint32_t indexVal)
:x(xVal)
,y(yVal)
,z(zVal)
,index(indexVal)
{
}
bool operator==(const IntVertex& rhs) const
{
return (x == rhs.x) && (y == rhs.y) && (z == rhs.z);
}
bool operator<(const IntVertex& rhs) const
{
if (z < rhs.z)
return true;
if (rhs.z < z)
return false;
if (y < rhs.y)
return true;
if (rhs.y < y)
return false;
if (x < rhs.x)
return true;
if (rhs.x < x)
return false;
return false;
}
};
public:
///Constructor
POLYVOX_DEPRECATED MeshDecimator(const SurfaceMesh<VertexType>* pInputMesh, SurfaceMesh<VertexType>* pOutputMesh, float fEdgeCollapseThreshold = 0.95f);
///Performs the decimation.
POLYVOX_DEPRECATED void execute();
private:
void fillInitialVertexMetadata(std::vector<InitialVertexMetadata>& vecInitialVertexMetadata);
void buildConnectivityData(void);
bool attemptEdgeCollapse(uint32_t uSrc, uint32_t uDst);
const SurfaceMesh<VertexType>* m_pInputMesh;
SurfaceMesh<VertexType>* m_pOutputMesh;
uint32_t performDecimationPass(float m_fMinDotProductForCollapse);
bool isSubset(std::bitset<RFF_NO_OF_REGION_FACE_FLAGS> a, std::bitset<RFF_NO_OF_REGION_FACE_FLAGS> b);
bool canCollapseEdge(uint32_t uSrc, uint32_t uDst);
bool canCollapseNormalEdge(uint32_t uSrc, uint32_t uDst);
bool canCollapseRegionEdge(uint32_t uSrc, uint32_t uDst);
bool canCollapseMaterialEdge(uint32_t uSrc, uint32_t uDst);
bool collapseChangesFaceNormals(uint32_t uSrc, uint32_t uDst, float fThreshold);
//Data structures used during decimation
std::vector<bool> vertexLocked;
std::vector<uint32_t> vertexMapper;
std::vector<Triangle> m_vecTriangles;
std::vector< std::vector<uint32_t> > trianglesUsingVertex; //Should probably use vector of vectors, and resise in advance.
std::vector<InitialVertexMetadata> m_vecInitialVertexMetadata;
float m_fMinDotProductForCollapse;
};
}
#include "PolyVoxCore/MeshDecimator.inl"
#endif //__PolyVox_MeshDecimator_H__

View File

@ -1,347 +0,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.
*******************************************************************************/
namespace PolyVox
{
////////////////////////////////////////////////////////////////////////////////
/// Builds a MeshDecimator.
/// \param pInputMesh A pointer to the mesh to be decimated.
/// \param[out] pOutputMesh A pointer to where the result should be stored. Any existing
/// contents will be deleted.
/// \param fEdgeCollapseThreshold This is only use in the case of a Marching Cubes
/// surface and controls how close two normals must be to collapse. The dot product
/// between the normals is computed and compared to this threshold. A threshold of
/// 1.0 means nothing will collapse, a threshold of 0.0 means everything will collapse.
////////////////////////////////////////////////////////////////////////////////
template <typename VertexType>
MeshDecimator<VertexType>::MeshDecimator(const SurfaceMesh<VertexType>* pInputMesh, SurfaceMesh<VertexType>* pOutputMesh, float fEdgeCollapseThreshold)
:m_pInputMesh(pInputMesh)
,m_pOutputMesh(pOutputMesh)
,m_fMinDotProductForCollapse(fEdgeCollapseThreshold)
{
*m_pOutputMesh = *m_pInputMesh;
}
template <typename VertexType>
void MeshDecimator<VertexType>::execute()
{
//Sanity check.
if((m_pOutputMesh->m_vecVertices.empty()) || (m_pOutputMesh->m_vecTriangleIndices.empty()))
{
return;
}
buildConnectivityData();
fillInitialVertexMetadata(m_vecInitialVertexMetadata);
uint32_t noOfEdgesCollapsed;
do
{
noOfEdgesCollapsed = performDecimationPass(m_fMinDotProductForCollapse);
m_pOutputMesh->removeDegenerateTris();
if(noOfEdgesCollapsed > 0)
{
//Build the connectivity data for the next pass. If this is slow, then look
//at adjusting it (based on vertex mapper?) rather than bulding from scratch.
buildConnectivityData();
}
}while(noOfEdgesCollapsed > 0);
m_pOutputMesh->removeUnusedVertices();
//Decimation will have invalidated LOD levels.
m_pOutputMesh->m_vecLodRecords.clear();
LodRecord lodRecord;
lodRecord.beginIndex = 0;
lodRecord.endIndex = m_pOutputMesh->getNoOfIndices();
m_pOutputMesh->m_vecLodRecords.push_back(lodRecord);
}
template <typename VertexType>
void MeshDecimator<VertexType>::buildConnectivityData(void)
{
//Build a list of all the triangles, complete with face normals.
m_vecTriangles.clear();
m_vecTriangles.resize(m_pOutputMesh->m_vecTriangleIndices.size() / 3);
for(uint32_t triCt = 0; triCt < m_vecTriangles.size(); triCt++)
{
m_vecTriangles[triCt].v0 = m_pOutputMesh->m_vecTriangleIndices[triCt * 3 + 0];
m_vecTriangles[triCt].v1 = m_pOutputMesh->m_vecTriangleIndices[triCt * 3 + 1];
m_vecTriangles[triCt].v2 = m_pOutputMesh->m_vecTriangleIndices[triCt * 3 + 2];
Vector3DFloat v0Pos = m_pOutputMesh->m_vecVertices[m_vecTriangles[triCt].v0].position;
Vector3DFloat v1Pos = m_pOutputMesh->m_vecVertices[m_vecTriangles[triCt].v1].position;
Vector3DFloat v2Pos = m_pOutputMesh->m_vecVertices[m_vecTriangles[triCt].v2].position;
Vector3DFloat v0v1 = v1Pos - v0Pos;
Vector3DFloat v0v2 = v2Pos - v0Pos;
Vector3DFloat normal = v0v1.cross(v0v2);
normal.normalise();
m_vecTriangles[triCt].normal = normal;
}
//For each vertex, determine which triangles are using it.
trianglesUsingVertex.clear();
trianglesUsingVertex.resize(m_pOutputMesh->m_vecVertices.size());
for(uint32_t ct = 0; ct < trianglesUsingVertex.size(); ct++)
{
trianglesUsingVertex[ct].reserve(6);
}
for(uint32_t ct = 0; ct < m_vecTriangles.size(); ct++)
{
trianglesUsingVertex[m_vecTriangles[ct].v0].push_back(ct);
trianglesUsingVertex[m_vecTriangles[ct].v1].push_back(ct);
trianglesUsingVertex[m_vecTriangles[ct].v2].push_back(ct);
}
}
template <typename VertexType>
uint32_t MeshDecimator<VertexType>::performDecimationPass(float /*m_fMinDotProductForCollapse*/)
{
// Count how many edges we have collapsed
uint32_t noOfEdgesCollapsed = 0;
// The vertex mapper track whick vertices collapse onto which.
vertexMapper.clear();
vertexMapper.resize(m_pOutputMesh->m_vecVertices.size());
// Once a vertex is involved in a collapse (either because it
// moves onto a different vertex, or because a different vertex
// moves onto it) it is forbidden to take part in another collapse
// this pass. We enforce this by setting the vertex locked flag.
vertexLocked.clear();
vertexLocked.resize(m_pOutputMesh->m_vecVertices.size());
// Initialise the vectors
for(uint32_t ct = 0; ct < m_pOutputMesh->m_vecVertices.size(); ct++)
{
// Initiall all vertices points to themselves
vertexMapper[ct] = ct;
// All vertices are initially unlocked
vertexLocked[ct] = false;
}
//For each triangle...
for(uint32_t ctIter = 0; ctIter < m_vecTriangles.size(); ctIter++)
{
if(attemptEdgeCollapse(m_vecTriangles[ctIter].v0, m_vecTriangles[ctIter].v1))
{
++noOfEdgesCollapsed;
}
if(attemptEdgeCollapse(m_vecTriangles[ctIter].v1, m_vecTriangles[ctIter].v2))
{
++noOfEdgesCollapsed;
}
if(attemptEdgeCollapse(m_vecTriangles[ctIter].v2, m_vecTriangles[ctIter].v0))
{
++noOfEdgesCollapsed;
}
}
if(noOfEdgesCollapsed > 0)
{
//Fix up the indices
for(uint32_t triCt = 0; triCt < m_pOutputMesh->m_vecTriangleIndices.size(); triCt++)
{
uint32_t before = m_pOutputMesh->m_vecTriangleIndices[triCt];
uint32_t after = vertexMapper[m_pOutputMesh->m_vecTriangleIndices[triCt]];
if(before != after)
{
m_pOutputMesh->m_vecTriangleIndices[triCt] = vertexMapper[m_pOutputMesh->m_vecTriangleIndices[triCt]];
}
}
}
return noOfEdgesCollapsed;
}
template <typename VertexType>
bool MeshDecimator<VertexType>::attemptEdgeCollapse(uint32_t uSrc, uint32_t uDst)
{
//A vertex will be locked if it has already been involved in a collapse this pass.
if(vertexLocked[uSrc] || vertexLocked[uDst])
{
return false;
}
if(canCollapseEdge(uSrc, uDst))
{
//Move v0 onto v1
vertexMapper[uSrc] = uDst; //vertexMapper[v1];
vertexLocked[uSrc] = true;
vertexLocked[uDst] = true;
//Increment the counter
return true;
}
return false;
}
template <typename VertexType>
bool MeshDecimator<VertexType>::canCollapseEdge(uint32_t uSrc, uint32_t uDst)
{
bool bCanCollapse = true;
if(m_vecInitialVertexMetadata[uSrc].isOnMaterialEdge)
{
bCanCollapse &= canCollapseMaterialEdge(uSrc, uDst);
}
if(m_vecInitialVertexMetadata[uSrc].isOnRegionFace.any())
{
bCanCollapse &= canCollapseRegionEdge(uSrc, uDst);
}
if(bCanCollapse) //Only bother with this if the earlier tests passed.
{
bCanCollapse &= canCollapseNormalEdge(uSrc, uDst);
}
return bCanCollapse;
}
template <typename VertexType>
bool MeshDecimator<VertexType>::canCollapseRegionEdge(uint32_t uSrc, uint32_t uDst)
{
// We can collapse normal vertices onto edge vertices, and edge vertices
// onto corner vertices, but not vice-versa. Hence we check whether all
// the edge flags in the source vertex are also set in the destination vertex.
if(isSubset(m_vecInitialVertexMetadata[uSrc].isOnRegionFace, m_vecInitialVertexMetadata[uDst].isOnRegionFace) == false)
{
return false;
}
// In general adjacent regions surface meshes may collapse differently
// and this can cause cracks. We solve this by only allowing the collapse
// is the normals are exactly the same. We do not use the user provided
// tolerence here (but do allow for floating point error).
if(m_vecInitialVertexMetadata[uSrc].normal.dot(m_vecInitialVertexMetadata[uDst].normal) < 0.999f)
{
return false;
}
return true;
}
template <typename VertexType>
bool MeshDecimator<VertexType>::canCollapseMaterialEdge(uint32_t /*uSrc*/, uint32_t /*uDst*/)
{
return false;
}
//This function should really use some work. For a start we already have the
//faces normals for the input mesh yet we are computing them on the fly here.
template <typename VertexType>
bool MeshDecimator<VertexType>::collapseChangesFaceNormals(uint32_t uSrc, uint32_t uDst, float fThreshold)
{
bool faceFlipped = false;
std::vector<uint32_t>& triangles = trianglesUsingVertex[uSrc];
for(std::vector<uint32_t>::iterator triIter = triangles.begin(); triIter != triangles.end(); triIter++)
{
uint32_t tri = *triIter;
const uint32_t& v0Old = m_pOutputMesh->m_vecTriangleIndices[tri * 3];
const uint32_t& v1Old = m_pOutputMesh->m_vecTriangleIndices[tri * 3 + 1];
const uint32_t& v2Old = m_pOutputMesh->m_vecTriangleIndices[tri * 3 + 2];
//Check if degenerate
if((v0Old == v1Old) || (v1Old == v2Old) || (v2Old == v0Old))
{
continue;
}
uint32_t v0New = v0Old;
uint32_t v1New = v1Old;
uint32_t v2New = v2Old;
if(v0New == uSrc)
v0New = uDst;
if(v1New == uSrc)
v1New = uDst;
if(v2New == uSrc)
v2New = uDst;
//Check if degenerate
if((v0New == v1New) || (v1New == v2New) || (v2New == v0New))
{
continue;
}
const Vector3DFloat& v0OldPos = m_pOutputMesh->m_vecVertices[vertexMapper[v0Old]].getPosition(); //Note: we need the vertex mapper here. These neighbouring vertices may have been moved.
const Vector3DFloat& v1OldPos = m_pOutputMesh->m_vecVertices[vertexMapper[v1Old]].getPosition();
const Vector3DFloat& v2OldPos = m_pOutputMesh->m_vecVertices[vertexMapper[v2Old]].getPosition();
const Vector3DFloat& v0NewPos = m_pOutputMesh->m_vecVertices[vertexMapper[v0New]].getPosition();
const Vector3DFloat& v1NewPos = m_pOutputMesh->m_vecVertices[vertexMapper[v1New]].getPosition();
const Vector3DFloat& v2NewPos = m_pOutputMesh->m_vecVertices[vertexMapper[v2New]].getPosition();
Vector3DFloat OldNormal = (v1OldPos - v0OldPos).cross(v2OldPos - v1OldPos);
Vector3DFloat NewNormal = (v1NewPos - v0NewPos).cross(v2NewPos - v1NewPos);
OldNormal.normalise();
NewNormal.normalise();
float dotProduct = OldNormal.dot(NewNormal);
//NOTE: I don't think we should be using the threshold here, we're just checking for a complete face flip
if(dotProduct < fThreshold)
{
//cout << " Face flipped!!" << endl;
faceFlipped = true;
/*vertexLocked[v0] = true;
vertexLocked[v1] = true;*/
break;
}
}
return faceFlipped;
}
// Returns true if every bit which is set in 'a' is also set in 'b'. The reverse does not need to be true.
template <typename VertexType>
bool MeshDecimator<VertexType>::isSubset(std::bitset<RFF_NO_OF_REGION_FACE_FLAGS> a, std::bitset<RFF_NO_OF_REGION_FACE_FLAGS> b)
{
bool result = true;
for(int ct = 0; ct < RFF_NO_OF_REGION_FACE_FLAGS; ct++)
{
if(a.test(ct))
{
if(b.test(ct) == false)
{
result = false;
break;
}
}
}
return result;
}
}

View File

@ -249,8 +249,8 @@ namespace PolyVox
template <typename VoxelType> template <typename VoxelType>
VoxelType RawVolume<VoxelType>::Sampler::peekVoxel0px1ny1nz(void) const VoxelType RawVolume<VoxelType>::Sampler::peekVoxel0px1ny1nz(void) const
{ {
if((this->isCurrentPositionValid()) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) if((this->isCurrentPositionValid()) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) )
{ {
return *(mCurrentVoxel - this->mVolume->getWidth() - this->mVolume->getWidth() * this->mVolume->getHeight()); return *(mCurrentVoxel - this->mVolume->getWidth() - this->mVolume->getWidth() * this->mVolume->getHeight());
} }

View File

@ -1,46 +0,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.
*******************************************************************************/
#ifndef __PolyVox_SimpleInterface_H__
#define __PolyVox_SimpleInterface_H__
#include "PolyVoxCore/CubicSurfaceExtractorWithNormals.h"
#include "PolyVoxCore/MaterialDensityPair.h"
#include "PolyVoxCore/SimpleVolume.h"
#include "PolyVoxCore/MarchingCubesSurfaceExtractor.h"
namespace PolyVox
{
//The PolyVox simple interface only exposes one voxel type and one volume type. But if you like you can
//adjust these typedefs and rebuild the library in order to modify which one volume and voxel is exposed.
typedef SimpleVolume<MaterialDensityPair88> Volume;
typedef SurfaceMesh<PositionMaterialNormal> Mesh;
/// \deprecated
POLYVOX_DEPRECATED void extractCubicMesh(Volume& volume, const Region& region, Mesh& resultMesh);
/// \deprecated
POLYVOX_DEPRECATED void extractSmoothMesh(Volume& volume, const Region& region, Mesh& resultMesh);
}
#endif //__PolyVox_SimpleInterface_H__

View File

@ -131,15 +131,15 @@ namespace PolyVox
void setW(StorageType tW); void setW(StorageType tW);
///Get the length of the vector. ///Get the length of the vector.
double length(void) const; float length(void) const;
///Get the squared length of the vector. ///Get the squared length of the vector.
double lengthSquared(void) const; OperationType lengthSquared(void) const;
///Find the angle between this vector and that which is passed as a parameter. ///Find the angle between this vector and that which is passed as a parameter.
double angleTo(const Vector<Size,StorageType,OperationType>& vector) const; float angleTo(const Vector<Size,StorageType,OperationType>& vector) const;
///Find the cross product between this vector and the vector passed as a parameter. ///Find the cross product between this vector and the vector passed as a parameter.
Vector<Size,StorageType,OperationType> cross(const Vector<Size,StorageType,OperationType>& vector) const; Vector<Size,StorageType,OperationType> cross(const Vector<Size,StorageType,OperationType>& vector) const;
///Find the dot product between this vector and the vector passed as a parameter. ///Find the dot product between this vector and the vector passed as a parameter.
StorageType dot(const Vector<Size,StorageType,OperationType>& rhs) const; OperationType dot(const Vector<Size,StorageType,OperationType>& rhs) const;
///Normalise the vector. ///Normalise the vector.
void normalise(void); void normalise(void);

View File

@ -573,23 +573,23 @@ namespace PolyVox
\return Length of the Vector. \return Length of the Vector.
*/ */
template <uint32_t Size, typename StorageType, typename OperationType> template <uint32_t Size, typename StorageType, typename OperationType>
inline double Vector<Size, StorageType, OperationType>::length(void) const inline float Vector<Size, StorageType, OperationType>::length(void) const
{ {
return sqrt(lengthSquared()); return sqrt(static_cast<float>(lengthSquared()));
} }
/** /**
\return Squared length of the Vector. \return Squared length of the Vector.
*/ */
template <uint32_t Size, typename StorageType, typename OperationType> template <uint32_t Size, typename StorageType, typename OperationType>
inline double Vector<Size, StorageType, OperationType>::lengthSquared(void) const inline OperationType Vector<Size, StorageType, OperationType>::lengthSquared(void) const
{ {
double result = 0.0f; OperationType tLengthSquared = static_cast<OperationType>(0);
for(uint32_t ct = 0; ct < Size; ++ct) for(uint32_t ct = 0; ct < Size; ++ct)
{ {
result += m_tElements[ct] * m_tElements[ct]; tLengthSquared += static_cast<OperationType>(m_tElements[ct]) * static_cast<OperationType>(m_tElements[ct]);
} }
return result; return tLengthSquared;
} }
/** /**
@ -602,9 +602,9 @@ namespace PolyVox
\return The angle between them in radians. \return The angle between them in radians.
*/ */
template <uint32_t Size, typename StorageType, typename OperationType> template <uint32_t Size, typename StorageType, typename OperationType>
inline double Vector<Size, StorageType, OperationType>::angleTo(const Vector<Size, StorageType, OperationType>& vector) const inline float Vector<Size, StorageType, OperationType>::angleTo(const Vector<Size, StorageType, OperationType>& vector) const
{ {
return acos(dot(vector) / (vector.length() * this->length())); return acos(static_cast<float>(dot(vector)) / (vector.length() * this->length()));
} }
/** /**
@ -636,12 +636,12 @@ namespace PolyVox
\see cross() \see cross()
*/ */
template <uint32_t Size, typename StorageType, typename OperationType> template <uint32_t Size, typename StorageType, typename OperationType>
inline StorageType Vector<Size, StorageType, OperationType>::dot(const Vector<Size, StorageType, OperationType>& rhs) const inline OperationType Vector<Size, StorageType, OperationType>::dot(const Vector<Size, StorageType, OperationType>& rhs) const
{ {
StorageType dotProduct = static_cast<StorageType>(0); OperationType dotProduct = static_cast<OperationType>(0);
for(uint32_t ct = 0; ct < Size; ++ct) for(uint32_t ct = 0; ct < Size; ++ct)
{ {
dotProduct += m_tElements[ct] * rhs.m_tElements[ct]; dotProduct += static_cast<OperationType>(m_tElements[ct]) * static_cast<OperationType>(rhs.m_tElements[ct]);
} }
return dotProduct; return dotProduct;
} }

View File

@ -1,181 +0,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/MeshDecimator.h"
#include "PolyVoxCore/SurfaceMesh.h"
using namespace std;
namespace PolyVox
{
template<>
POLYVOX_API void MeshDecimator<PositionMaterial>::fillInitialVertexMetadata(std::vector<InitialVertexMetadata>& vecVertexMetadata)
{
vecVertexMetadata.clear();
vecVertexMetadata.resize(m_pOutputMesh->m_vecVertices.size());
//Initialise the metadata
for(uint32_t ct = 0; ct < vecVertexMetadata.size(); ct++)
{
vecVertexMetadata[ct].normal.setElements(0,0,0);
vecVertexMetadata[ct].isOnMaterialEdge = false;
vecVertexMetadata[ct].isOnRegionFace.reset();
}
//Identify duplicate vertices, as they lie on the material edge. To do this we convert into integers and sort
//(first on z, then y, then x). They should be mostly in order as this is the order they come out of the
//CubicSurfaceExtractor in. Duplicates are now neighbours in the resulting list so just scan through for pairs.
std::vector<IntVertex> intVertices;
intVertices.reserve(m_pOutputMesh->m_vecVertices.size());
for(uint32_t ct = 0; ct < m_pOutputMesh->m_vecVertices.size(); ct++)
{
const Vector3DFloat& floatPos = m_pOutputMesh->m_vecVertices[ct].position;
IntVertex intVertex(static_cast<uint32_t>(floatPos.getX()), static_cast<uint32_t>(floatPos.getY()), static_cast<uint32_t>(floatPos.getZ()), ct);
intVertices.push_back(intVertex);
}
//Do the sorting so that duplicate become neighbours
sort(intVertices.begin(), intVertices.end());
//Find neighbours which are duplicates.
for(uint32_t ct = 0; ct < intVertices.size() - 1; ct++)
{
const IntVertex& v0 = intVertices[ct+0];
const IntVertex& v1 = intVertices[ct+1];
if((v0.x == v1.x) && (v0.y == v1.y) && (v0.z == v1.z))
{
vecVertexMetadata[v0.index].isOnMaterialEdge = true;
vecVertexMetadata[v1.index].isOnMaterialEdge = true;
}
}
//Compute an approcimation to the normal, used when deciding if an edge can collapse.
for(uint32_t ct = 0; ct < m_pOutputMesh->m_vecVertices.size(); ct++)
{
Vector3DFloat sumOfNormals(0.0f,0.0f,0.0f);
for(vector<uint32_t>::iterator iter = trianglesUsingVertex[ct].begin(); iter != trianglesUsingVertex[ct].end(); iter++)
{
sumOfNormals += m_vecTriangles[*iter].normal;
}
vecVertexMetadata[ct].normal = sumOfNormals;
vecVertexMetadata[ct].normal.normalise();
}
//Identify those vertices on the edge of a region. Care will need to be taken when moving them.
for(uint32_t ct = 0; ct < vecVertexMetadata.size(); ct++)
{
Region regTransformed = m_pOutputMesh->m_Region;
regTransformed.shift(regTransformed.getLowerCorner() * static_cast<int32_t>(-1));
//Plus and minus X
vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_NEG_X, m_pOutputMesh->m_vecVertices[ct].getPosition().getX() < regTransformed.getLowerCorner().getX() + 0.001f);
vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_POS_X, m_pOutputMesh->m_vecVertices[ct].getPosition().getX() > regTransformed.getUpperCorner().getX() - 0.001f);
//Plus and minus Y
vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_NEG_Y, m_pOutputMesh->m_vecVertices[ct].getPosition().getY() < regTransformed.getLowerCorner().getY() + 0.001f);
vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_POS_Y, m_pOutputMesh->m_vecVertices[ct].getPosition().getY() > regTransformed.getUpperCorner().getY() - 0.001f);
//Plus and minus Z
vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_NEG_Z, m_pOutputMesh->m_vecVertices[ct].getPosition().getZ() < regTransformed.getLowerCorner().getZ() + 0.001f);
vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_POS_Z, m_pOutputMesh->m_vecVertices[ct].getPosition().getZ() > regTransformed.getUpperCorner().getZ() - 0.001f);
}
}
template<>
POLYVOX_API void MeshDecimator<PositionMaterialNormal>::fillInitialVertexMetadata(std::vector<InitialVertexMetadata>& vecVertexMetadata)
{
vecVertexMetadata.clear();
vecVertexMetadata.resize(m_pOutputMesh->m_vecVertices.size());
//Initialise the metadata
for(uint32_t ct = 0; ct < vecVertexMetadata.size(); ct++)
{
vecVertexMetadata[ct].isOnRegionFace.reset();
vecVertexMetadata[ct].isOnMaterialEdge = false;
vecVertexMetadata[ct].normal = m_pOutputMesh->m_vecVertices[ct].normal;
}
//Identify those vertices on the edge of a region. Care will need to be taken when moving them.
for(uint32_t ct = 0; ct < vecVertexMetadata.size(); ct++)
{
Region regTransformed = m_pOutputMesh->m_Region;
regTransformed.shift(regTransformed.getLowerCorner() * static_cast<int32_t>(-1));
//Plus and minus X
vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_NEG_X, m_pOutputMesh->m_vecVertices[ct].getPosition().getX() < regTransformed.getLowerCorner().getX() + 0.001f);
vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_POS_X, m_pOutputMesh->m_vecVertices[ct].getPosition().getX() > regTransformed.getUpperCorner().getX() - 0.001f);
//Plus and minus Y
vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_NEG_Y, m_pOutputMesh->m_vecVertices[ct].getPosition().getY() < regTransformed.getLowerCorner().getY() + 0.001f);
vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_POS_Y, m_pOutputMesh->m_vecVertices[ct].getPosition().getY() > regTransformed.getUpperCorner().getY() - 0.001f);
//Plus and minus Z
vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_NEG_Z, m_pOutputMesh->m_vecVertices[ct].getPosition().getZ() < regTransformed.getLowerCorner().getZ() + 0.001f);
vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_POS_Z, m_pOutputMesh->m_vecVertices[ct].getPosition().getZ() > regTransformed.getUpperCorner().getZ() - 0.001f);
}
//If all three vertices have the same material then we are not on a material edge. If any vertex has a different
//material then all three vertices are on a material edge. E.g. If one vertex has material 'a' and the other two
//have material 'b', then the two 'b's are still on an edge (with 'a') even though they are the same as eachother.
for(uint32_t ct = 0; ct < m_vecTriangles.size(); ct++)
{
uint32_t v0 = m_vecTriangles[ct].v0;
uint32_t v1 = m_vecTriangles[ct].v1;
uint32_t v2 = m_vecTriangles[ct].v2;
bool allMatch =
(m_pOutputMesh->m_vecVertices[v0].material - m_pOutputMesh->m_vecVertices[v1].material < 0.001f) &&
(m_pOutputMesh->m_vecVertices[v1].material - m_pOutputMesh->m_vecVertices[v2].material < 0.001f);
if(!allMatch)
{
vecVertexMetadata[v0].isOnMaterialEdge = true;
vecVertexMetadata[v1].isOnMaterialEdge = true;
vecVertexMetadata[v2].isOnMaterialEdge = true;
}
}
}
template<>
POLYVOX_API bool MeshDecimator<PositionMaterialNormal>::canCollapseNormalEdge(uint32_t uSrc, uint32_t uDst)
{
if(m_vecInitialVertexMetadata[uSrc].normal.dot(m_vecInitialVertexMetadata[uDst].normal) < m_fMinDotProductForCollapse)
{
return false;
}
//With the marching cubes surface we honour the user specified threshold
return !collapseChangesFaceNormals(uSrc, uDst, m_fMinDotProductForCollapse);
}
template<>
POLYVOX_API bool MeshDecimator<PositionMaterial>::canCollapseNormalEdge(uint32_t uSrc, uint32_t uDst)
{
//We don't actually use the normal here, because we want to allow face
//vertices to collapse onto edge vertices. Simply checking whether anything
//has flipped has proved to be the most robust approach, though rather slow.
//It's not sufficient to just check the normals, there can be holes in the middle
//of the mesh for example.
//User specified threshold is not used for cubic surface, any
//movement is too much (but allow for floating point error).
return !collapseChangesFaceNormals(uSrc, uDst, 0.999f);
}
}

View File

@ -1,43 +0,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/SimpleInterface.h"
//DOESN'T BELONG HERE - JUST FOR TESTING!!
#include "PolyVoxCore/Density.h"
#include "PolyVoxCore/MaterialDensityPair.h"
namespace PolyVox
{
void extractCubicMesh(Volume& volume, const Region& region, Mesh& resultMesh)
{
CubicSurfaceExtractorWithNormals< SimpleVolume<MaterialDensityPair88> > surfaceExtractor(&volume, region, &resultMesh);
surfaceExtractor.execute();
}
void extractSmoothMesh(Volume& volume, const Region& region, Mesh& resultMesh)
{
MarchingCubesSurfaceExtractor< SimpleVolume<MaterialDensityPair88> > surfaceExtractor(&volume, region, &resultMesh);
surfaceExtractor.execute();
}
}

View File

@ -31,10 +31,7 @@ SET(UTIL_SRC_FILES
#Projects headers files #Projects headers files
SET(UTIL_INC_FILES SET(UTIL_INC_FILES
include/PolyVoxUtil/Serialization.h #Nothing here at the moment...
include/PolyVoxUtil/Serialization.inl
include/PolyVoxUtil/VolumeChangeTracker.h
include/PolyVoxUtil/VolumeChangeTracker.inl
) )
ADD_DEFINITIONS(-DPOLYVOX_SHARED_EXPORTS) #Export symbols in the .dll ADD_DEFINITIONS(-DPOLYVOX_SHARED_EXPORTS) #Export symbols in the .dll

View File

@ -0,0 +1 @@
I don't think Git allows empty directories, so this is just a placeholder until we decide what to do with PolyVoxUtil.

View File

@ -1,77 +0,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.
*******************************************************************************/
#ifndef __PolyVox_Serialization_H__
#define __PolyVox_Serialization_H__
#include "PolyVoxCore/Impl/Utility.h"
#include "PolyVoxCore/Region.h"
#include <iostream>
#include <memory>
namespace PolyVox
{
/// \deprecated
class POLYVOX_DEPRECATED VolumeSerializationProgressListener
{
public:
virtual void onProgressUpdated(float fProgress) = 0;
};
////////////////////////////////////////////////////////////////////////////////
// THESE FUNCTIONS ARE DEPRECATED.
////////////////////////////////////////////////////////////////////////////////
/// \deprecated
template< typename VolumeType >
POLYVOX_DEPRECATED polyvox_shared_ptr< VolumeType > loadVolumeRaw(std::istream& stream, VolumeSerializationProgressListener* progressListener = 0);
/// \deprecated
template< typename VolumeType >
POLYVOX_DEPRECATED void saveVolumeRaw(std::ostream& stream, VolumeType& volume, VolumeSerializationProgressListener* progressListener = 0);
/// \deprecated
template< typename VolumeType >
POLYVOX_DEPRECATED polyvox_shared_ptr< VolumeType > loadVolumeRle(std::istream& stream, VolumeSerializationProgressListener* progressListener = 0);
/// \deprecated
template< typename VolumeType >
POLYVOX_DEPRECATED void saveVolumeRle(std::ostream& stream, VolumeType& volume, VolumeSerializationProgressListener* progressListener = 0);
/// \deprecated
template< typename VolumeType >
POLYVOX_DEPRECATED bool loadVolume(std::istream& stream, VolumeType& volume, VolumeSerializationProgressListener* progressListener = 0);
/// \deprecated
template< typename VolumeType >
POLYVOX_DEPRECATED bool saveVolume(std::ostream& stream, VolumeType& volume, VolumeSerializationProgressListener* progressListener = 0);
/// \deprecated
template< typename VolumeType >
POLYVOX_DEPRECATED bool loadVersion0(std::istream& stream, VolumeType& volume, VolumeSerializationProgressListener* progressListener = 0);
/// \deprecated
template< typename VolumeType >
POLYVOX_DEPRECATED bool saveVersion0(std::ostream& stream, VolumeType& volume, VolumeSerializationProgressListener* progressListener = 0);
}
#include "PolyVoxUtil/Serialization.inl"
#endif

View File

@ -1,433 +0,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.
*******************************************************************************/
namespace PolyVox
{
//Note: we don't do much error handling in here - exceptions will simply be propergated up to the caller.
//FIXME - think about pointer ownership issues. Or could return volume by value if the copy constructor is shallow
template< typename VolumeType >
polyvox_shared_ptr< VolumeType > loadVolumeRaw(std::istream& stream, VolumeSerializationProgressListener* progressListener)
{
assert(false); //THIS FUNCTION IS DEPRECATED. REMOVE THIS ASSERT TO CONTINUE, BUT SWITCH TO 'loadVolume()' ASAP.
//Read volume dimensions
uint8_t volumeWidthPower = 0;
uint8_t volumeHeightPower = 0;
uint8_t volumeDepthPower = 0;
stream.read(reinterpret_cast<char*>(&volumeWidthPower), sizeof(volumeWidthPower));
stream.read(reinterpret_cast<char*>(&volumeHeightPower), sizeof(volumeHeightPower));
stream.read(reinterpret_cast<char*>(&volumeDepthPower), sizeof(volumeDepthPower));
uint16_t volumeWidth = 0x0001 << volumeWidthPower;
uint16_t volumeHeight = 0x0001 << volumeHeightPower;
uint16_t volumeDepth = 0x0001 << volumeDepthPower;
//FIXME - need to support non cubic volumes
polyvox_shared_ptr< VolumeType > volume(new LargeVolume<VolumeType::VoxelType>(volumeWidth, volumeHeight, volumeDepth));
//Read data
for(uint16_t z = 0; z < volumeDepth; ++z)
{
//Update progress once per slice.
if(progressListener)
{
float fProgress = static_cast<float>(z) / static_cast<float>(volumeDepth);
progressListener->onProgressUpdated(fProgress);
}
for(uint16_t y = 0; y < volumeHeight; ++y)
{
for(uint16_t x = 0; x < volumeWidth; ++x)
{
VolumeType::VoxelType value;
stream.read(reinterpret_cast<char*>(&value), sizeof(value));
volume->setVoxelAt(x,y,z,value);
}
}
}
//Finished
if(progressListener)
{
progressListener->onProgressUpdated(1.0f);
}
return volume;
}
template< typename VolumeType >
void saveVolumeRaw(std::ostream& stream, VolumeType& volume, VolumeSerializationProgressListener* progressListener)
{
assert(false); //THIS FUNCTION IS DEPRECATED. REMOVE THIS ASSERT TO CONTINUE, BUT SWITCH TO 'saveVolume()' ASAP.
//Write volume dimensions
uint16_t volumeWidth = volume.getWidth();
uint16_t volumeHeight = volume.getHeight();
uint16_t volumeDepth = volume.getDepth();
uint8_t volumeWidthPower = logBase2(volumeWidth);
uint8_t volumeHeightPower = logBase2(volumeHeight);
uint8_t volumeDepthPower = logBase2(volumeDepth);
stream.write(reinterpret_cast<char*>(&volumeWidthPower), sizeof(volumeWidthPower));
stream.write(reinterpret_cast<char*>(&volumeHeightPower), sizeof(volumeHeightPower));
stream.write(reinterpret_cast<char*>(&volumeDepthPower), sizeof(volumeDepthPower));
//Write data
VolumeType::Sampler volIter(&volume);
for(uint16_t z = 0; z < volumeDepth; ++z)
{
//Update progress once per slice.
if(progressListener)
{
float fProgress = static_cast<float>(z) / static_cast<float>(volumeDepth);
progressListener->onProgressUpdated(fProgress);
}
for(uint16_t y = 0; y < volumeHeight; ++y)
{
for(uint16_t x = 0; x < volumeWidth; ++x)
{
volIter.setPosition(x,y,z);
VolumeType::VoxelType value = volIter.getVoxel();
stream.write(reinterpret_cast<char*>(&value), sizeof(value));
}
}
}
//Finished
if(progressListener)
{
progressListener->onProgressUpdated(1.0f);
}
}
//Note: we don't do much error handling in here - exceptions will simply be propergated up to the caller.
//FIXME - think about pointer ownership issues. Or could return volume by value if the copy constructor is shallow
template< typename VolumeType >
polyvox_shared_ptr< VolumeType > loadVolumeRle(std::istream& stream, VolumeSerializationProgressListener* progressListener)
{
assert(false); //THIS FUNCTION IS DEPRECATED. REMOVE THIS ASSERT TO CONTINUE, BUT SWITCH TO 'loadVolume()' ASAP.
//Read volume dimensions
uint8_t volumeWidthPower = 0;
uint8_t volumeHeightPower = 0;
uint8_t volumeDepthPower = 0;
stream.read(reinterpret_cast<char*>(&volumeWidthPower), sizeof(volumeWidthPower));
stream.read(reinterpret_cast<char*>(&volumeHeightPower), sizeof(volumeHeightPower));
stream.read(reinterpret_cast<char*>(&volumeDepthPower), sizeof(volumeDepthPower));
uint16_t volumeWidth = 0x0001 << volumeWidthPower;
uint16_t volumeHeight = 0x0001 << volumeHeightPower;
uint16_t volumeDepth = 0x0001 << volumeDepthPower;
//FIXME - need to support non cubic volumes
polyvox_shared_ptr< VolumeType > volume(new LargeVolume<VolumeType::VoxelType>(volumeWidth, volumeHeight, volumeDepth));
//Read data
bool firstTime = true;
uint32_t runLength = 0;
VolumeType::VoxelType value;
stream.read(reinterpret_cast<char*>(&value), sizeof(value));
stream.read(reinterpret_cast<char*>(&runLength), sizeof(runLength));
for(uint16_t z = 0; z < volumeDepth; ++z)
{
//Update progress once per slice.
if(progressListener)
{
float fProgress = static_cast<float>(z) / static_cast<float>(volumeDepth);
progressListener->onProgressUpdated(fProgress);
}
for(uint16_t y = 0; y < volumeHeight; ++y)
{
for(uint16_t x = 0; x < volumeWidth; ++x)
{
if(runLength != 0)
{
volume->setVoxelAt(x,y,z,value);
runLength--;
}
else
{
stream.read(reinterpret_cast<char*>(&value), sizeof(value));
stream.read(reinterpret_cast<char*>(&runLength), sizeof(runLength));
volume->setVoxelAt(x,y,z,value);
runLength--;
}
}
}
}
//Finished
if(progressListener)
{
progressListener->onProgressUpdated(1.0f);
}
return volume;
}
template< typename VolumeType >
void saveVolumeRle(std::ostream& stream, VolumeType& volume, VolumeSerializationProgressListener* progressListener)
{
assert(false); //THIS FUNCTION IS DEPRECATED. REMOVE THIS ASSERT TO CONTINUE, BUT SWITCH TO 'saveVolume()' ASAP.
//Write volume dimensions
uint16_t volumeWidth = volume.getWidth();
uint16_t volumeHeight = volume.getHeight();
uint16_t volumeDepth = volume.getDepth();
uint8_t volumeWidthPower = logBase2(volumeWidth);
uint8_t volumeHeightPower = logBase2(volumeHeight);
uint8_t volumeDepthPower = logBase2(volumeDepth);
stream.write(reinterpret_cast<char*>(&volumeWidthPower), sizeof(volumeWidthPower));
stream.write(reinterpret_cast<char*>(&volumeHeightPower), sizeof(volumeHeightPower));
stream.write(reinterpret_cast<char*>(&volumeDepthPower), sizeof(volumeDepthPower));
//Write data
VolumeType::Sampler volIter(&volume);
VolumeType::VoxelType current;
uint32_t runLength = 0;
bool firstTime = true;
for(uint16_t z = 0; z < volumeDepth; ++z)
{
//Update progress once per slice.
if(progressListener)
{
float fProgress = static_cast<float>(z) / static_cast<float>(volumeDepth);
progressListener->onProgressUpdated(fProgress);
}
for(uint16_t y = 0; y < volumeHeight; ++y)
{
for(uint16_t x = 0; x < volumeWidth; ++x)
{
volIter.setPosition(x,y,z);
VolumeType::VoxelType value = volIter.getVoxel();
if(firstTime)
{
current = value;
runLength = 1;
firstTime = false;
}
else
{
if(value == current)
{
runLength++;
}
else
{
stream.write(reinterpret_cast<char*>(&current), sizeof(current));
stream.write(reinterpret_cast<char*>(&runLength), sizeof(runLength));
current = value;
runLength = 1;
}
}
}
}
}
stream.write(reinterpret_cast<char*>(&current), sizeof(current));
stream.write(reinterpret_cast<char*>(&runLength), sizeof(runLength));
//Finished
if(progressListener)
{
progressListener->onProgressUpdated(1.0f);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// New version of load/save code with versioning
////////////////////////////////////////////////////////////////////////////////////////////////////
template< typename VolumeType >
bool loadVolume(std::istream& stream, VolumeType& volume, VolumeSerializationProgressListener* progressListener)
{
char pIdentifier[8];
stream.read(pIdentifier, 7);
pIdentifier[7] = '\0'; //Set the null terminator
if(strcmp(pIdentifier, "PolyVox") != 0)
{
return false;
}
uint16_t uVersion;
stream.read(reinterpret_cast<char*>(&uVersion), sizeof(uVersion));
switch(uVersion)
{
case 0:
return loadVersion0(stream, volume, progressListener);
//Return means no need to break...
default:
return false;
}
}
template< typename VolumeType >
bool saveVolume(std::ostream& stream, VolumeType& volume, VolumeSerializationProgressListener* progressListener)
{
char pIdentifier[] = "PolyVox";
stream.write(pIdentifier, 7);
uint16_t uVersion = 0;
stream.write(reinterpret_cast<const char*>(&uVersion), sizeof(uVersion));
return saveVersion0(stream, volume, progressListener);
}
//Note: we don't do much error handling in here - exceptions will simply be propergated up to the caller.
//FIXME - think about pointer ownership issues. Or could return volume by value if the copy constructor is shallow
template< typename VolumeType >
bool loadVersion0(std::istream& stream, VolumeType& volume, VolumeSerializationProgressListener* progressListener)
{
//Read volume dimensions
uint16_t volumeWidth = 0;
uint16_t volumeHeight = 0;
uint16_t volumeDepth = 0;
stream.read(reinterpret_cast<char*>(&volumeWidth), sizeof(volumeWidth));
stream.read(reinterpret_cast<char*>(&volumeHeight), sizeof(volumeHeight));
stream.read(reinterpret_cast<char*>(&volumeDepth), sizeof(volumeDepth));
//Resize the volume
//HACK - Forces block size to 32. This functions needs reworking anyway due to large volume support.
volume.resize(Region(Vector3DInt32(0,0,0), Vector3DInt32(volumeWidth-1, volumeHeight-1, volumeDepth-1)), 32);
//Read data
bool firstTime = true;
uint32_t runLength = 0;
VolumeType::VoxelType value;
stream.read(reinterpret_cast<char*>(&value), sizeof(value));
stream.read(reinterpret_cast<char*>(&runLength), sizeof(runLength));
for(uint16_t z = 0; z < volumeDepth; ++z)
{
//Update progress once per slice.
if(progressListener)
{
float fProgress = static_cast<float>(z) / static_cast<float>(volumeDepth);
progressListener->onProgressUpdated(fProgress);
}
for(uint16_t y = 0; y < volumeHeight; ++y)
{
for(uint16_t x = 0; x < volumeWidth; ++x)
{
if(runLength != 0)
{
volume.setVoxelAt(x,y,z,value);
runLength--;
}
else
{
stream.read(reinterpret_cast<char*>(&value), sizeof(value));
stream.read(reinterpret_cast<char*>(&runLength), sizeof(runLength));
volume.setVoxelAt(x,y,z,value);
runLength--;
}
}
}
}
//Finished
if(progressListener)
{
progressListener->onProgressUpdated(1.0f);
}
return true;
}
template< typename VolumeType >
bool saveVersion0(std::ostream& stream, VolumeType& volume, VolumeSerializationProgressListener* progressListener)
{
//Write volume dimensions
uint16_t volumeWidth = volume.getWidth();
uint16_t volumeHeight = volume.getHeight();
uint16_t volumeDepth = volume.getDepth();
stream.write(reinterpret_cast<char*>(&volumeWidth), sizeof(volumeWidth));
stream.write(reinterpret_cast<char*>(&volumeHeight), sizeof(volumeHeight));
stream.write(reinterpret_cast<char*>(&volumeDepth), sizeof(volumeDepth));
//Write data
VolumeType::Sampler volIter(&volume);
VolumeType::VoxelType current;
uint32_t runLength = 0;
bool firstTime = true;
for(uint16_t z = 0; z < volumeDepth; ++z)
{
//Update progress once per slice.
if(progressListener)
{
float fProgress = static_cast<float>(z) / static_cast<float>(volumeDepth);
progressListener->onProgressUpdated(fProgress);
}
for(uint16_t y = 0; y < volumeHeight; ++y)
{
for(uint16_t x = 0; x < volumeWidth; ++x)
{
volIter.setPosition(x,y,z);
VolumeType::VoxelType value = volIter.getVoxel();
if(firstTime)
{
current = value;
runLength = 1;
firstTime = false;
}
else
{
if(value == current)
{
runLength++;
}
else
{
stream.write(reinterpret_cast<char*>(&current), sizeof(current));
stream.write(reinterpret_cast<char*>(&runLength), sizeof(runLength));
current = value;
runLength = 1;
}
}
}
}
}
stream.write(reinterpret_cast<char*>(&current), sizeof(current));
stream.write(reinterpret_cast<char*>(&runLength), sizeof(runLength));
//Finished
if(progressListener)
{
progressListener->onProgressUpdated(1.0f);
}
return true;
}
}

View File

@ -1,83 +0,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.
*******************************************************************************/
#ifndef __PolyVox_VolumeChangeTracker_H__
#define __PolyVox_VolumeChangeTracker_H__
#include "Impl/Utility.h"
#include "PolyVoxCore/Region.h"
#include "PolyVoxCore/SurfaceMesh.h"
#include "PolyVoxCore/Vector.h"
namespace PolyVox
{
/// Voxel scene manager
/// \deprecated
template <typename VoxelType>
class POLYVOX_DEPRECATED VolumeChangeTracker
{
public:
//Constructors, etc
VolumeChangeTracker(LargeVolume<VoxelType>* volumeDataToSet, uint16_t regionSideLength);
~VolumeChangeTracker();
//Getters
int32_t getCurrentTime(void) const;
int32_t getLastModifiedTimeForRegion(uint16_t uX, uint16_t uY, uint16_t uZ);
LargeVolume<VoxelType>* getWrappedVolume(void) const;
//Setters
void setAllRegionsModified(void);
void setLockedVoxelAt(uint16_t x, uint16_t y, uint16_t z, VoxelType value);
void setVoxelAt(uint16_t x, uint16_t y, uint16_t z, VoxelType value);
//Others
void lockRegion(const Region& regToLock);
void unlockRegion(void);
//void markRegionChanged(uint16_t firstX, uint16_t firstY, uint16_t firstZ, uint16_t lastX, uint16_t lastY, uint16_t lastZ);
public:
void incrementCurrentTime(void);
bool m_bIsLocked;
Region m_regLastLocked;
LargeVolume<VoxelType>* volumeData;
uint16_t m_uRegionSideLength;
uint8_t m_uRegionSideLengthPower;
uint16_t m_uVolumeWidthInRegions;
uint16_t m_uVolumeHeightInRegions;
uint16_t m_uVolumeDepthInRegions;
//It's not what the block class was designed for, but it
//provides a handy way of storing a 3D grid of values.
LargeVolume<int32_t>* volRegionLastModified;
static uint32_t m_uCurrentTime;
};
}
#include "PolyVoxUtil/VolumeChangeTracker.inl"
#endif

View File

@ -1,212 +0,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.
*******************************************************************************/
namespace PolyVox
{
template <typename VoxelType>
uint32_t VolumeChangeTracker<VoxelType>::m_uCurrentTime = 0;
//////////////////////////////////////////////////////////////////////////
// VolumeChangeTracker
//////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
VolumeChangeTracker<VoxelType>::VolumeChangeTracker(LargeVolume<VoxelType>* volumeDataToSet, uint16_t regionSideLength)
:m_bIsLocked(false)
,volumeData(0)
,m_uRegionSideLength(regionSideLength)
{
volumeData = volumeDataToSet;
m_uVolumeWidthInRegions = volumeData->getWidth() / m_uRegionSideLength;
m_uVolumeHeightInRegions = volumeData->getHeight() / m_uRegionSideLength;
m_uVolumeDepthInRegions = volumeData->getDepth() / m_uRegionSideLength;
m_uRegionSideLengthPower = PolyVox::logBase2(m_uRegionSideLength);
volRegionLastModified = new LargeVolume<int32_t>(m_uVolumeWidthInRegions, m_uVolumeHeightInRegions, m_uVolumeDepthInRegions, 0);
}
template <typename VoxelType>
VolumeChangeTracker<VoxelType>::~VolumeChangeTracker()
{
}
template <typename VoxelType>
void VolumeChangeTracker<VoxelType>::setAllRegionsModified(void)
{
incrementCurrentTime();
for(uint16_t blockZ = 0; blockZ < m_uVolumeDepthInRegions; ++blockZ)
{
for(uint16_t blockY = 0; blockY < m_uVolumeHeightInRegions; ++blockY)
{
for(uint16_t blockX = 0; blockX < m_uVolumeWidthInRegions; ++blockX)
{
volRegionLastModified->setVoxelAt(blockX, blockY, blockZ, m_uCurrentTime);
}
}
}
}
template <typename VoxelType>
int32_t VolumeChangeTracker<VoxelType>::getCurrentTime(void) const
{
return m_uCurrentTime;
}
template <typename VoxelType>
int32_t VolumeChangeTracker<VoxelType>::getLastModifiedTimeForRegion(uint16_t uX, uint16_t uY, uint16_t uZ)
{
return volRegionLastModified->getVoxelAt(uX, uY, uZ);
}
template <typename VoxelType>
LargeVolume<VoxelType>* VolumeChangeTracker<VoxelType>::getWrappedVolume(void) const
{
return volumeData;
}
template <typename VoxelType>
void VolumeChangeTracker<VoxelType>::setVoxelAt(uint16_t x, uint16_t y, uint16_t z, VoxelType value)
{
//Note: We increase the time stamp both at the start and the end
//to avoid ambiguity about whether the timestamp comparison should
//be '<' vs '<=' or '>' vs '>=' in the users code.
incrementCurrentTime();
volumeData->setVoxelAt(x,y,z,value);
//If we are not on a boundary, just mark one region.
if((x % m_uRegionSideLength != 0) &&
(x % m_uRegionSideLength != m_uRegionSideLength-1) &&
(y % m_uRegionSideLength != 0) &&
(y % m_uRegionSideLength != m_uRegionSideLength-1) &&
(z % m_uRegionSideLength != 0) &&
(z % m_uRegionSideLength != m_uRegionSideLength-1))
{
volRegionLastModified->setVoxelAt(x >> m_uRegionSideLengthPower, y >> m_uRegionSideLengthPower, z >> m_uRegionSideLengthPower, m_uCurrentTime);
}
else //Mark surrounding regions as well
{
const uint16_t regionX = x >> m_uRegionSideLengthPower;
const uint16_t regionY = y >> m_uRegionSideLengthPower;
const uint16_t regionZ = z >> m_uRegionSideLengthPower;
const uint16_t minRegionX = (std::max)(uint16_t(0),uint16_t(regionX-1));
const uint16_t minRegionY = (std::max)(uint16_t(0),uint16_t(regionY-1));
const uint16_t minRegionZ = (std::max)(uint16_t(0),uint16_t(regionZ-1));
const uint16_t maxRegionX = (std::min)(uint16_t(m_uVolumeWidthInRegions-1),uint16_t(regionX+1));
const uint16_t maxRegionY = (std::min)(uint16_t(m_uVolumeHeightInRegions-1),uint16_t(regionY+1));
const uint16_t maxRegionZ = (std::min)(uint16_t(m_uVolumeDepthInRegions-1),uint16_t(regionZ+1));
for(uint16_t zCt = minRegionZ; zCt <= maxRegionZ; zCt++)
{
for(uint16_t yCt = minRegionY; yCt <= maxRegionY; yCt++)
{
for(uint16_t xCt = minRegionX; xCt <= maxRegionX; xCt++)
{
volRegionLastModified->setVoxelAt(xCt,yCt,zCt,m_uCurrentTime);
}
}
}
}
//Increment time stamp. See earlier note.
incrementCurrentTime();
}
template <typename VoxelType>
void VolumeChangeTracker<VoxelType>::setLockedVoxelAt(uint16_t x, uint16_t y, uint16_t z, VoxelType value)
{
assert(m_bIsLocked);
//FIXME - rather than creating a iterator each time we should have one stored
/*Sampler<VoxelType> iterVol(*volumeData);
iterVol.setPosition(x,y,z);
iterVol.setVoxel(value);*/
volumeData->setVoxelAt(x,y,z,value);
}
template <typename VoxelType>
void VolumeChangeTracker<VoxelType>::lockRegion(const Region& regToLock)
{
if(m_bIsLocked)
{
throw std::logic_error("A region is already locked. Please unlock it before locking another.");
}
m_regLastLocked = regToLock;
m_bIsLocked = true;
}
template <typename VoxelType>
void VolumeChangeTracker<VoxelType>::unlockRegion(void)
{
if(!m_bIsLocked)
{
throw std::logic_error("No region is locked. You must lock a region before you can unlock it.");
}
//Note: We increase the time stamp both at the start and the end
//to avoid ambiguity about whether the timestamp comparison should
//be '<' vs '<=' or '>' vs '>=' in the users code.
incrementCurrentTime();
const uint16_t firstRegionX = m_regLastLocked.getLowerCorner().getX() >> m_uRegionSideLengthPower;
const uint16_t firstRegionY = m_regLastLocked.getLowerCorner().getY() >> m_uRegionSideLengthPower;
const uint16_t firstRegionZ = m_regLastLocked.getLowerCorner().getZ() >> m_uRegionSideLengthPower;
const uint16_t lastRegionX = m_regLastLocked.getUpperCorner().getX() >> m_uRegionSideLengthPower;
const uint16_t lastRegionY = m_regLastLocked.getUpperCorner().getY() >> m_uRegionSideLengthPower;
const uint16_t lastRegionZ = m_regLastLocked.getUpperCorner().getZ() >> m_uRegionSideLengthPower;
for(uint16_t zCt = firstRegionZ; zCt <= lastRegionZ; zCt++)
{
for(uint16_t yCt = firstRegionY; yCt <= lastRegionY; yCt++)
{
for(uint16_t xCt = firstRegionX; xCt <= lastRegionX; xCt++)
{
volRegionLastModified->setVoxelAt(xCt,yCt,zCt,m_uCurrentTime);
}
}
}
m_bIsLocked = false;
//Increment time stamp. See earlier note.
incrementCurrentTime();
}
template <typename VoxelType>
void VolumeChangeTracker<VoxelType>::incrementCurrentTime(void)
{
//Increment the current time.
uint32_t time = m_uCurrentTime++;
//Watch out for wraparound. Hopefully this will never happen
//as we have a pretty big counter, but it's best to be sure...
assert(time < m_uCurrentTime);
if(time >= m_uCurrentTime)
{
throw std::overflow_error("The VolumeChangeTracker time has overflowed.");
}
}
}

View File

@ -32,7 +32,7 @@ using namespace PolyVox;
void TestVector::testLength() void TestVector::testLength()
{ {
Vector3DInt8 vec(3, 4, 5); Vector3DInt8 vec(3, 4, 5);
QCOMPARE(vec.lengthSquared(), double(3*3+4*4+5*5)); QCOMPARE(vec.lengthSquared(), int32_t(3*3+4*4+5*5)); // QCOMPARE is strict on types. For an int8 vector, the OperationType is int32_t.
} }
void TestVector::testDotProduct() void TestVector::testDotProduct()
@ -40,7 +40,7 @@ void TestVector::testDotProduct()
Vector3DInt8 vecxy(3, 4, 0); Vector3DInt8 vecxy(3, 4, 0);
Vector3DInt8 vecz(0, 0, 1); Vector3DInt8 vecz(0, 0, 1);
QCOMPARE(vecxy.dot(vecz), int8_t(0)); //QCOMPARE is very strict on the types matching QCOMPARE(vecxy.dot(vecz), int32_t(0)); // QCOMPARE is strict on types. For an int8 vector, the OperationType is int32_t .
} }
void TestVector::testEquality() void TestVector::testEquality()