From 032accd9151a46ceae160f5b92fd2891b58f5fed Mon Sep 17 00:00:00 2001 From: David Williams Date: Fri, 29 Apr 2011 22:43:44 +0100 Subject: [PATCH] Moved Block into SimpleVolume class. --- library/PolyVoxCore/include/SimpleVolume.h | 52 ++++- library/PolyVoxCore/include/SimpleVolume.inl | 6 +- .../PolyVoxCore/include/SimpleVolumeBlock.inl | 214 ++++++++++++++++++ 3 files changed, 262 insertions(+), 10 deletions(-) create mode 100644 library/PolyVoxCore/include/SimpleVolumeBlock.inl diff --git a/library/PolyVoxCore/include/SimpleVolume.h b/library/PolyVoxCore/include/SimpleVolume.h index 08ee5615..eb468533 100644 --- a/library/PolyVoxCore/include/SimpleVolume.h +++ b/library/PolyVoxCore/include/SimpleVolume.h @@ -24,7 +24,6 @@ freely, subject to the following restrictions: #ifndef __PolyVox_SimpleVolume_H__ #define __PolyVox_SimpleVolume_H__ -#include "PolyVoxImpl/Block.h" #include "Region.h" #include "PolyVoxForwardDeclarations.h" @@ -40,6 +39,47 @@ namespace PolyVox class SimpleVolume { public: + class Block + { + template + struct RunlengthEntry + { + LengthType length; + VoxelType value; + + //We can parametise the length on anything up to uint32_t. + //This lets us experiment with the optimal size in the future. + static uint32_t maxRunlength(void) {return (std::numeric_limits::max)();} + }; + + //Make Sampler a friend + friend class LargeVolume::Sampler; + public: + Block(uint16_t uSideLength = 0); + + uint16_t getSideLength(void) const; + VoxelType getVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos) const; + VoxelType getVoxelAt(const Vector3DUint16& v3dPos) const; + + void setVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos, VoxelType tValue); + void setVoxelAt(const Vector3DUint16& v3dPos, VoxelType tValue); + + void fill(VoxelType tValue); + void initialise(uint16_t uSideLength); + uint32_t calculateSizeInBytes(void); + + public: + void compress(void); + void uncompress(void); + + std::vector< RunlengthEntry > m_vecCompressedData; + VoxelType* m_tUncompressedData; + uint16_t m_uSideLength; + uint8_t m_uSideLengthPower; + bool m_bIsCompressed; + bool m_bIsUncompressedDataModified; + }; + class Sampler { public: @@ -110,9 +150,6 @@ namespace PolyVox VoxelType* mCurrentVoxel; }; - // Make the ConstVolumeProxy a friend - friend class ConstVolumeProxy; - struct LoadedBlock { public: @@ -122,7 +159,7 @@ namespace PolyVox { } - Block block; + Block block; uint32_t timestamp; }; @@ -194,7 +231,7 @@ private: /// is absolutely unsafe polyvox_function&, const Region&)> m_funcDataOverflowHandler; - Block* getUncompressedBlock(int32_t uBlockX, int32_t uBlockY, int32_t uBlockZ) const; + Block* getUncompressedBlock(int32_t uBlockX, int32_t uBlockY, int32_t uBlockZ) const; void eraseBlock(typename std::map::iterator itBlock) const; /// this function can be called by m_funcDataRequiredHandler without causing any weird effects bool setVoxelAtConst(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue) const; @@ -209,7 +246,7 @@ private: mutable std::vector< LoadedBlock* > m_vecUncompressedBlockCache; mutable uint32_t m_uTimestamper; mutable Vector3DInt32 m_v3dLastAccessedBlockPos; - mutable Block* m_pLastAccessedBlock; + mutable Block* m_pLastAccessedBlock; uint32_t m_uMaxNumberOfUncompressedBlocks; //We don't store an actual Block for the border, just the uncompressed data. This is partly because the border @@ -235,6 +272,7 @@ private: }; } +#include "SimpleVolumeBlock.inl" #include "SimpleVolume.inl" #include "SimpleVolumeSampler.inl" diff --git a/library/PolyVoxCore/include/SimpleVolume.inl b/library/PolyVoxCore/include/SimpleVolume.inl index 4bfc66f2..159b258f 100644 --- a/library/PolyVoxCore/include/SimpleVolume.inl +++ b/library/PolyVoxCore/include/SimpleVolume.inl @@ -187,7 +187,7 @@ namespace PolyVox const uint16_t yOffset = uYPos - (blockY << m_uBlockSideLengthPower); const uint16_t zOffset = uZPos - (blockZ << m_uBlockSideLengthPower); - Block* pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ); + SimpleVolume::Block* pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ); return pUncompressedBlock->getVoxelAt(xOffset,yOffset,zOffset); } @@ -276,7 +276,7 @@ namespace PolyVox const uint16_t yOffset = uYPos - (blockY << m_uBlockSideLengthPower); const uint16_t zOffset = uZPos - (blockZ << m_uBlockSideLengthPower); - Block* pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ); + SimpleVolume::Block* pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ); pUncompressedBlock->setVoxelAt(xOffset,yOffset,zOffset, tValue); @@ -420,7 +420,7 @@ namespace PolyVox template - Block* SimpleVolume::getUncompressedBlock(int32_t uBlockX, int32_t uBlockY, int32_t uBlockZ) const + typename SimpleVolume::Block* SimpleVolume::getUncompressedBlock(int32_t uBlockX, int32_t uBlockY, int32_t uBlockZ) const { Vector3DInt32 v3dBlockPos(uBlockX, uBlockY, uBlockZ); diff --git a/library/PolyVoxCore/include/SimpleVolumeBlock.inl b/library/PolyVoxCore/include/SimpleVolumeBlock.inl new file mode 100644 index 00000000..753518a6 --- /dev/null +++ b/library/PolyVoxCore/include/SimpleVolumeBlock.inl @@ -0,0 +1,214 @@ +/******************************************************************************* +Copyright (c) 2005-2009 David Williams + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*******************************************************************************/ + +#include "PolyVoxImpl/Utility.h" +#include "Vector.h" + +#include +#include //For memcpy +#include +#include //for std::invalid_argument + +namespace PolyVox +{ + template + SimpleVolume::Block::Block(uint16_t uSideLength) + :m_uSideLength(0) + ,m_uSideLengthPower(0) + ,m_tUncompressedData(0) + ,m_bIsCompressed(true) + ,m_bIsUncompressedDataModified(true) + { + if(uSideLength != 0) + { + initialise(uSideLength); + } + } + + template + uint16_t SimpleVolume::Block::getSideLength(void) const + { + return m_uSideLength; + } + + template + VoxelType SimpleVolume::Block::getVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos) const + { + assert(uXPos < m_uSideLength); + assert(uYPos < m_uSideLength); + assert(uZPos < m_uSideLength); + + assert(m_tUncompressedData); + + return m_tUncompressedData + [ + uXPos + + uYPos * m_uSideLength + + uZPos * m_uSideLength * m_uSideLength + ]; + } + + template + VoxelType SimpleVolume::Block::getVoxelAt(const Vector3DUint16& v3dPos) const + { + return getVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ()); + } + + template + void SimpleVolume::Block::setVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos, VoxelType tValue) + { + assert(uXPos < m_uSideLength); + assert(uYPos < m_uSideLength); + assert(uZPos < m_uSideLength); + + assert(m_tUncompressedData); + + m_tUncompressedData + [ + uXPos + + uYPos * m_uSideLength + + uZPos * m_uSideLength * m_uSideLength + ] = tValue; + + m_bIsUncompressedDataModified = true; + } + + template + void SimpleVolume::Block::setVoxelAt(const Vector3DUint16& v3dPos, VoxelType tValue) + { + setVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ(), tValue); + } + + template + void SimpleVolume::Block::fill(VoxelType tValue) + { + if(!m_bIsCompressed) + { + //The memset *may* be faster than the std::fill(), but it doesn't compile nicely + //in 64-bit mode as casting the pointer to an int causes a loss of precision. + const uint32_t uNoOfVoxels = m_uSideLength * m_uSideLength * m_uSideLength; + std::fill(m_tUncompressedData, m_tUncompressedData + uNoOfVoxels, tValue); + + m_bIsUncompressedDataModified = true; + } + else + { + RunlengthEntry rle; + rle.length = m_uSideLength*m_uSideLength*m_uSideLength; + rle.value = tValue; + m_vecCompressedData.clear(); + m_vecCompressedData.push_back(rle); + } + } + + template + void SimpleVolume::Block::initialise(uint16_t uSideLength) + { + //Debug mode validation + assert(isPowerOf2(uSideLength)); + + //Release mode validation + if(!isPowerOf2(uSideLength)) + { + throw std::invalid_argument("Block side length must be a power of two."); + } + + //Compute the side length + m_uSideLength = uSideLength; + m_uSideLengthPower = logBase2(uSideLength); + + SimpleVolume::Block::fill(VoxelType()); + } + + template + uint32_t SimpleVolume::Block::calculateSizeInBytes(void) + { + uint32_t uSizeInBytes = sizeof(Block); + uSizeInBytes += m_vecCompressedData.capacity() * sizeof(RunlengthEntry); + return uSizeInBytes; + } + + template + void SimpleVolume::Block::compress(void) + { + assert(m_bIsCompressed == false); + assert(m_tUncompressedData != 0); + + //If the uncompressed data hasn't actually been + //modified then we don't need to redo the compression. + if(m_bIsUncompressedDataModified) + { + uint32_t uNoOfVoxels = m_uSideLength * m_uSideLength * m_uSideLength; + m_vecCompressedData.clear(); + + RunlengthEntry entry; + entry.length = 1; + entry.value = m_tUncompressedData[0]; + + for(uint32_t ct = 1; ct < uNoOfVoxels; ++ct) + { + VoxelType value = m_tUncompressedData[ct]; + if((value == entry.value) && (entry.length < entry.maxRunlength())) + { + entry.length++; + } + else + { + m_vecCompressedData.push_back(entry); + entry.value = value; + entry.length = 1; + } + } + + m_vecCompressedData.push_back(entry); + + //Shrink the vectors to their contents (maybe slow?): + //http://stackoverflow.com/questions/1111078/reduce-the-capacity-of-an-stl-vector + //C++0x may have a shrink_to_fit() function? + std::vector< RunlengthEntry >(m_vecCompressedData).swap(m_vecCompressedData); + } + + //Flag the uncompressed data as no longer being used. + delete[] m_tUncompressedData; + m_tUncompressedData = 0; + m_bIsCompressed = true; + } + + template + void SimpleVolume::Block::uncompress(void) + { + assert(m_bIsCompressed == true); + assert(m_tUncompressedData == 0); + m_tUncompressedData = new VoxelType[m_uSideLength * m_uSideLength * m_uSideLength]; + + VoxelType* pUncompressedData = m_tUncompressedData; + for(uint32_t ct = 0; ct < m_vecCompressedData.size(); ++ct) + { + std::fill(pUncompressedData, pUncompressedData + m_vecCompressedData[ct].length, m_vecCompressedData[ct].value); + pUncompressedData += m_vecCompressedData[ct].length; + } + + m_bIsCompressed = false; + m_bIsUncompressedDataModified = false; + } +}