diff --git a/library/PolyVoxCore/CMakeLists.txt b/library/PolyVoxCore/CMakeLists.txt index 486cdf97..4e18c75e 100644 --- a/library/PolyVoxCore/CMakeLists.txt +++ b/library/PolyVoxCore/CMakeLists.txt @@ -50,8 +50,6 @@ SET(CORE_INC_FILES include/PolyVoxCore/IteratorController.h include/PolyVoxCore/IteratorController.inl include/PolyVoxCore/LargeVolume.h - include/PolyVoxCore/LargeVolume.inl - include/PolyVoxCore/LargeVolumeSampler.inl include/PolyVoxCore/LowPassFilter.h include/PolyVoxCore/LowPassFilter.inl include/PolyVoxCore/MarchingCubesSurfaceExtractor.h @@ -60,6 +58,9 @@ SET(CORE_INC_FILES include/PolyVoxCore/MaterialDensityPair.h include/PolyVoxCore/Mesh.h include/PolyVoxCore/Mesh.inl + include/PolyVoxCore/PagedVolume.h + include/PolyVoxCore/PagedVolume.inl + include/PolyVoxCore/PagedVolumeSampler.inl include/PolyVoxCore/Pager.h include/PolyVoxCore/PolyVoxForwardDeclarations.h include/PolyVoxCore/Picking.h @@ -71,9 +72,6 @@ SET(CORE_INC_FILES include/PolyVoxCore/Raycast.inl include/PolyVoxCore/Region.h include/PolyVoxCore/SimpleVolume.h - include/PolyVoxCore/SimpleVolume.inl - include/PolyVoxCore/SimpleVolumeBlock.inl - include/PolyVoxCore/SimpleVolumeSampler.inl include/PolyVoxCore/UncompressedBlock.h include/PolyVoxCore/UncompressedBlock.inl include/PolyVoxCore/Vector.h diff --git a/library/PolyVoxCore/include/PolyVoxCore/BaseVolume.h b/library/PolyVoxCore/include/PolyVoxCore/BaseVolume.h index eeb3c50d..3f5f1c0f 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/BaseVolume.h +++ b/library/PolyVoxCore/include/PolyVoxCore/BaseVolume.h @@ -32,7 +32,7 @@ freely, subject to the following restrictions: namespace PolyVox { /// The BaseVolume class provides common functionality and an interface for other volume classes to implement. You should not try to create an instance of this - /// class directly. Instead you should use RawVolume, SimpleVolume, or LargeVolume. + /// class directly. Instead you should use RawVolume, SimpleVolume, or PagedVolume. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// More details to come... //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/library/PolyVoxCore/include/PolyVoxCore/BaseVolume.inl b/library/PolyVoxCore/include/PolyVoxCore/BaseVolume.inl index dc280ee2..7669a4ca 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/BaseVolume.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/BaseVolume.inl @@ -26,7 +26,7 @@ namespace PolyVox //////////////////////////////////////////////////////////////////////////////// /// This is protected because you should never create a BaseVolume directly, you should instead use one of the derived classes. /// - /// \sa RawVolume, SimpleVolume, LargeVolume + /// \sa RawVolume, SimpleVolume, PagedVolume //////////////////////////////////////////////////////////////////////////////// template BaseVolume::BaseVolume(const Region& regValid) diff --git a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.h b/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.h index 968f82da..ff60e49f 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.h +++ b/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.h @@ -1,333 +1,7 @@ -/******************************************************************************* -Copyright (c) 2005-2009 David Williams - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*******************************************************************************/ - #ifndef __PolyVox_LargeVolume_H__ #define __PolyVox_LargeVolume_H__ -#include "PolyVoxCore/BaseVolume.h" -#include "PolyVoxCore/Pager.h" -#include "PolyVoxCore/Region.h" -#include "PolyVoxCore/UncompressedBlock.h" -#include "PolyVoxCore/Vector.h" +#include "PagedVolume.h" +#include "PolyVoxForwardDeclarations.h" -#include -#include //For abort() -#include //For memcpy -#include -#include -#include -#include -#include //For invalid_argument -#include - -namespace PolyVox -{ - /// The LargeVolume class provides a memory efficient method of storing voxel data while also allowing fast access and modification. - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /// A LargeVolume is essentially a 3D array in which each element (or voxel) is identified by a three dimensional (x,y,z) coordinate. - /// We use the LargeVolume class to store our data in an efficient way, and it is the input to many of the algorithms (such as the surface - /// extractors) which form the heart of PolyVox. The LargeVolume class is templatised so that different types of data can be stored within each voxel. - /// - /// Basic usage - /// ----------- - /// - /// The following code snippet shows how to construct a volume and demonstrates basic usage: - /// - /// \code - /// LargeVolume volume(Region(Vector3DInt32(0,0,0), Vector3DInt32(63,127,255))); - /// volume.setVoxelAt(15, 90, 42, Material8(5)); - /// std::cout << "Voxel at (15, 90, 42) has value: " << volume.getVoxelAt(15, 90, 42).getMaterial() << std::endl; - /// std::cout << "Width = " << volume.getWidth() << ", Height = " << volume.getHeight() << ", Depth = " << volume.getDepth() << std::endl; - /// \endcode - /// - /// In this particular example each voxel in the LargeVolume is of type 'Material8', as specified by the template parameter. This is one of several - /// predefined voxel types, and it is also possible to define your own. The Material8 type simply holds an integer value where zero represents - /// empty space and any other value represents a solid material. - /// - /// The LargeVolume constructor takes a Region as a parameter. This specifies the valid range of voxels which can be held in the volume, so in this - /// particular case the valid voxel positions are (0,0,0) to (63, 127, 255). Attempts to access voxels outside this range will result is accessing the - /// border value (see getBorderValue() and setBorderValue()). PolyVox also has support for near infinite volumes which will be discussed later. - /// - /// Access to individual voxels is provided via the setVoxelAt() and getVoxelAt() member functions. Advanced users may also be interested in - /// the Sampler class for faster read-only access to a large number of voxels. - /// - /// Lastly the example prints out some properties of the LargeVolume. Note that the dimentsions getWidth(), getHeight(), and getDepth() are inclusive, such - /// that the width is 64 when the range of valid x coordinates goes from 0 to 63. - /// - /// Data Representaion - /// ------------------ - /// 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 LargeVolume 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 LargeVolume 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. The data for each block is stored in a compressed form, which uses only a small amout of - /// memory but it is hard to modify the data. Therefore, before any given voxel can be modified, its corresponding block must be uncompressed. - /// - /// The compression and decompression of block is a relatively slow process and so we aim to do this as rarely as possible. In order - /// to achive this, the volume class stores a cache of recently used blocks and their associated uncompressed data. Each time a voxel - /// is touched a timestamp is updated on the corresponding block. When the cache becomes full the block with the oldest timestamp is - /// recompressed and moved out of the cache. - /// - /// Achieving high compression rates - /// -------------------------------- - /// The compression rates which can be achieved can vary significantly depending the nature of the data you are storing, but you can - /// encourage high compression rates by making your data as homogenous as possible. If you are simply storing a material with each - /// voxel then this will probably happen naturally. Games such as Minecraft which use this approach will typically involve large areas - /// of the same material which will compress down well. - /// - /// However, if you are storing density values then you may want to take some care. The advantage of storing smoothly changing values - /// is that you can get smooth surfaces extracted, but storing smoothly changing values inside or outside objects (rather than just - /// on the boundary) does not benefit the surface and is very hard to compress effectively. You may wish to apply some thresholding to - /// your density values to reduce this problem (this threasholding should only be applied to voxels who don't contribute to the surface). - /// - /// Paging large volumes - /// -------------------- - /// The compression scheme described previously will typically allow you to load several billion voxels into a few hundred megabytes of memory, - /// though as explained the exact compression rate is highly dependant on your data. If you have more data than this then PolyVox provides a - /// mechanism by which parts of the volume can be paged out of memory by calling user supplied callback functions. This mechanism allows a - /// potentially unlimited amount of data to be loaded, provided the user is able to take responsibility for storing any data which PolyVox - /// cannot fit in memory, and then returning it back to PolyVox on demand. For example, the user might choose to temporarily store this data - /// on disk or stream it to a remote database. - /// - /// You can construct such a LargeVolume as follows: - /// - /// \code - /// void myDataRequiredHandler(const ConstVolumeProxy& volume, const PolyVox::Region& reg) - /// { - /// //This function is being called because part of the data is missing from memory and needs to be supplied. The parameter - /// //'volume' provides access to the volume data, and the parameter 'reg' indicates which region of the volume you need fill. - /// } - /// - /// void myDataOverflowHandler(const ConstVolumeProxy& vol, const PolyVox::Region& reg) - /// { - /// //This function is being called because part of the data is about to be removed from memory. The parameter 'volume' - /// //provides access to the volume data, and the parameter 'reg' indicates which region of the volume you need to store. - /// } - /// - /// LargeVolumevolData(&myDataRequiredHandler, &myDataOverflowHandler); - /// \endcode - /// - /// Essentially you are providing an extension to the LargeVolume class - a way for data to be stored once PolyVox has run out of memory for it. Note - /// that you don't actually have to do anything with the data - you could simply decide that once it gets removed from memory it doesn't matter - /// anymore. But you still need to be ready to then provide something to PolyVox (even if it's just default data) in the event that it is requested. - /// - /// Cache-aware traversal - /// --------------------- - /// You might be suprised at just how many cache misses can occur when you traverse the volume in a naive manner. Consider a 1024x1024x1024 volume - /// with blocks of size 32x32x32. And imagine you iterate over this volume with a simple three-level for loop which iterates over x, the y, then z. - /// If you start at position (0,0,0) then ny the time you reach position (1023,0,0) you have touched 1024 voxels along one edge of the volume and - /// have pulled 32 blocks into the cache. By the time you reach (1023,1023,0) you have hit 1024x1024 voxels and pulled 32x32 blocks into the cache. - /// You are now ready to touch voxel (0,0,1) which is right nect to where you started, but unless your cache is at least 32x32 blocks large then this - /// initial block has already been cleared from the cache. - /// - /// Ensuring you have a large enough cache size can obviously help the above situation, but you might also consider iterating over the voxels in a - /// different order. For example, if you replace your three-level loop with a six-level loop then you can first process all the voxels between (0,0,0) - /// and (31,31,31), then process all the voxels between (32,0,0) and (63,0,0), and so forth. Using this approach you will have no cache misses even - /// is your cache sise is only one. Of course the logic is more complex, but writing code in such a cache-aware manner may be beneficial in some situations. - /// - /// Threading - /// --------- - /// The LargeVolume class does not make any guarentees about thread safety. You should ensure that all accesses are performed from the same thread. - /// This is true even if you are only reading data from the volume, as concurrently reading from different threads can invalidate the contents - /// of the block cache (amoung other problems). - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - template - class LargeVolume : public BaseVolume - { - public: - //There seems to be some descrepency between Visual Studio and GCC about how the following class should be declared. - //There is a work around (see also See http://goo.gl/qu1wn) given below which appears to work on VS2010 and GCC, but - //which seems to cause internal compiler errors on VS2008 when building with the /Gm 'Enable Minimal Rebuild' compiler - //option. For now it seems best to 'fix' it with the preprocessor insstead, but maybe the workaround can be reinstated - //in the future - //typedef Volume VolumeOfVoxelType; //Workaround for GCC/VS2010 differences. - //class Sampler : public VolumeOfVoxelType::template Sampler< LargeVolume > - #ifndef SWIG -#if defined(_MSC_VER) - class Sampler : public BaseVolume::Sampler< LargeVolume > //This line works on VS2010 -#else - class Sampler : public BaseVolume::template Sampler< LargeVolume > //This line works on GCC -#endif - { - public: - Sampler(LargeVolume* volume); - ~Sampler(); - - /// \deprecated - POLYVOX_DEPRECATED VoxelType getSubSampledVoxel(uint8_t uLevel) const; - inline VoxelType getVoxel(void) const; - - void setPosition(const Vector3DInt32& v3dNewPos); - void setPosition(int32_t xPos, int32_t yPos, int32_t zPos); - inline bool setVoxel(VoxelType tValue); - - void movePositiveX(void); - void movePositiveY(void); - void movePositiveZ(void); - - void moveNegativeX(void); - void moveNegativeY(void); - void moveNegativeZ(void); - - inline VoxelType peekVoxel1nx1ny1nz(void) const; - inline VoxelType peekVoxel1nx1ny0pz(void) const; - inline VoxelType peekVoxel1nx1ny1pz(void) const; - inline VoxelType peekVoxel1nx0py1nz(void) const; - inline VoxelType peekVoxel1nx0py0pz(void) const; - inline VoxelType peekVoxel1nx0py1pz(void) const; - inline VoxelType peekVoxel1nx1py1nz(void) const; - inline VoxelType peekVoxel1nx1py0pz(void) const; - inline VoxelType peekVoxel1nx1py1pz(void) const; - - inline VoxelType peekVoxel0px1ny1nz(void) const; - inline VoxelType peekVoxel0px1ny0pz(void) const; - inline VoxelType peekVoxel0px1ny1pz(void) const; - inline VoxelType peekVoxel0px0py1nz(void) const; - inline VoxelType peekVoxel0px0py0pz(void) const; - inline VoxelType peekVoxel0px0py1pz(void) const; - inline VoxelType peekVoxel0px1py1nz(void) const; - inline VoxelType peekVoxel0px1py0pz(void) const; - inline VoxelType peekVoxel0px1py1pz(void) const; - - inline VoxelType peekVoxel1px1ny1nz(void) const; - inline VoxelType peekVoxel1px1ny0pz(void) const; - inline VoxelType peekVoxel1px1ny1pz(void) const; - inline VoxelType peekVoxel1px0py1nz(void) const; - inline VoxelType peekVoxel1px0py0pz(void) const; - inline VoxelType peekVoxel1px0py1pz(void) const; - inline VoxelType peekVoxel1px1py1nz(void) const; - inline VoxelType peekVoxel1px1py0pz(void) const; - inline VoxelType peekVoxel1px1py1pz(void) const; - - private: - //Other current position information - VoxelType* mCurrentVoxel; - }; - - #endif - - public: - /// Constructor for creating a fixed size volume. - LargeVolume - ( - const Region& regValid, - Pager* pPager = nullptr, - uint16_t uBlockSideLength = 32 - ); - /// Destructor - ~LargeVolume(); - - /// Gets a voxel at the position given by x,y,z coordinates - 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 - 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::Validate, VoxelType tBorder = VoxelType()) const; - /// Gets a voxel at the position given by a 3D vector - VoxelType getVoxel(const Vector3DInt32& v3dPos, WrapMode eWrapMode = WrapModes::Validate, VoxelType tBorder = VoxelType()) const; - - /// Gets a voxel at the position given by x,y,z coordinates - POLYVOX_DEPRECATED VoxelType getVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos) const; - /// Gets a voxel at the position given by a 3D vector - POLYVOX_DEPRECATED VoxelType getVoxelAt(const Vector3DInt32& v3dPos) const; - - /// Sets the number of blocks for which uncompressed data is stored - void setMemoryUsageLimit(uint32_t uMemoryUsageInBytes); - /// 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, WrapMode eWrapMode = WrapModes::Validate); - /// Sets the voxel at the position given by a 3D vector - void setVoxel(const Vector3DInt32& v3dPos, VoxelType tValue, WrapMode eWrapMode = WrapModes::Validate); - /// Sets the voxel at the position given by x,y,z coordinates - bool setVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue); - /// Sets the voxel at the position given by a 3D vector - bool setVoxelAt(const Vector3DInt32& v3dPos, VoxelType tValue); - /// Tries to ensure that the voxels within the specified Region are loaded into memory. - void prefetch(Region regPrefetch); - /// Ensures that any voxels within the specified Region are removed from memory. - void flush(Region regFlush); - /// Removes all voxels from memory - void flushAll(); - - /// Calculates approximatly how many bytes of memory the volume is currently using. - uint32_t calculateSizeInBytes(void); - - protected: - /// Copy constructor - LargeVolume(const LargeVolume& rhs); - - /// Assignment operator - LargeVolume& operator=(const LargeVolume& rhs); - - private: - - typedef std::unordered_map > > SharedPtrBlockMap; - typedef std::unordered_map > > WeakPtrBlockMap; - - void initialise(); - - // 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; - - std::shared_ptr< UncompressedBlock > getUncompressedBlock(int32_t uBlockX, int32_t uBlockY, int32_t uBlockZ) const; - - void purgeNullPtrsFromAllBlocks(void) const; - - // The block data - mutable WeakPtrBlockMap m_pAllBlocks; - mutable SharedPtrBlockMap m_pRecentlyUsedBlocks; - - mutable uint32_t m_uTimestamper; - mutable Vector3DInt32 m_v3dLastAccessedBlockPos; - mutable std::shared_ptr< UncompressedBlock > m_pLastAccessedBlock; - uint32_t m_uBlockCountLimit; - - // The size of the volume - Region m_regValidRegionInBlocks; - - // The size of the blocks - uint16_t m_uBlockSideLength; - uint8_t m_uBlockSideLengthPower; - - Pager* m_pPager; - - // Enough to make sure a blocks and it's neighbours can be loaded, with a few to spare. - static const uint32_t uMinPracticalNoOfBlocks = 32; - // Should preent multi-gigabyte volumes with reasonable block sizes. - static const uint32_t uMaxPracticalNoOfBlocks = 32768; - }; -} - -#include "PolyVoxCore/LargeVolume.inl" -#include "PolyVoxCore/LargeVolumeSampler.inl" - -#endif //__PolyVox_LargeVolume_H__ +#endif //__PolyVox_LargeVolume_H__ \ No newline at end of file diff --git a/library/PolyVoxCore/include/PolyVoxCore/PagedVolume.h b/library/PolyVoxCore/include/PolyVoxCore/PagedVolume.h new file mode 100644 index 00000000..4c08721c --- /dev/null +++ b/library/PolyVoxCore/include/PolyVoxCore/PagedVolume.h @@ -0,0 +1,333 @@ +/******************************************************************************* +Copyright (c) 2005-2009 David Williams + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*******************************************************************************/ + +#ifndef __PolyVox_PagedVolume_H__ +#define __PolyVox_PagedVolume_H__ + +#include "PolyVoxCore/BaseVolume.h" +#include "PolyVoxCore/Pager.h" +#include "PolyVoxCore/Region.h" +#include "PolyVoxCore/UncompressedBlock.h" +#include "PolyVoxCore/Vector.h" + +#include +#include //For abort() +#include //For memcpy +#include +#include +#include +#include +#include //For invalid_argument +#include + +namespace PolyVox +{ + /// The PagedVolume class provides a memory efficient method of storing voxel data while also allowing fast access and modification. + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /// A PagedVolume is essentially a 3D array in which each element (or voxel) is identified by a three dimensional (x,y,z) coordinate. + /// We use the PagedVolume class to store our data in an efficient way, and it is the input to many of the algorithms (such as the surface + /// extractors) which form the heart of PolyVox. The PagedVolume class is templatised so that different types of data can be stored within each voxel. + /// + /// Basic usage + /// ----------- + /// + /// The following code snippet shows how to construct a volume and demonstrates basic usage: + /// + /// \code + /// PagedVolume volume(Region(Vector3DInt32(0,0,0), Vector3DInt32(63,127,255))); + /// volume.setVoxelAt(15, 90, 42, Material8(5)); + /// std::cout << "Voxel at (15, 90, 42) has value: " << volume.getVoxelAt(15, 90, 42).getMaterial() << std::endl; + /// std::cout << "Width = " << volume.getWidth() << ", Height = " << volume.getHeight() << ", Depth = " << volume.getDepth() << std::endl; + /// \endcode + /// + /// In this particular example each voxel in the PagedVolume is of type 'Material8', as specified by the template parameter. This is one of several + /// predefined voxel types, and it is also possible to define your own. The Material8 type simply holds an integer value where zero represents + /// empty space and any other value represents a solid material. + /// + /// The PagedVolume constructor takes a Region as a parameter. This specifies the valid range of voxels which can be held in the volume, so in this + /// particular case the valid voxel positions are (0,0,0) to (63, 127, 255). Attempts to access voxels outside this range will result is accessing the + /// border value (see getBorderValue() and setBorderValue()). PolyVox also has support for near infinite volumes which will be discussed later. + /// + /// Access to individual voxels is provided via the setVoxelAt() and getVoxelAt() member functions. Advanced users may also be interested in + /// the Sampler class for faster read-only access to a large number of voxels. + /// + /// Lastly the example prints out some properties of the PagedVolume. Note that the dimentsions getWidth(), getHeight(), and getDepth() are inclusive, such + /// that the width is 64 when the range of valid x coordinates goes from 0 to 63. + /// + /// Data Representaion + /// ------------------ + /// 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 PagedVolume 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 PagedVolume 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. The data for each block is stored in a compressed form, which uses only a small amout of + /// memory but it is hard to modify the data. Therefore, before any given voxel can be modified, its corresponding block must be uncompressed. + /// + /// The compression and decompression of block is a relatively slow process and so we aim to do this as rarely as possible. In order + /// to achive this, the volume class stores a cache of recently used blocks and their associated uncompressed data. Each time a voxel + /// is touched a timestamp is updated on the corresponding block. When the cache becomes full the block with the oldest timestamp is + /// recompressed and moved out of the cache. + /// + /// Achieving high compression rates + /// -------------------------------- + /// The compression rates which can be achieved can vary significantly depending the nature of the data you are storing, but you can + /// encourage high compression rates by making your data as homogenous as possible. If you are simply storing a material with each + /// voxel then this will probably happen naturally. Games such as Minecraft which use this approach will typically involve large areas + /// of the same material which will compress down well. + /// + /// However, if you are storing density values then you may want to take some care. The advantage of storing smoothly changing values + /// is that you can get smooth surfaces extracted, but storing smoothly changing values inside or outside objects (rather than just + /// on the boundary) does not benefit the surface and is very hard to compress effectively. You may wish to apply some thresholding to + /// your density values to reduce this problem (this threasholding should only be applied to voxels who don't contribute to the surface). + /// + /// Paging large volumes + /// -------------------- + /// The compression scheme described previously will typically allow you to load several billion voxels into a few hundred megabytes of memory, + /// though as explained the exact compression rate is highly dependant on your data. If you have more data than this then PolyVox provides a + /// mechanism by which parts of the volume can be paged out of memory by calling user supplied callback functions. This mechanism allows a + /// potentially unlimited amount of data to be loaded, provided the user is able to take responsibility for storing any data which PolyVox + /// cannot fit in memory, and then returning it back to PolyVox on demand. For example, the user might choose to temporarily store this data + /// on disk or stream it to a remote database. + /// + /// You can construct such a PagedVolume as follows: + /// + /// \code + /// void myDataRequiredHandler(const ConstVolumeProxy& volume, const PolyVox::Region& reg) + /// { + /// //This function is being called because part of the data is missing from memory and needs to be supplied. The parameter + /// //'volume' provides access to the volume data, and the parameter 'reg' indicates which region of the volume you need fill. + /// } + /// + /// void myDataOverflowHandler(const ConstVolumeProxy& vol, const PolyVox::Region& reg) + /// { + /// //This function is being called because part of the data is about to be removed from memory. The parameter 'volume' + /// //provides access to the volume data, and the parameter 'reg' indicates which region of the volume you need to store. + /// } + /// + /// PagedVolumevolData(&myDataRequiredHandler, &myDataOverflowHandler); + /// \endcode + /// + /// Essentially you are providing an extension to the PagedVolume class - a way for data to be stored once PolyVox has run out of memory for it. Note + /// that you don't actually have to do anything with the data - you could simply decide that once it gets removed from memory it doesn't matter + /// anymore. But you still need to be ready to then provide something to PolyVox (even if it's just default data) in the event that it is requested. + /// + /// Cache-aware traversal + /// --------------------- + /// You might be suprised at just how many cache misses can occur when you traverse the volume in a naive manner. Consider a 1024x1024x1024 volume + /// with blocks of size 32x32x32. And imagine you iterate over this volume with a simple three-level for loop which iterates over x, the y, then z. + /// If you start at position (0,0,0) then ny the time you reach position (1023,0,0) you have touched 1024 voxels along one edge of the volume and + /// have pulled 32 blocks into the cache. By the time you reach (1023,1023,0) you have hit 1024x1024 voxels and pulled 32x32 blocks into the cache. + /// You are now ready to touch voxel (0,0,1) which is right nect to where you started, but unless your cache is at least 32x32 blocks large then this + /// initial block has already been cleared from the cache. + /// + /// Ensuring you have a large enough cache size can obviously help the above situation, but you might also consider iterating over the voxels in a + /// different order. For example, if you replace your three-level loop with a six-level loop then you can first process all the voxels between (0,0,0) + /// and (31,31,31), then process all the voxels between (32,0,0) and (63,0,0), and so forth. Using this approach you will have no cache misses even + /// is your cache sise is only one. Of course the logic is more complex, but writing code in such a cache-aware manner may be beneficial in some situations. + /// + /// Threading + /// --------- + /// The PagedVolume class does not make any guarentees about thread safety. You should ensure that all accesses are performed from the same thread. + /// This is true even if you are only reading data from the volume, as concurrently reading from different threads can invalidate the contents + /// of the block cache (amoung other problems). + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + template + class PagedVolume : public BaseVolume + { + public: + //There seems to be some descrepency between Visual Studio and GCC about how the following class should be declared. + //There is a work around (see also See http://goo.gl/qu1wn) given below which appears to work on VS2010 and GCC, but + //which seems to cause internal compiler errors on VS2008 when building with the /Gm 'Enable Minimal Rebuild' compiler + //option. For now it seems best to 'fix' it with the preprocessor insstead, but maybe the workaround can be reinstated + //in the future + //typedef Volume VolumeOfVoxelType; //Workaround for GCC/VS2010 differences. + //class Sampler : public VolumeOfVoxelType::template Sampler< PagedVolume > + #ifndef SWIG +#if defined(_MSC_VER) + class Sampler : public BaseVolume::Sampler< PagedVolume > //This line works on VS2010 +#else + class Sampler : public BaseVolume::template Sampler< PagedVolume > //This line works on GCC +#endif + { + public: + Sampler(PagedVolume* volume); + ~Sampler(); + + /// \deprecated + POLYVOX_DEPRECATED VoxelType getSubSampledVoxel(uint8_t uLevel) const; + inline VoxelType getVoxel(void) const; + + void setPosition(const Vector3DInt32& v3dNewPos); + void setPosition(int32_t xPos, int32_t yPos, int32_t zPos); + inline bool setVoxel(VoxelType tValue); + + void movePositiveX(void); + void movePositiveY(void); + void movePositiveZ(void); + + void moveNegativeX(void); + void moveNegativeY(void); + void moveNegativeZ(void); + + inline VoxelType peekVoxel1nx1ny1nz(void) const; + inline VoxelType peekVoxel1nx1ny0pz(void) const; + inline VoxelType peekVoxel1nx1ny1pz(void) const; + inline VoxelType peekVoxel1nx0py1nz(void) const; + inline VoxelType peekVoxel1nx0py0pz(void) const; + inline VoxelType peekVoxel1nx0py1pz(void) const; + inline VoxelType peekVoxel1nx1py1nz(void) const; + inline VoxelType peekVoxel1nx1py0pz(void) const; + inline VoxelType peekVoxel1nx1py1pz(void) const; + + inline VoxelType peekVoxel0px1ny1nz(void) const; + inline VoxelType peekVoxel0px1ny0pz(void) const; + inline VoxelType peekVoxel0px1ny1pz(void) const; + inline VoxelType peekVoxel0px0py1nz(void) const; + inline VoxelType peekVoxel0px0py0pz(void) const; + inline VoxelType peekVoxel0px0py1pz(void) const; + inline VoxelType peekVoxel0px1py1nz(void) const; + inline VoxelType peekVoxel0px1py0pz(void) const; + inline VoxelType peekVoxel0px1py1pz(void) const; + + inline VoxelType peekVoxel1px1ny1nz(void) const; + inline VoxelType peekVoxel1px1ny0pz(void) const; + inline VoxelType peekVoxel1px1ny1pz(void) const; + inline VoxelType peekVoxel1px0py1nz(void) const; + inline VoxelType peekVoxel1px0py0pz(void) const; + inline VoxelType peekVoxel1px0py1pz(void) const; + inline VoxelType peekVoxel1px1py1nz(void) const; + inline VoxelType peekVoxel1px1py0pz(void) const; + inline VoxelType peekVoxel1px1py1pz(void) const; + + private: + //Other current position information + VoxelType* mCurrentVoxel; + }; + + #endif + + public: + /// Constructor for creating a fixed size volume. + PagedVolume + ( + const Region& regValid, + Pager* pPager = nullptr, + uint16_t uBlockSideLength = 32 + ); + /// Destructor + ~PagedVolume(); + + /// Gets a voxel at the position given by x,y,z coordinates + 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 + 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::Validate, VoxelType tBorder = VoxelType()) const; + /// Gets a voxel at the position given by a 3D vector + VoxelType getVoxel(const Vector3DInt32& v3dPos, WrapMode eWrapMode = WrapModes::Validate, VoxelType tBorder = VoxelType()) const; + + /// Gets a voxel at the position given by x,y,z coordinates + POLYVOX_DEPRECATED VoxelType getVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos) const; + /// Gets a voxel at the position given by a 3D vector + POLYVOX_DEPRECATED VoxelType getVoxelAt(const Vector3DInt32& v3dPos) const; + + /// Sets the number of blocks for which uncompressed data is stored + void setMemoryUsageLimit(uint32_t uMemoryUsageInBytes); + /// 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, WrapMode eWrapMode = WrapModes::Validate); + /// Sets the voxel at the position given by a 3D vector + void setVoxel(const Vector3DInt32& v3dPos, VoxelType tValue, WrapMode eWrapMode = WrapModes::Validate); + /// Sets the voxel at the position given by x,y,z coordinates + bool setVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue); + /// Sets the voxel at the position given by a 3D vector + bool setVoxelAt(const Vector3DInt32& v3dPos, VoxelType tValue); + /// Tries to ensure that the voxels within the specified Region are loaded into memory. + void prefetch(Region regPrefetch); + /// Ensures that any voxels within the specified Region are removed from memory. + void flush(Region regFlush); + /// Removes all voxels from memory + void flushAll(); + + /// Calculates approximatly how many bytes of memory the volume is currently using. + uint32_t calculateSizeInBytes(void); + + protected: + /// Copy constructor + PagedVolume(const PagedVolume& rhs); + + /// Assignment operator + PagedVolume& operator=(const PagedVolume& rhs); + + private: + + typedef std::unordered_map > > SharedPtrBlockMap; + typedef std::unordered_map > > WeakPtrBlockMap; + + void initialise(); + + // 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; + + std::shared_ptr< UncompressedBlock > getUncompressedBlock(int32_t uBlockX, int32_t uBlockY, int32_t uBlockZ) const; + + void purgeNullPtrsFromAllBlocks(void) const; + + // The block data + mutable WeakPtrBlockMap m_pAllBlocks; + mutable SharedPtrBlockMap m_pRecentlyUsedBlocks; + + mutable uint32_t m_uTimestamper; + mutable Vector3DInt32 m_v3dLastAccessedBlockPos; + mutable std::shared_ptr< UncompressedBlock > m_pLastAccessedBlock; + uint32_t m_uBlockCountLimit; + + // The size of the volume + Region m_regValidRegionInBlocks; + + // The size of the blocks + uint16_t m_uBlockSideLength; + uint8_t m_uBlockSideLengthPower; + + Pager* m_pPager; + + // Enough to make sure a blocks and it's neighbours can be loaded, with a few to spare. + static const uint32_t uMinPracticalNoOfBlocks = 32; + // Should preent multi-gigabyte volumes with reasonable block sizes. + static const uint32_t uMaxPracticalNoOfBlocks = 32768; + }; +} + +#include "PolyVoxCore/PagedVolume.inl" +#include "PolyVoxCore/PagedVolumeSampler.inl" + +#endif //__PolyVox_PagedVolume_H__ diff --git a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl b/library/PolyVoxCore/include/PolyVoxCore/PagedVolume.inl similarity index 92% rename from library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl rename to library/PolyVoxCore/include/PolyVoxCore/PagedVolume.inl index 25588e69..07c0e23e 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/PagedVolume.inl @@ -1,699 +1,699 @@ -/******************************************************************************* -Copyright (c) 2005-2009 David Williams - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*******************************************************************************/ - -#include "PolyVoxCore/Impl/ErrorHandling.h" - -#include -#include - -namespace PolyVox -{ - //////////////////////////////////////////////////////////////////////////////// - /// This constructor creates a volume with a fixed size which is specified as a parameter. By default this constructor will not enable paging but you can override this if desired. If you do wish to enable paging then you are required to provide the call back function (see the other LargeVolume constructor). - /// \param regValid Specifies the minimum and maximum valid voxel positions. - /// \param pBlockCompressor An implementation of the Compressor interface which is used to compress blocks in memory. - /// \param dataRequiredHandler The callback function which will be called when PolyVox tries to use data which is not currently in momory. - /// \param dataOverflowHandler The callback function which will be called when PolyVox has too much data and needs to remove some from memory. - /// \param bPagingEnabled Controls whether or not paging is enabled for this LargeVolume. - /// \param uBlockSideLength The size of the blocks making up the volume. Small blocks will compress/decompress faster, but there will also be more of them meaning voxel access could be slower. - //////////////////////////////////////////////////////////////////////////////// - template - LargeVolume::LargeVolume - ( - const Region& regValid, - Pager* pPager, - uint16_t uBlockSideLength - ) - :BaseVolume(regValid) - { - m_uBlockSideLength = uBlockSideLength; - m_pPager = pPager; - - if (m_pPager) - { - // If the user is creating a vast (almost infinite) volume then we can bet they will be - // expecting a high memory usage and will want a fair number of blocks to play around with. - if (regValid == Region::MaxRegion) - { - m_uBlockCountLimit = 1024; - } - else - { - // Otherwise we try to choose a block count to avoid too much thrashing, particularly when iterating - // over the whole volume. This means at least enough blocks to cover one edge of the volume, and ideally - // enough for a whole face. Which face? Longest edge by shortest edge seems like a reasonable guess. - uint32_t longestSide = (std::max)(regValid.getWidthInVoxels(), (std::max)(regValid.getHeightInVoxels(), regValid.getDepthInVoxels())); - uint32_t shortestSide = (std::min)(regValid.getWidthInVoxels(), (std::min)(regValid.getHeightInVoxels(), regValid.getDepthInVoxels())); - - longestSide /= m_uBlockSideLength; - shortestSide /= m_uBlockSideLength; - - m_uBlockCountLimit = longestSide * shortestSide; - } - } - else - { - // If there is no pager provided then we set the block limit to the maximum - // value to ensure the system never attempts to page blocks out of memory. - m_uBlockCountLimit = (std::numeric_limits::max)(); - } - - // Make sure the calculated block limit is within practical bounds - m_uBlockCountLimit = (std::max)(m_uBlockCountLimit, uMinPracticalNoOfBlocks); - m_uBlockCountLimit = (std::min)(m_uBlockCountLimit, uMaxPracticalNoOfBlocks); - - uint32_t uUncompressedBlockSizeInBytes = UncompressedBlock::calculateSizeInBytes(m_uBlockSideLength); - POLYVOX_LOG_DEBUG("Memory usage limit for volume initially set to " << (m_uBlockCountLimit * uUncompressedBlockSizeInBytes) / (1024 * 1024) - << "Mb (" << m_uBlockCountLimit << " blocks of " << uUncompressedBlockSizeInBytes / 1024 << "Kb each)."); - - initialise(); - } - - //////////////////////////////////////////////////////////////////////////////// - /// This function should never be called. Copying volumes by value would be expensive, and we want to prevent users from doing - /// it by accident (such as when passing them as paramenters to functions). That said, there are times when you really do want to - /// make a copy of a volume and in this case you should look at the VolumeResampler. - /// - /// \sa VolumeResampler - //////////////////////////////////////////////////////////////////////////////// - template - LargeVolume::LargeVolume(const LargeVolume& /*rhs*/) - { - POLYVOX_THROW(not_implemented, "Volume copy constructor not implemented for performance reasons."); - } - - //////////////////////////////////////////////////////////////////////////////// - /// Destroys the volume The destructor will call flushAll() to ensure that a paging volume has the chance to save it's data via the dataOverflowHandler() if desired. - //////////////////////////////////////////////////////////////////////////////// - template - LargeVolume::~LargeVolume() - { - flushAll(); - } - - //////////////////////////////////////////////////////////////////////////////// - /// This function should never be called. Copying volumes by value would be expensive, and we want to prevent users from doing - /// it by accident (such as when passing them as paramenters to functions). That said, there are times when you really do want to - /// make a copy of a volume and in this case you should look at the Volumeresampler. - /// - /// \sa VolumeResampler - //////////////////////////////////////////////////////////////////////////////// - template - LargeVolume& LargeVolume::operator=(const LargeVolume& /*rhs*/) - { - POLYVOX_THROW(not_implemented, "Volume assignment operator not implemented for performance reasons."); - } - - //////////////////////////////////////////////////////////////////////////////// - /// 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 - template - VoxelType LargeVolume::getVoxel(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tBorder) const - { - // Simply call through to the real implementation - return getVoxelImpl(uXPos, uYPos, uZPos, WrapModeType(), tBorder); - } - - //////////////////////////////////////////////////////////////////////////////// - /// 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 - template - VoxelType LargeVolume::getVoxel(const Vector3DInt32& v3dPos, VoxelType tBorder) const - { - // 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::Validate: - 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::AssumeValid: - 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); - } - - //////////////////////////////////////////////////////////////////////////////// - /// \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::getVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos) const - { - if(this->m_regValidRegion.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos))) - { - 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)); - - auto pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ); - - return pUncompressedBlock->getVoxel(xOffset, yOffset, zOffset); - } - else - { - return this->getBorderValue(); - } - } - - //////////////////////////////////////////////////////////////////////////////// - /// \param v3dPos The 3D position of the voxel - /// \return The voxel value - //////////////////////////////////////////////////////////////////////////////// - template - VoxelType LargeVolume::getVoxelAt(const Vector3DInt32& v3dPos) const - { - return getVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ()); - } - - //////////////////////////////////////////////////////////////////////////////// - /// 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 - /// volume data and then set it to a smaller value (e.g.64) for general processing. - /// \param uMaxNumberOfUncompressedBlocks The number of blocks for which uncompressed data can be cached. - //////////////////////////////////////////////////////////////////////////////// - template - void LargeVolume::setMemoryUsageLimit(uint32_t uMemoryUsageInBytes) - { - POLYVOX_THROW_IF(!m_pPager, invalid_operation, "You cannot limit the memory usage of the volume because it was created without a pager attached."); - - // Calculate the number of blocks based on the memory limit and the size of each block. - uint32_t uUncompressedBlockSizeInBytes = UncompressedBlock::calculateSizeInBytes(m_uBlockSideLength); - m_uBlockCountLimit = uMemoryUsageInBytes / uUncompressedBlockSizeInBytes; - - // We need at least a few blocks available to avoid thrashing, and in pratice there will probably be hundreds. - POLYVOX_LOG_WARNING_IF(m_uBlockCountLimit < uMinPracticalNoOfBlocks, "Requested memory usage limit of " - << uMemoryUsageInBytes / (1024 * 1024) << "Mb is too low and cannot be adhered to."); - m_uBlockCountLimit = (std::max)(m_uBlockCountLimit, uMinPracticalNoOfBlocks); - m_uBlockCountLimit = (std::min)(m_uBlockCountLimit, uMaxPracticalNoOfBlocks); - - // If the new limit is less than the number of blocks already loaded then the easiest solution is to flush and start loading again. - if (m_pRecentlyUsedBlocks.size() > m_uBlockCountLimit) - { - flushAll(); - } - - POLYVOX_LOG_DEBUG("Memory usage limit for volume now set to " << (m_uBlockCountLimit * uUncompressedBlockSizeInBytes) / (1024 * 1024) - << "Mb (" << m_uBlockCountLimit << " blocks of " << uUncompressedBlockSizeInBytes / 1024 << "Kb each)."); - } - - //////////////////////////////////////////////////////////////////////////////// - /// \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 tValue the value to which the voxel will be set - /// \param eWrapMode Specifies the behaviour when the requested position is outside of the volume. - /// This must be set to 'None' or 'DontCheck'. Other wrap modes cannot be used when writing to volume data. - //////////////////////////////////////////////////////////////////////////////// - template - void LargeVolume::setVoxel(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue, WrapMode eWrapMode) - { - if((eWrapMode != WrapModes::Validate) && (eWrapMode != WrapModes::AssumeValid)) - { - POLYVOX_THROW(std::invalid_argument, "Invalid wrap mode in call to setVoxel(). It must be 'None' or 'DontCheck'."); - } - - // This validation is skipped if the wrap mode is 'DontCheck' - if(eWrapMode == WrapModes::Validate) - { - 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)); - - auto pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ); - pUncompressedBlock->setVoxelAt(xOffset, yOffset, zOffset, tValue); - } - - //////////////////////////////////////////////////////////////////////////////// - /// \param v3dPos the 3D position of the voxel - /// \param tValue the value to which the voxel will be set - /// \param eWrapMode Specifies the behaviour when the requested position is outside of the volume. - /// This must be set to 'None' or 'DontCheck'. Other wrap modes cannot be used when writing to volume data. - //////////////////////////////////////////////////////////////////////////////// - template - void LargeVolume::setVoxel(const Vector3DInt32& v3dPos, VoxelType tValue, WrapMode eWrapMode) - { - setVoxel(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ(), tValue, eWrapMode); - } - - //////////////////////////////////////////////////////////////////////////////// - /// \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 tValue the value to which the voxel will be set - /// \return whether the requested position is inside the volume - //////////////////////////////////////////////////////////////////////////////// - template - bool LargeVolume::setVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue) - { - // PolyVox does not throw an exception when a voxel is out of range. Please see 'Error Handling' in the User Manual. - POLYVOX_ASSERT(this->m_regValidRegion.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos)), "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)); - - auto pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ); - - pUncompressedBlock->setVoxelAt(xOffset, yOffset, zOffset, tValue); - - //Return true to indicate that we modified a voxel. - return true; - } - - //////////////////////////////////////////////////////////////////////////////// - /// \param v3dPos the 3D position of the voxel - /// \param tValue the value to which the voxel will be set - /// \return whether the requested position is inside the volume - //////////////////////////////////////////////////////////////////////////////// - template - bool LargeVolume::setVoxelAt(const Vector3DInt32& v3dPos, VoxelType tValue) - { - return setVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ(), tValue); - } - - - //////////////////////////////////////////////////////////////////////////////// - /// Note that if *NOTE - update docs - MaxNumberOfBlocksInMemory no longer exists* MaxNumberOfBlocksInMemory is not large enough to support the region this function will only load part of the region. In this case it is undefined which parts will actually be loaded. If all the voxels in the given region are already loaded, this function will not do anything. Other voxels might be unloaded to make space for the new voxels. - /// \param regPrefetch The Region of voxels to prefetch into memory. - //////////////////////////////////////////////////////////////////////////////// - template - void LargeVolume::prefetch(Region regPrefetch) - { - // Convert the start and end positions into block space coordinates - Vector3DInt32 v3dStart; - for(int i = 0; i < 3; i++) - { - v3dStart.setElement(i, regPrefetch.getLowerCorner().getElement(i) >> m_uBlockSideLengthPower); - } - - Vector3DInt32 v3dEnd; - for(int i = 0; i < 3; i++) - { - v3dEnd.setElement(i, regPrefetch.getUpperCorner().getElement(i) >> m_uBlockSideLengthPower); - } - - // Ensure we don't page in more blocks than the volume can hold. - Region region(v3dStart, v3dEnd); - uint32_t uNoOfBlocks = static_cast(region.getWidthInVoxels() * region.getHeightInVoxels() * region.getDepthInVoxels()); - POLYVOX_LOG_WARNING_IF(uNoOfBlocks > m_uBlockCountLimit, "Attempting to prefetch more than the maximum number of blocks."); - uNoOfBlocks = (std::min)(uNoOfBlocks, m_uBlockCountLimit); - - // Loops over the specified positions and touch the corresponding blocks. - for(int32_t x = v3dStart.getX(); x <= v3dEnd.getX(); x++) - { - for(int32_t y = v3dStart.getY(); y <= v3dEnd.getY(); y++) - { - for(int32_t z = v3dStart.getZ(); z <= v3dEnd.getZ(); z++) - { - getUncompressedBlock(x,y,z); - } - } - } - } - - //////////////////////////////////////////////////////////////////////////////// - /// Removes all voxels from memory, and calls dataOverflowHandler() to ensure the application has a chance to store the data. - //////////////////////////////////////////////////////////////////////////////// - template - void LargeVolume::flushAll() - { - POLYVOX_LOG_WARNING_IF(!m_pPager, "Data discarded by flush operation as no pager is attached."); - - // Clear this pointer so it doesn't hang on to any blocks. - m_pLastAccessedBlock = nullptr; - - // Erase all the most recently used blocks. - m_pRecentlyUsedBlocks.clear(); - - // Remove deleted blocks from the list of all loaded blocks. - purgeNullPtrsFromAllBlocks(); - - // If there are still some blocks left then this is a cause for concern. Perhaps samplers are holding on to them? - POLYVOX_LOG_WARNING_IF(m_pAllBlocks.size() > 0, "Blocks still exist after performing flushAll()! Perhaps you have samplers attached?"); - } - - //////////////////////////////////////////////////////////////////////////////// - /// Removes all voxels in the specified Region from memory, and calls dataOverflowHandler() to ensure the application has a chance to store the data. It is possible that there are no voxels loaded in the Region, in which case the function will have no effect. - //////////////////////////////////////////////////////////////////////////////// - template - void LargeVolume::flush(Region regFlush) - { - POLYVOX_LOG_WARNING_IF(!m_pPager, "Data discarded by flush operation as no pager is attached."); - - // Clear this pointer so it doesn't hang on to any blocks. - m_pLastAccessedBlock = nullptr; - - // Convert the start and end positions into block space coordinates - Vector3DInt32 v3dStart; - for(int i = 0; i < 3; i++) - { - v3dStart.setElement(i, regFlush.getLowerCorner().getElement(i) >> m_uBlockSideLengthPower); - } - - Vector3DInt32 v3dEnd; - for(int i = 0; i < 3; i++) - { - v3dEnd.setElement(i, regFlush.getUpperCorner().getElement(i) >> m_uBlockSideLengthPower); - } - - // Loops over the specified positions and delete the corresponding blocks. - for(int32_t x = v3dStart.getX(); x <= v3dEnd.getX(); x++) - { - for(int32_t y = v3dStart.getY(); y <= v3dEnd.getY(); y++) - { - for(int32_t z = v3dStart.getZ(); z <= v3dEnd.getZ(); z++) - { - m_pRecentlyUsedBlocks.erase(Vector3DInt32(x, y, z)); m_pLastAccessedBlock = 0; - } - } - } - - // We might now have so null pointers in the 'all blocks' list so clean them up. - purgeNullPtrsFromAllBlocks(); - } - - //////////////////////////////////////////////////////////////////////////////// - /// This function should probably be made internal... - //////////////////////////////////////////////////////////////////////////////// - template - void LargeVolume::initialise() - { - //Validate parameters - if(m_uBlockSideLength == 0) - { - POLYVOX_THROW(std::invalid_argument, "Block side length cannot be zero."); - } - - if(!isPowerOf2(m_uBlockSideLength)) - { - POLYVOX_THROW(std::invalid_argument, "Block side length must be a power of two."); - } - - m_uTimestamper = 0; - //m_uBlockCountLimit = 16; - //m_uMaxNumberOfBlocksInMemory = 1024; - m_v3dLastAccessedBlockPos = Vector3DInt32(0,0,0); //There are no invalid positions, but initially the m_pLastAccessedBlock pointer will be null; - m_pLastAccessedBlock = 0; - - //Compute the block side length - m_uBlockSideLengthPower = logBase2(m_uBlockSideLength); - - m_regValidRegionInBlocks.setLowerX(this->m_regValidRegion.getLowerX() >> m_uBlockSideLengthPower); - m_regValidRegionInBlocks.setLowerY(this->m_regValidRegion.getLowerY() >> m_uBlockSideLengthPower); - m_regValidRegionInBlocks.setLowerZ(this->m_regValidRegion.getLowerZ() >> m_uBlockSideLengthPower); - m_regValidRegionInBlocks.setUpperX(this->m_regValidRegion.getUpperX() >> m_uBlockSideLengthPower); - m_regValidRegionInBlocks.setUpperY(this->m_regValidRegion.getUpperY() >> m_uBlockSideLengthPower); - m_regValidRegionInBlocks.setUpperZ(this->m_regValidRegion.getUpperZ() >> m_uBlockSideLengthPower); - - //setMaxNumberOfUncompressedBlocks(m_uBlockCountLimit); - - //Clear the previous data - m_pRecentlyUsedBlocks.clear(); - - //Other properties we might find useful later - this->m_uLongestSideLength = (std::max)((std::max)(this->getWidth(),this->getHeight()),this->getDepth()); - this->m_uShortestSideLength = (std::min)((std::min)(this->getWidth(),this->getHeight()),this->getDepth()); - this->m_fDiagonalLength = sqrtf(static_cast(this->getWidth() * this->getWidth() + this->getHeight() * this->getHeight() + this->getDepth() * this->getDepth())); - } - - template - std::shared_ptr< UncompressedBlock > LargeVolume::getUncompressedBlock(int32_t uBlockX, int32_t uBlockY, int32_t uBlockZ) const - { - Vector3DInt32 v3dBlockPos(uBlockX, uBlockY, uBlockZ); - - //Check if we have the same block as last time, if so there's no need to even update - //the time stamp. If we updated it everytime then that would be every time we touched - //a voxel, which would overflow a uint32_t and require us to use a uint64_t instead. - //This check should also provide a significant speed boost as usually it is true. - if((v3dBlockPos == m_v3dLastAccessedBlockPos) && (m_pLastAccessedBlock != 0)) - { - return m_pLastAccessedBlock; - } - - // The block was not the same as last time, but we can now hope it is in the set of most recently used blocks. - std::shared_ptr< UncompressedBlock > pUncompressedBlock = nullptr; - typename SharedPtrBlockMap::iterator itUncompressedBlock = m_pRecentlyUsedBlocks.find(v3dBlockPos); - - // Check whether the block was found. - if ((itUncompressedBlock) != m_pRecentlyUsedBlocks.end()) - { - // The block was found so we can use it. - pUncompressedBlock = itUncompressedBlock->second; - POLYVOX_ASSERT(pUncompressedBlock, "Recent block list shold never contain a null pointer."); - } - - if (!pUncompressedBlock) - { - // Although it's not in our recently use blocks, there's some (slim) chance that it - // exists in the list of all loaded blocks, because a sampler may be holding on to it. - typename WeakPtrBlockMap::iterator itWeakUncompressedBlock = m_pAllBlocks.find(v3dBlockPos); - if (itWeakUncompressedBlock != m_pAllBlocks.end()) - { - // We've found an entry in the 'all blocks' list, but it can be null. This happens if a sampler was the - // last thing to be keeping it alive and then the sampler let it go. In this case we remove it from the - // list, and it will get added again soon when we page it in and fill it with valid data. - if (itWeakUncompressedBlock->second.expired()) - { - m_pAllBlocks.erase(itWeakUncompressedBlock); - } - else - { - // The block is valid. We know it's not in the recently used list (we checked earlier) so it should be added. - pUncompressedBlock = std::shared_ptr< UncompressedBlock >(itWeakUncompressedBlock->second); - m_pRecentlyUsedBlocks.insert(std::make_pair(v3dBlockPos, pUncompressedBlock)); - } - } - } - - // If we still haven't found the block then it's time to create a new one and page it in from disk. - if (!pUncompressedBlock) - { - // The block was not found so we will create a new one. - pUncompressedBlock = std::make_shared< UncompressedBlock >(v3dBlockPos, m_uBlockSideLength, m_pPager); - - // As we are loading a new block we should try to ensure we don't go over our target memory usage. - bool erasedBlock = false; - while (m_pRecentlyUsedBlocks.size() + 1 > m_uBlockCountLimit) // +1 ready for new block we will add next. - { - // This should never hit, because it should not have been possible for - // the user to limit the number of blocks if they did not provide a pager. - POLYVOX_ASSERT(m_pPager, "A valid pager is required to limit number of blocks"); - - // Find the least recently used block. Hopefully this isn't too slow. - typename SharedPtrBlockMap::iterator itUnloadBlock = m_pRecentlyUsedBlocks.begin(); - for (typename SharedPtrBlockMap::iterator i = m_pRecentlyUsedBlocks.begin(); i != m_pRecentlyUsedBlocks.end(); i++) - { - if (i->second->m_uBlockLastAccessed < itUnloadBlock->second->m_uBlockLastAccessed) - { - itUnloadBlock = i; - } - } - - // Erase the least recently used block - m_pRecentlyUsedBlocks.erase(itUnloadBlock); - erasedBlock = true; - } - - // If we've deleted any blocks from the recently used list then this - // seems like a good place to purge the 'all blocks' list as well. - if (erasedBlock) - { - purgeNullPtrsFromAllBlocks(); - } - - // Add our new block to the maps. - m_pAllBlocks.insert(std::make_pair(v3dBlockPos, pUncompressedBlock)); - m_pRecentlyUsedBlocks.insert(std::make_pair(v3dBlockPos, pUncompressedBlock)); - } - - pUncompressedBlock->m_uBlockLastAccessed = ++m_uTimestamper; - m_pLastAccessedBlock = pUncompressedBlock; - m_v3dLastAccessedBlockPos = v3dBlockPos; - - return pUncompressedBlock; - } - - //////////////////////////////////////////////////////////////////////////////// - /// Calculate the memory usage of the volume. - //////////////////////////////////////////////////////////////////////////////// - template - uint32_t LargeVolume::calculateSizeInBytes(void) - { - // Purge null blocks so we know that all blocks are used. - purgeNullPtrsFromAllBlocks(); - - // Note: We disregard the size of the other class members as they are likely to be very small compared to the size of the - // allocated voxel data. This also keeps the reported size as a power of two, which makes other memory calculations easier. - return UncompressedBlock::calculateSizeInBytes(m_uBlockSideLength) * m_pAllBlocks.size(); - } - - template - void LargeVolume::purgeNullPtrsFromAllBlocks(void) const - { - for (auto blockIter = m_pAllBlocks.begin(); blockIter != m_pAllBlocks.end();) - { - if (blockIter->second.expired()) - { - blockIter = m_pAllBlocks.erase(blockIter); - } - else - { - blockIter++; - } - } - } - - 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)); - - auto pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ); - return pUncompressedBlock->getVoxel(xOffset, yOffset, zOffset); - } -} - +/******************************************************************************* +Copyright (c) 2005-2009 David Williams + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*******************************************************************************/ + +#include "PolyVoxCore/Impl/ErrorHandling.h" + +#include +#include + +namespace PolyVox +{ + //////////////////////////////////////////////////////////////////////////////// + /// This constructor creates a volume with a fixed size which is specified as a parameter. By default this constructor will not enable paging but you can override this if desired. If you do wish to enable paging then you are required to provide the call back function (see the other PagedVolume constructor). + /// \param regValid Specifies the minimum and maximum valid voxel positions. + /// \param pBlockCompressor An implementation of the Compressor interface which is used to compress blocks in memory. + /// \param dataRequiredHandler The callback function which will be called when PolyVox tries to use data which is not currently in momory. + /// \param dataOverflowHandler The callback function which will be called when PolyVox has too much data and needs to remove some from memory. + /// \param bPagingEnabled Controls whether or not paging is enabled for this PagedVolume. + /// \param uBlockSideLength The size of the blocks making up the volume. Small blocks will compress/decompress faster, but there will also be more of them meaning voxel access could be slower. + //////////////////////////////////////////////////////////////////////////////// + template + PagedVolume::PagedVolume + ( + const Region& regValid, + Pager* pPager, + uint16_t uBlockSideLength + ) + :BaseVolume(regValid) + { + m_uBlockSideLength = uBlockSideLength; + m_pPager = pPager; + + if (m_pPager) + { + // If the user is creating a vast (almost infinite) volume then we can bet they will be + // expecting a high memory usage and will want a fair number of blocks to play around with. + if (regValid == Region::MaxRegion) + { + m_uBlockCountLimit = 1024; + } + else + { + // Otherwise we try to choose a block count to avoid too much thrashing, particularly when iterating + // over the whole volume. This means at least enough blocks to cover one edge of the volume, and ideally + // enough for a whole face. Which face? Longest edge by shortest edge seems like a reasonable guess. + uint32_t longestSide = (std::max)(regValid.getWidthInVoxels(), (std::max)(regValid.getHeightInVoxels(), regValid.getDepthInVoxels())); + uint32_t shortestSide = (std::min)(regValid.getWidthInVoxels(), (std::min)(regValid.getHeightInVoxels(), regValid.getDepthInVoxels())); + + longestSide /= m_uBlockSideLength; + shortestSide /= m_uBlockSideLength; + + m_uBlockCountLimit = longestSide * shortestSide; + } + } + else + { + // If there is no pager provided then we set the block limit to the maximum + // value to ensure the system never attempts to page blocks out of memory. + m_uBlockCountLimit = (std::numeric_limits::max)(); + } + + // Make sure the calculated block limit is within practical bounds + m_uBlockCountLimit = (std::max)(m_uBlockCountLimit, uMinPracticalNoOfBlocks); + m_uBlockCountLimit = (std::min)(m_uBlockCountLimit, uMaxPracticalNoOfBlocks); + + uint32_t uUncompressedBlockSizeInBytes = UncompressedBlock::calculateSizeInBytes(m_uBlockSideLength); + POLYVOX_LOG_DEBUG("Memory usage limit for volume initially set to " << (m_uBlockCountLimit * uUncompressedBlockSizeInBytes) / (1024 * 1024) + << "Mb (" << m_uBlockCountLimit << " blocks of " << uUncompressedBlockSizeInBytes / 1024 << "Kb each)."); + + initialise(); + } + + //////////////////////////////////////////////////////////////////////////////// + /// This function should never be called. Copying volumes by value would be expensive, and we want to prevent users from doing + /// it by accident (such as when passing them as paramenters to functions). That said, there are times when you really do want to + /// make a copy of a volume and in this case you should look at the VolumeResampler. + /// + /// \sa VolumeResampler + //////////////////////////////////////////////////////////////////////////////// + template + PagedVolume::PagedVolume(const PagedVolume& /*rhs*/) + { + POLYVOX_THROW(not_implemented, "Volume copy constructor not implemented for performance reasons."); + } + + //////////////////////////////////////////////////////////////////////////////// + /// Destroys the volume The destructor will call flushAll() to ensure that a paging volume has the chance to save it's data via the dataOverflowHandler() if desired. + //////////////////////////////////////////////////////////////////////////////// + template + PagedVolume::~PagedVolume() + { + flushAll(); + } + + //////////////////////////////////////////////////////////////////////////////// + /// This function should never be called. Copying volumes by value would be expensive, and we want to prevent users from doing + /// it by accident (such as when passing them as paramenters to functions). That said, there are times when you really do want to + /// make a copy of a volume and in this case you should look at the Volumeresampler. + /// + /// \sa VolumeResampler + //////////////////////////////////////////////////////////////////////////////// + template + PagedVolume& PagedVolume::operator=(const PagedVolume& /*rhs*/) + { + POLYVOX_THROW(not_implemented, "Volume assignment operator not implemented for performance reasons."); + } + + //////////////////////////////////////////////////////////////////////////////// + /// 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 + template + VoxelType PagedVolume::getVoxel(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tBorder) const + { + // Simply call through to the real implementation + return getVoxelImpl(uXPos, uYPos, uZPos, WrapModeType(), tBorder); + } + + //////////////////////////////////////////////////////////////////////////////// + /// 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 + template + VoxelType PagedVolume::getVoxel(const Vector3DInt32& v3dPos, VoxelType tBorder) const + { + // 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 PagedVolume::getVoxel(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapMode eWrapMode, VoxelType tBorder) const + { + switch(eWrapMode) + { + case WrapModes::Validate: + 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::AssumeValid: + 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 PagedVolume::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 + /// \param uZPos The \c z position of the voxel + /// \return The voxel value + //////////////////////////////////////////////////////////////////////////////// + template + VoxelType PagedVolume::getVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos) const + { + if(this->m_regValidRegion.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos))) + { + 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)); + + auto pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ); + + return pUncompressedBlock->getVoxel(xOffset, yOffset, zOffset); + } + else + { + return this->getBorderValue(); + } + } + + //////////////////////////////////////////////////////////////////////////////// + /// \param v3dPos The 3D position of the voxel + /// \return The voxel value + //////////////////////////////////////////////////////////////////////////////// + template + VoxelType PagedVolume::getVoxelAt(const Vector3DInt32& v3dPos) const + { + return getVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ()); + } + + //////////////////////////////////////////////////////////////////////////////// + /// 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 + /// volume data and then set it to a smaller value (e.g.64) for general processing. + /// \param uMaxNumberOfUncompressedBlocks The number of blocks for which uncompressed data can be cached. + //////////////////////////////////////////////////////////////////////////////// + template + void PagedVolume::setMemoryUsageLimit(uint32_t uMemoryUsageInBytes) + { + POLYVOX_THROW_IF(!m_pPager, invalid_operation, "You cannot limit the memory usage of the volume because it was created without a pager attached."); + + // Calculate the number of blocks based on the memory limit and the size of each block. + uint32_t uUncompressedBlockSizeInBytes = UncompressedBlock::calculateSizeInBytes(m_uBlockSideLength); + m_uBlockCountLimit = uMemoryUsageInBytes / uUncompressedBlockSizeInBytes; + + // We need at least a few blocks available to avoid thrashing, and in pratice there will probably be hundreds. + POLYVOX_LOG_WARNING_IF(m_uBlockCountLimit < uMinPracticalNoOfBlocks, "Requested memory usage limit of " + << uMemoryUsageInBytes / (1024 * 1024) << "Mb is too low and cannot be adhered to."); + m_uBlockCountLimit = (std::max)(m_uBlockCountLimit, uMinPracticalNoOfBlocks); + m_uBlockCountLimit = (std::min)(m_uBlockCountLimit, uMaxPracticalNoOfBlocks); + + // If the new limit is less than the number of blocks already loaded then the easiest solution is to flush and start loading again. + if (m_pRecentlyUsedBlocks.size() > m_uBlockCountLimit) + { + flushAll(); + } + + POLYVOX_LOG_DEBUG("Memory usage limit for volume now set to " << (m_uBlockCountLimit * uUncompressedBlockSizeInBytes) / (1024 * 1024) + << "Mb (" << m_uBlockCountLimit << " blocks of " << uUncompressedBlockSizeInBytes / 1024 << "Kb each)."); + } + + //////////////////////////////////////////////////////////////////////////////// + /// \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 tValue the value to which the voxel will be set + /// \param eWrapMode Specifies the behaviour when the requested position is outside of the volume. + /// This must be set to 'None' or 'DontCheck'. Other wrap modes cannot be used when writing to volume data. + //////////////////////////////////////////////////////////////////////////////// + template + void PagedVolume::setVoxel(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue, WrapMode eWrapMode) + { + if((eWrapMode != WrapModes::Validate) && (eWrapMode != WrapModes::AssumeValid)) + { + POLYVOX_THROW(std::invalid_argument, "Invalid wrap mode in call to setVoxel(). It must be 'None' or 'DontCheck'."); + } + + // This validation is skipped if the wrap mode is 'DontCheck' + if(eWrapMode == WrapModes::Validate) + { + 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)); + + auto pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ); + pUncompressedBlock->setVoxelAt(xOffset, yOffset, zOffset, tValue); + } + + //////////////////////////////////////////////////////////////////////////////// + /// \param v3dPos the 3D position of the voxel + /// \param tValue the value to which the voxel will be set + /// \param eWrapMode Specifies the behaviour when the requested position is outside of the volume. + /// This must be set to 'None' or 'DontCheck'. Other wrap modes cannot be used when writing to volume data. + //////////////////////////////////////////////////////////////////////////////// + template + void PagedVolume::setVoxel(const Vector3DInt32& v3dPos, VoxelType tValue, WrapMode eWrapMode) + { + setVoxel(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ(), tValue, eWrapMode); + } + + //////////////////////////////////////////////////////////////////////////////// + /// \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 tValue the value to which the voxel will be set + /// \return whether the requested position is inside the volume + //////////////////////////////////////////////////////////////////////////////// + template + bool PagedVolume::setVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue) + { + // PolyVox does not throw an exception when a voxel is out of range. Please see 'Error Handling' in the User Manual. + POLYVOX_ASSERT(this->m_regValidRegion.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos)), "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)); + + auto pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ); + + pUncompressedBlock->setVoxelAt(xOffset, yOffset, zOffset, tValue); + + //Return true to indicate that we modified a voxel. + return true; + } + + //////////////////////////////////////////////////////////////////////////////// + /// \param v3dPos the 3D position of the voxel + /// \param tValue the value to which the voxel will be set + /// \return whether the requested position is inside the volume + //////////////////////////////////////////////////////////////////////////////// + template + bool PagedVolume::setVoxelAt(const Vector3DInt32& v3dPos, VoxelType tValue) + { + return setVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ(), tValue); + } + + + //////////////////////////////////////////////////////////////////////////////// + /// Note that if *NOTE - update docs - MaxNumberOfBlocksInMemory no longer exists* MaxNumberOfBlocksInMemory is not large enough to support the region this function will only load part of the region. In this case it is undefined which parts will actually be loaded. If all the voxels in the given region are already loaded, this function will not do anything. Other voxels might be unloaded to make space for the new voxels. + /// \param regPrefetch The Region of voxels to prefetch into memory. + //////////////////////////////////////////////////////////////////////////////// + template + void PagedVolume::prefetch(Region regPrefetch) + { + // Convert the start and end positions into block space coordinates + Vector3DInt32 v3dStart; + for(int i = 0; i < 3; i++) + { + v3dStart.setElement(i, regPrefetch.getLowerCorner().getElement(i) >> m_uBlockSideLengthPower); + } + + Vector3DInt32 v3dEnd; + for(int i = 0; i < 3; i++) + { + v3dEnd.setElement(i, regPrefetch.getUpperCorner().getElement(i) >> m_uBlockSideLengthPower); + } + + // Ensure we don't page in more blocks than the volume can hold. + Region region(v3dStart, v3dEnd); + uint32_t uNoOfBlocks = static_cast(region.getWidthInVoxels() * region.getHeightInVoxels() * region.getDepthInVoxels()); + POLYVOX_LOG_WARNING_IF(uNoOfBlocks > m_uBlockCountLimit, "Attempting to prefetch more than the maximum number of blocks."); + uNoOfBlocks = (std::min)(uNoOfBlocks, m_uBlockCountLimit); + + // Loops over the specified positions and touch the corresponding blocks. + for(int32_t x = v3dStart.getX(); x <= v3dEnd.getX(); x++) + { + for(int32_t y = v3dStart.getY(); y <= v3dEnd.getY(); y++) + { + for(int32_t z = v3dStart.getZ(); z <= v3dEnd.getZ(); z++) + { + getUncompressedBlock(x,y,z); + } + } + } + } + + //////////////////////////////////////////////////////////////////////////////// + /// Removes all voxels from memory, and calls dataOverflowHandler() to ensure the application has a chance to store the data. + //////////////////////////////////////////////////////////////////////////////// + template + void PagedVolume::flushAll() + { + POLYVOX_LOG_WARNING_IF(!m_pPager, "Data discarded by flush operation as no pager is attached."); + + // Clear this pointer so it doesn't hang on to any blocks. + m_pLastAccessedBlock = nullptr; + + // Erase all the most recently used blocks. + m_pRecentlyUsedBlocks.clear(); + + // Remove deleted blocks from the list of all loaded blocks. + purgeNullPtrsFromAllBlocks(); + + // If there are still some blocks left then this is a cause for concern. Perhaps samplers are holding on to them? + POLYVOX_LOG_WARNING_IF(m_pAllBlocks.size() > 0, "Blocks still exist after performing flushAll()! Perhaps you have samplers attached?"); + } + + //////////////////////////////////////////////////////////////////////////////// + /// Removes all voxels in the specified Region from memory, and calls dataOverflowHandler() to ensure the application has a chance to store the data. It is possible that there are no voxels loaded in the Region, in which case the function will have no effect. + //////////////////////////////////////////////////////////////////////////////// + template + void PagedVolume::flush(Region regFlush) + { + POLYVOX_LOG_WARNING_IF(!m_pPager, "Data discarded by flush operation as no pager is attached."); + + // Clear this pointer so it doesn't hang on to any blocks. + m_pLastAccessedBlock = nullptr; + + // Convert the start and end positions into block space coordinates + Vector3DInt32 v3dStart; + for(int i = 0; i < 3; i++) + { + v3dStart.setElement(i, regFlush.getLowerCorner().getElement(i) >> m_uBlockSideLengthPower); + } + + Vector3DInt32 v3dEnd; + for(int i = 0; i < 3; i++) + { + v3dEnd.setElement(i, regFlush.getUpperCorner().getElement(i) >> m_uBlockSideLengthPower); + } + + // Loops over the specified positions and delete the corresponding blocks. + for(int32_t x = v3dStart.getX(); x <= v3dEnd.getX(); x++) + { + for(int32_t y = v3dStart.getY(); y <= v3dEnd.getY(); y++) + { + for(int32_t z = v3dStart.getZ(); z <= v3dEnd.getZ(); z++) + { + m_pRecentlyUsedBlocks.erase(Vector3DInt32(x, y, z)); m_pLastAccessedBlock = 0; + } + } + } + + // We might now have so null pointers in the 'all blocks' list so clean them up. + purgeNullPtrsFromAllBlocks(); + } + + //////////////////////////////////////////////////////////////////////////////// + /// This function should probably be made internal... + //////////////////////////////////////////////////////////////////////////////// + template + void PagedVolume::initialise() + { + //Validate parameters + if(m_uBlockSideLength == 0) + { + POLYVOX_THROW(std::invalid_argument, "Block side length cannot be zero."); + } + + if(!isPowerOf2(m_uBlockSideLength)) + { + POLYVOX_THROW(std::invalid_argument, "Block side length must be a power of two."); + } + + m_uTimestamper = 0; + //m_uBlockCountLimit = 16; + //m_uMaxNumberOfBlocksInMemory = 1024; + m_v3dLastAccessedBlockPos = Vector3DInt32(0,0,0); //There are no invalid positions, but initially the m_pLastAccessedBlock pointer will be null; + m_pLastAccessedBlock = 0; + + //Compute the block side length + m_uBlockSideLengthPower = logBase2(m_uBlockSideLength); + + m_regValidRegionInBlocks.setLowerX(this->m_regValidRegion.getLowerX() >> m_uBlockSideLengthPower); + m_regValidRegionInBlocks.setLowerY(this->m_regValidRegion.getLowerY() >> m_uBlockSideLengthPower); + m_regValidRegionInBlocks.setLowerZ(this->m_regValidRegion.getLowerZ() >> m_uBlockSideLengthPower); + m_regValidRegionInBlocks.setUpperX(this->m_regValidRegion.getUpperX() >> m_uBlockSideLengthPower); + m_regValidRegionInBlocks.setUpperY(this->m_regValidRegion.getUpperY() >> m_uBlockSideLengthPower); + m_regValidRegionInBlocks.setUpperZ(this->m_regValidRegion.getUpperZ() >> m_uBlockSideLengthPower); + + //setMaxNumberOfUncompressedBlocks(m_uBlockCountLimit); + + //Clear the previous data + m_pRecentlyUsedBlocks.clear(); + + //Other properties we might find useful later + this->m_uLongestSideLength = (std::max)((std::max)(this->getWidth(),this->getHeight()),this->getDepth()); + this->m_uShortestSideLength = (std::min)((std::min)(this->getWidth(),this->getHeight()),this->getDepth()); + this->m_fDiagonalLength = sqrtf(static_cast(this->getWidth() * this->getWidth() + this->getHeight() * this->getHeight() + this->getDepth() * this->getDepth())); + } + + template + std::shared_ptr< UncompressedBlock > PagedVolume::getUncompressedBlock(int32_t uBlockX, int32_t uBlockY, int32_t uBlockZ) const + { + Vector3DInt32 v3dBlockPos(uBlockX, uBlockY, uBlockZ); + + //Check if we have the same block as last time, if so there's no need to even update + //the time stamp. If we updated it everytime then that would be every time we touched + //a voxel, which would overflow a uint32_t and require us to use a uint64_t instead. + //This check should also provide a significant speed boost as usually it is true. + if((v3dBlockPos == m_v3dLastAccessedBlockPos) && (m_pLastAccessedBlock != 0)) + { + return m_pLastAccessedBlock; + } + + // The block was not the same as last time, but we can now hope it is in the set of most recently used blocks. + std::shared_ptr< UncompressedBlock > pUncompressedBlock = nullptr; + typename SharedPtrBlockMap::iterator itUncompressedBlock = m_pRecentlyUsedBlocks.find(v3dBlockPos); + + // Check whether the block was found. + if ((itUncompressedBlock) != m_pRecentlyUsedBlocks.end()) + { + // The block was found so we can use it. + pUncompressedBlock = itUncompressedBlock->second; + POLYVOX_ASSERT(pUncompressedBlock, "Recent block list shold never contain a null pointer."); + } + + if (!pUncompressedBlock) + { + // Although it's not in our recently use blocks, there's some (slim) chance that it + // exists in the list of all loaded blocks, because a sampler may be holding on to it. + typename WeakPtrBlockMap::iterator itWeakUncompressedBlock = m_pAllBlocks.find(v3dBlockPos); + if (itWeakUncompressedBlock != m_pAllBlocks.end()) + { + // We've found an entry in the 'all blocks' list, but it can be null. This happens if a sampler was the + // last thing to be keeping it alive and then the sampler let it go. In this case we remove it from the + // list, and it will get added again soon when we page it in and fill it with valid data. + if (itWeakUncompressedBlock->second.expired()) + { + m_pAllBlocks.erase(itWeakUncompressedBlock); + } + else + { + // The block is valid. We know it's not in the recently used list (we checked earlier) so it should be added. + pUncompressedBlock = std::shared_ptr< UncompressedBlock >(itWeakUncompressedBlock->second); + m_pRecentlyUsedBlocks.insert(std::make_pair(v3dBlockPos, pUncompressedBlock)); + } + } + } + + // If we still haven't found the block then it's time to create a new one and page it in from disk. + if (!pUncompressedBlock) + { + // The block was not found so we will create a new one. + pUncompressedBlock = std::make_shared< UncompressedBlock >(v3dBlockPos, m_uBlockSideLength, m_pPager); + + // As we are loading a new block we should try to ensure we don't go over our target memory usage. + bool erasedBlock = false; + while (m_pRecentlyUsedBlocks.size() + 1 > m_uBlockCountLimit) // +1 ready for new block we will add next. + { + // This should never hit, because it should not have been possible for + // the user to limit the number of blocks if they did not provide a pager. + POLYVOX_ASSERT(m_pPager, "A valid pager is required to limit number of blocks"); + + // Find the least recently used block. Hopefully this isn't too slow. + typename SharedPtrBlockMap::iterator itUnloadBlock = m_pRecentlyUsedBlocks.begin(); + for (typename SharedPtrBlockMap::iterator i = m_pRecentlyUsedBlocks.begin(); i != m_pRecentlyUsedBlocks.end(); i++) + { + if (i->second->m_uBlockLastAccessed < itUnloadBlock->second->m_uBlockLastAccessed) + { + itUnloadBlock = i; + } + } + + // Erase the least recently used block + m_pRecentlyUsedBlocks.erase(itUnloadBlock); + erasedBlock = true; + } + + // If we've deleted any blocks from the recently used list then this + // seems like a good place to purge the 'all blocks' list as well. + if (erasedBlock) + { + purgeNullPtrsFromAllBlocks(); + } + + // Add our new block to the maps. + m_pAllBlocks.insert(std::make_pair(v3dBlockPos, pUncompressedBlock)); + m_pRecentlyUsedBlocks.insert(std::make_pair(v3dBlockPos, pUncompressedBlock)); + } + + pUncompressedBlock->m_uBlockLastAccessed = ++m_uTimestamper; + m_pLastAccessedBlock = pUncompressedBlock; + m_v3dLastAccessedBlockPos = v3dBlockPos; + + return pUncompressedBlock; + } + + //////////////////////////////////////////////////////////////////////////////// + /// Calculate the memory usage of the volume. + //////////////////////////////////////////////////////////////////////////////// + template + uint32_t PagedVolume::calculateSizeInBytes(void) + { + // Purge null blocks so we know that all blocks are used. + purgeNullPtrsFromAllBlocks(); + + // Note: We disregard the size of the other class members as they are likely to be very small compared to the size of the + // allocated voxel data. This also keeps the reported size as a power of two, which makes other memory calculations easier. + return UncompressedBlock::calculateSizeInBytes(m_uBlockSideLength) * m_pAllBlocks.size(); + } + + template + void PagedVolume::purgeNullPtrsFromAllBlocks(void) const + { + for (auto blockIter = m_pAllBlocks.begin(); blockIter != m_pAllBlocks.end();) + { + if (blockIter->second.expired()) + { + blockIter = m_pAllBlocks.erase(blockIter); + } + else + { + blockIter++; + } + } + } + + template + template + VoxelType PagedVolume::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 PagedVolume::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 PagedVolume::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 PagedVolume::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 PagedVolume::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)); + + auto pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ); + return pUncompressedBlock->getVoxel(xOffset, yOffset, zOffset); + } +} + diff --git a/library/PolyVoxCore/include/PolyVoxCore/LargeVolumeSampler.inl b/library/PolyVoxCore/include/PolyVoxCore/PagedVolumeSampler.inl similarity index 84% rename from library/PolyVoxCore/include/PolyVoxCore/LargeVolumeSampler.inl rename to library/PolyVoxCore/include/PolyVoxCore/PagedVolumeSampler.inl index df07bb77..5c55e8ae 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/LargeVolumeSampler.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/PagedVolumeSampler.inl @@ -1,562 +1,562 @@ -/******************************************************************************* -Copyright (c) 2005-2009 David Williams - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*******************************************************************************/ - -#define CAN_GO_NEG_X(val) ((val > this->mVolume->getEnclosingRegion().getLowerX()) && (val % this->mVolume->m_uBlockSideLength != 0)) -#define CAN_GO_POS_X(val) ((val < this->mVolume->getEnclosingRegion().getUpperX()) && ((val + 1) % this->mVolume->m_uBlockSideLength != 0)) -#define CAN_GO_NEG_Y(val) ((val > this->mVolume->getEnclosingRegion().getLowerY()) && (val % this->mVolume->m_uBlockSideLength != 0)) -#define CAN_GO_POS_Y(val) ((val < this->mVolume->getEnclosingRegion().getUpperY()) && ((val + 1) % this->mVolume->m_uBlockSideLength != 0)) -#define CAN_GO_NEG_Z(val) ((val > this->mVolume->getEnclosingRegion().getLowerZ()) && (val % this->mVolume->m_uBlockSideLength != 0)) -#define CAN_GO_POS_Z(val) ((val < this->mVolume->getEnclosingRegion().getUpperZ()) && ((val + 1) % this->mVolume->m_uBlockSideLength != 0)) - -namespace PolyVox -{ - template - LargeVolume::Sampler::Sampler(LargeVolume* volume) - :BaseVolume::template Sampler< LargeVolume >(volume) - { - } - - template - LargeVolume::Sampler::~Sampler() - { - } - - template - VoxelType LargeVolume::Sampler::getSubSampledVoxel(uint8_t uLevel) const - { - if(uLevel == 0) - { - return getVoxel(); - } - else if(uLevel == 1) - { - VoxelType tValue = getVoxel(); - tValue = (std::min)(tValue, peekVoxel1px0py0pz()); - tValue = (std::min)(tValue, peekVoxel0px1py0pz()); - tValue = (std::min)(tValue, peekVoxel1px1py0pz()); - tValue = (std::min)(tValue, peekVoxel0px0py1pz()); - tValue = (std::min)(tValue, peekVoxel1px0py1pz()); - tValue = (std::min)(tValue, peekVoxel0px1py1pz()); - tValue = (std::min)(tValue, peekVoxel1px1py1pz()); - return tValue; - } - else - { - const uint8_t uSize = 1 << uLevel; - - VoxelType tValue = (std::numeric_limits::max)(); - for(uint8_t z = 0; z < uSize; ++z) - { - for(uint8_t y = 0; y < uSize; ++y) - { - for(uint8_t x = 0; x < uSize; ++x) - { - tValue = (std::min)(tValue, this->mVolume->getVoxelAt(this->mXPosInVolume + x, this->mYPosInVolume + y, this->mZPosInVolume + z)); - } - } - } - return tValue; - } - } - - template - VoxelType LargeVolume::Sampler::getVoxel(void) const - { - if(this->isCurrentPositionValid()) - { - return *mCurrentVoxel; - } - else - { - return this->getVoxelImpl(this->mXPosInVolume, this->mYPosInVolume, this->mZPosInVolume); - } - } - - template - void LargeVolume::Sampler::setPosition(const Vector3DInt32& v3dNewPos) - { - setPosition(v3dNewPos.getX(), v3dNewPos.getY(), v3dNewPos.getZ()); - } - - template - void LargeVolume::Sampler::setPosition(int32_t xPos, int32_t yPos, int32_t zPos) - { - // Base version updates position and validity flags. - BaseVolume::template Sampler< LargeVolume >::setPosition(xPos, yPos, zPos); - - // Then we update the voxel pointer - if(this->isCurrentPositionValid()) - { - const int32_t uXBlock = this->mXPosInVolume >> this->mVolume->m_uBlockSideLengthPower; - const int32_t uYBlock = this->mYPosInVolume >> this->mVolume->m_uBlockSideLengthPower; - const int32_t uZBlock = this->mZPosInVolume >> this->mVolume->m_uBlockSideLengthPower; - - const uint16_t uXPosInBlock = static_cast(this->mXPosInVolume - (uXBlock << this->mVolume->m_uBlockSideLengthPower)); - const uint16_t uYPosInBlock = static_cast(this->mYPosInVolume - (uYBlock << this->mVolume->m_uBlockSideLengthPower)); - const uint16_t uZPosInBlock = static_cast(this->mZPosInVolume - (uZBlock << this->mVolume->m_uBlockSideLengthPower)); - - const uint32_t uVoxelIndexInBlock = uXPosInBlock + - uYPosInBlock * this->mVolume->m_uBlockSideLength + - uZPosInBlock * this->mVolume->m_uBlockSideLength * this->mVolume->m_uBlockSideLength; - - auto pUncompressedCurrentBlock = this->mVolume->getUncompressedBlock(uXBlock, uYBlock, uZBlock); - - mCurrentVoxel = pUncompressedCurrentBlock->m_tData + uVoxelIndexInBlock; - } - else - { - mCurrentVoxel = 0; - } - } - - template - bool LargeVolume::Sampler::setVoxel(VoxelType tValue) - { - /*if(m_bIsCurrentPositionValidInX && m_bIsCurrentPositionValidInY && m_bIsCurrentPositionValidInZ) - { - *mCurrentVoxel = tValue; - return true; - } - else - { - return false; - }*/ - - //Need to think what effect this has on any existing iterators. - POLYVOX_THROW(not_implemented, "This function cannot be used on LargeVolume samplers."); - return false; - } - - template - void LargeVolume::Sampler::movePositiveX(void) - { - // We'll need this in a moment... - bool bIsOldPositionValid = this->isCurrentPositionValid(); - - // Base version updates position and validity flags. - BaseVolume::template Sampler< LargeVolume >::movePositiveX(); - - // Then we update the voxel pointer - if((this->isCurrentPositionValid()) && bIsOldPositionValid && ((this->mXPosInVolume) % this->mVolume->m_uBlockSideLength != 0)) - { - //No need to compute new block. - ++mCurrentVoxel; - } - else - { - //We've hit the block boundary. Just calling setPosition() is the easiest way to resolve this. - setPosition(this->mXPosInVolume, this->mYPosInVolume, this->mZPosInVolume); - } - } - - template - void LargeVolume::Sampler::movePositiveY(void) - { - // We'll need this in a moment... - bool bIsOldPositionValid = this->isCurrentPositionValid(); - - // Base version updates position and validity flags. - BaseVolume::template Sampler< LargeVolume >::movePositiveY(); - - // Then we update the voxel pointer - if((this->isCurrentPositionValid()) && bIsOldPositionValid && ((this->mYPosInVolume) % this->mVolume->m_uBlockSideLength != 0)) - { - //No need to compute new block. - mCurrentVoxel += this->mVolume->m_uBlockSideLength; - } - else - { - //We've hit the block boundary. Just calling setPosition() is the easiest way to resolve this. - setPosition(this->mXPosInVolume, this->mYPosInVolume, this->mZPosInVolume); - } - } - - template - void LargeVolume::Sampler::movePositiveZ(void) - { - // We'll need this in a moment... - bool bIsOldPositionValid = this->isCurrentPositionValid(); - - // Base version updates position and validity flags. - BaseVolume::template Sampler< LargeVolume >::movePositiveZ(); - - // Then we update the voxel pointer - if((this->isCurrentPositionValid()) && bIsOldPositionValid && ((this->mZPosInVolume) % this->mVolume->m_uBlockSideLength != 0)) - { - //No need to compute new block. - mCurrentVoxel += this->mVolume->m_uBlockSideLength * this->mVolume->m_uBlockSideLength; - } - else - { - //We've hit the block boundary. Just calling setPosition() is the easiest way to resolve this. - setPosition(this->mXPosInVolume, this->mYPosInVolume, this->mZPosInVolume); - } - } - - template - void LargeVolume::Sampler::moveNegativeX(void) - { - // We'll need this in a moment... - bool bIsOldPositionValid = this->isCurrentPositionValid(); - - // Base version updates position and validity flags. - BaseVolume::template Sampler< LargeVolume >::moveNegativeX(); - - // Then we update the voxel pointer - if((this->isCurrentPositionValid()) && bIsOldPositionValid && ((this->mXPosInVolume + 1) % this->mVolume->m_uBlockSideLength != 0)) - { - //No need to compute new block. - --mCurrentVoxel; - } - else - { - //We've hit the block boundary. Just calling setPosition() is the easiest way to resolve this. - setPosition(this->mXPosInVolume, this->mYPosInVolume, this->mZPosInVolume); - } - } - - template - void LargeVolume::Sampler::moveNegativeY(void) - { - // We'll need this in a moment... - bool bIsOldPositionValid = this->isCurrentPositionValid(); - - // Base version updates position and validity flags. - BaseVolume::template Sampler< LargeVolume >::moveNegativeY(); - - // Then we update the voxel pointer - if((this->isCurrentPositionValid()) && bIsOldPositionValid && ((this->mYPosInVolume + 1) % this->mVolume->m_uBlockSideLength != 0)) - { - //No need to compute new block. - mCurrentVoxel -= this->mVolume->m_uBlockSideLength; - } - else - { - //We've hit the block boundary. Just calling setPosition() is the easiest way to resolve this. - setPosition(this->mXPosInVolume, this->mYPosInVolume, this->mZPosInVolume); - } - } - - template - void LargeVolume::Sampler::moveNegativeZ(void) - { - // We'll need this in a moment... - bool bIsOldPositionValid = this->isCurrentPositionValid(); - - // Base version updates position and validity flags. - BaseVolume::template Sampler< LargeVolume >::moveNegativeZ(); - - // Then we update the voxel pointer - if((this->isCurrentPositionValid()) && bIsOldPositionValid && ((this->mZPosInVolume + 1) % this->mVolume->m_uBlockSideLength != 0)) - { - //No need to compute new block. - mCurrentVoxel -= this->mVolume->m_uBlockSideLength * this->mVolume->m_uBlockSideLength; - } - else - { - //We've hit the block boundary. Just calling setPosition() is the easiest way to resolve this. - setPosition(this->mXPosInVolume, this->mYPosInVolume, this->mZPosInVolume); - } - } - - template - VoxelType LargeVolume::Sampler::peekVoxel1nx1ny1nz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel - 1 - this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume-1,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType LargeVolume::Sampler::peekVoxel1nx1ny0pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) ) - { - return *(mCurrentVoxel - 1 - this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume-1,this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType LargeVolume::Sampler::peekVoxel1nx1ny1pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel - 1 - this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume-1,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType LargeVolume::Sampler::peekVoxel1nx0py1nz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel - 1 - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType LargeVolume::Sampler::peekVoxel1nx0py0pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) ) - { - return *(mCurrentVoxel - 1); - } - return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume,this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType LargeVolume::Sampler::peekVoxel1nx0py1pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel - 1 + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType LargeVolume::Sampler::peekVoxel1nx1py1nz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel - 1 + this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume+1,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType LargeVolume::Sampler::peekVoxel1nx1py0pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) ) - { - return *(mCurrentVoxel - 1 + this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume+1,this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType LargeVolume::Sampler::peekVoxel1nx1py1pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel - 1 + this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume+1,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); - } - - ////////////////////////////////////////////////////////////////////////// - - template - VoxelType LargeVolume::Sampler::peekVoxel0px1ny1nz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel - this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume,this->mYPosInVolume-1,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType LargeVolume::Sampler::peekVoxel0px1ny0pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_NEG_Y(this->mYPosInVolume) ) - { - return *(mCurrentVoxel - this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume,this->mYPosInVolume-1,this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType LargeVolume::Sampler::peekVoxel0px1ny1pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel - this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume,this->mYPosInVolume-1,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType LargeVolume::Sampler::peekVoxel0px0py1nz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_NEG_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume,this->mYPosInVolume,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType LargeVolume::Sampler::peekVoxel0px0py0pz(void) const - { - if((this->isCurrentPositionValid())) - { - return *mCurrentVoxel; - } - return this->mVolume->getVoxel(this->mXPosInVolume, this->mYPosInVolume, this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType LargeVolume::Sampler::peekVoxel0px0py1pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_POS_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume,this->mYPosInVolume,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType LargeVolume::Sampler::peekVoxel0px1py1nz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel + this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume,this->mYPosInVolume+1,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType LargeVolume::Sampler::peekVoxel0px1py0pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_POS_Y(this->mYPosInVolume) ) - { - return *(mCurrentVoxel + this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume,this->mYPosInVolume+1,this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType LargeVolume::Sampler::peekVoxel0px1py1pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel + this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume,this->mYPosInVolume+1,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); - } - - ////////////////////////////////////////////////////////////////////////// - - template - VoxelType LargeVolume::Sampler::peekVoxel1px1ny1nz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel + 1 - this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume-1,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType LargeVolume::Sampler::peekVoxel1px1ny0pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) ) - { - return *(mCurrentVoxel + 1 - this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume-1,this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType LargeVolume::Sampler::peekVoxel1px1ny1pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel + 1 - this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume-1,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType LargeVolume::Sampler::peekVoxel1px0py1nz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel + 1 - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType LargeVolume::Sampler::peekVoxel1px0py0pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) ) - { - return *(mCurrentVoxel + 1); - } - return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume,this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType LargeVolume::Sampler::peekVoxel1px0py1pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel + 1 + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType LargeVolume::Sampler::peekVoxel1px1py1nz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel + 1 + this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume+1,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType LargeVolume::Sampler::peekVoxel1px1py0pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) ) - { - return *(mCurrentVoxel + 1 + this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume+1,this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType LargeVolume::Sampler::peekVoxel1px1py1pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel + 1 + this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume+1,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); - } -} - -#undef CAN_GO_NEG_X -#undef CAN_GO_POS_X -#undef CAN_GO_NEG_Y -#undef CAN_GO_POS_Y -#undef CAN_GO_NEG_Z -#undef CAN_GO_POS_Z +/******************************************************************************* +Copyright (c) 2005-2009 David Williams + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*******************************************************************************/ + +#define CAN_GO_NEG_X(val) ((val > this->mVolume->getEnclosingRegion().getLowerX()) && (val % this->mVolume->m_uBlockSideLength != 0)) +#define CAN_GO_POS_X(val) ((val < this->mVolume->getEnclosingRegion().getUpperX()) && ((val + 1) % this->mVolume->m_uBlockSideLength != 0)) +#define CAN_GO_NEG_Y(val) ((val > this->mVolume->getEnclosingRegion().getLowerY()) && (val % this->mVolume->m_uBlockSideLength != 0)) +#define CAN_GO_POS_Y(val) ((val < this->mVolume->getEnclosingRegion().getUpperY()) && ((val + 1) % this->mVolume->m_uBlockSideLength != 0)) +#define CAN_GO_NEG_Z(val) ((val > this->mVolume->getEnclosingRegion().getLowerZ()) && (val % this->mVolume->m_uBlockSideLength != 0)) +#define CAN_GO_POS_Z(val) ((val < this->mVolume->getEnclosingRegion().getUpperZ()) && ((val + 1) % this->mVolume->m_uBlockSideLength != 0)) + +namespace PolyVox +{ + template + PagedVolume::Sampler::Sampler(PagedVolume* volume) + :BaseVolume::template Sampler< PagedVolume >(volume) + { + } + + template + PagedVolume::Sampler::~Sampler() + { + } + + template + VoxelType PagedVolume::Sampler::getSubSampledVoxel(uint8_t uLevel) const + { + if(uLevel == 0) + { + return getVoxel(); + } + else if(uLevel == 1) + { + VoxelType tValue = getVoxel(); + tValue = (std::min)(tValue, peekVoxel1px0py0pz()); + tValue = (std::min)(tValue, peekVoxel0px1py0pz()); + tValue = (std::min)(tValue, peekVoxel1px1py0pz()); + tValue = (std::min)(tValue, peekVoxel0px0py1pz()); + tValue = (std::min)(tValue, peekVoxel1px0py1pz()); + tValue = (std::min)(tValue, peekVoxel0px1py1pz()); + tValue = (std::min)(tValue, peekVoxel1px1py1pz()); + return tValue; + } + else + { + const uint8_t uSize = 1 << uLevel; + + VoxelType tValue = (std::numeric_limits::max)(); + for(uint8_t z = 0; z < uSize; ++z) + { + for(uint8_t y = 0; y < uSize; ++y) + { + for(uint8_t x = 0; x < uSize; ++x) + { + tValue = (std::min)(tValue, this->mVolume->getVoxelAt(this->mXPosInVolume + x, this->mYPosInVolume + y, this->mZPosInVolume + z)); + } + } + } + return tValue; + } + } + + template + VoxelType PagedVolume::Sampler::getVoxel(void) const + { + if(this->isCurrentPositionValid()) + { + return *mCurrentVoxel; + } + else + { + return this->getVoxelImpl(this->mXPosInVolume, this->mYPosInVolume, this->mZPosInVolume); + } + } + + template + void PagedVolume::Sampler::setPosition(const Vector3DInt32& v3dNewPos) + { + setPosition(v3dNewPos.getX(), v3dNewPos.getY(), v3dNewPos.getZ()); + } + + template + void PagedVolume::Sampler::setPosition(int32_t xPos, int32_t yPos, int32_t zPos) + { + // Base version updates position and validity flags. + BaseVolume::template Sampler< PagedVolume >::setPosition(xPos, yPos, zPos); + + // Then we update the voxel pointer + if(this->isCurrentPositionValid()) + { + const int32_t uXBlock = this->mXPosInVolume >> this->mVolume->m_uBlockSideLengthPower; + const int32_t uYBlock = this->mYPosInVolume >> this->mVolume->m_uBlockSideLengthPower; + const int32_t uZBlock = this->mZPosInVolume >> this->mVolume->m_uBlockSideLengthPower; + + const uint16_t uXPosInBlock = static_cast(this->mXPosInVolume - (uXBlock << this->mVolume->m_uBlockSideLengthPower)); + const uint16_t uYPosInBlock = static_cast(this->mYPosInVolume - (uYBlock << this->mVolume->m_uBlockSideLengthPower)); + const uint16_t uZPosInBlock = static_cast(this->mZPosInVolume - (uZBlock << this->mVolume->m_uBlockSideLengthPower)); + + const uint32_t uVoxelIndexInBlock = uXPosInBlock + + uYPosInBlock * this->mVolume->m_uBlockSideLength + + uZPosInBlock * this->mVolume->m_uBlockSideLength * this->mVolume->m_uBlockSideLength; + + auto pUncompressedCurrentBlock = this->mVolume->getUncompressedBlock(uXBlock, uYBlock, uZBlock); + + mCurrentVoxel = pUncompressedCurrentBlock->m_tData + uVoxelIndexInBlock; + } + else + { + mCurrentVoxel = 0; + } + } + + template + bool PagedVolume::Sampler::setVoxel(VoxelType tValue) + { + /*if(m_bIsCurrentPositionValidInX && m_bIsCurrentPositionValidInY && m_bIsCurrentPositionValidInZ) + { + *mCurrentVoxel = tValue; + return true; + } + else + { + return false; + }*/ + + //Need to think what effect this has on any existing iterators. + POLYVOX_THROW(not_implemented, "This function cannot be used on PagedVolume samplers."); + return false; + } + + template + void PagedVolume::Sampler::movePositiveX(void) + { + // We'll need this in a moment... + bool bIsOldPositionValid = this->isCurrentPositionValid(); + + // Base version updates position and validity flags. + BaseVolume::template Sampler< PagedVolume >::movePositiveX(); + + // Then we update the voxel pointer + if((this->isCurrentPositionValid()) && bIsOldPositionValid && ((this->mXPosInVolume) % this->mVolume->m_uBlockSideLength != 0)) + { + //No need to compute new block. + ++mCurrentVoxel; + } + else + { + //We've hit the block boundary. Just calling setPosition() is the easiest way to resolve this. + setPosition(this->mXPosInVolume, this->mYPosInVolume, this->mZPosInVolume); + } + } + + template + void PagedVolume::Sampler::movePositiveY(void) + { + // We'll need this in a moment... + bool bIsOldPositionValid = this->isCurrentPositionValid(); + + // Base version updates position and validity flags. + BaseVolume::template Sampler< PagedVolume >::movePositiveY(); + + // Then we update the voxel pointer + if((this->isCurrentPositionValid()) && bIsOldPositionValid && ((this->mYPosInVolume) % this->mVolume->m_uBlockSideLength != 0)) + { + //No need to compute new block. + mCurrentVoxel += this->mVolume->m_uBlockSideLength; + } + else + { + //We've hit the block boundary. Just calling setPosition() is the easiest way to resolve this. + setPosition(this->mXPosInVolume, this->mYPosInVolume, this->mZPosInVolume); + } + } + + template + void PagedVolume::Sampler::movePositiveZ(void) + { + // We'll need this in a moment... + bool bIsOldPositionValid = this->isCurrentPositionValid(); + + // Base version updates position and validity flags. + BaseVolume::template Sampler< PagedVolume >::movePositiveZ(); + + // Then we update the voxel pointer + if((this->isCurrentPositionValid()) && bIsOldPositionValid && ((this->mZPosInVolume) % this->mVolume->m_uBlockSideLength != 0)) + { + //No need to compute new block. + mCurrentVoxel += this->mVolume->m_uBlockSideLength * this->mVolume->m_uBlockSideLength; + } + else + { + //We've hit the block boundary. Just calling setPosition() is the easiest way to resolve this. + setPosition(this->mXPosInVolume, this->mYPosInVolume, this->mZPosInVolume); + } + } + + template + void PagedVolume::Sampler::moveNegativeX(void) + { + // We'll need this in a moment... + bool bIsOldPositionValid = this->isCurrentPositionValid(); + + // Base version updates position and validity flags. + BaseVolume::template Sampler< PagedVolume >::moveNegativeX(); + + // Then we update the voxel pointer + if((this->isCurrentPositionValid()) && bIsOldPositionValid && ((this->mXPosInVolume + 1) % this->mVolume->m_uBlockSideLength != 0)) + { + //No need to compute new block. + --mCurrentVoxel; + } + else + { + //We've hit the block boundary. Just calling setPosition() is the easiest way to resolve this. + setPosition(this->mXPosInVolume, this->mYPosInVolume, this->mZPosInVolume); + } + } + + template + void PagedVolume::Sampler::moveNegativeY(void) + { + // We'll need this in a moment... + bool bIsOldPositionValid = this->isCurrentPositionValid(); + + // Base version updates position and validity flags. + BaseVolume::template Sampler< PagedVolume >::moveNegativeY(); + + // Then we update the voxel pointer + if((this->isCurrentPositionValid()) && bIsOldPositionValid && ((this->mYPosInVolume + 1) % this->mVolume->m_uBlockSideLength != 0)) + { + //No need to compute new block. + mCurrentVoxel -= this->mVolume->m_uBlockSideLength; + } + else + { + //We've hit the block boundary. Just calling setPosition() is the easiest way to resolve this. + setPosition(this->mXPosInVolume, this->mYPosInVolume, this->mZPosInVolume); + } + } + + template + void PagedVolume::Sampler::moveNegativeZ(void) + { + // We'll need this in a moment... + bool bIsOldPositionValid = this->isCurrentPositionValid(); + + // Base version updates position and validity flags. + BaseVolume::template Sampler< PagedVolume >::moveNegativeZ(); + + // Then we update the voxel pointer + if((this->isCurrentPositionValid()) && bIsOldPositionValid && ((this->mZPosInVolume + 1) % this->mVolume->m_uBlockSideLength != 0)) + { + //No need to compute new block. + mCurrentVoxel -= this->mVolume->m_uBlockSideLength * this->mVolume->m_uBlockSideLength; + } + else + { + //We've hit the block boundary. Just calling setPosition() is the easiest way to resolve this. + setPosition(this->mXPosInVolume, this->mYPosInVolume, this->mZPosInVolume); + } + } + + template + VoxelType PagedVolume::Sampler::peekVoxel1nx1ny1nz(void) const + { + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) + { + return *(mCurrentVoxel - 1 - this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); + } + return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume-1,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); + } + + template + VoxelType PagedVolume::Sampler::peekVoxel1nx1ny0pz(void) const + { + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) ) + { + return *(mCurrentVoxel - 1 - this->mVolume->m_uBlockSideLength); + } + return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume-1,this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); + } + + template + VoxelType PagedVolume::Sampler::peekVoxel1nx1ny1pz(void) const + { + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) + { + return *(mCurrentVoxel - 1 - this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); + } + return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume-1,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); + } + + template + VoxelType PagedVolume::Sampler::peekVoxel1nx0py1nz(void) const + { + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) + { + return *(mCurrentVoxel - 1 - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); + } + return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); + } + + template + VoxelType PagedVolume::Sampler::peekVoxel1nx0py0pz(void) const + { + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) ) + { + return *(mCurrentVoxel - 1); + } + return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume,this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); + } + + template + VoxelType PagedVolume::Sampler::peekVoxel1nx0py1pz(void) const + { + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) + { + return *(mCurrentVoxel - 1 + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); + } + return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); + } + + template + VoxelType PagedVolume::Sampler::peekVoxel1nx1py1nz(void) const + { + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) + { + return *(mCurrentVoxel - 1 + this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); + } + return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume+1,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); + } + + template + VoxelType PagedVolume::Sampler::peekVoxel1nx1py0pz(void) const + { + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) ) + { + return *(mCurrentVoxel - 1 + this->mVolume->m_uBlockSideLength); + } + return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume+1,this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); + } + + template + VoxelType PagedVolume::Sampler::peekVoxel1nx1py1pz(void) const + { + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) + { + return *(mCurrentVoxel - 1 + this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); + } + return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume+1,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); + } + + ////////////////////////////////////////////////////////////////////////// + + template + VoxelType PagedVolume::Sampler::peekVoxel0px1ny1nz(void) const + { + if((this->isCurrentPositionValid()) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) + { + return *(mCurrentVoxel - this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); + } + return this->mVolume->getVoxel(this->mXPosInVolume,this->mYPosInVolume-1,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); + } + + template + VoxelType PagedVolume::Sampler::peekVoxel0px1ny0pz(void) const + { + if((this->isCurrentPositionValid()) && CAN_GO_NEG_Y(this->mYPosInVolume) ) + { + return *(mCurrentVoxel - this->mVolume->m_uBlockSideLength); + } + return this->mVolume->getVoxel(this->mXPosInVolume,this->mYPosInVolume-1,this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); + } + + template + VoxelType PagedVolume::Sampler::peekVoxel0px1ny1pz(void) const + { + if((this->isCurrentPositionValid()) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) + { + return *(mCurrentVoxel - this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); + } + return this->mVolume->getVoxel(this->mXPosInVolume,this->mYPosInVolume-1,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); + } + + template + VoxelType PagedVolume::Sampler::peekVoxel0px0py1nz(void) const + { + if((this->isCurrentPositionValid()) && CAN_GO_NEG_Z(this->mZPosInVolume) ) + { + return *(mCurrentVoxel - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); + } + return this->mVolume->getVoxel(this->mXPosInVolume,this->mYPosInVolume,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); + } + + template + VoxelType PagedVolume::Sampler::peekVoxel0px0py0pz(void) const + { + if((this->isCurrentPositionValid())) + { + return *mCurrentVoxel; + } + return this->mVolume->getVoxel(this->mXPosInVolume, this->mYPosInVolume, this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); + } + + template + VoxelType PagedVolume::Sampler::peekVoxel0px0py1pz(void) const + { + if((this->isCurrentPositionValid()) && CAN_GO_POS_Z(this->mZPosInVolume) ) + { + return *(mCurrentVoxel + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); + } + return this->mVolume->getVoxel(this->mXPosInVolume,this->mYPosInVolume,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); + } + + template + VoxelType PagedVolume::Sampler::peekVoxel0px1py1nz(void) const + { + if((this->isCurrentPositionValid()) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) + { + return *(mCurrentVoxel + this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); + } + return this->mVolume->getVoxel(this->mXPosInVolume,this->mYPosInVolume+1,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); + } + + template + VoxelType PagedVolume::Sampler::peekVoxel0px1py0pz(void) const + { + if((this->isCurrentPositionValid()) && CAN_GO_POS_Y(this->mYPosInVolume) ) + { + return *(mCurrentVoxel + this->mVolume->m_uBlockSideLength); + } + return this->mVolume->getVoxel(this->mXPosInVolume,this->mYPosInVolume+1,this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); + } + + template + VoxelType PagedVolume::Sampler::peekVoxel0px1py1pz(void) const + { + if((this->isCurrentPositionValid()) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) + { + return *(mCurrentVoxel + this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); + } + return this->mVolume->getVoxel(this->mXPosInVolume,this->mYPosInVolume+1,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); + } + + ////////////////////////////////////////////////////////////////////////// + + template + VoxelType PagedVolume::Sampler::peekVoxel1px1ny1nz(void) const + { + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) + { + return *(mCurrentVoxel + 1 - this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); + } + return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume-1,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); + } + + template + VoxelType PagedVolume::Sampler::peekVoxel1px1ny0pz(void) const + { + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) ) + { + return *(mCurrentVoxel + 1 - this->mVolume->m_uBlockSideLength); + } + return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume-1,this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); + } + + template + VoxelType PagedVolume::Sampler::peekVoxel1px1ny1pz(void) const + { + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) + { + return *(mCurrentVoxel + 1 - this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); + } + return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume-1,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); + } + + template + VoxelType PagedVolume::Sampler::peekVoxel1px0py1nz(void) const + { + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) + { + return *(mCurrentVoxel + 1 - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); + } + return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); + } + + template + VoxelType PagedVolume::Sampler::peekVoxel1px0py0pz(void) const + { + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) ) + { + return *(mCurrentVoxel + 1); + } + return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume,this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); + } + + template + VoxelType PagedVolume::Sampler::peekVoxel1px0py1pz(void) const + { + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) + { + return *(mCurrentVoxel + 1 + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); + } + return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); + } + + template + VoxelType PagedVolume::Sampler::peekVoxel1px1py1nz(void) const + { + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) + { + return *(mCurrentVoxel + 1 + this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); + } + return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume+1,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); + } + + template + VoxelType PagedVolume::Sampler::peekVoxel1px1py0pz(void) const + { + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) ) + { + return *(mCurrentVoxel + 1 + this->mVolume->m_uBlockSideLength); + } + return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume+1,this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); + } + + template + VoxelType PagedVolume::Sampler::peekVoxel1px1py1pz(void) const + { + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) + { + return *(mCurrentVoxel + 1 + this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); + } + return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume+1,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); + } +} + +#undef CAN_GO_NEG_X +#undef CAN_GO_POS_X +#undef CAN_GO_NEG_Y +#undef CAN_GO_POS_Y +#undef CAN_GO_NEG_Z +#undef CAN_GO_POS_Z diff --git a/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h b/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h index 4c63906d..449ff4a9 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h +++ b/library/PolyVoxCore/include/PolyVoxCore/PolyVoxForwardDeclarations.h @@ -103,11 +103,6 @@ namespace PolyVox //////////////////////////////////////////////////////////////////////////////// template class FilePager; - //////////////////////////////////////////////////////////////////////////////// - // LargeVolume - //////////////////////////////////////////////////////////////////////////////// - template class LargeVolume; - //////////////////////////////////////////////////////////////////////////////// // MarchingCubesSurfaceExtractor //////////////////////////////////////////////////////////////////////////////// @@ -145,6 +140,15 @@ namespace PolyVox typedef uint32_t DefaultIndexType; template class Mesh; + //////////////////////////////////////////////////////////////////////////////// + // PagedVolume + //////////////////////////////////////////////////////////////////////////////// + template class PagedVolume; + template + using LargeVolume = PagedVolume; + template + using SimpleVolume = PagedVolume; + //////////////////////////////////////////////////////////////////////////////// // Pager //////////////////////////////////////////////////////////////////////////////// @@ -160,11 +164,6 @@ namespace PolyVox //////////////////////////////////////////////////////////////////////////////// class Region; - //////////////////////////////////////////////////////////////////////////////// - // SimpleVolume - //////////////////////////////////////////////////////////////////////////////// - template class SimpleVolume; - //////////////////////////////////////////////////////////////////////////////// // Vector //////////////////////////////////////////////////////////////////////////////// diff --git a/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.h b/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.h index e8dda3e3..9967d874 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.h +++ b/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.h @@ -1,229 +1,7 @@ -/******************************************************************************* -Copyright (c) 2005-2009 David Williams - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*******************************************************************************/ - #ifndef __PolyVox_SimpleVolume_H__ #define __PolyVox_SimpleVolume_H__ -#include "Impl/Utility.h" +#include "PagedVolume.h" +#include "PolyVoxForwardDeclarations.h" -#include "PolyVoxCore/BaseVolume.h" -#include "PolyVoxCore/Region.h" -#include "PolyVoxCore/Vector.h" - -#include //For abort() -#include //For memcpy -#include -#include -#include //For invalid_argument - -namespace PolyVox -{ - template - class SimpleVolume : public BaseVolume - { - public: - #ifndef SWIG - //Could be made private? - class Block - { - public: - Block(uint16_t uSideLength = 0); - ~Block(); - - uint16_t getSideLength(void) const; - VoxelType getVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos) const; - VoxelType getVoxelAt(const Vector3DUint16& v3dPos) const; - - void setVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos, VoxelType tValue); - void setVoxelAt(const Vector3DUint16& v3dPos, VoxelType tValue); - - void fill(VoxelType tValue); - void initialise(uint16_t uSideLength); - uint32_t calculateSizeInBytes(void); - - public: - VoxelType* m_tUncompressedData; - uint16_t m_uSideLength; - uint8_t m_uSideLengthPower; - }; - - //There seems to be some descrepency between Visual Studio and GCC about how the following class should be declared. - //There is a work around (see also See http://goo.gl/qu1wn) given below which appears to work on VS2010 and GCC, but - //which seems to cause internal compiler errors on VS2008 when building with the /Gm 'Enable Minimal Rebuild' compiler - //option. For now it seems best to 'fix' it with the preprocessor insstead, but maybe the workaround can be reinstated - //in the future - //typedef Volume VolumeOfVoxelType; //Workaround for GCC/VS2010 differences. - //class Sampler : public VolumeOfVoxelType::template Sampler< SimpleVolume > -#if defined(_MSC_VER) - class Sampler : public BaseVolume::Sampler< SimpleVolume > //This line works on VS2010 -#else - class Sampler : public BaseVolume::template Sampler< SimpleVolume > //This line works on GCC -#endif - { - public: - /// Construct a new Sampler - Sampler(SimpleVolume* volume); - ~Sampler(); - - /// \deprecated - POLYVOX_DEPRECATED VoxelType getSubSampledVoxel(uint8_t uLevel) const; - /// Get the value of the current voxel - inline VoxelType getVoxel(void) const; - - /// Set the current voxel position - void setPosition(const Vector3DInt32& v3dNewPos); - /// Set the current voxel position - void setPosition(int32_t xPos, int32_t yPos, int32_t zPos); - /// Set the value of the current voxel - inline bool setVoxel(VoxelType tValue); - - /// Increase the \a x position by \a 1 - void movePositiveX(void); - /// Increase the \a y position by \a 1 - void movePositiveY(void); - /// Increase the \a z position by \a 1 - void movePositiveZ(void); - - /// Decrease the \a x position by \a 1 - void moveNegativeX(void); - /// Decrease the \a y position by \a 1 - void moveNegativeY(void); - /// Decrease the \a z position by \a 1 - void moveNegativeZ(void); - - inline VoxelType peekVoxel1nx1ny1nz(void) const; - inline VoxelType peekVoxel1nx1ny0pz(void) const; - inline VoxelType peekVoxel1nx1ny1pz(void) const; - inline VoxelType peekVoxel1nx0py1nz(void) const; - inline VoxelType peekVoxel1nx0py0pz(void) const; - inline VoxelType peekVoxel1nx0py1pz(void) const; - inline VoxelType peekVoxel1nx1py1nz(void) const; - inline VoxelType peekVoxel1nx1py0pz(void) const; - inline VoxelType peekVoxel1nx1py1pz(void) const; - - inline VoxelType peekVoxel0px1ny1nz(void) const; - inline VoxelType peekVoxel0px1ny0pz(void) const; - inline VoxelType peekVoxel0px1ny1pz(void) const; - inline VoxelType peekVoxel0px0py1nz(void) const; - inline VoxelType peekVoxel0px0py0pz(void) const; - inline VoxelType peekVoxel0px0py1pz(void) const; - inline VoxelType peekVoxel0px1py1nz(void) const; - inline VoxelType peekVoxel0px1py0pz(void) const; - inline VoxelType peekVoxel0px1py1pz(void) const; - - inline VoxelType peekVoxel1px1ny1nz(void) const; - inline VoxelType peekVoxel1px1ny0pz(void) const; - inline VoxelType peekVoxel1px1ny1pz(void) const; - inline VoxelType peekVoxel1px0py1nz(void) const; - inline VoxelType peekVoxel1px0py0pz(void) const; - inline VoxelType peekVoxel1px0py1pz(void) const; - inline VoxelType peekVoxel1px1py1nz(void) const; - inline VoxelType peekVoxel1px1py0pz(void) const; - inline VoxelType peekVoxel1px1py1pz(void) const; - - private: - //Other current position information - VoxelType* mCurrentVoxel; - }; - #endif - - public: - /// Constructor for creating a fixed size volume. - SimpleVolume(const Region& regValid, uint16_t uBlockSideLength = 32); - - /// Destructor - ~SimpleVolume(); - - /// Gets a voxel at the position given by x,y,z coordinates - 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 - 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::Validate, VoxelType tBorder = VoxelType()) const; - /// Gets a voxel at the position given by a 3D vector - VoxelType getVoxel(const Vector3DInt32& v3dPos, WrapMode eWrapMode = WrapModes::Validate, VoxelType tBorder = VoxelType()) const; - - /// Gets a voxel at the position given by x,y,z coordinates - POLYVOX_DEPRECATED VoxelType getVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos) const; - /// Gets a voxel at the position given by a 3D vector - POLYVOX_DEPRECATED VoxelType getVoxelAt(const Vector3DInt32& v3dPos) 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, WrapMode eWrapMode = WrapModes::Validate); - /// Sets the voxel at the position given by a 3D vector - void setVoxel(const Vector3DInt32& v3dPos, VoxelType tValue, WrapMode eWrapMode = WrapModes::Validate); - /// Sets the voxel at the position given by x,y,z coordinates - bool setVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue); - /// Sets the voxel at the position given by a 3D vector - bool setVoxelAt(const Vector3DInt32& v3dPos, VoxelType tValue); - - /// Calculates approximatly how many bytes of memory the volume is currently using. - uint32_t calculateSizeInBytes(void); - - protected: - /// Copy constructor - SimpleVolume(const SimpleVolume& rhs); - - /// Assignment operator - SimpleVolume& operator=(const SimpleVolume& rhs); - - 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 - Block* m_pBlocks; - - //The size of the volume in vlocks - Region m_regValidRegionInBlocks; - - //Volume size measured in blocks. - uint32_t m_uNoOfBlocksInVolume; - uint16_t m_uWidthInBlocks; - uint16_t m_uHeightInBlocks; - uint16_t m_uDepthInBlocks; - - //The size of the blocks - uint32_t m_uNoOfVoxelsPerBlock; - uint16_t m_uBlockSideLength; - uint8_t m_uBlockSideLengthPower; - }; -} - -#include "PolyVoxCore/SimpleVolumeBlock.inl" -#include "PolyVoxCore/SimpleVolume.inl" -#include "PolyVoxCore/SimpleVolumeSampler.inl" - -#endif //__PolyVox_SimpleVolume_H__ +#endif //__PolyVox_SimpleVolume_H__ \ No newline at end of file diff --git a/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.inl b/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.inl deleted file mode 100644 index f32aacdd..00000000 --- a/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.inl +++ /dev/null @@ -1,441 +0,0 @@ -/******************************************************************************* -Copyright (c) 2005-2009 David Williams - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*******************************************************************************/ - -#include "PolyVoxCore/Impl/ErrorHandling.h" - -namespace PolyVox -{ - //////////////////////////////////////////////////////////////////////////////// - /// This constructor creates a volume with a fixed size which is specified as a parameter. - /// \param regValid Specifies the minimum and maximum valid voxel positions. - /// \param uBlockSideLength The size of the block to use within the volume - //////////////////////////////////////////////////////////////////////////////// - template - SimpleVolume::SimpleVolume(const Region& regValid, uint16_t uBlockSideLength) - :BaseVolume(regValid) - { - //Create a volume of the right size. - initialise(regValid,uBlockSideLength); - } - - //////////////////////////////////////////////////////////////////////////////// - /// This function should never be called. Copying volumes by value would be expensive, and we want to prevent users from doing - /// it by accident (such as when passing them as paramenters to functions). That said, there are times when you really do want to - /// make a copy of a volume and in this case you should look at the Volumeresampler. - /// - /// \sa VolumeResampler - //////////////////////////////////////////////////////////////////////////////// - template - SimpleVolume::SimpleVolume(const SimpleVolume& /*rhs*/) - { - POLYVOX_THROW(not_implemented, "Volume copy constructor not implemented for performance reasons."); - } - - //////////////////////////////////////////////////////////////////////////////// - /// Destroys the volume - //////////////////////////////////////////////////////////////////////////////// - template - SimpleVolume::~SimpleVolume() - { - delete[] m_pBlocks; - } - - //////////////////////////////////////////////////////////////////////////////// - /// This function should never be called. Copying volumes by value would be expensive, and we want to prevent users from doing - /// it by accident (such as when passing them as paramenters to functions). That said, there are times when you really do want to - /// make a copy of a volume and in this case you should look at the Volumeresampler. - /// - /// \sa VolumeResampler - //////////////////////////////////////////////////////////////////////////////// - template - SimpleVolume& SimpleVolume::operator=(const SimpleVolume& /*rhs*/) - { - POLYVOX_THROW(not_implemented, "Volume assignment operator not implemented for performance reasons."); - } - - //////////////////////////////////////////////////////////////////////////////// - /// 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 - template - VoxelType SimpleVolume::getVoxel(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tBorder) const - { - // Simply call through to the real implementation - return getVoxelImpl(uXPos, uYPos, uZPos, WrapModeType(), tBorder); - } - - //////////////////////////////////////////////////////////////////////////////// - /// 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 - template - VoxelType SimpleVolume::getVoxel(const Vector3DInt32& v3dPos, VoxelType tBorder) const - { - // 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::Validate: - 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::AssumeValid: - 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); - } - - //////////////////////////////////////////////////////////////////////////////// - /// \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::getVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos) const - { - if(this->m_regValidRegion.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos))) - { - 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); - } - else - { - return this->getBorderValue(); - } - } - - //////////////////////////////////////////////////////////////////////////////// - /// \param v3dPos The 3D position of the voxel - /// \return The voxel value - //////////////////////////////////////////////////////////////////////////////// - template - VoxelType SimpleVolume::getVoxelAt(const Vector3DInt32& v3dPos) const - { - 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 - /// \param tValue the value to which the voxel will be set - /// \param eWrapMode Specifies the behaviour when the requested position is outside of the volume. - /// This must be set to 'None' or 'DontCheck'. Other wrap modes cannot be used when writing to volume data. - //////////////////////////////////////////////////////////////////////////////// - template - void SimpleVolume::setVoxel(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue, WrapMode eWrapMode) - { - if((eWrapMode != WrapModes::Validate) && (eWrapMode != WrapModes::AssumeValid)) - { - POLYVOX_THROW(std::invalid_argument, "Invalid wrap mode in call to setVoxel(). It must be 'None' or 'DontCheck'."); - } - - // This validation is skipped if the wrap mode is 'DontCheck' - if(eWrapMode == WrapModes::Validate) - { - 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 = uXPos - (blockX << m_uBlockSideLengthPower); - const uint16_t yOffset = uYPos - (blockY << m_uBlockSideLengthPower); - const uint16_t zOffset = uZPos - (blockZ << m_uBlockSideLengthPower); - - typename SimpleVolume::Block* pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ); - - pUncompressedBlock->setVoxelAt(xOffset,yOffset,zOffset, tValue); - } - - //////////////////////////////////////////////////////////////////////////////// - /// \param v3dPos the 3D position of the voxel - /// \param tValue the value to which the voxel will be set - /// \param eWrapMode Specifies the behaviour when the requested position is outside of the volume. - /// This must be set to 'None' or 'DontCheck'. Other wrap modes cannot be used when writing to volume data. - //////////////////////////////////////////////////////////////////////////////// - template - void SimpleVolume::setVoxel(const Vector3DInt32& v3dPos, VoxelType tValue, WrapMode eWrapMode) - { - setVoxel(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ(), tValue, eWrapMode); - } - - //////////////////////////////////////////////////////////////////////////////// - /// \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 tValue the value to which the voxel will be set - /// \return whether the requested position is inside the volume - //////////////////////////////////////////////////////////////////////////////// - template - bool SimpleVolume::setVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue) - { - // PolyVox does not throw an exception when a voxel is out of range. Please see 'Error Handling' in the User Manual. - POLYVOX_ASSERT(this->m_regValidRegion.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos)), "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 = uXPos - (blockX << m_uBlockSideLengthPower); - const uint16_t yOffset = uYPos - (blockY << m_uBlockSideLengthPower); - const uint16_t zOffset = uZPos - (blockZ << m_uBlockSideLengthPower); - - typename SimpleVolume::Block* pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ); - - pUncompressedBlock->setVoxelAt(xOffset,yOffset,zOffset, tValue); - - //Return true to indicate that we modified a voxel. - return true; - } - - //////////////////////////////////////////////////////////////////////////////// - /// \param v3dPos the 3D position of the voxel - /// \param tValue the value to which the voxel will be set - /// \return whether the requested position is inside the volume - //////////////////////////////////////////////////////////////////////////////// - template - bool SimpleVolume::setVoxelAt(const Vector3DInt32& v3dPos, VoxelType tValue) - { - return setVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ(), tValue); - } - - //////////////////////////////////////////////////////////////////////////////// - /// This function should probably be made internal... - //////////////////////////////////////////////////////////////////////////////// - template - void SimpleVolume::initialise(const Region& regValidRegion, uint16_t uBlockSideLength) - { - //Release mode validation - if(uBlockSideLength < 8) - { - POLYVOX_THROW(std::invalid_argument, "Block side length should be at least 8"); - } - if(uBlockSideLength > 256) - { - POLYVOX_THROW(std::invalid_argument, "Block side length should not be more than 256"); - } - if(!isPowerOf2(uBlockSideLength)) - { - POLYVOX_THROW(std::invalid_argument, "Block side length must be a power of two."); - } - - this->m_regValidRegion = regValidRegion; - - //Compute the block side length - m_uBlockSideLength = uBlockSideLength; - m_uBlockSideLengthPower = logBase2(m_uBlockSideLength); - m_uNoOfVoxelsPerBlock = m_uBlockSideLength * m_uBlockSideLength * m_uBlockSideLength; - - m_regValidRegionInBlocks.setLowerX(this->m_regValidRegion.getLowerX() >> m_uBlockSideLengthPower); - m_regValidRegionInBlocks.setLowerY(this->m_regValidRegion.getLowerY() >> m_uBlockSideLengthPower); - m_regValidRegionInBlocks.setLowerZ(this->m_regValidRegion.getLowerZ() >> m_uBlockSideLengthPower); - m_regValidRegionInBlocks.setUpperX(this->m_regValidRegion.getUpperX() >> m_uBlockSideLengthPower); - m_regValidRegionInBlocks.setUpperY(this->m_regValidRegion.getUpperY() >> m_uBlockSideLengthPower); - m_regValidRegionInBlocks.setUpperZ(this->m_regValidRegion.getUpperZ() >> m_uBlockSideLengthPower); - - //Compute the size of the volume in blocks (and note +1 at the end) - m_uWidthInBlocks = m_regValidRegionInBlocks.getUpperX() - m_regValidRegionInBlocks.getLowerX() + 1; - m_uHeightInBlocks = m_regValidRegionInBlocks.getUpperY() - m_regValidRegionInBlocks.getLowerY() + 1; - m_uDepthInBlocks = m_regValidRegionInBlocks.getUpperZ() - m_regValidRegionInBlocks.getLowerZ() + 1; - m_uNoOfBlocksInVolume = m_uWidthInBlocks * m_uHeightInBlocks * m_uDepthInBlocks; - - //Allocate the data - m_pBlocks = new Block[m_uNoOfBlocksInVolume]; - for(uint32_t i = 0; i < m_uNoOfBlocksInVolume; ++i) - { - m_pBlocks[i].initialise(m_uBlockSideLength); - } - - //Other properties we might find useful later - this->m_uLongestSideLength = (std::max)((std::max)(this->getWidth(),this->getHeight()),this->getDepth()); - this->m_uShortestSideLength = (std::min)((std::min)(this->getWidth(),this->getHeight()),this->getDepth()); - this->m_fDiagonalLength = sqrtf(static_cast(this->getWidth() * this->getWidth() + this->getHeight() * this->getHeight() + this->getDepth() * this->getDepth())); - } - - template - typename SimpleVolume::Block* SimpleVolume::getUncompressedBlock(int32_t uBlockX, int32_t uBlockY, int32_t uBlockZ) const - { - //The lower left corner of the volume could be - //anywhere, but array indices need to start at zero. - uBlockX -= m_regValidRegionInBlocks.getLowerX(); - uBlockY -= m_regValidRegionInBlocks.getLowerY(); - uBlockZ -= m_regValidRegionInBlocks.getLowerZ(); - - POLYVOX_ASSERT(uBlockX >= 0, "Block coordinate must not be negative."); - POLYVOX_ASSERT(uBlockY >= 0, "Block coordinate must not be negative."); - POLYVOX_ASSERT(uBlockZ >= 0, "Block coordinate must not be negative."); - - //Compute the block index - uint32_t uBlockIndex = - uBlockX + - uBlockY * m_uWidthInBlocks + - uBlockZ * m_uWidthInBlocks * m_uHeightInBlocks; - - //Return the block - return &(m_pBlocks[uBlockIndex]); - } - - //////////////////////////////////////////////////////////////////////////////// - /// \todo This function needs reviewing for accuracy... - /// - /// \return The number of bytes used - //////////////////////////////////////////////////////////////////////////////// - template - uint32_t SimpleVolume::calculateSizeInBytes(void) - { - uint32_t uSizeInBytes = sizeof(SimpleVolume); - - uint32_t uSizeOfBlockInBytes = m_uNoOfVoxelsPerBlock * sizeof(VoxelType); - - //Memory used by the blocks - uSizeInBytes += uSizeOfBlockInBytes * (m_uNoOfBlocksInVolume); - - 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); - } -} - diff --git a/library/PolyVoxCore/include/PolyVoxCore/SimpleVolumeBlock.inl b/library/PolyVoxCore/include/PolyVoxCore/SimpleVolumeBlock.inl deleted file mode 100644 index 08e06c1e..00000000 --- a/library/PolyVoxCore/include/PolyVoxCore/SimpleVolumeBlock.inl +++ /dev/null @@ -1,130 +0,0 @@ -/******************************************************************************* -Copyright (c) 2005-2009 David Williams - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*******************************************************************************/ - -#include "PolyVoxCore/Impl/ErrorHandling.h" - -namespace PolyVox -{ - template - SimpleVolume::Block::Block(uint16_t uSideLength) - :m_tUncompressedData(0) - ,m_uSideLength(0) - ,m_uSideLengthPower(0) - { - if(uSideLength != 0) - { - initialise(uSideLength); - } - } - - template - SimpleVolume::Block::~Block() - { - delete[] m_tUncompressedData; - } - - template - uint16_t SimpleVolume::Block::getSideLength(void) const - { - return m_uSideLength; - } - - template - VoxelType SimpleVolume::Block::getVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos) const - { - // This is internal code not directly called by the user. For efficiency we assert rather than throwing. - POLYVOX_ASSERT(uXPos < m_uSideLength, "Position is outside of the block."); - POLYVOX_ASSERT(uYPos < m_uSideLength, "Position is outside of the block."); - POLYVOX_ASSERT(uZPos < m_uSideLength, "Position is outside of the block."); - POLYVOX_ASSERT(m_tUncompressedData, "No uncompressed data available"); - - return m_tUncompressedData - [ - uXPos + - uYPos * m_uSideLength + - uZPos * m_uSideLength * m_uSideLength - ]; - } - - template - VoxelType SimpleVolume::Block::getVoxelAt(const Vector3DUint16& v3dPos) const - { - return getVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ()); - } - - template - void SimpleVolume::Block::setVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos, VoxelType tValue) - { - // This is internal code not directly called by the user. For efficiency we assert rather than throwing. - POLYVOX_ASSERT(uXPos < m_uSideLength, "Position is outside of the block."); - POLYVOX_ASSERT(uYPos < m_uSideLength, "Position is outside of the block."); - POLYVOX_ASSERT(uZPos < m_uSideLength, "Position is outside of the block."); - POLYVOX_ASSERT(m_tUncompressedData, "No uncompressed data available"); - - m_tUncompressedData - [ - uXPos + - uYPos * m_uSideLength + - uZPos * m_uSideLength * m_uSideLength - ] = tValue; - } - - template - void SimpleVolume::Block::setVoxelAt(const Vector3DUint16& v3dPos, VoxelType tValue) - { - setVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ(), tValue); - } - - template - void SimpleVolume::Block::fill(VoxelType tValue) - { - const uint32_t uNoOfVoxels = m_uSideLength * m_uSideLength * m_uSideLength; - std::fill(m_tUncompressedData, m_tUncompressedData + uNoOfVoxels, tValue); - } - - template - void SimpleVolume::Block::initialise(uint16_t uSideLength) - { - //Release mode validation - if(!isPowerOf2(uSideLength)) - { - POLYVOX_THROW(std::invalid_argument, "Block side length must be a power of two."); - } - - //Compute the side length - m_uSideLength = uSideLength; - m_uSideLengthPower = logBase2(uSideLength); - - m_tUncompressedData = new VoxelType[m_uSideLength * m_uSideLength * m_uSideLength]; - - SimpleVolume::Block::fill(VoxelType()); - } - - template - uint32_t SimpleVolume::Block::calculateSizeInBytes(void) - { - uint32_t uSizeInBytes = sizeof(Block); - uSizeInBytes += sizeof(VoxelType) * m_uSideLength * m_uSideLength * m_uSideLength; - return uSizeInBytes; - } -} diff --git a/library/PolyVoxCore/include/PolyVoxCore/SimpleVolumeSampler.inl b/library/PolyVoxCore/include/PolyVoxCore/SimpleVolumeSampler.inl deleted file mode 100644 index 6cf16bef..00000000 --- a/library/PolyVoxCore/include/PolyVoxCore/SimpleVolumeSampler.inl +++ /dev/null @@ -1,581 +0,0 @@ -/******************************************************************************* -Copyright (c) 2005-2009 David Williams - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*******************************************************************************/ - -#define CAN_GO_NEG_X(val) ((val > this->mVolume->getEnclosingRegion().getLowerX()) && (val % this->mVolume->m_uBlockSideLength != 0)) -#define CAN_GO_POS_X(val) ((val < this->mVolume->getEnclosingRegion().getUpperX()) && ((val + 1) % this->mVolume->m_uBlockSideLength != 0)) -#define CAN_GO_NEG_Y(val) ((val > this->mVolume->getEnclosingRegion().getLowerY()) && (val % this->mVolume->m_uBlockSideLength != 0)) -#define CAN_GO_POS_Y(val) ((val < this->mVolume->getEnclosingRegion().getUpperY()) && ((val + 1) % this->mVolume->m_uBlockSideLength != 0)) -#define CAN_GO_NEG_Z(val) ((val > this->mVolume->getEnclosingRegion().getLowerZ()) && (val % this->mVolume->m_uBlockSideLength != 0)) -#define CAN_GO_POS_Z(val) ((val < this->mVolume->getEnclosingRegion().getUpperZ()) && ((val + 1) % this->mVolume->m_uBlockSideLength != 0)) - -namespace PolyVox -{ - /** - * \param volume The SimpleVolume you want to sample - */ - template - SimpleVolume::Sampler::Sampler(SimpleVolume* volume) - :BaseVolume::template Sampler< SimpleVolume >(volume) - { - } - - template - SimpleVolume::Sampler::~Sampler() - { - } - - template - VoxelType SimpleVolume::Sampler::getSubSampledVoxel(uint8_t uLevel) const - { - if(uLevel == 0) - { - return getVoxel(); - } - else if(uLevel == 1) - { - VoxelType tValue = getVoxel(); - tValue = (std::min)(tValue, peekVoxel1px0py0pz()); - tValue = (std::min)(tValue, peekVoxel0px1py0pz()); - tValue = (std::min)(tValue, peekVoxel1px1py0pz()); - tValue = (std::min)(tValue, peekVoxel0px0py1pz()); - tValue = (std::min)(tValue, peekVoxel1px0py1pz()); - tValue = (std::min)(tValue, peekVoxel0px1py1pz()); - tValue = (std::min)(tValue, peekVoxel1px1py1pz()); - return tValue; - } - else - { - const uint8_t uSize = 1 << uLevel; - - VoxelType tValue = (std::numeric_limits::max)(); - for(uint8_t z = 0; z < uSize; ++z) - { - for(uint8_t y = 0; y < uSize; ++y) - { - for(uint8_t x = 0; x < uSize; ++x) - { - tValue = (std::min)(tValue, this->mVolume->getVoxelAt(this->mXPosInVolume + x, this->mYPosInVolume + y, this->mZPosInVolume + z)); - } - } - } - return tValue; - } - } - - /** - * \return The current voxel - */ - template - VoxelType SimpleVolume::Sampler::getVoxel(void) const - { - if(this->isCurrentPositionValid()) - { - return *mCurrentVoxel; - } - else - { - return this->getVoxelImpl(this->mXPosInVolume, this->mYPosInVolume, this->mZPosInVolume); - } - } - - /** - * \param v3dNewPos The position to set to - */ - template - void SimpleVolume::Sampler::setPosition(const Vector3DInt32& v3dNewPos) - { - setPosition(v3dNewPos.getX(), v3dNewPos.getY(), v3dNewPos.getZ()); - } - - /** - * \param xPos The \a x position to set to - * \param yPos The \a y position to set to - * \param zPos The \a z position to set to - */ - template - void SimpleVolume::Sampler::setPosition(int32_t xPos, int32_t yPos, int32_t zPos) - { - // Base version updates position and validity flags. - BaseVolume::template Sampler< SimpleVolume >::setPosition(xPos, yPos, zPos); - - // Then we update the voxel pointer - if(this->isCurrentPositionValid()) - { - const int32_t uXBlock = this->mXPosInVolume >> this->mVolume->m_uBlockSideLengthPower; - const int32_t uYBlock = this->mYPosInVolume >> this->mVolume->m_uBlockSideLengthPower; - const int32_t uZBlock = this->mZPosInVolume >> this->mVolume->m_uBlockSideLengthPower; - - const uint16_t uXPosInBlock = static_cast(this->mXPosInVolume - (uXBlock << this->mVolume->m_uBlockSideLengthPower)); - const uint16_t uYPosInBlock = static_cast(this->mYPosInVolume - (uYBlock << this->mVolume->m_uBlockSideLengthPower)); - const uint16_t uZPosInBlock = static_cast(this->mZPosInVolume - (uZBlock << this->mVolume->m_uBlockSideLengthPower)); - - const uint32_t uVoxelIndexInBlock = uXPosInBlock + - uYPosInBlock * this->mVolume->m_uBlockSideLength + - uZPosInBlock * this->mVolume->m_uBlockSideLength * this->mVolume->m_uBlockSideLength; - - Block* pUncompressedCurrentBlock = this->mVolume->getUncompressedBlock(uXBlock, uYBlock, uZBlock); - - mCurrentVoxel = pUncompressedCurrentBlock->m_tUncompressedData + uVoxelIndexInBlock; - } - else - { - mCurrentVoxel = 0; - } - } - - /** - * \details - * - * This function checks that the current voxel position that you're trying - * to set is not outside the volume. If it is, this function returns - * \a false, otherwise it will return \a true. - * - * \param tValue The value to set to voxel to - */ - template - bool SimpleVolume::Sampler::setVoxel(VoxelType tValue) - { - if(this->m_bIsCurrentPositionValidInX && this->m_bIsCurrentPositionValidInY && this->m_bIsCurrentPositionValidInZ) - { - *mCurrentVoxel = tValue; - return true; - } - else - { - return false; - } - } - - template - void SimpleVolume::Sampler::movePositiveX(void) - { - // We'll need this in a moment... - bool bIsOldPositionValid = this->isCurrentPositionValid(); - - // Base version updates position and validity flags. - BaseVolume::template Sampler< SimpleVolume >::movePositiveX(); - - // Then we update the voxel pointer - if((this->isCurrentPositionValid()) && bIsOldPositionValid && ((this->mXPosInVolume) % this->mVolume->m_uBlockSideLength != 0)) - { - //No need to compute new block. - ++mCurrentVoxel; - } - else - { - //We've hit the block boundary. Just calling setPosition() is the easiest way to resolve this. - setPosition(this->mXPosInVolume, this->mYPosInVolume, this->mZPosInVolume); - } - } - - template - void SimpleVolume::Sampler::movePositiveY(void) - { - // We'll need this in a moment... - bool bIsOldPositionValid = this->isCurrentPositionValid(); - - // Base version updates position and validity flags. - BaseVolume::template Sampler< SimpleVolume >::movePositiveY(); - - // Then we update the voxel pointer - if((this->isCurrentPositionValid()) && bIsOldPositionValid && ((this->mYPosInVolume) % this->mVolume->m_uBlockSideLength != 0)) - { - //No need to compute new block. - mCurrentVoxel += this->mVolume->m_uBlockSideLength; - } - else - { - //We've hit the block boundary. Just calling setPosition() is the easiest way to resolve this. - setPosition(this->mXPosInVolume, this->mYPosInVolume, this->mZPosInVolume); - } - } - - template - void SimpleVolume::Sampler::movePositiveZ(void) - { - // We'll need this in a moment... - bool bIsOldPositionValid = this->isCurrentPositionValid(); - - // Base version updates position and validity flags. - BaseVolume::template Sampler< SimpleVolume >::movePositiveZ(); - - // Then we update the voxel pointer - if((this->isCurrentPositionValid()) && bIsOldPositionValid && ((this->mZPosInVolume) % this->mVolume->m_uBlockSideLength != 0)) - { - //No need to compute new block. - mCurrentVoxel += this->mVolume->m_uBlockSideLength * this->mVolume->m_uBlockSideLength; - } - else - { - //We've hit the block boundary. Just calling setPosition() is the easiest way to resolve this. - setPosition(this->mXPosInVolume, this->mYPosInVolume, this->mZPosInVolume); - } - } - - template - void SimpleVolume::Sampler::moveNegativeX(void) - { - // We'll need this in a moment... - bool bIsOldPositionValid = this->isCurrentPositionValid(); - - // Base version updates position and validity flags. - BaseVolume::template Sampler< SimpleVolume >::moveNegativeX(); - - // Then we update the voxel pointer - if((this->isCurrentPositionValid()) && bIsOldPositionValid && ((this->mXPosInVolume + 1) % this->mVolume->m_uBlockSideLength != 0)) - { - //No need to compute new block. - --mCurrentVoxel; - } - else - { - //We've hit the block boundary. Just calling setPosition() is the easiest way to resolve this. - setPosition(this->mXPosInVolume, this->mYPosInVolume, this->mZPosInVolume); - } - } - - template - void SimpleVolume::Sampler::moveNegativeY(void) - { - // We'll need this in a moment... - bool bIsOldPositionValid = this->isCurrentPositionValid(); - - // Base version updates position and validity flags. - BaseVolume::template Sampler< SimpleVolume >::moveNegativeY(); - - // Then we update the voxel pointer - if((this->isCurrentPositionValid()) && bIsOldPositionValid && ((this->mYPosInVolume + 1) % this->mVolume->m_uBlockSideLength != 0)) - { - //No need to compute new block. - mCurrentVoxel -= this->mVolume->m_uBlockSideLength; - } - else - { - //We've hit the block boundary. Just calling setPosition() is the easiest way to resolve this. - setPosition(this->mXPosInVolume, this->mYPosInVolume, this->mZPosInVolume); - } - } - - template - void SimpleVolume::Sampler::moveNegativeZ(void) - { - // We'll need this in a moment... - bool bIsOldPositionValid = this->isCurrentPositionValid(); - - // Base version updates position and validity flags. - BaseVolume::template Sampler< SimpleVolume >::moveNegativeZ(); - - // Then we update the voxel pointer - if((this->isCurrentPositionValid()) && bIsOldPositionValid && ((this->mZPosInVolume + 1) % this->mVolume->m_uBlockSideLength != 0)) - { - //No need to compute new block. - mCurrentVoxel -= this->mVolume->m_uBlockSideLength * this->mVolume->m_uBlockSideLength; - } - else - { - //We've hit the block boundary. Just calling setPosition() is the easiest way to resolve this. - setPosition(this->mXPosInVolume, this->mYPosInVolume, this->mZPosInVolume); - } - } - - template - VoxelType SimpleVolume::Sampler::peekVoxel1nx1ny1nz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel - 1 - this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume-1,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType SimpleVolume::Sampler::peekVoxel1nx1ny0pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) ) - { - return *(mCurrentVoxel - 1 - this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume-1,this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType SimpleVolume::Sampler::peekVoxel1nx1ny1pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel - 1 - this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume-1,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType SimpleVolume::Sampler::peekVoxel1nx0py1nz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel - 1 - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType SimpleVolume::Sampler::peekVoxel1nx0py0pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) ) - { - return *(mCurrentVoxel - 1); - } - return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume,this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType SimpleVolume::Sampler::peekVoxel1nx0py1pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel - 1 + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType SimpleVolume::Sampler::peekVoxel1nx1py1nz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel - 1 + this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume+1,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType SimpleVolume::Sampler::peekVoxel1nx1py0pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) ) - { - return *(mCurrentVoxel - 1 + this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume+1,this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType SimpleVolume::Sampler::peekVoxel1nx1py1pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel - 1 + this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume-1,this->mYPosInVolume+1,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); - } - - ////////////////////////////////////////////////////////////////////////// - - template - VoxelType SimpleVolume::Sampler::peekVoxel0px1ny1nz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel - this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume,this->mYPosInVolume-1,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType SimpleVolume::Sampler::peekVoxel0px1ny0pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_NEG_Y(this->mYPosInVolume) ) - { - return *(mCurrentVoxel - this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume,this->mYPosInVolume-1,this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType SimpleVolume::Sampler::peekVoxel0px1ny1pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel - this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume,this->mYPosInVolume-1,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType SimpleVolume::Sampler::peekVoxel0px0py1nz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_NEG_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume,this->mYPosInVolume,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType SimpleVolume::Sampler::peekVoxel0px0py0pz(void) const - { - if((this->isCurrentPositionValid())) - { - return *mCurrentVoxel; - } - return this->mVolume->getVoxel(this->mXPosInVolume, this->mYPosInVolume, this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType SimpleVolume::Sampler::peekVoxel0px0py1pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_POS_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume,this->mYPosInVolume,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType SimpleVolume::Sampler::peekVoxel0px1py1nz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel + this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume,this->mYPosInVolume+1,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType SimpleVolume::Sampler::peekVoxel0px1py0pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_POS_Y(this->mYPosInVolume) ) - { - return *(mCurrentVoxel + this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume,this->mYPosInVolume+1,this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType SimpleVolume::Sampler::peekVoxel0px1py1pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel + this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume,this->mYPosInVolume+1,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); - } - - ////////////////////////////////////////////////////////////////////////// - - template - VoxelType SimpleVolume::Sampler::peekVoxel1px1ny1nz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel + 1 - this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume-1,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType SimpleVolume::Sampler::peekVoxel1px1ny0pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) ) - { - return *(mCurrentVoxel + 1 - this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume-1,this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType SimpleVolume::Sampler::peekVoxel1px1ny1pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel + 1 - this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume-1,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType SimpleVolume::Sampler::peekVoxel1px0py1nz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel + 1 - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType SimpleVolume::Sampler::peekVoxel1px0py0pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) ) - { - return *(mCurrentVoxel + 1); - } - return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume,this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType SimpleVolume::Sampler::peekVoxel1px0py1pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel + 1 + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType SimpleVolume::Sampler::peekVoxel1px1py1nz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel + 1 + this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume+1,this->mZPosInVolume-1,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType SimpleVolume::Sampler::peekVoxel1px1py0pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) ) - { - return *(mCurrentVoxel + 1 + this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume+1,this->mZPosInVolume,this->m_eWrapMode, this->m_tBorder); - } - - template - VoxelType SimpleVolume::Sampler::peekVoxel1px1py1pz(void) const - { - if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) - { - return *(mCurrentVoxel + 1 + this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); - } - return this->mVolume->getVoxel(this->mXPosInVolume+1,this->mYPosInVolume+1,this->mZPosInVolume+1,this->m_eWrapMode, this->m_tBorder); - } -} - -#undef CAN_GO_NEG_X -#undef CAN_GO_POS_X -#undef CAN_GO_NEG_Y -#undef CAN_GO_POS_Y -#undef CAN_GO_NEG_Z -#undef CAN_GO_POS_Z diff --git a/library/PolyVoxCore/include/PolyVoxCore/UncompressedBlock.h b/library/PolyVoxCore/include/PolyVoxCore/UncompressedBlock.h index f65b0dc2..abc3ca63 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/UncompressedBlock.h +++ b/library/PolyVoxCore/include/PolyVoxCore/UncompressedBlock.h @@ -32,7 +32,7 @@ namespace PolyVox template class UncompressedBlock { - friend class LargeVolume; + friend class PagedVolume; public: UncompressedBlock(Vector3DInt32 v3dPosition, uint16_t uSideLength, Pager* pPager = nullptr); @@ -54,7 +54,7 @@ namespace PolyVox /// Private assignment operator to prevent accisdental copying UncompressedBlock& operator=(const UncompressedBlock& /*rhs*/) {}; - // This is updated by the LargeVolume and used to discard the least recently used blocks. + // This is updated by the PagedVolume and used to discard the least recently used blocks. uint32_t m_uBlockLastAccessed; // This is so we can tell whether a uncompressed block has to be recompressed and whether