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:
David Williams 2013-03-04 21:05:51 +01:00
commit fcdb175f1c
10 changed files with 250 additions and 58 deletions

View File

@ -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.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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