diff --git a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.h b/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.h
index 0ce3bfc7..3250e6c0 100644
--- a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.h
+++ b/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.h
@@ -266,17 +266,21 @@ namespace PolyVox
~LargeVolume();
/// 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 number of blocks for which uncompressed data is stored
void setMaxNumberOfUncompressedBlocks(uint32_t uMaxNumberOfUncompressedBlocks);
@@ -330,6 +334,14 @@ namespace PolyVox
};
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;
+
/// gets called when a new region is allocated and needs to be filled
/// NOTE: accessing ANY voxels outside this region during the process of this function
/// is absolutely unsafe
diff --git a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl b/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl
index 741ee1c2..9f0bd3dc 100644
--- a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl
+++ b/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl
@@ -116,51 +116,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 LargeVolume::getVoxel(int32_t uXPos, int32_t uYPos, int32_t uZPos, BoundsCheck eBoundsCheck) const
+ template
+ VoxelType LargeVolume::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));
-
- 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 LargeVolume::getVoxel(const Vector3DInt32& v3dPos, BoundsCheck eBoundsCheck) const
+ template
+ VoxelType LargeVolume::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 LargeVolume::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 LargeVolume::getVoxel(const Vector3DInt32& v3dPos, WrapMode eWrapMode, VoxelType tBorder) const
+ {
+ return getVoxel(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ(), eWrapMode, tBorder);
}
////////////////////////////////////////////////////////////////////////////////
@@ -202,62 +234,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 LargeVolume::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 LargeVolume::getVoxel(const Vector3DInt32& v3dPos, WrapMode eWrapMode, VoxelType tBorder) const
- {
- return getVoxel(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ(), eWrapMode, tBorder);
- }
-
////////////////////////////////////////////////////////////////////////////////
/// Increasing the size of the block cache will increase memory but may improve performance.
/// You may want to set this to a large value (e.g. 1024) when you are first loading your
@@ -767,5 +743,66 @@ namespace PolyVox
return uSizeInBytes;
}
+ template
+ template
+ VoxelType LargeVolume::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 LargeVolume::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 LargeVolume::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 LargeVolume::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 LargeVolume::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));
+
+ Block* pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ);
+
+ return pUncompressedBlock->getVoxelAt(xOffset,yOffset,zOffset);
+ }
}