Added some documentation for Volume class.

This commit is contained in:
David Williams 2009-04-26 23:12:45 +00:00
parent cb37ab39da
commit 362bf9283a
2 changed files with 143 additions and 32 deletions

View File

@ -36,6 +36,73 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
namespace PolyVox
{
///The Volume class provides a memory efficient method of storing voxel data while also allowing fast access and modification.
/*******************************************************************************
A Volume is essentially a '3D image' in which each element (voxel) is identified
by a three dimensional (x,y,z) coordinate, rather than the two dimensional (x,y)
coordinate which is used to identify an element (pixel) in a normal image. Within
PolyVox, the Volume class is used to store and manipulate our data before we extract
our SurfacePatch's from it.
<b>Data Representaion - feel free to skip</b>
If stored carelessly, volume data can take up a huge amount of memory. For example, a
volume of dimensions 1024x1024x1024 with 1 byte per voxel will require 1GB of memory
if stored in an uncompressed form. Natuarally our Volume class is much more efficient
than this and it is worth understanding (at least at a high level) the approach
which is used.
Essentially, the Volume class stores its data as a collection of blocks. Each
of these block is much smaller than the whole volume, for example a typical size
might be 32x32x32 voxels (though is is configurable by the user). In this case,
a 256x512x1024 volume would contain 8x16x32 = 4096 blocks. However, it is unlikely that
all these blocks actually have to be stored because usually there are duplicates
in which case common data can be shared.
Identifying duplicate blocks is in general a difficult task which involves looking at pairs
of blocks and comparing all the voxels. This is a time consuming task which is not amiable
to being performed when the volume is being modified in real time. However, there are two
specific scenarios which are easily spotted and which PolyVox uses to identify block
sharing opportunities.
-# Homogeneous blocks (those which contain just a single voxel value) are easy to
spot and fairly common becuase volumes often contain large homogeous regions. Any time
you change the value of a voxel you have potentially made the block which contains
it homogeneous. PolyVox does not check the homogeneity immediatly as this would slow
down the process of modifying voxels, but you can use the tidyUpMemory() function
to check for and remove duplicate homogeneous regions whenever you have spare
processing time.
-# Copying a volume naturally means that all the voxels in the second voluem are
the same as the first. Therefore volume copying is a relatively fast operation in
which all the blocks in the second volume simply reference the first volume. Future
modifications to either volume will, of course, cause the blocks to become unshared.
Other advantages of breaking the volume down into blocks include enhancing data locality
(i.e. voxels which are spatially near to each other are also likely to be near in
memory) and the ability to load larger volumes as no large contiguous areas of
memory are needed. However, these advantages are more transparent to user code
so we will not dwell on them here.
<b>Usage</b>
Volumes are constructed by passing the desired width height and depth to the
constructor. Note that for speed reasons only values which are a power of two
are permitted for these sidelengths.
Access to specific voxels is provided by the getVoxelAt() and setVoxelAt fuctions.
Each of these has two forms so that voxels can be identified by integer triples
or by Vector3DUint16's.
The tidyUpMemory() function should normally be called after you first populate
the volume with data, and then at periodic intervals as the volume is modified.
However, you don't actually <i>have</i> to call it at all. See the functions
documentation for further details.
One further important point of note is that this class is templatised on the voxel
type. This allows you to store volumes of data types you might not normally expect,
for example theOpenGL example 'abuses' this class to store a 3D grid of pointers.
However, it is not guarentted that all functionality works correctly with non-integer
voxel types.
*******************************************************************************/
template <typename VoxelType>
class Volume
{
@ -43,18 +110,24 @@ namespace PolyVox
friend class VolumeIterator<VoxelType>;
public:
///Constructor
Volume(uint16_t uWidth, uint16_t uHeight, uint16_t uDepth, uint16_t uBlockSideLength = 64);
//Volume(const Volume& rhs);
///Destructor
~Volume();
//Volume& operator=(const Volume& rhs);
///Gets a Region representing the extents of the Volume.
Region getEnclosingRegion(void) const;
///Gets the width of the volume in voxels.
uint16_t getWidth(void) const;
///Gets the height of the volume in voxels.
uint16_t getHeight(void) const;
///Gets the depth of the volume in voxels.
uint16_t getDepth(void) const;
///Gets the length of the longest side in voxels
uint16_t getLongestSideLength(void) const;
///Gets the length of the shortest side in voxels
uint16_t getShortestSideLength(void) const;
///Gets the length of the diagonal in voxels
float getDiagonalLength(void) const;
VoxelType getVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos) const;
VoxelType getVoxelAt(const Vector3DUint16& v3dPos) const;
@ -76,6 +149,7 @@ namespace PolyVox
//shared. A call to shared_ptr::unique() from within setVoxel was not sufficient as weak_ptr's did
//not contribute to the reference count. Instead we store shared_ptr's here, and check if they
//are used by anyone else (i.e are non-unique) when we tidy the volume.
//FIXME - How do we handle sharing between two volumes with different block sizes?!
static std::map<VoxelType, POLYVOX_SHARED_PTR< Block<VoxelType> > > m_pHomogenousBlock;
uint32_t m_uNoOfBlocksInVolume;

View File

@ -35,16 +35,27 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
namespace PolyVox
{
#pragma region Constructors/Destructors
/*******************************************************************************
Builds a volume of the desired dimensions
\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. 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.
*******************************************************************************/
template <typename VoxelType>
Volume<VoxelType>::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. This can be useful if you are
//creating only a small volume which doesn't need to be broken down into many blocks. This
//'largest possible block size' will be equal to the shortest volume dimension, as a volume
//dimension can never be less than a block side length.
//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);
@ -119,12 +130,9 @@ namespace PolyVox
m_fDiagonalLength = sqrtf(static_cast<float>(m_uWidth * m_uWidth + m_uHeight * m_uHeight + m_uDepth * m_uDepth));
}
/*template <typename VoxelType>
Volume<VoxelType>::Volume(const Volume<VoxelType>& rhs)
{
*this = rhs;
}*/
/*******************************************************************************
Destroys the volume and frees any blocks which are not in use by other volumes.
*******************************************************************************/
template <typename VoxelType>
Volume<VoxelType>::~Volume()
{
@ -132,55 +140,84 @@ namespace PolyVox
#pragma endregion
#pragma region Operators
/*template <typename VoxelType>
Volume<VoxelType>& Volume<VoxelType>::operator=(const Volume& rhs)
{
}*/
#pragma endregion
#pragma region Getters
/*******************************************************************************
The result will always have a lower corner at (0,0,0) and an upper corner at one
less than the side length. For example, if a volume has dimensions 256x512x1024
then the upper corner of the enclosing region will be at (255,511,1023).
\return A Region representing the extent of the volume.
*******************************************************************************/
template <typename VoxelType>
Region Volume<VoxelType>::getEnclosingRegion(void) const
{
return Region(Vector3DInt32(0,0,0), Vector3DInt32(m_uWidth-1,m_uHeight-1,m_uDepth-1));
}
/*******************************************************************************
\return The width of the volume in voxels
\sa getHeight(), getDepth()
*******************************************************************************/
template <typename VoxelType>
uint16_t Volume<VoxelType>::getDepth(void) const
uint16_t Volume<VoxelType>::getWidth(void) const
{
return m_uDepth;
}
template <typename VoxelType>
float Volume<VoxelType>::getDiagonalLength(void) const
{
return m_fDiagonalLength;
return m_uWidth;
}
/*******************************************************************************
\return The height of the volume in voxels
\sa getWidth(), getDepth()
*******************************************************************************/
template <typename VoxelType>
uint16_t Volume<VoxelType>::getHeight(void) const
{
return m_uHeight;
}
/*******************************************************************************
\return The depth of the volume in voxels
\sa getWidth(), getHeight()
*******************************************************************************/
template <typename VoxelType>
uint16_t Volume<VoxelType>::getLongestSideLength(void) const
uint16_t Volume<VoxelType>::getDepth(void) const
{
return m_uLongestSideLength;
return m_uDepth;
}
/*******************************************************************************
\return The length of the shortest side in voxels. For example, if a volume has
dimensions 256x512x1024 this function will return 256.
\sa getLongestSideLength(), getDiagonalLength()
*******************************************************************************/
template <typename VoxelType>
uint16_t Volume<VoxelType>::getShortestSideLength(void) const
{
return m_uShortestSideLength;
}
/*******************************************************************************
\return The length of the longest side in voxels. For example, if a volume has
dimensions 256x512x1024 this function will return 1024.
\sa getShortestSideLength(), getDiagonalLength()
*******************************************************************************/
template <typename VoxelType>
uint16_t Volume<VoxelType>::getWidth(void) const
uint16_t Volume<VoxelType>::getLongestSideLength(void) const
{
return m_uWidth;
}
return m_uLongestSideLength;
}
/*******************************************************************************
\return The length of the diagonal in voxels. For example, if a volume has
dimensions 256x512x1024 this function will return sqrt(256*256+512*512+1024*1024)
= 1173.139. This value is computed on volume creation so retrieving it is fast.
\sa getShortestSideLength(), getLongestSideLength()
*******************************************************************************/
template <typename VoxelType>
float Volume<VoxelType>::getDiagonalLength(void) const
{
return m_fDiagonalLength;
}
template <typename VoxelType>
VoxelType Volume<VoxelType>::getVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos) const