diff --git a/library/PolyVoxCore/include/Volume.h b/library/PolyVoxCore/include/Volume.h index dd6dd34a..6f18ec0a 100644 --- a/library/PolyVoxCore/include/Volume.h +++ b/library/PolyVoxCore/include/Volume.h @@ -156,6 +156,8 @@ namespace PolyVox ///Sets the voxel at a 3D vector position bool setVoxelAt(const Vector3DUint16& v3dPos, VoxelType tValue); + ///Resises the volume to the specified dimensions + void resize(uint16_t uWidth, uint16_t uHeight, uint16_t uDepth, uint16_t uBlockSideLength = 32); void tidyUpMemory(uint32_t uNoOfBlocksToProcess = (std::numeric_limits::max)()); private: diff --git a/library/PolyVoxCore/include/Volume.inl b/library/PolyVoxCore/include/Volume.inl index 10adefed..64631fc3 100644 --- a/library/PolyVoxCore/include/Volume.inl +++ b/library/PolyVoxCore/include/Volume.inl @@ -48,87 +48,19 @@ namespace PolyVox /// blocks are more likely to be homogeneous (so more easily shared) and have better /// cache behaviour. However, there is a memory overhead per block so if they are /// not shared it could actually be less efficient (this will depend on the data). - /// The size of the volume may also be a factor when choosing block size. Specifying - /// '0' for the block side length will cause the blocks to be as large as possible, - /// which will basically be the length of the shortest side. Accept the default if - /// you are not sure what to choose here. + /// The size of the volume may also be a factor when choosing block size. Accept + /// the default if you are not sure what to choose here. //////////////////////////////////////////////////////////////////////////////// template Volume::Volume(uint16_t uWidth, uint16_t uHeight, uint16_t uDepth, uint16_t uBlockSideLength) - :m_pBlocks(0) - ,m_uCurrentBlockForTidying(0) { - //A values of zero for a block side length is a special value to indicate - //that the block side length should simply be made as large as possible. - if(uBlockSideLength == 0) - { - uBlockSideLength = (std::min)((std::min)(uWidth,uHeight),uDepth); - } - - //Debug mode validation - assert(isPowerOf2(uBlockSideLength)); - assert(uBlockSideLength <= uWidth); - assert(uBlockSideLength <= uHeight); - assert(uBlockSideLength <= uDepth); - - //Release mode validation - if(!isPowerOf2(uBlockSideLength)) - { - throw std::invalid_argument("Block side length must be a power of two."); - } - if(uBlockSideLength > uWidth) - { - throw std::invalid_argument("Block side length cannot be greater than volume width."); - } - if(uBlockSideLength > uHeight) - { - throw std::invalid_argument("Block side length cannot be greater than volume height."); - } - if(uBlockSideLength > uDepth) - { - throw std::invalid_argument("Block side length cannot be greater than volume depth."); - } - - //Compute the volume side lengths - m_uWidth = uWidth; - //m_uWidthPower = logBase2(m_uWidth); - - m_uHeight = uHeight; - //m_uHeightPower = logBase2(m_uHeight); - - m_uDepth = uDepth; - //m_uDepthPower = logBase2(m_uDepth); - - //Compute the block side length - m_uBlockSideLength = uBlockSideLength; - m_uBlockSideLengthPower = logBase2(m_uBlockSideLength); - - //Compute the side lengths in blocks - m_uWidthInBlocks = m_uWidth / m_uBlockSideLength; - m_uHeightInBlocks = m_uHeight / m_uBlockSideLength; - m_uDepthInBlocks = m_uDepth / m_uBlockSideLength; - - //Compute number of blocks in the volume - m_uNoOfBlocksInVolume = m_uWidthInBlocks * m_uHeightInBlocks * m_uDepthInBlocks; - - //Create the blocks - m_pBlocks.resize(m_uNoOfBlocksInVolume); - m_vecBlockIsPotentiallyHomogenous.resize(m_uNoOfBlocksInVolume); - for(uint32_t i = 0; i < m_uNoOfBlocksInVolume; ++i) - { - m_pBlocks[i] = getHomogenousBlock(VoxelType()); - m_vecBlockIsPotentiallyHomogenous[i] = false; - } + //Create a volume of the right size. + resize(uWidth, uHeight, uDepth, uBlockSideLength); //Create the border block polyvox_shared_ptr< Block > pTempBlock(new Block(m_uBlockSideLength)); pTempBlock->fill(VoxelType()); m_pBorderBlock = pTempBlock; - - //Other properties we might find useful later - m_uLongestSideLength = (std::max)((std::max)(m_uWidth,m_uHeight),m_uDepth); - m_uShortestSideLength = (std::min)((std::min)(m_uWidth,m_uHeight),m_uDepth); - m_fDiagonalLength = sqrtf(static_cast(m_uWidth * m_uWidth + m_uHeight * m_uHeight + m_uDepth * m_uDepth)); } //////////////////////////////////////////////////////////////////////////////// @@ -361,6 +293,91 @@ namespace PolyVox #pragma endregion #pragma region Other + + //////////////////////////////////////////////////////////////////////////////// + /// Note: Calling this function will destroy all existing data in the volume. + /// \param uWidth The desired width in voxels. This must be a power of two. + /// \param uHeight The desired height in voxels. This must be a power of two. + /// \param uDepth The desired depth in voxels. This must be a power of two. + /// \param uBlockSideLength The size of the blocks which make up the volume. Small + /// blocks are more likely to be homogeneous (so more easily shared) and have better + /// cache behaviour. However, there is a memory overhead per block so if they are + /// not shared it could actually be less efficient (this will depend on the data). + /// The size of the volume may also be a factor when choosing block size. Accept + /// the default if you are not sure what to choose here. + //////////////////////////////////////////////////////////////////////////////// + template + void Volume::resize(uint16_t uWidth, uint16_t uHeight, uint16_t uDepth, uint16_t uBlockSideLength) + { + //Debug mode validation + assert(uBlockSideLength > 0); + assert(isPowerOf2(uBlockSideLength)); + assert(uBlockSideLength <= uWidth); + assert(uBlockSideLength <= uHeight); + assert(uBlockSideLength <= uDepth); + + //Release mode validation + if(uBlockSideLength == 0) + { + throw std::invalid_argument("Block side length cannot be zero."); + } + if(!isPowerOf2(uBlockSideLength)) + { + throw std::invalid_argument("Block side length must be a power of two."); + } + if(uBlockSideLength > uWidth) + { + throw std::invalid_argument("Block side length cannot be greater than volume width."); + } + if(uBlockSideLength > uHeight) + { + throw std::invalid_argument("Block side length cannot be greater than volume height."); + } + if(uBlockSideLength > uDepth) + { + throw std::invalid_argument("Block side length cannot be greater than volume depth."); + } + + //Empty the previous data + m_pBlocks.empty(); + m_vecBlockIsPotentiallyHomogenous.empty(); + m_pHomogenousBlock.empty(); + + //Compute the volume side lengths + m_uWidth = uWidth; + m_uHeight = uHeight; + m_uDepth = uDepth; + + //Compute the block side length + m_uBlockSideLength = uBlockSideLength; + m_uBlockSideLengthPower = logBase2(m_uBlockSideLength); + + //Compute the side lengths in blocks + m_uWidthInBlocks = m_uWidth / m_uBlockSideLength; + m_uHeightInBlocks = m_uHeight / m_uBlockSideLength; + m_uDepthInBlocks = m_uDepth / m_uBlockSideLength; + + //Compute number of blocks in the volume + m_uNoOfBlocksInVolume = m_uWidthInBlocks * m_uHeightInBlocks * m_uDepthInBlocks; + + //Create the blocks + m_pBlocks.resize(m_uNoOfBlocksInVolume); + m_vecBlockIsPotentiallyHomogenous.resize(m_uNoOfBlocksInVolume); + for(uint32_t i = 0; i < m_uNoOfBlocksInVolume; ++i) + { + m_pBlocks[i] = getHomogenousBlock(VoxelType()); + m_vecBlockIsPotentiallyHomogenous[i] = false; + } + + //Other properties we might find useful later + m_uLongestSideLength = (std::max)((std::max)(m_uWidth,m_uHeight),m_uDepth); + m_uShortestSideLength = (std::min)((std::min)(m_uWidth,m_uHeight),m_uDepth); + m_fDiagonalLength = sqrtf(static_cast(m_uWidth * m_uWidth + m_uHeight * m_uHeight + m_uDepth * m_uDepth)); + + //Reset the counter for tidying + m_uCurrentBlockForTidying = 0; + } + //////////////////////////////////////////////////////////////////////////////// /// Clean up the memory usage of the volume. Checks for any blocks which are /// homogeneous and flags them as such for faster processing and reduced memory diff --git a/library/PolyVoxUtil/include/Serialization.h b/library/PolyVoxUtil/include/Serialization.h index 2e60aac6..0c8412e5 100644 --- a/library/PolyVoxUtil/include/Serialization.h +++ b/library/PolyVoxUtil/include/Serialization.h @@ -41,6 +41,9 @@ namespace PolyVox virtual void onProgressUpdated(float fProgress) = 0; }; + //////////////////////////////////////////////////////////////////////////////// + // THESE FUNCTIONS ARE DEPRECATED. USE VERSIONED 'loadVolume' AND 'saveVolume' INSTEAD. + //////////////////////////////////////////////////////////////////////////////// template polyvox_shared_ptr< Volume > loadVolumeRaw(std::istream& stream, VolumeSerializationProgressListener* progressListener = 0); template @@ -50,6 +53,20 @@ namespace PolyVox polyvox_shared_ptr< Volume > loadVolumeRle(std::istream& stream, VolumeSerializationProgressListener* progressListener = 0); template void saveVolumeRle(std::ostream& stream, Volume& volume, VolumeSerializationProgressListener* progressListener = 0); + + //////////////////////////////////////////////////////////////////////////////// + // END OF DEPRECATED FUNCTIONS + //////////////////////////////////////////////////////////////////////////////// + + template + bool loadVolume(std::istream& stream, Volume& volume, VolumeSerializationProgressListener* progressListener = 0); + template + bool saveVolume(std::ostream& stream, Volume& volume, VolumeSerializationProgressListener* progressListener = 0); + + template + bool loadVersion0(std::istream& stream, Volume& volume, VolumeSerializationProgressListener* progressListener = 0); + template + bool saveVersion0(std::ostream& stream, Volume& volume, VolumeSerializationProgressListener* progressListener = 0); } #include "Serialization.inl" diff --git a/library/PolyVoxUtil/include/Serialization.inl b/library/PolyVoxUtil/include/Serialization.inl index 03fdd497..f04e73e6 100644 --- a/library/PolyVoxUtil/include/Serialization.inl +++ b/library/PolyVoxUtil/include/Serialization.inl @@ -32,6 +32,8 @@ namespace PolyVox template polyvox_shared_ptr< Volume > loadVolumeRaw(std::istream& stream, VolumeSerializationProgressListener* progressListener) { + assert(false); //THIS FUNCTION IS DEPRECATED. REMOVE THIS ASSERT TO CONTINUE, BUT SWITCH TO 'loadVolume()' ASAP. + //Read volume dimensions uint8_t volumeWidthPower = 0; uint8_t volumeHeightPower = 0; @@ -81,6 +83,8 @@ namespace PolyVox template void saveVolumeRaw(std::ostream& stream, Volume& volume, VolumeSerializationProgressListener* progressListener) { + assert(false); //THIS FUNCTION IS DEPRECATED. REMOVE THIS ASSERT TO CONTINUE, BUT SWITCH TO 'saveVolume()' ASAP. + //Write volume dimensions uint16_t volumeWidth = volume.getWidth(); uint16_t volumeHeight = volume.getHeight(); @@ -128,6 +132,8 @@ namespace PolyVox template polyvox_shared_ptr< Volume > loadVolumeRle(std::istream& stream, VolumeSerializationProgressListener* progressListener) { + assert(false); //THIS FUNCTION IS DEPRECATED. REMOVE THIS ASSERT TO CONTINUE, BUT SWITCH TO 'loadVolume()' ASAP. + //Read volume dimensions uint8_t volumeWidthPower = 0; uint8_t volumeHeightPower = 0; @@ -191,6 +197,8 @@ namespace PolyVox template void saveVolumeRle(std::ostream& stream, Volume& volume, VolumeSerializationProgressListener* progressListener) { + assert(false); //THIS FUNCTION IS DEPRECATED. REMOVE THIS ASSERT TO CONTINUE, BUT SWITCH TO 'saveVolume()' ASAP. + //Write volume dimensions uint16_t volumeWidth = volume.getWidth(); uint16_t volumeHeight = volume.getHeight(); @@ -256,4 +264,173 @@ namespace PolyVox progressListener->onProgressUpdated(1.0f); } } + + //////////////////////////////////////////////////////////////////////////////////////////////////// + // New version of load/save code with versioning + //////////////////////////////////////////////////////////////////////////////////////////////////// + + template + bool loadVolume(std::istream& stream, Volume& volume, VolumeSerializationProgressListener* progressListener) + { + char pIdentifier[8]; + stream.read(pIdentifier, 7); + pIdentifier[7] = '\0'; //Set the null terminator + if(strcmp(pIdentifier, "PolyVox") != 0) + { + return false; + } + + uint16_t uVersion; + stream.read(reinterpret_cast(&uVersion), sizeof(uVersion)); + + switch(uVersion) + { + case 0: + return loadVersion0(stream, volume, progressListener); + //Return means no need to break... + default: + return false; + } + + } + + template + bool saveVolume(std::ostream& stream, Volume& volume, VolumeSerializationProgressListener* progressListener) + { + char pIdentifier[] = "PolyVox"; + stream.write(pIdentifier, 7); + + uint16_t uVersion = 0; + stream.write(reinterpret_cast(&uVersion), sizeof(uVersion)); + + return saveVersion0(stream, volume, progressListener); + } + + //Note: we don't do much error handling in here - exceptions will simply be propergated up to the caller. + //FIXME - think about pointer ownership issues. Or could return volume by value if the copy constructor is shallow + template + bool loadVersion0(std::istream& stream, Volume& volume, VolumeSerializationProgressListener* progressListener) + { + //Read volume dimensions + uint16_t volumeWidth = 0; + uint16_t volumeHeight = 0; + uint16_t volumeDepth = 0; + stream.read(reinterpret_cast(&volumeWidth), sizeof(volumeWidth)); + stream.read(reinterpret_cast(&volumeHeight), sizeof(volumeHeight)); + stream.read(reinterpret_cast(&volumeDepth), sizeof(volumeDepth)); + + //Resize the volume + volume.resize(volumeWidth, volumeHeight, volumeDepth); + + //Read data + bool firstTime = true; + uint32_t runLength = 0; + VoxelType value; + stream.read(reinterpret_cast(&value), sizeof(value)); + stream.read(reinterpret_cast(&runLength), sizeof(runLength)); + for(uint16_t z = 0; z < volumeDepth; ++z) + { + //Update progress once per slice. + if(progressListener) + { + float fProgress = static_cast(z) / static_cast(volumeDepth); + progressListener->onProgressUpdated(fProgress); + } + + for(uint16_t y = 0; y < volumeHeight; ++y) + { + for(uint16_t x = 0; x < volumeWidth; ++x) + { + if(runLength != 0) + { + volume.setVoxelAt(x,y,z,value); + runLength--; + } + else + { + stream.read(reinterpret_cast(&value), sizeof(value)); + stream.read(reinterpret_cast(&runLength), sizeof(runLength)); + + volume.setVoxelAt(x,y,z,value); + runLength--; + } + } + } + } + + //Finished + if(progressListener) + { + progressListener->onProgressUpdated(1.0f); + } + + return true; + } + + template + bool saveVersion0(std::ostream& stream, Volume& volume, VolumeSerializationProgressListener* progressListener) + { + //Write volume dimensions + uint16_t volumeWidth = volume.getWidth(); + uint16_t volumeHeight = volume.getHeight(); + uint16_t volumeDepth = volume.getDepth(); + + stream.write(reinterpret_cast(&volumeWidth), sizeof(volumeWidth)); + stream.write(reinterpret_cast(&volumeHeight), sizeof(volumeHeight)); + stream.write(reinterpret_cast(&volumeDepth), sizeof(volumeDepth)); + + //Write data + VolumeSampler volIter(&volume); + VoxelType current; + uint32_t runLength = 0; + bool firstTime = true; + for(uint16_t z = 0; z < volumeDepth; ++z) + { + //Update progress once per slice. + if(progressListener) + { + float fProgress = static_cast(z) / static_cast(volumeDepth); + progressListener->onProgressUpdated(fProgress); + } + + for(uint16_t y = 0; y < volumeHeight; ++y) + { + for(uint16_t x = 0; x < volumeWidth; ++x) + { + volIter.setPosition(x,y,z); + VoxelType value = volIter.getVoxel(); + if(firstTime) + { + current = value; + runLength = 1; + firstTime = false; + } + else + { + if(value == current) + { + runLength++; + } + else + { + stream.write(reinterpret_cast(¤t), sizeof(current)); + stream.write(reinterpret_cast(&runLength), sizeof(runLength)); + current = value; + runLength = 1; + } + } + } + } + } + stream.write(reinterpret_cast(¤t), sizeof(current)); + stream.write(reinterpret_cast(&runLength), sizeof(runLength)); + + //Finished + if(progressListener) + { + progressListener->onProgressUpdated(1.0f); + } + + return true; + } }