From 3d82b9643f7ad498536096d000ce5a67fc3b69f9 Mon Sep 17 00:00:00 2001 From: Daviw Williams Date: Fri, 7 Jun 2013 15:58:24 +0200 Subject: [PATCH] Applied BoundsCheck and WrapMode merging changes to SimpleVolume. --- .../include/PolyVoxCore/SimpleVolume.h | 24 +- .../include/PolyVoxCore/SimpleVolume.inl | 211 ++++++++++-------- 2 files changed, 142 insertions(+), 93 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.h b/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.h index 2bd0a366..5503f64b 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.h +++ b/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.h @@ -155,17 +155,21 @@ namespace PolyVox ~SimpleVolume(); /// Gets a voxel at the position given by x,y,z coordinates - VoxelType getVoxel(int32_t uXPos, int32_t uYPos, int32_t uZPos, BoundsCheck eBoundsCheck = BoundsChecks::Full) const; + template + VoxelType getVoxel(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tBorder = VoxelType()) const; /// Gets a voxel at the position given by a 3D vector - VoxelType getVoxel(const Vector3DInt32& v3dPos, BoundsCheck eBoundsCheck = BoundsChecks::Full) const; + template + VoxelType getVoxel(const Vector3DInt32& v3dPos, VoxelType tBorder = VoxelType()) const; + + /// Gets a voxel at the position given by x,y,z coordinates + VoxelType getVoxel(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapMode eWrapMode = WrapModes::None, VoxelType tBorder = VoxelType()) const; + /// Gets a voxel at the position given by a 3D vector + VoxelType getVoxel(const Vector3DInt32& v3dPos, WrapMode eWrapMode = WrapModes::None, VoxelType tBorder = VoxelType()) const; + /// Gets a voxel at the position given by x,y,z coordinates VoxelType getVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos) const; /// Gets a voxel at the position given by a 3D vector VoxelType getVoxelAt(const Vector3DInt32& v3dPos) const; - /// Gets a voxel at the position given by x,y,z coordinates - VoxelType getVoxel(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapMode eWrapMode, VoxelType tBorder = VoxelType()) const; - /// Gets a voxel at the position given by a 3D vector - VoxelType getVoxel(const Vector3DInt32& v3dPos, WrapMode eWrapMode, VoxelType tBorder = VoxelType()) const; /// Sets the voxel at the position given by x,y,z coordinates void setVoxel(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue, BoundsCheck eBoundsCheck = BoundsChecks::Full); @@ -189,6 +193,14 @@ namespace PolyVox private: void initialise(const Region& regValidRegion, uint16_t uBlockSideLength); + // A trick to implement specialization of template member functions in template classes. See http://stackoverflow.com/a/4951057 + template + VoxelType getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType, VoxelType tBorder) const; + VoxelType getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType, VoxelType tBorder) const; + VoxelType getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType, VoxelType tBorder) const; + VoxelType getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType, VoxelType tBorder) const; + VoxelType getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType, VoxelType tBorder) const; + Block* getUncompressedBlock(int32_t uBlockX, int32_t uBlockY, int32_t uBlockZ) const; //The block data diff --git a/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.inl b/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.inl index 4dc5af0a..93ef557d 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.inl @@ -74,51 +74,83 @@ namespace PolyVox } //////////////////////////////////////////////////////////////////////////////// + /// This version of the function requires the wrap mode to be specified as a + /// template parameter, which can provide better performance. /// \param uXPos The \c x position of the voxel /// \param uYPos The \c y position of the voxel /// \param uZPos The \c z position of the voxel - /// \param eBoundsCheck Controls whether bounds checking is performed on voxel access. It's safest to - /// set this to BoundsChecks::Full (the default), but if you are certain that the voxel you are accessing - /// is inside the volume's enclosing region then you can skip this check to gain some performance. + /// \tparam eWrapMode Specifies the behaviour when the requested position is outside of the volume. + /// \param tBorder The border value to use if the wrap mode is set to 'Border'. /// \return The voxel value //////////////////////////////////////////////////////////////////////////////// template - VoxelType SimpleVolume::getVoxel(int32_t uXPos, int32_t uYPos, int32_t uZPos, BoundsCheck eBoundsCheck) const + template + VoxelType SimpleVolume::getVoxel(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tBorder) const { - // If bounds checking is enabled then we validate the - // bounds, and throw an exception if they are violated. - if(eBoundsCheck == BoundsChecks::Full) - { - if(this->m_regValidRegion.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos)) == false) - { - POLYVOX_THROW(std::out_of_range, "Position is outside valid region"); - } - } - - const int32_t blockX = uXPos >> m_uBlockSideLengthPower; - const int32_t blockY = uYPos >> m_uBlockSideLengthPower; - const int32_t blockZ = uZPos >> m_uBlockSideLengthPower; - - const uint16_t xOffset = static_cast(uXPos - (blockX << m_uBlockSideLengthPower)); - const uint16_t yOffset = static_cast(uYPos - (blockY << m_uBlockSideLengthPower)); - const uint16_t zOffset = static_cast(uZPos - (blockZ << m_uBlockSideLengthPower)); - - typename SimpleVolume::Block* pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ); - - return pUncompressedBlock->getVoxelAt(xOffset,yOffset,zOffset); + // Simply call through to the real implementation + return getVoxelImpl(uXPos, uYPos, uZPos, WrapModeType(), tBorder); } //////////////////////////////////////////////////////////////////////////////// - /// \param v3dPos The 3D position of the voxel - /// \param eBoundsCheck Controls whether bounds checking is performed on voxel access. It's safest to - /// set this to BoundsChecks::Full (the default), but if you are certain that the voxel you are accessing - /// is inside the volume's enclosing region then you can skip this check to gain some performance. + /// This version of the function requires the wrap mode to be specified as a + /// template parameter, which can provide better performance. + /// \param uXPos The \c x position of the voxel + /// \param uYPos The \c y position of the voxel + /// \param uZPos The \c z position of the voxel + /// \tparam eWrapMode Specifies the behaviour when the requested position is outside of the volume. + /// \param tBorder The border value to use if the wrap mode is set to 'Border'. /// \return The voxel value //////////////////////////////////////////////////////////////////////////////// template - VoxelType SimpleVolume::getVoxel(const Vector3DInt32& v3dPos, BoundsCheck eBoundsCheck) const + template + VoxelType SimpleVolume::getVoxel(const Vector3DInt32& v3dPos, VoxelType tBorder) const { - return getVoxel(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ(), eBoundsCheck); + // Simply call through to the real implementation + return getVoxel(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ(), tBorder); + } + + //////////////////////////////////////////////////////////////////////////////// + /// This version of the function is provided so that the wrap mode does not need + /// to be specified as a template parameter, as it may be confusing to some users. + /// \param uXPos The \c x position of the voxel + /// \param uYPos The \c y position of the voxel + /// \param uZPos The \c z position of the voxel + /// \param eWrapMode Specifies the behaviour when the requested position is outside of the volume. + /// \param tBorder The border value to use if the wrap mode is set to 'Border'. + /// \return The voxel value + //////////////////////////////////////////////////////////////////////////////// + template + VoxelType SimpleVolume::getVoxel(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapMode eWrapMode, VoxelType tBorder) const + { + switch(eWrapMode) + { + case WrapModes::None: + return getVoxelImpl(uXPos, uYPos, uZPos, WrapModeType(), tBorder); + case WrapModes::Clamp: + return getVoxelImpl(uXPos, uYPos, uZPos, WrapModeType(), tBorder); + case WrapModes::Border: + return getVoxelImpl(uXPos, uYPos, uZPos, WrapModeType(), tBorder); + case WrapModes::DontCheck: + return getVoxelImpl(uXPos, uYPos, uZPos, WrapModeType(), tBorder); + default: + // Should never happen + POLYVOX_ASSERT(false, "Invalid wrap mode"); + return VoxelType(); + } + } + + //////////////////////////////////////////////////////////////////////////////// + /// This version of the function is provided so that the wrap mode does not need + /// to be specified as a template parameter, as it may be confusing to some users. + /// \param v3dPos The 3D position of the voxel + /// \param eWrapMode Specifies the behaviour when the requested position is outside of the volume. + /// \param tBorder The border value to use if the wrap mode is set to 'Border'. + /// \return The voxel value + //////////////////////////////////////////////////////////////////////////////// + template + VoxelType SimpleVolume::getVoxel(const Vector3DInt32& v3dPos, WrapMode eWrapMode, VoxelType tBorder) const + { + return getVoxel(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ(), eWrapMode, tBorder); } //////////////////////////////////////////////////////////////////////////////// @@ -160,62 +192,6 @@ namespace PolyVox return getVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ()); } - //////////////////////////////////////////////////////////////////////////////// - /// \param uXPos The \c x position of the voxel - /// \param uYPos The \c y position of the voxel - /// \param uZPos The \c z position of the voxel - /// \return The voxel value - //////////////////////////////////////////////////////////////////////////////// - template - VoxelType SimpleVolume::getVoxel(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapMode eWrapMode, VoxelType tBorder) const - { - switch(eWrapMode) - { - case WrapModes::Clamp: - { - //Perform clamping - uXPos = (std::max)(uXPos, this->m_regValidRegion.getLowerX()); - uYPos = (std::max)(uYPos, this->m_regValidRegion.getLowerY()); - uZPos = (std::max)(uZPos, this->m_regValidRegion.getLowerZ()); - uXPos = (std::min)(uXPos, this->m_regValidRegion.getUpperX()); - uYPos = (std::min)(uYPos, this->m_regValidRegion.getUpperY()); - uZPos = (std::min)(uZPos, this->m_regValidRegion.getUpperZ()); - - //Get the voxel value - return getVoxel(uXPos, uYPos, uZPos, BoundsChecks::None); // No bounds checks as we've just validated the position. - //No need to break as we've returned - } - case WrapModes::Border: - { - if(this->m_regValidRegion.containsPoint(uXPos, uYPos, uZPos)) - { - return getVoxel(uXPos, uYPos, uZPos, BoundsChecks::None); // No bounds checks as we've just validated the position. - } - else - { - return tBorder; - } - //No need to break as we've returned - } - default: - { - //Should never happen. However, this assert appears to hurt performance (logging prevents inlining?). - POLYVOX_ASSERT(false, "Wrap mode parameter has an unrecognised value."); - return VoxelType(); - } - } - } - - //////////////////////////////////////////////////////////////////////////////// - /// \param v3dPos The 3D position of the voxel - /// \return The voxel value - //////////////////////////////////////////////////////////////////////////////// - template - VoxelType SimpleVolume::getVoxel(const Vector3DInt32& v3dPos, WrapMode eWrapMode, VoxelType tBorder) const - { - return getVoxel(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ(), eWrapMode, tBorder); - } - //////////////////////////////////////////////////////////////////////////////// /// \param uXPos the \c x position of the voxel /// \param uYPos the \c y position of the voxel @@ -398,5 +374,66 @@ namespace PolyVox return uSizeInBytes; } + template + template + VoxelType SimpleVolume::getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType, VoxelType tBorder) const + { + // This function should never be called because one of the specialisations should always match. + POLYVOX_ASSERT(false, "This function is not implemented and should never be called!"); + } + + template + VoxelType SimpleVolume::getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType, VoxelType tBorder) const + { + if(this->m_regValidRegion.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos)) == false) + { + POLYVOX_THROW(std::out_of_range, "Position is outside valid region"); + } + + return getVoxelImpl(uXPos, uYPos, uZPos, WrapModeType(), tBorder); // No wrapping as we've just validated the position. + } + + template + VoxelType SimpleVolume::getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType, VoxelType tBorder) const + { + //Perform clamping + uXPos = (std::max)(uXPos, this->m_regValidRegion.getLowerX()); + uYPos = (std::max)(uYPos, this->m_regValidRegion.getLowerY()); + uZPos = (std::max)(uZPos, this->m_regValidRegion.getLowerZ()); + uXPos = (std::min)(uXPos, this->m_regValidRegion.getUpperX()); + uYPos = (std::min)(uYPos, this->m_regValidRegion.getUpperY()); + uZPos = (std::min)(uZPos, this->m_regValidRegion.getUpperZ()); + + return getVoxelImpl(uXPos, uYPos, uZPos, WrapModeType(), tBorder); // No wrapping as we've just validated the position. + } + + template + VoxelType SimpleVolume::getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType, VoxelType tBorder) const + { + if(this->m_regValidRegion.containsPoint(uXPos, uYPos, uZPos)) + { + return getVoxelImpl(uXPos, uYPos, uZPos, WrapModeType(), tBorder); // No wrapping as we've just validated the position. + } + else + { + return tBorder; + } + } + + template + VoxelType SimpleVolume::getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType, VoxelType tBorder) const + { + const int32_t blockX = uXPos >> m_uBlockSideLengthPower; + const int32_t blockY = uYPos >> m_uBlockSideLengthPower; + const int32_t blockZ = uZPos >> m_uBlockSideLengthPower; + + const uint16_t xOffset = static_cast(uXPos - (blockX << m_uBlockSideLengthPower)); + const uint16_t yOffset = static_cast(uYPos - (blockY << m_uBlockSideLengthPower)); + const uint16_t zOffset = static_cast(uZPos - (blockZ << m_uBlockSideLengthPower)); + + typename SimpleVolume::Block* pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ); + + return pUncompressedBlock->getVoxelAt(xOffset,yOffset,zOffset); + } }