Merge branch 'develop' into feature/cubiquity-version
Conflicts: library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.inl
This commit is contained in:
commit
fcdb175f1c
@ -2,6 +2,8 @@ Changes for PolyVox version 0.3
|
|||||||
===============================
|
===============================
|
||||||
This release has focused on... (some introduction here).
|
This release has focused on... (some introduction here).
|
||||||
|
|
||||||
|
This line was added just for testing.
|
||||||
|
|
||||||
LargeVolume
|
LargeVolume
|
||||||
-----------
|
-----------
|
||||||
It is now possible to provide custom compressors for the data which is stored in a LargeVolume. Two compressor implementation are provided with PolyVox - RLECompressor which is suitable for cubic-style terrain, and MinizCompressor which uses the 'miniz' zlib implementation for smooth terrain or general purpose use. Users can provide their own implementation of the compressor interface if they wish.
|
It is now possible to provide custom compressors for the data which is stored in a LargeVolume. Two compressor implementation are provided with PolyVox - RLECompressor which is suitable for cubic-style terrain, and MinizCompressor which uses the 'miniz' zlib implementation for smooth terrain or general purpose use. Users can provide their own implementation of the compressor interface if they wish.
|
||||||
|
@ -153,7 +153,7 @@ SET_PROPERTY(TARGET PolyVoxCore PROPERTY FOLDER "Library")
|
|||||||
|
|
||||||
SET_TARGET_PROPERTIES(PolyVoxCore PROPERTIES VERSION ${POLYVOX_VERSION} SOVERSION ${POLYVOX_VERSION_MAJOR})
|
SET_TARGET_PROPERTIES(PolyVoxCore PROPERTIES VERSION ${POLYVOX_VERSION} SOVERSION ${POLYVOX_VERSION_MAJOR})
|
||||||
IF(MSVC)
|
IF(MSVC)
|
||||||
SET_TARGET_PROPERTIES(PolyVoxCore PROPERTIES COMPILE_FLAGS "/W4 /wd4251 /wd4127") #Disable warning on STL exports
|
SET_TARGET_PROPERTIES(PolyVoxCore PROPERTIES COMPILE_FLAGS "/W4 /wd4251") #Disable warning on STL exports
|
||||||
ENDIF(MSVC)
|
ENDIF(MSVC)
|
||||||
|
|
||||||
#Install
|
#Install
|
||||||
|
@ -28,20 +28,71 @@ freely, subject to the following restrictions:
|
|||||||
|
|
||||||
namespace PolyVox
|
namespace PolyVox
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Provides an interface for performing compression of data.
|
||||||
|
*
|
||||||
|
* This class provides an interface which can be implemented by derived classes which perform data compression.
|
||||||
|
* The main purpose of this is to allow the user to change the compression algorithm which is used by a LargeVolume,
|
||||||
|
* based on the kind of voxel data it is storing. Users may also wish to use Compressor subclasses in more general
|
||||||
|
* compression-related scenarios but this is not well tested.
|
||||||
|
*
|
||||||
|
* If you wish to implement your own compression algorithms for use in PolyVox then you should begin by subclassing this class.
|
||||||
|
*
|
||||||
|
* \sa MinizCompressor, RLECompressor
|
||||||
|
*/
|
||||||
class Compressor
|
class Compressor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
/// Constructor
|
||||||
Compressor() {};
|
Compressor() {};
|
||||||
|
/// Destructor
|
||||||
virtual ~Compressor() {};
|
virtual ~Compressor() {};
|
||||||
|
|
||||||
// Computes a worst-case scenario for how big the output can be for a given input size. If
|
/**
|
||||||
// necessary you can use this as a destination buffer size, though it may be somewhat wasteful.
|
* Computes a worst-case scenario for how big the output can be for a given input size.
|
||||||
|
*
|
||||||
|
* If necessary you can use this as a destination buffer size, though it may be somewhat
|
||||||
|
* wasteful. It is not guarenteed that compression actually shrinks the data, so the
|
||||||
|
* worst-case value returned by this function may be bigger than the input size.
|
||||||
|
*
|
||||||
|
* \param uUncompressedInputSize The size of the uncompressed input data
|
||||||
|
* \return The largest possible size of the compressed output data.
|
||||||
|
*/
|
||||||
virtual uint32_t getMaxCompressedSize(uint32_t uUncompressedInputSize) = 0;
|
virtual uint32_t getMaxCompressedSize(uint32_t uUncompressedInputSize) = 0;
|
||||||
|
|
||||||
// Compresses the data.
|
/**
|
||||||
|
* Compresses the data.
|
||||||
|
*
|
||||||
|
* Performs compression of the data pointed to by pSrcData and stores the result in pDstData.
|
||||||
|
* The user is responsible for allocating both buffers and for making sure that the destination
|
||||||
|
* buffer is large enough to hold the result. If you don't know how big the compressed data
|
||||||
|
* will be (and you probably won't know this) then you can call getMaxCompressedSize() to get
|
||||||
|
* an upper bound. The *actual* compressed size is then returned by this function and you can
|
||||||
|
* use this to copy your compressed data to a more suitably size buffer.
|
||||||
|
*
|
||||||
|
* \param pSrcData A pointer to the data to be compressed.
|
||||||
|
* \param uSrcLength The length of the data to be compressed.
|
||||||
|
* \param pDstData A pointer to the memory where the result should be stored.
|
||||||
|
* \param uDstLength The length of the destination buffer (compression will fail if this isn't big enough).
|
||||||
|
* \return The size of the resulting compressed data.
|
||||||
|
*/
|
||||||
virtual uint32_t compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength) = 0;
|
virtual uint32_t compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength) = 0;
|
||||||
|
|
||||||
// Decompresses the data.
|
/**
|
||||||
|
* Decompresses the data.
|
||||||
|
*
|
||||||
|
* Performs decompression of the data pointed to by pSrcData and stores the result in pDstData.
|
||||||
|
* The user is responsible for allocating both buffers and for making sure that the destination
|
||||||
|
* buffer is large enough to hold the result. This means you need to know how large the resulting
|
||||||
|
* data might be, so before you compress the data it may be worth storing this information somewhere.
|
||||||
|
* The *actual* decompressed size is then returned by this function
|
||||||
|
*
|
||||||
|
* \param pSrcData A pointer to the data to be decompressed.
|
||||||
|
* \param uSrcLength The length of the data to be decompressed.
|
||||||
|
* \param pDstData A pointer to the memory where the result should be stored.
|
||||||
|
* \param uDstLength The length of the destination buffer (decompression will fail if this isn't big enough).
|
||||||
|
* \return The size of the resulting uncompressed data.
|
||||||
|
*/
|
||||||
virtual uint32_t decompress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength) = 0;
|
virtual uint32_t decompress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength) = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -121,7 +121,7 @@ namespace PolyVox
|
|||||||
void execute();
|
void execute();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int32_t addVertex(float fX, float fY, float fZ, typename VolumeType::VoxelType uMaterial, Array<3, IndexAndMaterial>& existingVertices);
|
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 performQuadMerging(std::list<Quad>& quads);
|
||||||
bool mergeQuads(Quad& q1, Quad& q2);
|
bool mergeQuads(Quad& q1, Quad& q2);
|
||||||
|
|
||||||
|
@ -94,20 +94,20 @@ namespace PolyVox
|
|||||||
// X
|
// X
|
||||||
if(m_funcIsQuadNeededCallback(currentVoxel, negXVoxel, material))
|
if(m_funcIsQuadNeededCallback(currentVoxel, negXVoxel, material))
|
||||||
{
|
{
|
||||||
uint32_t v0 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
uint32_t v0 = addVertex(regX , regY , regZ , material, m_previousSliceVertices);
|
||||||
uint32_t v1 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) + 0.5f, material, m_currentSliceVertices);
|
uint32_t v1 = addVertex(regX , regY , regZ + 1, material, m_currentSliceVertices);
|
||||||
uint32_t v2 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) + 0.5f, static_cast<float>(regZ) + 0.5f, material, m_currentSliceVertices);
|
uint32_t v2 = addVertex(regX , regY + 1, regZ + 1, material, m_currentSliceVertices);
|
||||||
uint32_t v3 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) + 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
uint32_t v3 = addVertex(regX , regY + 1, regZ , material, m_previousSliceVertices);
|
||||||
|
|
||||||
m_vecQuads[NegativeX][regX].push_back(Quad(v0, v1, v2, v3));
|
m_vecQuads[NegativeX][regX].push_back(Quad(v0, v1, v2, v3));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(m_funcIsQuadNeededCallback(negXVoxel, currentVoxel, material))
|
if(m_funcIsQuadNeededCallback(negXVoxel, currentVoxel, material))
|
||||||
{
|
{
|
||||||
uint32_t v0 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
uint32_t v0 = addVertex(regX , regY , regZ , material, m_previousSliceVertices);
|
||||||
uint32_t v1 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) + 0.5f, material, m_currentSliceVertices);
|
uint32_t v1 = addVertex(regX , regY , regZ + 1, material, m_currentSliceVertices);
|
||||||
uint32_t v2 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) + 0.5f, static_cast<float>(regZ) + 0.5f, material, m_currentSliceVertices);
|
uint32_t v2 = addVertex(regX , regY + 1, regZ + 1, material, m_currentSliceVertices);
|
||||||
uint32_t v3 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) + 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
uint32_t v3 = addVertex(regX , regY + 1, regZ , material, m_previousSliceVertices);
|
||||||
|
|
||||||
m_vecQuads[PositiveX][regX].push_back(Quad(v0, v3, v2, v1));
|
m_vecQuads[PositiveX][regX].push_back(Quad(v0, v3, v2, v1));
|
||||||
}
|
}
|
||||||
@ -115,20 +115,20 @@ namespace PolyVox
|
|||||||
// Y
|
// Y
|
||||||
if(m_funcIsQuadNeededCallback(currentVoxel, negYVoxel, material))
|
if(m_funcIsQuadNeededCallback(currentVoxel, negYVoxel, material))
|
||||||
{
|
{
|
||||||
uint32_t v0 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
uint32_t v0 = addVertex(regX , regY , regZ , material, m_previousSliceVertices);
|
||||||
uint32_t v1 = addVertex(static_cast<float>(regX) + 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
uint32_t v1 = addVertex(regX + 1, regY , regZ , material, m_previousSliceVertices);
|
||||||
uint32_t v2 = addVertex(static_cast<float>(regX) + 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) + 0.5f, material, m_currentSliceVertices);
|
uint32_t v2 = addVertex(regX + 1, regY , regZ + 1, material, m_currentSliceVertices);
|
||||||
uint32_t v3 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) + 0.5f, material, m_currentSliceVertices);
|
uint32_t v3 = addVertex(regX , regY , regZ + 1, material, m_currentSliceVertices);
|
||||||
|
|
||||||
m_vecQuads[NegativeY][regY].push_back(Quad(v0, v1, v2, v3));
|
m_vecQuads[NegativeY][regY].push_back(Quad(v0, v1, v2, v3));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(m_funcIsQuadNeededCallback(negYVoxel, currentVoxel, material))
|
if(m_funcIsQuadNeededCallback(negYVoxel, currentVoxel, material))
|
||||||
{
|
{
|
||||||
uint32_t v0 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
uint32_t v0 = addVertex(regX , regY , regZ , material, m_previousSliceVertices);
|
||||||
uint32_t v1 = addVertex(static_cast<float>(regX) + 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
uint32_t v1 = addVertex(regX + 1, regY , regZ , material, m_previousSliceVertices);
|
||||||
uint32_t v2 = addVertex(static_cast<float>(regX) + 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) + 0.5f, material, m_currentSliceVertices);
|
uint32_t v2 = addVertex(regX + 1, regY , regZ + 1, material, m_currentSliceVertices);
|
||||||
uint32_t v3 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) + 0.5f, material, m_currentSliceVertices);
|
uint32_t v3 = addVertex(regX , regY , regZ + 1, material, m_currentSliceVertices);
|
||||||
|
|
||||||
m_vecQuads[PositiveY][regY].push_back(Quad(v0, v3, v2, v1));
|
m_vecQuads[PositiveY][regY].push_back(Quad(v0, v3, v2, v1));
|
||||||
}
|
}
|
||||||
@ -136,20 +136,20 @@ namespace PolyVox
|
|||||||
// Z
|
// Z
|
||||||
if(m_funcIsQuadNeededCallback(currentVoxel, negZVoxel, material))
|
if(m_funcIsQuadNeededCallback(currentVoxel, negZVoxel, material))
|
||||||
{
|
{
|
||||||
uint32_t v0 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
uint32_t v0 = addVertex(regX , regY , regZ , material, m_previousSliceVertices);
|
||||||
uint32_t v1 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) + 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
uint32_t v1 = addVertex(regX , regY + 1, regZ , material, m_previousSliceVertices);
|
||||||
uint32_t v2 = addVertex(static_cast<float>(regX) + 0.5f, static_cast<float>(regY) + 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
uint32_t v2 = addVertex(regX + 1, regY + 1, regZ , material, m_previousSliceVertices);
|
||||||
uint32_t v3 = addVertex(static_cast<float>(regX) + 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
uint32_t v3 = addVertex(regX + 1, regY , regZ , material, m_previousSliceVertices);
|
||||||
|
|
||||||
m_vecQuads[NegativeZ][regZ].push_back(Quad(v0, v1, v2, v3));
|
m_vecQuads[NegativeZ][regZ].push_back(Quad(v0, v1, v2, v3));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(m_funcIsQuadNeededCallback(negZVoxel, currentVoxel, material))
|
if(m_funcIsQuadNeededCallback(negZVoxel, currentVoxel, material))
|
||||||
{
|
{
|
||||||
uint32_t v0 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
uint32_t v0 = addVertex(regX , regY , regZ , material, m_previousSliceVertices);
|
||||||
uint32_t v1 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) + 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
uint32_t v1 = addVertex(regX , regY + 1, regZ , material, m_previousSliceVertices);
|
||||||
uint32_t v2 = addVertex(static_cast<float>(regX) + 0.5f, static_cast<float>(regY) + 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
uint32_t v2 = addVertex(regX + 1, regY + 1, regZ , material, m_previousSliceVertices);
|
||||||
uint32_t v3 = addVertex(static_cast<float>(regX) + 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
uint32_t v3 = addVertex(regX + 1, regY , regZ , material, m_previousSliceVertices);
|
||||||
|
|
||||||
m_vecQuads[PositiveZ][regZ].push_back(Quad(v0, v3, v2, v1));
|
m_vecQuads[PositiveZ][regZ].push_back(Quad(v0, v3, v2, v1));
|
||||||
}
|
}
|
||||||
@ -198,19 +198,16 @@ namespace PolyVox
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename VolumeType, typename IsQuadNeeded>
|
template<typename VolumeType, typename IsQuadNeeded>
|
||||||
int32_t CubicSurfaceExtractor<VolumeType, IsQuadNeeded>::addVertex(float fX, float fY, float fZ, typename VolumeType::VoxelType uMaterialIn, Array<3, IndexAndMaterial>& existingVertices)
|
int32_t CubicSurfaceExtractor<VolumeType, IsQuadNeeded>::addVertex(uint32_t uX, uint32_t uY, uint32_t uZ, typename VolumeType::VoxelType uMaterialIn, Array<3, IndexAndMaterial>& existingVertices)
|
||||||
{
|
{
|
||||||
uint32_t uX = static_cast<uint32_t>(fX + 0.75f);
|
|
||||||
uint32_t uY = static_cast<uint32_t>(fY + 0.75f);
|
|
||||||
|
|
||||||
for(uint32_t ct = 0; ct < MaxVerticesPerPosition; ct++)
|
for(uint32_t ct = 0; ct < MaxVerticesPerPosition; ct++)
|
||||||
{
|
{
|
||||||
IndexAndMaterial& rEntry = existingVertices[uX][uY][ct];
|
IndexAndMaterial& rEntry = existingVertices[uX][uY][ct];
|
||||||
|
|
||||||
if(rEntry.iIndex == -1)
|
if(rEntry.iIndex == -1)
|
||||||
{
|
{
|
||||||
//No vertices matched and we've now hit an empty space. Fill it by creating a vertex.
|
//No vertices matched and we've now hit an empty space. Fill it by creating a vertex. The 0.5f offset is because vertices set between voxels in order to build cubes around them.
|
||||||
rEntry.iIndex = m_meshCurrent->addVertex(PositionMaterial<typename VolumeType::VoxelType> (Vector3DFloat(fX, fY, fZ), uMaterialIn));
|
rEntry.iIndex = m_meshCurrent->addVertex(PositionMaterial<typename VolumeType::VoxelType> (Vector3DFloat(static_cast<float>(uX) - 0.5f, static_cast<float>(uY) - 0.5f, static_cast<float>(uZ) - 0.5f), uMaterialIn));
|
||||||
rEntry.uMaterial = uMaterialIn;
|
rEntry.uMaterial = uMaterialIn;
|
||||||
|
|
||||||
return rEntry.iIndex;
|
return rEntry.iIndex;
|
||||||
@ -298,4 +295,4 @@ namespace PolyVox
|
|||||||
//Quads cannot be merged.
|
//Quads cannot be merged.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,19 @@ freely, subject to the following restrictions:
|
|||||||
#define POLYVOX_HALT() std::exit(EXIT_FAILURE)
|
#define POLYVOX_HALT() std::exit(EXIT_FAILURE)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Macros cannot contain #ifdefs, but some of our macros need to disable warnings and such warning supression is
|
||||||
|
// platform specific. But macros can contain other macros, so we create macros to control the warnings and use
|
||||||
|
// those instead. This set of warning supression macros can be extended to GCC/Clang when required.
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#define POLYVOX_MSC_WARNING_PUSH __pragma(warning(push))
|
||||||
|
#define POLYVOX_DISABLE_MSC_WARNING(x) __pragma(warning(disable:x))
|
||||||
|
#define POLYVOX_MSC_WARNING_POP __pragma(warning(pop))
|
||||||
|
#else
|
||||||
|
#define POLYVOX_MSC_WARNING_PUSH
|
||||||
|
#define POLYVOX_DISABLE_MSC_WARNING(x)
|
||||||
|
#define POLYVOX_MSC_WARNING_POP
|
||||||
|
#endif
|
||||||
|
|
||||||
#define POLYVOX_UNUSED(x) do { (void)sizeof(x); } while(0)
|
#define POLYVOX_UNUSED(x) do { (void)sizeof(x); } while(0)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -49,6 +62,11 @@ freely, subject to the following restrictions:
|
|||||||
#ifdef POLYVOX_ASSERTS_ENABLED
|
#ifdef POLYVOX_ASSERTS_ENABLED
|
||||||
|
|
||||||
#define POLYVOX_ASSERT(condition, message) \
|
#define POLYVOX_ASSERT(condition, message) \
|
||||||
|
/* We use the do...while(0) construct in our macros (for reasons see here: http://stackoverflow.com/a/154138) \
|
||||||
|
but Visual Studio gives unhelpful 'conditional expression is constant' warnings. The recommended solution \
|
||||||
|
(http://stackoverflow.com/a/1946485) is to disable these warnings. */ \
|
||||||
|
POLYVOX_MSC_WARNING_PUSH \
|
||||||
|
POLYVOX_DISABLE_MSC_WARNING(4127) \
|
||||||
do \
|
do \
|
||||||
{ \
|
{ \
|
||||||
if (!(condition)) \
|
if (!(condition)) \
|
||||||
@ -61,12 +79,19 @@ freely, subject to the following restrictions:
|
|||||||
std::cerr << " Location: " << "Line " << __LINE__ << " of " << __FILE__ << std::endl << std::endl; \
|
std::cerr << " Location: " << "Line " << __LINE__ << " of " << __FILE__ << std::endl << std::endl; \
|
||||||
POLYVOX_HALT(); \
|
POLYVOX_HALT(); \
|
||||||
} \
|
} \
|
||||||
} while(0)
|
} while(0) \
|
||||||
|
POLYVOX_MSC_WARNING_POP
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#define POLYVOX_ASSERT(condition, message) \
|
#define POLYVOX_ASSERT(condition, message) \
|
||||||
do { POLYVOX_UNUSED(condition); POLYVOX_UNUSED(message); } while(0)
|
/* We use the do...while(0) construct in our macros (for reasons see here: http://stackoverflow.com/a/154138) \
|
||||||
|
but Visual Studio gives unhelpful 'conditional expression is constant' warnings. The recommended solution \
|
||||||
|
(http://stackoverflow.com/a/1946485) is to disable these warnings. */ \
|
||||||
|
POLYVOX_MSC_WARNING_PUSH \
|
||||||
|
POLYVOX_DISABLE_MSC_WARNING(4127) \
|
||||||
|
do { POLYVOX_UNUSED(condition); POLYVOX_UNUSED(message); } while(0) \
|
||||||
|
POLYVOX_MSC_WARNING_POP
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1,3 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* CHANGES FOR POLYVOX
|
||||||
|
* -------------------
|
||||||
|
* This file gave compiler warnings on certain versions of GCC (at least version 4.3.5 used by out build machine)
|
||||||
|
* and I did not want to risk tampering with the code to fix them.Therefore the only difference between this file
|
||||||
|
* and the official 'miniz.c' is the pragma below which disables warnings for this file in GCC.
|
||||||
|
*/
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#pragma GCC system_header
|
||||||
|
#endif
|
||||||
|
|
||||||
/* miniz.c v1.14 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing
|
/* miniz.c v1.14 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing
|
||||||
See "unlicense" statement at the end of this file.
|
See "unlicense" statement at the end of this file.
|
||||||
Rich Geldreich <richgel99@gmail.com>, last updated May 20, 2012
|
Rich Geldreich <richgel99@gmail.com>, last updated May 20, 2012
|
||||||
|
@ -5,15 +5,36 @@
|
|||||||
|
|
||||||
namespace PolyVox
|
namespace PolyVox
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Performs compression of data using the miniz library.
|
||||||
|
*
|
||||||
|
* This compressor implements the DEFLATE (http://en.wikipedia.org/wiki/Deflate) compression algorithm via the pubic domain
|
||||||
|
* 'miniz' library (https://code.google.com/p/miniz/). This is a general purpose compression algorithm, and within PolyVox it
|
||||||
|
* is intended for situations in which the alternative RLECompressor is not appropriate. It is a good default choice if you
|
||||||
|
* are not sure which compressor is best for your needs.
|
||||||
|
*
|
||||||
|
* \sa RLECompressor
|
||||||
|
*/
|
||||||
class MinizCompressor : public Compressor
|
class MinizCompressor : public Compressor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MinizCompressor();
|
/// Constructor
|
||||||
|
MinizCompressor(int iCompressionLevel = 6); // Miniz defines MZ_DEFAULT_LEVEL = 6 so we use the same here
|
||||||
|
/// Destructor
|
||||||
~MinizCompressor();
|
~MinizCompressor();
|
||||||
|
|
||||||
|
// API documentation is in base class and gets inherited by Doxygen.
|
||||||
uint32_t getMaxCompressedSize(uint32_t uUncompressedInputSize);
|
uint32_t getMaxCompressedSize(uint32_t uUncompressedInputSize);
|
||||||
uint32_t compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength);
|
uint32_t compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength);
|
||||||
uint32_t decompress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength);
|
uint32_t decompress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength);
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned int m_uCompressionFlags;
|
||||||
|
|
||||||
|
// tdefl_compressor contains all the state needed by the low-level compressor so it's a pretty big struct (~300k).
|
||||||
|
// We're storing it by void* because miniz does not supply a header and we don't want to include the .c file from
|
||||||
|
// here as it will cause linker problems.
|
||||||
|
void* m_pDeflator;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,15 @@
|
|||||||
|
|
||||||
namespace PolyVox
|
namespace PolyVox
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Performs compression of data using Run Length Encoding (RLE).
|
||||||
|
*
|
||||||
|
* This compressor is designed for voxel data which contains long runs of the same value. Minecraft-style terrain and other
|
||||||
|
* cubic-style terrains are likely to fall under this category, whereas density fields for Marching Cubes terrain will not. Please
|
||||||
|
* see the following article if you want more details of how RLE compression works: http://en.wikipedia.org/wiki/Run-length_encoding
|
||||||
|
*
|
||||||
|
* \sa MinizCompressor
|
||||||
|
*/
|
||||||
template<typename ValueType, typename LengthType>
|
template<typename ValueType, typename LengthType>
|
||||||
class RLECompressor : public Compressor
|
class RLECompressor : public Compressor
|
||||||
{
|
{
|
||||||
@ -14,9 +23,12 @@ namespace PolyVox
|
|||||||
LengthType length;
|
LengthType length;
|
||||||
};
|
};
|
||||||
public:
|
public:
|
||||||
|
/// Constructor
|
||||||
RLECompressor();
|
RLECompressor();
|
||||||
|
/// Destructor
|
||||||
~RLECompressor();
|
~RLECompressor();
|
||||||
|
|
||||||
|
// API documentation is in base class and gets inherited by Doxygen.
|
||||||
uint32_t getMaxCompressedSize(uint32_t uUncompressedInputSize);
|
uint32_t getMaxCompressedSize(uint32_t uUncompressedInputSize);
|
||||||
uint32_t compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength);
|
uint32_t compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength);
|
||||||
uint32_t decompress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength);
|
uint32_t decompress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength);
|
||||||
|
@ -1,68 +1,141 @@
|
|||||||
#include "PolyVoxCore/MinizCompressor.h"
|
#include "PolyVoxCore/MinizCompressor.h"
|
||||||
|
|
||||||
|
#include "PolyVoxCore/Impl/Utility.h"
|
||||||
|
|
||||||
// Diable things we don't need, and in particular the zlib compatible names which
|
// Diable things we don't need, and in particular the zlib compatible names which
|
||||||
// would cause conflicts if a user application is using both PolyVox and zlib.
|
// would cause conflicts if a user application is using both PolyVox and zlib.
|
||||||
#define MINIZ_NO_STDIO
|
#define MINIZ_NO_STDIO
|
||||||
#define MINIZ_NO_ARCHIVE_APIS
|
#define MINIZ_NO_ARCHIVE_APIS
|
||||||
#define MINIZ_NO_TIME
|
#define MINIZ_NO_TIME
|
||||||
//#define MINIZ_NO_ZLIB_APIS
|
#define MINIZ_NO_ZLIB_APIS
|
||||||
#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
|
#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
|
||||||
//#define MINIZ_NO_MALLOC
|
#define MINIZ_NO_MALLOC
|
||||||
|
|
||||||
#include "PolyVoxCore/Impl/ErrorHandling.h"
|
#include "PolyVoxCore/Impl/ErrorHandling.h"
|
||||||
// For some unknown reason the miniz library is supplied only as a
|
|
||||||
// single .c file without a header. Apparently the only way to use
|
// The miniz library is supplied only as a single .c file without a header. The examples just include the .c file
|
||||||
// it is then to #include it directly which is what the examples do.
|
// directly which is also what we do here. Actually is is possible to define 'MINIZ_HEADER_FILE_ONLY' to treat
|
||||||
|
// the .c file as a header, but this seems messy in terms of our project and CMake as we keep the headers and source
|
||||||
|
// files in seperate folders. We could create our own header for miniz (based on the stuff between the MINIZ_HEADER_FILE_ONLY
|
||||||
|
// directives) but the other problem is that we are using #pragma GCC system_header to supress warnings which would
|
||||||
|
// then be in the .c part of the code. If we ever update GCC on the CDash machine so that it properly supports '#pragma
|
||||||
|
// GCC diagnosic ignored' (or so that it doesn't warn in the first place) then we can reconsider spliting miniz.c in two.
|
||||||
#include "PolyVoxCore/Impl/miniz.c"
|
#include "PolyVoxCore/Impl/miniz.c"
|
||||||
|
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace PolyVox
|
namespace PolyVox
|
||||||
{
|
{
|
||||||
MinizCompressor::MinizCompressor()
|
/**
|
||||||
|
* You can specify a compression level when constructing this compressor. This controls the tradeoff between speed and compression
|
||||||
|
* rate. Levels 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow).
|
||||||
|
* \param iCompressionLevel The desired compression level.
|
||||||
|
*/
|
||||||
|
MinizCompressor::MinizCompressor(int iCompressionLevel)
|
||||||
|
:m_pDeflator(0)
|
||||||
{
|
{
|
||||||
|
// Create and store the deflator.
|
||||||
|
tdefl_compressor* pDeflator = new tdefl_compressor;
|
||||||
|
m_pDeflator = reinterpret_cast<void*>(pDeflator);
|
||||||
|
|
||||||
|
// The number of dictionary probes to use at each compression level (0-10). 0=implies fastest/minimal possible probing.
|
||||||
|
// The discontinuity is unsettling but may be explained by the 'iCompressionLevel <= 3' check later?
|
||||||
|
static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 };
|
||||||
|
|
||||||
|
// Create tdefl() compatible flags (we have to compose the low-level flags ourselves, or use tdefl_create_comp_flags_from_zip_params() but that means MINIZ_NO_ZLIB_APIS can't be defined).
|
||||||
|
m_uCompressionFlags = TDEFL_WRITE_ZLIB_HEADER | s_tdefl_num_probes[MZ_MIN(10, iCompressionLevel)] | ((iCompressionLevel <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0);
|
||||||
|
if (!iCompressionLevel)
|
||||||
|
{
|
||||||
|
m_uCompressionFlags |= TDEFL_FORCE_ALL_RAW_BLOCKS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MinizCompressor::~MinizCompressor()
|
MinizCompressor::~MinizCompressor()
|
||||||
{
|
{
|
||||||
|
// Delete the deflator
|
||||||
|
tdefl_compressor* pDeflator = reinterpret_cast<tdefl_compressor*>(m_pDeflator);
|
||||||
|
delete pDeflator;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t MinizCompressor::getMaxCompressedSize(uint32_t uUncompressedInputSize)
|
uint32_t MinizCompressor::getMaxCompressedSize(uint32_t uUncompressedInputSize)
|
||||||
{
|
{
|
||||||
return static_cast<uint32_t>(mz_compressBound(static_cast<mz_ulong>(uUncompressedInputSize)));
|
// The contents of this function are copied from miniz's 'mz_deflateBound()'
|
||||||
|
// (which we can't use because it is part of the zlib-style higher level API).
|
||||||
|
unsigned long source_len = uUncompressedInputSize;
|
||||||
|
|
||||||
|
// This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.)
|
||||||
|
return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t MinizCompressor::compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength)
|
uint32_t MinizCompressor::compress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength)
|
||||||
{
|
{
|
||||||
mz_ulong ulDstLength = uDstLength;
|
//Get the deflator
|
||||||
|
tdefl_compressor* pDeflator = reinterpret_cast<tdefl_compressor*>(m_pDeflator);
|
||||||
|
|
||||||
// Do the compression
|
// It seems we have to reinitialise the deflator for each fresh dataset (it's probably intended for streaming, which we're not doing here)
|
||||||
int result = mz_compress((unsigned char*)pDstData, &ulDstLength, (const unsigned char*) pSrcData, uSrcLength);
|
tdefl_status status = tdefl_init(pDeflator, NULL, NULL, m_uCompressionFlags);
|
||||||
if(result != MZ_OK)
|
if (status != TDEFL_STATUS_OKAY)
|
||||||
{
|
{
|
||||||
stringstream ss;
|
stringstream ss;
|
||||||
ss << "mz_compress() failed with return code '" << result << "'";
|
ss << "tdefl_init() failed with return code '" << status << "'";
|
||||||
POLYVOX_THROW(std::runtime_error, ss.str());
|
POLYVOX_THROW(std::runtime_error, ss.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the number of bytes written to the output.
|
// Change the type to avoid compiler warnings
|
||||||
|
size_t ulSrcLength = uSrcLength;
|
||||||
|
size_t ulDstLength = uDstLength;
|
||||||
|
|
||||||
|
// Compress as much of the input as possible (or all of it) to the output buffer.
|
||||||
|
status = tdefl_compress(pDeflator, pSrcData, &ulSrcLength, pDstData, &ulDstLength, TDEFL_FINISH);
|
||||||
|
|
||||||
|
//Check whther the compression was successful.
|
||||||
|
if (status != TDEFL_STATUS_DONE)
|
||||||
|
{
|
||||||
|
stringstream ss;
|
||||||
|
ss << "tdefl_compress() failed with return code '" << status << "'";
|
||||||
|
POLYVOX_THROW(std::runtime_error, ss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// The compression modifies 'ulDstLength' to hold the new length.
|
||||||
return ulDstLength;
|
return ulDstLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t MinizCompressor::decompress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength)
|
uint32_t MinizCompressor::decompress(void* pSrcData, uint32_t uSrcLength, void* pDstData, uint32_t uDstLength)
|
||||||
{
|
{
|
||||||
mz_ulong ulDstLength = uDstLength;
|
// I don't know exactly why this limitation exists but it's an implementation detail of miniz. It shouldn't matter for our purposes
|
||||||
|
// as our detination is a Block and those are always a power of two. If you need to use this class for other purposes then you'll
|
||||||
|
// probably have to scale up your dst buffer to the nearest appropriate size. Alternatively you can use the mz_uncompress function,
|
||||||
|
// but that means enabling parts of the miniz API which are #defined out at the top of this file.
|
||||||
|
POLYVOX_ASSERT(isPowerOf2(uDstLength), "Miniz decompressor requires the destination buffer to have a size which is a power of two.");
|
||||||
|
if(isPowerOf2(uDstLength) == false)
|
||||||
|
{
|
||||||
|
POLYVOX_THROW(std::invalid_argument, "Miniz decompressor requires the destination buffer to have a size which is a power of two.");
|
||||||
|
}
|
||||||
|
|
||||||
int result = mz_uncompress((unsigned char*) pDstData, &ulDstLength, (const unsigned char*) pSrcData, uSrcLength);
|
// Change the type to avoid compiler warnings
|
||||||
if(result != MZ_OK)
|
size_t ulSrcLength = uSrcLength;
|
||||||
|
size_t ulDstLength = uDstLength;
|
||||||
|
|
||||||
|
// Create and initialise the decompressor (I believe this is much small than the compressor).
|
||||||
|
tinfl_decompressor inflator;
|
||||||
|
tinfl_init(&inflator);
|
||||||
|
|
||||||
|
// Do the decompression. In some scenarios 'tinfl_decompress' would be called multiple times with the same dest buffer but
|
||||||
|
// different locations within it. In our scenario it's only called once so the start and the location are the same (both pDstData).
|
||||||
|
tinfl_status status = tinfl_decompress(&inflator, (const mz_uint8 *)pSrcData, &ulSrcLength, (mz_uint8 *)pDstData, (mz_uint8 *)pDstData, &ulDstLength, TINFL_FLAG_PARSE_ZLIB_HEADER);
|
||||||
|
|
||||||
|
//Check whther the decompression was successful.
|
||||||
|
if (status != TINFL_STATUS_DONE)
|
||||||
{
|
{
|
||||||
stringstream ss;
|
stringstream ss;
|
||||||
ss << "mz_uncompress() failed with return code '" << result << "'";
|
ss << "tinfl_decompress() failed with return code '" << status << "'";
|
||||||
POLYVOX_THROW(std::runtime_error, ss.str());
|
POLYVOX_THROW(std::runtime_error, ss.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The decompression modifies 'ulDstLength' to hold the new length.
|
||||||
return ulDstLength;
|
return ulDstLength;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user