#pragma region License /****************************************************************************** This file is part of the PolyVox library Copyright (C) 2006 David Williams This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ******************************************************************************/ #pragma endregion #pragma region Headers #include "Block.h" #include "VolumeIterator.h" #include "Region.h" #include "Vector.h" #include #include //For memcpy #pragma endregion namespace PolyVox { #pragma region Constructors/Destructors template Volume::Volume(uint16 uSideLength, uint16 uBlockSideLength) :m_pBlocks(0) { //Debug mode validation assert(isPowerOf2(uSideLength)); assert(isPowerOf2(uBlockSideLength)); assert(uBlockSideLength <= uSideLength); //Release mode validation if(!isPowerOf2(uSideLength)) { throw std::invalid_argument("Volume side length must be a power of two."); } if(!isPowerOf2(uBlockSideLength)) { throw std::invalid_argument("Block side length must be a power of two."); } if(uBlockSideLength > uSideLength) { throw std::invalid_argument("Block side length cannot be less than volume side length."); } //Compute the volume side length m_uSideLength = uSideLength; m_uSideLengthPower = logBase2(m_uSideLength); //Compute the block side length m_uBlockSideLength = uBlockSideLength; m_uBlockSideLengthPower = logBase2(m_uBlockSideLength); //Compute the side length in blocks m_uSideLengthInBlocks = m_uSideLength / m_uBlockSideLength; //Compute number of blocks in the volume m_uNoOfBlocksInVolume = m_uSideLengthInBlocks * m_uSideLengthInBlocks * m_uSideLengthInBlocks; //Create the blocks m_pBlocks = new Block*[m_uNoOfBlocksInVolume]; m_pIsShared = new bool[m_uNoOfBlocksInVolume]; m_pIsPotentiallySharable = new bool[m_uNoOfBlocksInVolume]; m_pHomogenousValue = new VoxelType[m_uNoOfBlocksInVolume]; for(uint32 i = 0; i < m_uNoOfBlocksInVolume; ++i) { m_pBlocks[i] = getHomogenousBlock(0); m_pIsShared[i] = true; m_pIsPotentiallySharable[i] = false; m_pHomogenousValue[i] = 0; } } template Volume::Volume(const Volume& rhs) { *this = rhs; } template Volume::~Volume() { for(uint32 i = 0; i < m_uNoOfBlocksInVolume; ++i) { delete m_pBlocks[i]; } } #pragma endregion #pragma region Operators template Volume& Volume::operator=(const Volume& rhs) { if (this == &rhs) { return *this; } /*for(uint16 i = 0; i < POLYVOX_NO_OF_BLOCKS_IN_VOLUME; ++i) { //FIXME - Add checking... m_pBlocks[i] = SharedPtr(new Block); }*/ for(uint32 i = 0; i < m_uNoOfBlocksInVolume; ++i) { //I think this is OK... If a block is in the homogeneous array it's ref count will be greater //than 1 as there will be the pointer in the volume and the pointer in the static homogeneous array. /*if(rhs.m_pBlocks[i].unique()) { m_pBlocks[i] = SharedPtr(new Block(*(rhs.m_pBlocks[i]))); } else {*/ //we have a block in the homogeneous array - just copy the pointer. m_pBlocks[i] = rhs.m_pBlocks[i]; //} } return *this; } #pragma endregion #pragma region Getters template Region Volume::getEnclosingRegion(void) const { return Region(Vector3DInt32(0,0,0), Vector3DInt32(m_uSideLength-1,m_uSideLength-1,m_uSideLength-1)); } template uint16 Volume::getSideLength(void) const { return m_uSideLength; } template VoxelType Volume::getVoxelAt(uint16 uXPos, uint16 uYPos, uint16 uZPos) const { assert(uXPos < getSideLength()); assert(uYPos < getSideLength()); assert(uZPos < getSideLength()); const uint16 blockX = uXPos >> m_uBlockSideLengthPower; const uint16 blockY = uYPos >> m_uBlockSideLengthPower; const uint16 blockZ = uZPos >> m_uBlockSideLengthPower; const uint16 xOffset = uXPos - (blockX << m_uBlockSideLengthPower); const uint16 yOffset = uYPos - (blockY << m_uBlockSideLengthPower); const uint16 zOffset = uZPos - (blockZ << m_uBlockSideLengthPower); const Block* block = m_pBlocks [ blockX + blockY * m_uSideLengthInBlocks + blockZ * m_uSideLengthInBlocks * m_uSideLengthInBlocks ]; return block->getVoxelAt(xOffset,yOffset,zOffset); } template VoxelType Volume::getVoxelAt(const Vector3DUint16& v3dPos) const { assert(v3dPos.getX() < m_uSideLength); assert(v3dPos.getY() < m_uSideLength); assert(v3dPos.getZ() < m_uSideLength); return getVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ()); } #pragma endregion #pragma region Setters template void Volume::setVoxelAt(uint16 uXPos, uint16 uYPos, uint16 uZPos, VoxelType tValue) { const uint16 blockX = uXPos >> m_uBlockSideLengthPower; const uint16 blockY = uYPos >> m_uBlockSideLengthPower; const uint16 blockZ = uZPos >> m_uBlockSideLengthPower; const uint16 xOffset = uXPos - (blockX << m_uBlockSideLengthPower); const uint16 yOffset = uYPos - (blockY << m_uBlockSideLengthPower); const uint16 zOffset = uZPos - (blockZ << m_uBlockSideLengthPower); const uint32 uBlockIndex = blockX + blockY * m_uSideLengthInBlocks + blockZ * m_uSideLengthInBlocks * m_uSideLengthInBlocks; const bool bIsShared = m_pIsShared[uBlockIndex]; const VoxelType tHomogenousValue = m_pHomogenousValue[uBlockIndex]; if(bIsShared) { if(tHomogenousValue != tValue) { m_pBlocks[uBlockIndex] = new Block(m_uBlockSideLength); m_pIsShared[uBlockIndex] = false; m_pBlocks[uBlockIndex]->fill(tHomogenousValue); m_pBlocks[uBlockIndex]->setVoxelAt(xOffset,yOffset,zOffset, tValue); } } else { //There is a chance that setting this voxel makes the block homogenous and therefore shareable. m_pIsPotentiallySharable[uBlockIndex] = true; m_pBlocks[uBlockIndex]->setVoxelAt(xOffset,yOffset,zOffset, tValue); } } template void Volume::setVoxelAt(const Vector3DUint16& v3dPos, VoxelType tValue) { setVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ(), tValue); } #pragma endregion #pragma region Other template bool Volume::containsPoint(const Vector3DFloat& pos, float boundary) const { return (pos.getX() <= m_uSideLength - 1 - boundary) && (pos.getY() <= m_uSideLength - 1 - boundary) && (pos.getZ() <= m_uSideLength - 1 - boundary) && (pos.getX() >= boundary) && (pos.getY() >= boundary) && (pos.getZ() >= boundary); } template bool Volume::containsPoint(const Vector3DInt32& pos, uint16 boundary) const { return (pos.getX() <= m_uSideLength - 1 - boundary) && (pos.getY() <= m_uSideLength - 1 - boundary) && (pos.getZ() <= m_uSideLength - 1 - boundary) && (pos.getX() >= boundary) && (pos.getY() >= boundary) && (pos.getZ() >= boundary); } template VolumeIterator Volume::firstVoxel(void) { VolumeIterator iter(*this); iter.setPosition(0,0,0); return iter; } template void Volume::idle(uint32 uAmount) { } template bool Volume::isRegionHomogenous(const Region& region) { VolumeIterator iter(*this); iter.setValidRegion(region); iter.setPosition(static_cast(region.getLowerCorner())); VoxelType tFirst = iter.getVoxel(); iter.moveForwardInRegionXYZ(); do { VoxelType tCurrent = iter.getVoxel(); if(tCurrent != tFirst) { return false; } }while(iter.moveForwardInRegionXYZ()); return true; } template VolumeIterator Volume::lastVoxel(void) { VolumeIterator iter(*this); iter.setPosition(m_uSideLength-1,m_uSideLength-1,m_uSideLength-1); return iter; } #pragma endregion #pragma region Private Implementation template Block* Volume::getHomogenousBlock(VoxelType tHomogenousValue) const { typename std::map*>::iterator iterResult = m_pHomogenousBlocks.find(tHomogenousValue); if(iterResult == m_pHomogenousBlocks.end()) { Block* pBlock = new Block(m_uBlockSideLength); pBlock->fill(tHomogenousValue); m_pHomogenousBlocks.insert(std::make_pair(tHomogenousValue, pBlock)); return pBlock; } return iterResult->second; } #pragma endregion }