Added some documentation for Volume class.
This commit is contained in:
		| @@ -36,6 +36,73 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. | |||||||
|  |  | ||||||
| namespace PolyVox | 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> | 	template <typename VoxelType> | ||||||
| 	class Volume | 	class Volume | ||||||
| 	{ | 	{ | ||||||
| @@ -43,18 +110,24 @@ namespace PolyVox | |||||||
| 		friend class VolumeIterator<VoxelType>; | 		friend class VolumeIterator<VoxelType>; | ||||||
|  |  | ||||||
| 	public:		 | 	public:		 | ||||||
|  | 		///Constructor | ||||||
| 		Volume(uint16_t uWidth, uint16_t uHeight, uint16_t uDepth, uint16_t uBlockSideLength = 64); | 		Volume(uint16_t uWidth, uint16_t uHeight, uint16_t uDepth, uint16_t uBlockSideLength = 64); | ||||||
| 		//Volume(const Volume& rhs); | 		///Destructor | ||||||
| 		~Volume();	 | 		~Volume();	 | ||||||
|  |  | ||||||
| 		//Volume& operator=(const Volume& rhs); | 		///Gets a Region representing the extents of the Volume. | ||||||
|  |  | ||||||
| 		Region getEnclosingRegion(void) const; | 		Region getEnclosingRegion(void) const; | ||||||
|  | 		///Gets the width of the volume in voxels. | ||||||
| 		uint16_t getWidth(void) const; | 		uint16_t getWidth(void) const; | ||||||
|  | 		///Gets the height of the volume in voxels. | ||||||
| 		uint16_t getHeight(void) const; | 		uint16_t getHeight(void) const; | ||||||
|  | 		///Gets the depth of the volume in voxels. | ||||||
| 		uint16_t getDepth(void) const; | 		uint16_t getDepth(void) const; | ||||||
|  | 		///Gets the length of the longest side in voxels | ||||||
| 		uint16_t getLongestSideLength(void) const; | 		uint16_t getLongestSideLength(void) const; | ||||||
|  | 		///Gets the length of the shortest side in voxels | ||||||
| 		uint16_t getShortestSideLength(void) const; | 		uint16_t getShortestSideLength(void) const; | ||||||
|  | 		///Gets the length of the diagonal in voxels | ||||||
| 		float getDiagonalLength(void) const; | 		float getDiagonalLength(void) const; | ||||||
| 		VoxelType getVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos) const; | 		VoxelType getVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos) const; | ||||||
| 		VoxelType getVoxelAt(const Vector3DUint16& v3dPos) 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 | 		//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 | 		//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. | 		//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; | 		static std::map<VoxelType, POLYVOX_SHARED_PTR< Block<VoxelType> > > m_pHomogenousBlock; | ||||||
|  |  | ||||||
| 		uint32_t m_uNoOfBlocksInVolume; | 		uint32_t m_uNoOfBlocksInVolume; | ||||||
|   | |||||||
| @@ -35,16 +35,27 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. | |||||||
| namespace PolyVox | namespace PolyVox | ||||||
| { | { | ||||||
| 	#pragma region Constructors/Destructors | 	#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> | 	template <typename VoxelType> | ||||||
| 	Volume<VoxelType>::Volume(uint16_t uWidth, uint16_t uHeight, uint16_t uDepth, uint16_t uBlockSideLength) | 	Volume<VoxelType>::Volume(uint16_t uWidth, uint16_t uHeight, uint16_t uDepth, uint16_t uBlockSideLength) | ||||||
| 		:m_pBlocks(0) | 		:m_pBlocks(0) | ||||||
| 		,m_uCurrentBlockForTidying(0) | 		,m_uCurrentBlockForTidying(0) | ||||||
| 	{ | 	{ | ||||||
| 		//A values of zero for a block side length is a special value to indicate that the block | 		//A values of zero for a block side length is a special value to indicate  | ||||||
| 		//side length should simply be made as large as possible. This can be useful if you are | 		//that the block side length should simply be made as large as possible. | ||||||
| 		//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. |  | ||||||
| 		if(uBlockSideLength == 0) | 		if(uBlockSideLength == 0) | ||||||
| 		{ | 		{ | ||||||
| 			uBlockSideLength = (std::min)((std::min)(uWidth,uHeight),uDepth); | 			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)); | 		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) | 	Destroys the volume and frees any blocks which are not in use by other volumes. | ||||||
| 	{ | 	*******************************************************************************/ | ||||||
| 		*this = rhs; |  | ||||||
| 	}*/ |  | ||||||
|  |  | ||||||
| 	template <typename VoxelType> | 	template <typename VoxelType> | ||||||
| 	Volume<VoxelType>::~Volume() | 	Volume<VoxelType>::~Volume() | ||||||
| 	{ | 	{ | ||||||
| @@ -132,54 +140,83 @@ namespace PolyVox | |||||||
| 	#pragma endregion | 	#pragma endregion | ||||||
|  |  | ||||||
| 	#pragma region Operators | 	#pragma region Operators | ||||||
| 	/*template <typename VoxelType> |  | ||||||
| 	Volume<VoxelType>& Volume<VoxelType>::operator=(const Volume& rhs) |  | ||||||
| 	{ |  | ||||||
| 		 |  | ||||||
| 	}*/ |  | ||||||
| 	#pragma endregion		 | 	#pragma endregion		 | ||||||
|  |  | ||||||
| 	#pragma region Getters | 	#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> | 	template <typename VoxelType> | ||||||
| 	Region Volume<VoxelType>::getEnclosingRegion(void) const | 	Region Volume<VoxelType>::getEnclosingRegion(void) const | ||||||
| 	{ | 	{ | ||||||
| 		return Region(Vector3DInt32(0,0,0), Vector3DInt32(m_uWidth-1,m_uHeight-1,m_uDepth-1)); | 		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> | 	template <typename VoxelType> | ||||||
| 	uint16_t Volume<VoxelType>::getDepth(void) const | 	uint16_t Volume<VoxelType>::getWidth(void) const | ||||||
| 	{ | 	{ | ||||||
| 		return m_uDepth; | 		return m_uWidth; | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	template <typename VoxelType> |  | ||||||
| 	float Volume<VoxelType>::getDiagonalLength(void) const |  | ||||||
| 	{ |  | ||||||
| 		return m_fDiagonalLength; |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	/******************************************************************************* | ||||||
|  | 	\return The height of the volume in voxels | ||||||
|  | 	\sa getWidth(), getDepth() | ||||||
|  | 	*******************************************************************************/ | ||||||
| 	template <typename VoxelType> | 	template <typename VoxelType> | ||||||
| 	uint16_t Volume<VoxelType>::getHeight(void) const | 	uint16_t Volume<VoxelType>::getHeight(void) const | ||||||
| 	{ | 	{ | ||||||
| 		return m_uHeight; | 		return m_uHeight; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	/******************************************************************************* | ||||||
|  | 	\return The depth of the volume in voxels | ||||||
|  | 	\sa getWidth(), getHeight() | ||||||
|  | 	*******************************************************************************/ | ||||||
| 	template <typename VoxelType> | 	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> | 	template <typename VoxelType> | ||||||
| 	uint16_t Volume<VoxelType>::getShortestSideLength(void) const | 	uint16_t Volume<VoxelType>::getShortestSideLength(void) const | ||||||
| 	{ | 	{ | ||||||
| 		return m_uShortestSideLength; | 		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> | 	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> | 	template <typename VoxelType> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user