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 line was added just for testing.
|
||||
|
||||
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.
|
||||
|
@ -153,7 +153,7 @@ SET_PROPERTY(TARGET PolyVoxCore PROPERTY FOLDER "Library")
|
||||
|
||||
SET_TARGET_PROPERTIES(PolyVoxCore PROPERTIES VERSION ${POLYVOX_VERSION} SOVERSION ${POLYVOX_VERSION_MAJOR})
|
||||
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)
|
||||
|
||||
#Install
|
||||
|
@ -28,20 +28,71 @@ freely, subject to the following restrictions:
|
||||
|
||||
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
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
Compressor() {};
|
||||
/// Destructor
|
||||
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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
};
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ namespace PolyVox
|
||||
void execute();
|
||||
|
||||
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 mergeQuads(Quad& q1, Quad& q2);
|
||||
|
||||
|
@ -94,20 +94,20 @@ namespace PolyVox
|
||||
// X
|
||||
if(m_funcIsQuadNeededCallback(currentVoxel, negXVoxel, material))
|
||||
{
|
||||
uint32_t v0 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
||||
uint32_t v1 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) + 0.5f, material, m_currentSliceVertices);
|
||||
uint32_t v2 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) + 0.5f, static_cast<float>(regZ) + 0.5f, material, m_currentSliceVertices);
|
||||
uint32_t v3 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) + 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
||||
uint32_t v0 = addVertex(regX , regY , regZ , material, m_previousSliceVertices);
|
||||
uint32_t v1 = addVertex(regX , regY , regZ + 1, material, m_currentSliceVertices);
|
||||
uint32_t v2 = addVertex(regX , regY + 1, regZ + 1, material, m_currentSliceVertices);
|
||||
uint32_t v3 = addVertex(regX , regY + 1, regZ , material, m_previousSliceVertices);
|
||||
|
||||
m_vecQuads[NegativeX][regX].push_back(Quad(v0, v1, v2, v3));
|
||||
}
|
||||
|
||||
if(m_funcIsQuadNeededCallback(negXVoxel, currentVoxel, material))
|
||||
{
|
||||
uint32_t v0 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
||||
uint32_t v1 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) + 0.5f, material, m_currentSliceVertices);
|
||||
uint32_t v2 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) + 0.5f, static_cast<float>(regZ) + 0.5f, material, m_currentSliceVertices);
|
||||
uint32_t v3 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) + 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
||||
uint32_t v0 = addVertex(regX , regY , regZ , material, m_previousSliceVertices);
|
||||
uint32_t v1 = addVertex(regX , regY , regZ + 1, material, m_currentSliceVertices);
|
||||
uint32_t v2 = addVertex(regX , regY + 1, regZ + 1, material, m_currentSliceVertices);
|
||||
uint32_t v3 = addVertex(regX , regY + 1, regZ , material, m_previousSliceVertices);
|
||||
|
||||
m_vecQuads[PositiveX][regX].push_back(Quad(v0, v3, v2, v1));
|
||||
}
|
||||
@ -115,20 +115,20 @@ namespace PolyVox
|
||||
// Y
|
||||
if(m_funcIsQuadNeededCallback(currentVoxel, negYVoxel, material))
|
||||
{
|
||||
uint32_t v0 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
||||
uint32_t v1 = addVertex(static_cast<float>(regX) + 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
||||
uint32_t v2 = addVertex(static_cast<float>(regX) + 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) + 0.5f, material, m_currentSliceVertices);
|
||||
uint32_t v3 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) + 0.5f, material, m_currentSliceVertices);
|
||||
uint32_t v0 = addVertex(regX , regY , regZ , material, m_previousSliceVertices);
|
||||
uint32_t v1 = addVertex(regX + 1, regY , regZ , material, m_previousSliceVertices);
|
||||
uint32_t v2 = addVertex(regX + 1, regY , regZ + 1, 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));
|
||||
}
|
||||
|
||||
if(m_funcIsQuadNeededCallback(negYVoxel, currentVoxel, material))
|
||||
{
|
||||
uint32_t v0 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
||||
uint32_t v1 = addVertex(static_cast<float>(regX) + 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
||||
uint32_t v2 = addVertex(static_cast<float>(regX) + 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) + 0.5f, material, m_currentSliceVertices);
|
||||
uint32_t v3 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) + 0.5f, material, m_currentSliceVertices);
|
||||
uint32_t v0 = addVertex(regX , regY , regZ , material, m_previousSliceVertices);
|
||||
uint32_t v1 = addVertex(regX + 1, regY , regZ , material, m_previousSliceVertices);
|
||||
uint32_t v2 = addVertex(regX + 1, regY , regZ + 1, 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));
|
||||
}
|
||||
@ -136,20 +136,20 @@ namespace PolyVox
|
||||
// Z
|
||||
if(m_funcIsQuadNeededCallback(currentVoxel, negZVoxel, material))
|
||||
{
|
||||
uint32_t v0 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
||||
uint32_t v1 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) + 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
||||
uint32_t v2 = addVertex(static_cast<float>(regX) + 0.5f, static_cast<float>(regY) + 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
||||
uint32_t v3 = addVertex(static_cast<float>(regX) + 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
||||
uint32_t v0 = addVertex(regX , regY , regZ , material, m_previousSliceVertices);
|
||||
uint32_t v1 = addVertex(regX , regY + 1, regZ , material, m_previousSliceVertices);
|
||||
uint32_t v2 = addVertex(regX + 1, regY + 1, regZ , 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));
|
||||
}
|
||||
|
||||
if(m_funcIsQuadNeededCallback(negZVoxel, currentVoxel, material))
|
||||
{
|
||||
uint32_t v0 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
||||
uint32_t v1 = addVertex(static_cast<float>(regX) - 0.5f, static_cast<float>(regY) + 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
||||
uint32_t v2 = addVertex(static_cast<float>(regX) + 0.5f, static_cast<float>(regY) + 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
||||
uint32_t v3 = addVertex(static_cast<float>(regX) + 0.5f, static_cast<float>(regY) - 0.5f, static_cast<float>(regZ) - 0.5f, material, m_previousSliceVertices);
|
||||
uint32_t v0 = addVertex(regX , regY , regZ , material, m_previousSliceVertices);
|
||||
uint32_t v1 = addVertex(regX , regY + 1, regZ , material, m_previousSliceVertices);
|
||||
uint32_t v2 = addVertex(regX + 1, regY + 1, regZ , 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));
|
||||
}
|
||||
@ -198,19 +198,16 @@ namespace PolyVox
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
IndexAndMaterial& rEntry = existingVertices[uX][uY][ct];
|
||||
|
||||
if(rEntry.iIndex == -1)
|
||||
{
|
||||
//No vertices matched and we've now hit an empty space. Fill it by creating a vertex.
|
||||
rEntry.iIndex = m_meshCurrent->addVertex(PositionMaterial<typename VolumeType::VoxelType> (Vector3DFloat(fX, fY, fZ), uMaterialIn));
|
||||
//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(static_cast<float>(uX) - 0.5f, static_cast<float>(uY) - 0.5f, static_cast<float>(uZ) - 0.5f), uMaterialIn));
|
||||
rEntry.uMaterial = uMaterialIn;
|
||||
|
||||
return rEntry.iIndex;
|
||||
@ -298,4 +295,4 @@ namespace PolyVox
|
||||
//Quads cannot be merged.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,19 @@ freely, subject to the following restrictions:
|
||||
#define POLYVOX_HALT() std::exit(EXIT_FAILURE)
|
||||
#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)
|
||||
|
||||
/*
|
||||
@ -49,6 +62,11 @@ freely, subject to the following restrictions:
|
||||
#ifdef POLYVOX_ASSERTS_ENABLED
|
||||
|
||||
#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 \
|
||||
{ \
|
||||
if (!(condition)) \
|
||||
@ -61,12 +79,19 @@ freely, subject to the following restrictions:
|
||||
std::cerr << " Location: " << "Line " << __LINE__ << " of " << __FILE__ << std::endl << std::endl; \
|
||||
POLYVOX_HALT(); \
|
||||
} \
|
||||
} while(0)
|
||||
} while(0) \
|
||||
POLYVOX_MSC_WARNING_POP
|
||||
|
||||
#else
|
||||
|
||||
#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
|
||||
|
||||
|
@ -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
|
||||
See "unlicense" statement at the end of this file.
|
||||
Rich Geldreich <richgel99@gmail.com>, last updated May 20, 2012
|
||||
|
@ -5,15 +5,36 @@
|
||||
|
||||
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
|
||||
{
|
||||
public:
|
||||
MinizCompressor();
|
||||
/// Constructor
|
||||
MinizCompressor(int iCompressionLevel = 6); // Miniz defines MZ_DEFAULT_LEVEL = 6 so we use the same here
|
||||
/// Destructor
|
||||
~MinizCompressor();
|
||||
|
||||
|
||||
// API documentation is in base class and gets inherited by Doxygen.
|
||||
uint32_t getMaxCompressedSize(uint32_t uUncompressedInputSize);
|
||||
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);
|
||||
|
||||
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
|
||||
{
|
||||
/**
|
||||
* 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>
|
||||
class RLECompressor : public Compressor
|
||||
{
|
||||
@ -14,9 +23,12 @@ namespace PolyVox
|
||||
LengthType length;
|
||||
};
|
||||
public:
|
||||
/// Constructor
|
||||
RLECompressor();
|
||||
/// Destructor
|
||||
~RLECompressor();
|
||||
|
||||
// API documentation is in base class and gets inherited by Doxygen.
|
||||
uint32_t getMaxCompressedSize(uint32_t uUncompressedInputSize);
|
||||
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);
|
||||
|
@ -1,68 +1,141 @@
|
||||
#include "PolyVoxCore/MinizCompressor.h"
|
||||
|
||||
#include "PolyVoxCore/Impl/Utility.h"
|
||||
|
||||
// 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.
|
||||
#define MINIZ_NO_STDIO
|
||||
#define MINIZ_NO_ARCHIVE_APIS
|
||||
#define MINIZ_NO_TIME
|
||||
//#define MINIZ_NO_ZLIB_APIS
|
||||
#define MINIZ_NO_ZLIB_APIS
|
||||
#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
|
||||
//#define MINIZ_NO_MALLOC
|
||||
#define MINIZ_NO_MALLOC
|
||||
|
||||
#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
|
||||
// it is then to #include it directly which is what the examples do.
|
||||
|
||||
// The miniz library is supplied only as a single .c file without a header. The examples just include the .c file
|
||||
// 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 <sstream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
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()
|
||||
{
|
||||
// Delete the deflator
|
||||
tdefl_compressor* pDeflator = reinterpret_cast<tdefl_compressor*>(m_pDeflator);
|
||||
delete pDeflator;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
mz_ulong ulDstLength = uDstLength;
|
||||
//Get the deflator
|
||||
tdefl_compressor* pDeflator = reinterpret_cast<tdefl_compressor*>(m_pDeflator);
|
||||
|
||||
// Do the compression
|
||||
int result = mz_compress((unsigned char*)pDstData, &ulDstLength, (const unsigned char*) pSrcData, uSrcLength);
|
||||
if(result != MZ_OK)
|
||||
// It seems we have to reinitialise the deflator for each fresh dataset (it's probably intended for streaming, which we're not doing here)
|
||||
tdefl_status status = tdefl_init(pDeflator, NULL, NULL, m_uCompressionFlags);
|
||||
if (status != TDEFL_STATUS_OKAY)
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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);
|
||||
if(result != MZ_OK)
|
||||
// Change the type to avoid compiler warnings
|
||||
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;
|
||||
ss << "mz_uncompress() failed with return code '" << result << "'";
|
||||
ss << "tinfl_decompress() failed with return code '" << status << "'";
|
||||
POLYVOX_THROW(std::runtime_error, ss.str());
|
||||
}
|
||||
|
||||
// The decompression modifies 'ulDstLength' to hold the new length.
|
||||
return ulDstLength;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user