Added initial version of SimpleVolume by duplicating LargeVolume.

This commit is contained in:
David Williams 2011-04-29 22:02:40 +01:00
parent 34cd8c05ed
commit afbf49f626
7 changed files with 1648 additions and 12 deletions

View File

@ -26,14 +26,14 @@ freely, subject to the following restrictions:
#include "MaterialDensityPair.h"
#include "CubicSurfaceExtractorWithNormals.h"
#include "SurfaceMesh.h"
#include "LargeVolume.h"
#include "SimpleVolume.h"
#include <QApplication>
//Use the PolyVox namespace
using namespace PolyVox;
void createSphereInVolume(LargeVolume<MaterialDensityPair44>& volData, float fRadius)
void createSphereInVolume(SimpleVolume<MaterialDensityPair44>& volData, float fRadius)
{
//This vector hold the position of the center of the volume
Vector3DFloat v3dVolCenter(volData.getWidth() / 2, volData.getHeight() / 2, volData.getDepth() / 2);
@ -78,12 +78,12 @@ int main(int argc, char *argv[])
openGLWidget.show();
//Create an empty volume and then place a sphere in it
LargeVolume<MaterialDensityPair44> volData(PolyVox::Region(Vector3DInt32(0,0,0), Vector3DInt32(63, 63, 63)));
SimpleVolume<MaterialDensityPair44> volData(PolyVox::Region(Vector3DInt32(0,0,0), Vector3DInt32(63, 63, 63)));
createSphereInVolume(volData, 30);
//Extract the surface
SurfaceMesh<PositionMaterialNormal> mesh;
CubicSurfaceExtractorWithNormals<MaterialDensityPair44> surfaceExtractor(&volData, volData.getEnclosingRegion(), &mesh);
CubicSurfaceExtractorWithNormals<SimpleVolume <MaterialDensityPair44>, MaterialDensityPair44 > surfaceExtractor(&volData, volData.getEnclosingRegion(), &mesh);
surfaceExtractor.execute();
//Pass the surface to the OpenGL window

View File

@ -47,6 +47,9 @@ SET(CORE_INC_FILES
include/RaycastWithCallback.h
include/RaycastWithCallback.inl
include/Region.h
include/SimpleVolume.h
include/SimpleVolume.inl
include/SimpleVolumeSampler.inl
include/SurfaceExtractor.h
include/SurfaceExtractor.inl
include/SurfaceMesh.h

View File

@ -31,18 +31,18 @@ freely, subject to the following restrictions:
namespace PolyVox
{
template <typename VoxelType>
template <typename VolumeType, typename VoxelType>
class CubicSurfaceExtractorWithNormals
{
public:
CubicSurfaceExtractorWithNormals(LargeVolume<VoxelType>* volData, Region region, SurfaceMesh<PositionMaterialNormal>* result);
CubicSurfaceExtractorWithNormals(VolumeType* volData, Region region, SurfaceMesh<PositionMaterialNormal>* result);
void execute();
private:
//The volume data and a sampler to access it.
LargeVolume<VoxelType>* m_volData;
typename LargeVolume<VoxelType>::Sampler m_sampVolume;
VolumeType* m_volData;
typename VolumeType::Sampler m_sampVolume;
//The surface patch we are currently filling.
SurfaceMesh<PositionMaterialNormal>* m_meshCurrent;

View File

@ -29,8 +29,8 @@ freely, subject to the following restrictions:
namespace PolyVox
{
template <typename VoxelType>
CubicSurfaceExtractorWithNormals<VoxelType>::CubicSurfaceExtractorWithNormals(LargeVolume<VoxelType>* volData, Region region, SurfaceMesh<PositionMaterialNormal>* result)
template <typename VolumeType, typename VoxelType>
CubicSurfaceExtractorWithNormals<VolumeType, VoxelType>::CubicSurfaceExtractorWithNormals(VolumeType* volData, Region region, SurfaceMesh<PositionMaterialNormal>* result)
:m_volData(volData)
,m_sampVolume(volData)
,m_regSizeInVoxels(region)
@ -39,8 +39,8 @@ namespace PolyVox
m_meshCurrent->clear();
}
template <typename VoxelType>
void CubicSurfaceExtractorWithNormals<VoxelType>::execute()
template <typename VolumeType, typename VoxelType>
void CubicSurfaceExtractorWithNormals<VolumeType, VoxelType>::execute()
{
for(int32_t z = m_regSizeInVoxels.getLowerCorner().getZ(); z < m_regSizeInVoxels.getUpperCorner().getZ(); z++)
{

View File

@ -0,0 +1,365 @@
/*******************************************************************************
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 "PolyVoxImpl/Block.h"
#include "Region.h"
#include "PolyVoxForwardDeclarations.h"
#include <limits>
#include <map>
#include <set>
#include <memory>
#include <vector>
namespace PolyVox
{
///The SimpleVolume class provides a memory efficient method of storing voxel data while also allowing fast access and modification.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// A SimpleVolume is essentially a 3D array in which each element (or <i>voxel</i>) is identified by a three dimensional (x,y,z) coordinate.
/// We use the SimpleVolume 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 SimpleVolume class is templatised so that different types of data can be stored within each voxel.
///
/// <b> Basic usage</b>
/// The following code snippet shows how to construct a volume and demonstrates basic usage:
///
/// \code
/// SimpleVolume<Material8> 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 SimpleVolume 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 SimpleVolume 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 SimpleVolume. 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.
///
/// <b>Data Representaion</b>
/// If stored carelessly, volume data can take up a huge amount of memory. For example, a volume of dimensions 1024x1024x1024 with
/// 1 byte per voxel will require 1GB of memory if stored in an uncompressed form. Natuarally our SimpleVolume 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 SimpleVolume 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.
///
/// <b>Achieving high compression rates</b>
/// 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).
///
/// <b>Paging large volumes</b>
/// 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 SimpleVolume as follows:
///
/// \code
/// void myDataRequiredHandler(const ConstVolumeProxy<MaterialDensityPair44>& 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<MaterialDensityPair44>& 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.
/// }
///
/// SimpleVolume<Density>volData(&myDataRequiredHandler, &myDataOverflowHandler);
/// \endcode
///
/// Essentially you are providing an extension to the SimpleVolume 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.
///
/// <b>Cache-aware traversal</b>
/// 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.
///
/// <b>Threading</b>
/// The SimpleVolume 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 <typename VoxelType>
class SimpleVolume
{
public:
class Sampler
{
public:
Sampler(SimpleVolume<VoxelType>* volume);
~Sampler();
Sampler& operator=(const Sampler& rhs) throw();
int32_t getPosX(void) const;
int32_t getPosY(void) const;
int32_t getPosZ(void) const;
VoxelType getSubSampledVoxel(uint8_t uLevel) const;
const SimpleVolume<VoxelType>* getVolume(void) const;
inline VoxelType getVoxel(void) const;
void setPosition(const Vector3DInt32& v3dNewPos);
void setPosition(int32_t xPos, int32_t yPos, int32_t zPos);
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:
//The current volume
SimpleVolume<VoxelType>* mVolume;
//The current position in the volume
int32_t mXPosInVolume;
int32_t mYPosInVolume;
int32_t mZPosInVolume;
//Other current position information
VoxelType* mCurrentVoxel;
};
// Make the ConstVolumeProxy a friend
friend class ConstVolumeProxy<VoxelType>;
struct LoadedBlock
{
public:
LoadedBlock(uint16_t uSideLength = 0)
:block(uSideLength)
,timestamp(0)
{
}
Block<VoxelType> block;
uint32_t timestamp;
};
public:
/// Constructor for creating a very large paging volume.
SimpleVolume
(
polyvox_function<void(const ConstVolumeProxy<VoxelType>&, const Region&)> dataRequiredHandler,
polyvox_function<void(const ConstVolumeProxy<VoxelType>&, const Region&)> dataOverflowHandler,
uint16_t uBlockSideLength = 32
);
/// Constructor for creating a fixed size volume.
SimpleVolume
(
const Region& regValid,
polyvox_function<void(const ConstVolumeProxy<VoxelType>&, const Region&)> dataRequiredHandler = 0,
polyvox_function<void(const ConstVolumeProxy<VoxelType>&, const Region&)> dataOverflowHandler = 0,
bool bPagingEnabled = false,
uint16_t uBlockSideLength = 32
);
/// Deprecated constructor - do not use.
SimpleVolume
(
int32_t dont_use_this_constructor_1, int32_t dont_use_this_constructor_2, int32_t dont_use_this_constructor_3
);
/// Destructor
~SimpleVolume();
/// Gets the value used for voxels which are outside the volume
VoxelType getBorderValue(void) const;
/// Gets a Region representing the extents of the SimpleVolume.
Region getEnclosingRegion(void) const;
/// Gets the width of the volume in voxels.
int32_t getWidth(void) const;
/// Gets the height of the volume in voxels.
int32_t getHeight(void) const;
/// Gets the depth of the volume in voxels.
int32_t getDepth(void) const;
/// Gets the length of the longest side in voxels
int32_t getLongestSideLength(void) const;
/// Gets the length of the shortest side in voxels
int32_t getShortestSideLength(void) const;
/// Gets the length of the diagonal in voxels
float getDiagonalLength(void) const;
/// Gets a voxel at the position given by <tt>x,y,z</tt> coordinates
VoxelType getVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos) const;
/// Gets a voxel at the position given by a 3D vector
VoxelType getVoxelAt(const Vector3DInt32& v3dPos) const;
//Sets whether or not blocks are compressed in memory
void setCompressionEnabled(bool bCompressionEnabled);
/// Sets the number of blocks for which uncompressed data is stored
void setMaxNumberOfUncompressedBlocks(uint16_t uMaxNumberOfUncompressedBlocks);
/// Sets the number of blocks which can be in memory before the paging system starts unloading them
void setMaxNumberOfBlocksInMemory(uint16_t uMaxNumberOfBlocksInMemory);
/// Sets the value used for voxels which are outside the volume
void setBorderValue(const VoxelType& tBorder);
/// Sets the voxel at the position given by <tt>x,y,z</tt> 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();
/// Empties the cache of uncompressed blocks
void clearBlockCache(void);
/// Calculates the approximate compression ratio of the store volume data
float calculateCompressionRatio(void);
/// Calculates approximatly how many bytes of memory the volume is currently using.
uint32_t calculateSizeInBytes(void);
/// Deprecated - I don't think we should expose this function? Let us know if you disagree...
void resize(const Region& regValidRegion, uint16_t uBlockSideLength);
private:
/// gets called when a new region is allocated and needs to be filled
/// NOTE: accessing ANY voxels outside this region during the process of this function
/// is absolutely unsafe
polyvox_function<void(const ConstVolumeProxy<VoxelType>&, const Region&)> m_funcDataRequiredHandler;
/// gets called when a Region needs to be stored by the user, because SimpleVolume will erase it right after
/// this function returns
/// NOTE: accessing ANY voxels outside this region during the process of this function
/// is absolutely unsafe
polyvox_function<void(const ConstVolumeProxy<VoxelType>&, const Region&)> m_funcDataOverflowHandler;
Block<VoxelType>* getUncompressedBlock(int32_t uBlockX, int32_t uBlockY, int32_t uBlockZ) const;
void eraseBlock(typename std::map<Vector3DInt32, LoadedBlock >::iterator itBlock) const;
/// this function can be called by m_funcDataRequiredHandler without causing any weird effects
bool setVoxelAtConst(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue) const;
//The block data
mutable std::map<Vector3DInt32, LoadedBlock > m_pBlocks;
//The cache of uncompressed blocks. The uncompressed block data and the timestamps are stored here rather
//than in the Block class. This is so that in the future each VolumeIterator might to maintain its own cache
//of blocks. However, this could mean the same block data is uncompressed and modified in more than one
//location in memory... could be messy with threading.
mutable std::vector< LoadedBlock* > m_vecUncompressedBlockCache;
mutable uint32_t m_uTimestamper;
mutable Vector3DInt32 m_v3dLastAccessedBlockPos;
mutable Block<VoxelType>* m_pLastAccessedBlock;
uint32_t m_uMaxNumberOfUncompressedBlocks;
uint32_t m_uMaxNumberOfBlocksInMemory;
//We don't store an actual Block for the border, just the uncompressed data. This is partly because the border
//block does not have a position (so can't be passed to getUncompressedBlock()) and partly because there's a
//good chance we'll often hit it anyway. It's a chunk of homogenous data (rather than a single value) so that
//the VolumeIterator can do it's usual pointer arithmetic without needing to know it's gone outside the volume.
VoxelType* m_pUncompressedBorderData;
//The size of the volume
Region m_regValidRegion;
Region m_regValidRegionInBlocks;
//The size of the blocks
uint16_t m_uBlockSideLength;
uint8_t m_uBlockSideLengthPower;
//Some useful sizes
int32_t m_uLongestSideLength;
int32_t m_uShortestSideLength;
float m_fDiagonalLength;
bool m_bCompressionEnabled;
bool m_bPagingEnabled;
};
}
#include "SimpleVolume.inl"
#include "SimpleVolumeSampler.inl"
#endif //__PolyVox_SimpleVolume_H__

View File

@ -0,0 +1,735 @@
/*******************************************************************************
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 "ConstVolumeProxy.h"
#include "PolyVoxImpl/Block.h"
#include "Log.h"
#include "Region.h"
#include "Vector.h"
#include <limits>
#include <cassert>
#include <cstdlib> //For abort()
#include <cstring> //For memcpy
#include <list>
#include <stdexcept> //For invalid_argument
namespace PolyVox
{
////////////////////////////////////////////////////////////////////////////////
/// When construncting a very large volume you need to be prepared to handle the scenario where there is so much data that PolyVox cannot fit it all in memory. When PolyVox runs out of memory, it identifies the least recently used data and hands it back to the application via a callback function. It is then the responsibility of the application to store this data until PolyVox needs it again (as signalled by another callback function). Please see the SimpleVolume class documentation for a full description of this process and the required function signatures. If you really don't want to handle these events then you can provide null pointers here, in which case the data will be discarded and/or filled with default values.
/// \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 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 <typename VoxelType>
SimpleVolume<VoxelType>::SimpleVolume
(
polyvox_function<void(const ConstVolumeProxy<VoxelType>&, const Region&)> dataRequiredHandler,
polyvox_function<void(const ConstVolumeProxy<VoxelType>&, const Region&)> dataOverflowHandler,
uint16_t uBlockSideLength
)
{
m_funcDataRequiredHandler = dataRequiredHandler;
m_funcDataOverflowHandler = dataOverflowHandler;
m_bPagingEnabled = true;
//Create a volume of the right size.
resize(Region::MaxRegion,uBlockSideLength);
}
////////////////////////////////////////////////////////////////////////////////
/// Deprecated - do not use this constructor.
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
SimpleVolume<VoxelType>::SimpleVolume
(
int32_t dont_use_this_constructor_1, int32_t dont_use_this_constructor_2, int32_t dont_use_this_constructor_3
)
{
//In earlier verions of PolyVox the constructor took three values indicating width, height, and depth. However, this
//causes confusion because these three parameters can be interpreted as two function pointers and a block size instead,
//hence calling a different constructor. And simply removing this constructor will cause confusion because existing
//code with three parameters will then always resolve to the constructor with two function pointers and a block size.
//
//Eventually this constructor will be removed, it's just here to make people change their code to the new version.
//
//IF YOU HIT THIS ASSERT/ABORT, CHANGE YOUR CODE TO USE THE CONSTRUCTOR TAKING A 'Region' INSTEAD.
assert(false);
abort();
}
////////////////////////////////////////////////////////////////////////////////
/// 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 SimpleVolume constructor).
/// \param regValid Specifies the minimum and maximum valid voxel positions.
/// \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 SimpleVolume.
/// \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 <typename VoxelType>
SimpleVolume<VoxelType>::SimpleVolume
(
const Region& regValid,
polyvox_function<void(const ConstVolumeProxy<VoxelType>&, const Region&)> dataRequiredHandler,
polyvox_function<void(const ConstVolumeProxy<VoxelType>&, const Region&)> dataOverflowHandler,
bool bPagingEnabled,
uint16_t uBlockSideLength
)
{
m_funcDataRequiredHandler = dataRequiredHandler;
m_funcDataOverflowHandler = dataOverflowHandler;
m_bPagingEnabled = bPagingEnabled;
//Create a volume of the right size.
resize(regValid,uBlockSideLength);
}
////////////////////////////////////////////////////////////////////////////////
/// 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 <typename VoxelType>
SimpleVolume<VoxelType>::~SimpleVolume()
{
flushAll();
}
////////////////////////////////////////////////////////////////////////////////
/// The border value is returned whenever an atempt is made to read a voxel which
/// is outside the extents of the volume.
/// \return The value used for voxels outside of the volume
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::getBorderValue(void) const
{
return *m_pUncompressedBorderData;
}
////////////////////////////////////////////////////////////////////////////////
/// \return A Region representing the extent of the volume.
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
Region SimpleVolume<VoxelType>::getEnclosingRegion(void) const
{
return m_regValidRegion;
}
////////////////////////////////////////////////////////////////////////////////
/// \return The width of the volume in voxels. Note that this value is inclusive, so that if the valid range is e.g. 0 to 63 then the width is 64.
/// \sa getHeight(), getDepth()
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
int32_t SimpleVolume<VoxelType>::getWidth(void) const
{
return m_regValidRegion.getUpperCorner().getX() - m_regValidRegion.getLowerCorner().getX() + 1;
}
////////////////////////////////////////////////////////////////////////////////
/// \return The height of the volume in voxels. Note that this value is inclusive, so that if the valid range is e.g. 0 to 63 then the height is 64.
/// \sa getWidth(), getDepth()
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
int32_t SimpleVolume<VoxelType>::getHeight(void) const
{
return m_regValidRegion.getUpperCorner().getY() - m_regValidRegion.getLowerCorner().getY() + 1;
}
////////////////////////////////////////////////////////////////////////////////
/// \return The depth of the volume in voxels. Note that this value is inclusive, so that if the valid range is e.g. 0 to 63 then the depth is 64.
/// \sa getWidth(), getHeight()
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
int32_t SimpleVolume<VoxelType>::getDepth(void) const
{
return m_regValidRegion.getUpperCorner().getZ() - m_regValidRegion.getLowerCorner().getZ() + 1;
}
////////////////////////////////////////////////////////////////////////////////
/// \return The length of the shortest side in voxels. For example, if a volume has
/// dimensions 256x512x1024 this function will return 256.
/// \sa getLongestSideLength(), getDiagonalLength()
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
int32_t SimpleVolume<VoxelType>::getShortestSideLength(void) const
{
return m_uShortestSideLength;
}
////////////////////////////////////////////////////////////////////////////////
/// \return The length of the longest side in voxels. For example, if a volume has
/// dimensions 256x512x1024 this function will return 1024.
/// \sa getShortestSideLength(), getDiagonalLength()
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
int32_t SimpleVolume<VoxelType>::getLongestSideLength(void) const
{
return m_uLongestSideLength;
}
////////////////////////////////////////////////////////////////////////////////
/// \return The length of the diagonal in voxels. For example, if a volume has
/// dimensions 256x512x1024 this function will return sqrt(256*256+512*512+1024*1024)
/// = 1173.139. This value is computed on volume creation so retrieving it is fast.
/// \sa getShortestSideLength(), getLongestSideLength()
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
float SimpleVolume<VoxelType>::getDiagonalLength(void) const
{
return m_fDiagonalLength;
}
////////////////////////////////////////////////////////////////////////////////
/// \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 <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::getVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos) const
{
if(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 = uXPos - (blockX << m_uBlockSideLengthPower);
const uint16_t yOffset = uYPos - (blockY << m_uBlockSideLengthPower);
const uint16_t zOffset = uZPos - (blockZ << m_uBlockSideLengthPower);
Block<VoxelType>* pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ);
return pUncompressedBlock->getVoxelAt(xOffset,yOffset,zOffset);
}
else
{
return getBorderValue();
}
}
////////////////////////////////////////////////////////////////////////////////
/// \param v3dPos The 3D position of the voxel
/// \return The voxel value
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::getVoxelAt(const Vector3DInt32& v3dPos) const
{
return getVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ());
}
////////////////////////////////////////////////////////////////////////////////
/// Enabling compression allows significantly more data to be stored in memory.
/// \param bCompressionEnabled Specifies whether compression is enabled.
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
void SimpleVolume<VoxelType>::setCompressionEnabled(bool bCompressionEnabled)
{
//Early out - nothing to do
if(m_bCompressionEnabled == bCompressionEnabled)
{
return;
}
m_bCompressionEnabled = bCompressionEnabled;
if(m_bCompressionEnabled)
{
//If compression has been enabled then we need to start honouring the max number of
//uncompressed blocks. Because compression has been disabled for a while we might have
//gone above that limit. Easiest solution is just to clear the cache and start again.
clearBlockCache();
}
}
////////////////////////////////////////////////////////////////////////////////
/// 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 uBlockCacheSize The number of blocks for which uncompressed data can be cached.
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
void SimpleVolume<VoxelType>::setMaxNumberOfUncompressedBlocks(uint16_t uMaxNumberOfUncompressedBlocks)
{
clearBlockCache();
m_uMaxNumberOfUncompressedBlocks = uMaxNumberOfUncompressedBlocks;
}
////////////////////////////////////////////////////////////////////////////////
/// Increasing the number of blocks in memory causes fewer calls to dataRequiredHandler()/dataOverflowHandler()
/// \param uMaxBlocks The number of blocks
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
void SimpleVolume<VoxelType>::setMaxNumberOfBlocksInMemory(uint16_t uMaxNumberOfBlocksInMemory)
{
if(m_pBlocks.size() > uMaxNumberOfBlocksInMemory)
{
flushAll();
}
m_uMaxNumberOfBlocksInMemory = uMaxNumberOfBlocksInMemory;
}
////////////////////////////////////////////////////////////////////////////////
/// \param tBorder The value to use for voxels outside the volume.
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
void SimpleVolume<VoxelType>::setBorderValue(const VoxelType& tBorder)
{
/*Block<VoxelType>* pUncompressedBorderBlock = getUncompressedBlock(&m_pBorderBlock);
return pUncompressedBorderBlock->fill(tBorder);*/
std::fill(m_pUncompressedBorderData, m_pUncompressedBorderData + m_uBlockSideLength * m_uBlockSideLength * m_uBlockSideLength, 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
/// \param tValue the value to which the voxel will be set
/// \return whether the requested position is inside the volume
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
bool SimpleVolume<VoxelType>::setVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue)
{
assert(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 = uXPos - (blockX << m_uBlockSideLengthPower);
const uint16_t yOffset = uYPos - (blockY << m_uBlockSideLengthPower);
const uint16_t zOffset = uZPos - (blockZ << m_uBlockSideLengthPower);
Block<VoxelType>* 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 <typename VoxelType>
bool SimpleVolume<VoxelType>::setVoxelAt(const Vector3DInt32& v3dPos, VoxelType tValue)
{
return setVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ(), tValue);
}
////////////////////////////////////////////////////////////////////////////////
/// Note that if 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 <typename VoxelType>
void SimpleVolume<VoxelType>::prefetch(Region regPrefetch)
{
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);
}
Vector3DInt32 v3dSize = v3dEnd - v3dStart + Vector3DInt32(1,1,1);
int32_t numblocks = v3dSize.getX() * v3dSize.getY() * v3dSize.getZ();
if(numblocks > m_uMaxNumberOfBlocksInMemory)
{
// cannot support the amount of blocks... so only load the maximum possible
numblocks = m_uMaxNumberOfBlocksInMemory;
}
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++)
{
Vector3DInt32 pos(x,y,z);
typename std::map<Vector3DInt32, LoadedBlock>::iterator itBlock = m_pBlocks.find(pos);
if(itBlock != m_pBlocks.end())
{
// If the block is already loaded then we don't load it again. This means it does not get uncompressed,
// whereas if we were to call getUncompressedBlock() regardless then it would also get uncompressed.
// This might be nice, but on the prefetch region could be bigger than the uncompressed cache size.
// This would limit the amount of prefetching we could do.
continue;
}
if(numblocks == 0)
{
// Loading any more blocks would attempt to overflow the memory and therefore erase blocks
// we loaded in the beginning. This wouldn't cause logic problems but would be wasteful.
return;
}
// load a block
numblocks--;
Block<VoxelType>* block = getUncompressedBlock(x,y,z);
} // for z
} // for y
} // for x
}
////////////////////////////////////////////////////////////////////////////////
/// Removes all voxels from memory, and calls dataOverflowHandler() to ensure the application has a chance to store the data.
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
void SimpleVolume<VoxelType>::flushAll()
{
typename std::map<Vector3DInt32, LoadedBlock >::iterator i;
//Replaced the for loop here as the call to
//eraseBlock was invalidating the iterator.
while(m_pBlocks.size() > 0)
{
eraseBlock(m_pBlocks.begin());
}
}
////////////////////////////////////////////////////////////////////////////////
/// 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 <typename VoxelType>
void SimpleVolume<VoxelType>::flush(Region regFlush)
{
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);
}
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++)
{
Vector3DInt32 pos(x,y,z);
typename std::map<Vector3DInt32, LoadedBlock>::iterator itBlock = m_pBlocks.find(pos);
if(itBlock == m_pBlocks.end())
{
// not loaded, not unloading
continue;
}
eraseBlock(itBlock);
// eraseBlock might cause a call to getUncompressedBlock, which again sets m_pLastAccessedBlock
if(m_v3dLastAccessedBlockPos == pos)
{
m_pLastAccessedBlock = 0;
}
} // for z
} // for y
} // for x
}
////////////////////////////////////////////////////////////////////////////////
///
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
void SimpleVolume<VoxelType>::clearBlockCache(void)
{
for(uint32_t ct = 0; ct < m_vecUncompressedBlockCache.size(); ct++)
{
m_vecUncompressedBlockCache[ct]->block.compress();
}
m_vecUncompressedBlockCache.clear();
}
////////////////////////////////////////////////////////////////////////////////
/// This function should probably be made internal...
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
void SimpleVolume<VoxelType>::resize(const Region& regValidRegion, uint16_t uBlockSideLength)
{
//Debug mode validation
assert(uBlockSideLength > 0);
//Release mode validation
if(uBlockSideLength == 0)
{
throw std::invalid_argument("Block side length cannot be zero.");
}
if(!isPowerOf2(uBlockSideLength))
{
throw std::invalid_argument("Block side length must be a power of two.");
}
m_uTimestamper = 0;
m_uMaxNumberOfUncompressedBlocks = 16;
m_uBlockSideLength = uBlockSideLength;
m_pUncompressedBorderData = 0;
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;
m_bCompressionEnabled = true;
m_regValidRegion = regValidRegion;
m_regValidRegionInBlocks.setLowerCorner(m_regValidRegion.getLowerCorner() / static_cast<int32_t>(uBlockSideLength));
m_regValidRegionInBlocks.setUpperCorner(m_regValidRegion.getUpperCorner() / static_cast<int32_t>(uBlockSideLength));
setMaxNumberOfUncompressedBlocks(m_uMaxNumberOfUncompressedBlocks);
//Clear the previous data
m_pBlocks.clear();
//Compute the block side length
m_uBlockSideLength = uBlockSideLength;
m_uBlockSideLengthPower = logBase2(m_uBlockSideLength);
//Clear the previous data
m_pBlocks.clear();
//Create the border block
m_pUncompressedBorderData = new VoxelType[m_uBlockSideLength * m_uBlockSideLength * m_uBlockSideLength];
std::fill(m_pUncompressedBorderData, m_pUncompressedBorderData + m_uBlockSideLength * m_uBlockSideLength * m_uBlockSideLength, VoxelType());
//Other properties we might find useful later
m_uLongestSideLength = (std::max)((std::max)(getWidth(),getHeight()),getDepth());
m_uShortestSideLength = (std::min)((std::min)(getWidth(),getHeight()),getDepth());
m_fDiagonalLength = sqrtf(static_cast<float>(getWidth() * getWidth() + getHeight() * getHeight() + getDepth() * getDepth()));
}
template <typename VoxelType>
void SimpleVolume<VoxelType>::eraseBlock(typename std::map<Vector3DInt32, LoadedBlock >::iterator itBlock) const
{
/*if(m_funcDataOverflowHandler)
{
Vector3DInt32 v3dPos = itBlock->first;
Vector3DInt32 v3dLower(v3dPos.getX() << m_uBlockSideLengthPower, v3dPos.getY() << m_uBlockSideLengthPower, v3dPos.getZ() << m_uBlockSideLengthPower);
Vector3DInt32 v3dUpper = v3dLower + Vector3DInt32(m_uBlockSideLength-1, m_uBlockSideLength-1, m_uBlockSideLength-1);
Region reg(v3dLower, v3dUpper);
ConstVolumeProxy<VoxelType> ConstVolumeProxy(*this, reg);
m_funcDataOverflowHandler(ConstVolumeProxy, reg);
}*/
if(m_bCompressionEnabled) {
for(uint32_t ct = 0; ct < m_vecUncompressedBlockCache.size(); ct++)
{
// find the block in the uncompressed cache
if(m_vecUncompressedBlockCache[ct] == &(itBlock->second))
{
// TODO: compression is unneccessary? or will not compressing this cause a memleak?
itBlock->second.block.compress();
// put last object in cache here
m_vecUncompressedBlockCache[ct] = m_vecUncompressedBlockCache.back();
// decrease cache size by one since last element is now in here twice
m_vecUncompressedBlockCache.resize(m_vecUncompressedBlockCache.size()-1);
break;
}
}
}
m_pBlocks.erase(itBlock);
}
template <typename VoxelType>
bool SimpleVolume<VoxelType>::setVoxelAtConst(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue) const
{
//We don't have any range checks in this function because it
//is a private function only called by the ConstVolumeProxy. The
//ConstVolumeProxy takes care of ensuring the range is appropriate.
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);
Block<VoxelType>* pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ);
pUncompressedBlock->setVoxelAt(xOffset,yOffset,zOffset, tValue);
//Return true to indicate that we modified a voxel.
return true;
}
template <typename VoxelType>
Block<VoxelType>* SimpleVolume<VoxelType>::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))
{
assert(m_pLastAccessedBlock->m_tUncompressedData);
return m_pLastAccessedBlock;
}
typename std::map<Vector3DInt32, LoadedBlock >::iterator itBlock = m_pBlocks.find(v3dBlockPos);
// check whether the block is already loaded
if(itBlock == m_pBlocks.end())
{
//The block is not in the map, so we will have to create a new block and add it.
//Before we do so, we might want to dump some existing data to make space. We
//Only do this if paging is enabled.
/*if(m_bPagingEnabled)
{
// check wether another block needs to be unloaded before this one can be loaded
if(m_pBlocks.size() == m_uMaxNumberOfBlocksInMemory)
{
// find the least recently used block
typename std::map<Vector3DInt32, LoadedBlock >::iterator i;
typename std::map<Vector3DInt32, LoadedBlock >::iterator itUnloadBlock = m_pBlocks.begin();
for(i = m_pBlocks.begin(); i != m_pBlocks.end(); i++)
{
if(i->second.timestamp < itUnloadBlock->second.timestamp)
{
itUnloadBlock = i;
}
}
eraseBlock(itUnloadBlock);
}
}*/
// create the new block
LoadedBlock newBlock(m_uBlockSideLength);
itBlock = m_pBlocks.insert(std::make_pair(v3dBlockPos, newBlock)).first;
//We have created the new block. If paging is enabled it should be used to
//fill in the required data. Otherwise it is just left in the default state.
/*if(m_bPagingEnabled)
{
if(m_funcDataRequiredHandler)
{
// "load" will actually call setVoxel, which will in turn call this function again but the block will be found
// so this if(itBlock == m_pBlocks.end()) never is entered
//FIXME - can we pass the block around so that we don't have to find it again when we recursively call this function?
Vector3DInt32 v3dLower(v3dBlockPos.getX() << m_uBlockSideLengthPower, v3dBlockPos.getY() << m_uBlockSideLengthPower, v3dBlockPos.getZ() << m_uBlockSideLengthPower);
Vector3DInt32 v3dUpper = v3dLower + Vector3DInt32(m_uBlockSideLength-1, m_uBlockSideLength-1, m_uBlockSideLength-1);
Region reg(v3dLower, v3dUpper);
ConstVolumeProxy<VoxelType> ConstVolumeProxy(*this, reg);
m_funcDataRequiredHandler(ConstVolumeProxy, reg);
}
}*/
}
//Get the block and mark that we accessed it
LoadedBlock& loadedBlock = itBlock->second;
loadedBlock.timestamp = ++m_uTimestamper;
m_v3dLastAccessedBlockPos = v3dBlockPos;
m_pLastAccessedBlock = &(loadedBlock.block);
if(loadedBlock.block.m_bIsCompressed == false)
{
assert(m_pLastAccessedBlock->m_tUncompressedData);
return m_pLastAccessedBlock;
}
//If we are allowed to compress then check whether we need to
if((m_bCompressionEnabled) && (m_vecUncompressedBlockCache.size() == m_uMaxNumberOfUncompressedBlocks))
{
int32_t leastRecentlyUsedBlockIndex = -1;
uint32_t uLeastRecentTimestamp = (std::numeric_limits<uint32_t>::max)();
//Currently we find the oldest block by iterating over the whole array. Of course we could store the blocks sorted by
//timestamp (set, priority_queue, etc) but then we'll need to move them around as the timestamp changes. Can come back
//to this if it proves to be a bottleneck (compraed to the cost of actually doing the compression/decompression).
for(uint32_t ct = 0; ct < m_vecUncompressedBlockCache.size(); ct++)
{
if(m_vecUncompressedBlockCache[ct]->timestamp < uLeastRecentTimestamp)
{
uLeastRecentTimestamp = m_vecUncompressedBlockCache[ct]->timestamp;
leastRecentlyUsedBlockIndex = ct;
}
}
//Compress the least recently used block.
m_vecUncompressedBlockCache[leastRecentlyUsedBlockIndex]->block.compress();
//We don't actually remove any elements from this vector, we
//simply change the pointer to point at the new uncompressed bloack.
m_vecUncompressedBlockCache[leastRecentlyUsedBlockIndex] = &loadedBlock;
}
else
{
m_vecUncompressedBlockCache.push_back(&loadedBlock);
}
loadedBlock.block.uncompress();
m_pLastAccessedBlock = &(loadedBlock.block);
assert(m_pLastAccessedBlock->m_tUncompressedData);
return m_pLastAccessedBlock;
}
////////////////////////////////////////////////////////////////////////////////
/// Note: This function needs reviewing for accuracy...
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
float SimpleVolume<VoxelType>::calculateCompressionRatio(void)
{
float fRawSize = m_pBlocks.size() * m_uBlockSideLength * m_uBlockSideLength* m_uBlockSideLength * sizeof(VoxelType);
float fCompressedSize = calculateSizeInBytes();
return fCompressedSize/fRawSize;
}
////////////////////////////////////////////////////////////////////////////////
/// Note: This function needs reviewing for accuracy...
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
uint32_t SimpleVolume<VoxelType>::calculateSizeInBytes(void)
{
uint32_t uSizeInBytes = sizeof(SimpleVolume);
//Memory used by the blocks
typename std::map<Vector3DInt32, LoadedBlock >::iterator i;
for(i = m_pBlocks.begin(); i != m_pBlocks.end(); i++)
{
//Inaccurate - account for rest of loaded block.
uSizeInBytes += i->second.block.calculateSizeInBytes();
}
//Memory used by the block cache.
uSizeInBytes += m_vecUncompressedBlockCache.capacity() * sizeof(LoadedBlock);
uSizeInBytes += m_vecUncompressedBlockCache.size() * m_uBlockSideLength * m_uBlockSideLength * m_uBlockSideLength * sizeof(VoxelType);
//Memory used by border data.
if(m_pUncompressedBorderData)
{
uSizeInBytes += m_uBlockSideLength * m_uBlockSideLength * m_uBlockSideLength * sizeof(VoxelType);
}
return uSizeInBytes;
}
}

View File

@ -0,0 +1,533 @@
/*******************************************************************************
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 "PolyVoxImpl/Block.h"
#include "SimpleVolume.h"
#include "Vector.h"
#include "Region.h"
#define BORDER_LOW(x) ((( x >> mVolume->m_uBlockSideLengthPower) << mVolume->m_uBlockSideLengthPower) != x)
#define BORDER_HIGH(x) ((( (x+1) >> mVolume->m_uBlockSideLengthPower) << mVolume->m_uBlockSideLengthPower) != (x+1))
//#define BORDER_LOW(x) (( x % mVolume->m_uBlockSideLength) != 0)
//#define BORDER_HIGH(x) (( x % mVolume->m_uBlockSideLength) != mVolume->m_uBlockSideLength - 1)
#include <limits>
namespace PolyVox
{
template <typename VoxelType>
SimpleVolume<VoxelType>::Sampler::Sampler(SimpleVolume<VoxelType>* volume)
:mVolume(volume)
{
}
template <typename VoxelType>
SimpleVolume<VoxelType>::Sampler::~Sampler()
{
}
template <typename VoxelType>
typename SimpleVolume<VoxelType>::Sampler& SimpleVolume<VoxelType>::Sampler::operator=(const typename SimpleVolume<VoxelType>::Sampler& rhs) throw()
{
if(this == &rhs)
{
return *this;
}
mVolume = rhs.mVolume;
mXPosInVolume = rhs.mXPosInVolume;
mYPosInVolume = rhs.mYPosInVolume;
mZPosInVolume = rhs.mZPosInVolume;
mCurrentVoxel = rhs.mCurrentVoxel;
return *this;
}
template <typename VoxelType>
int32_t SimpleVolume<VoxelType>::Sampler::getPosX(void) const
{
return mXPosInVolume;
}
template <typename VoxelType>
int32_t SimpleVolume<VoxelType>::Sampler::getPosY(void) const
{
return mYPosInVolume;
}
template <typename VoxelType>
int32_t SimpleVolume<VoxelType>::Sampler::getPosZ(void) const
{
return mZPosInVolume;
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::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<VoxelType>::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, mVolume->getVoxelAt(mXPosInVolume + x, mYPosInVolume + y, mZPosInVolume + z));
}
}
}
return tValue;
}
}
template <typename VoxelType>
const SimpleVolume<VoxelType>* SimpleVolume<VoxelType>::Sampler::getVolume(void) const
{
return mVolume;
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::getVoxel(void) const
{
return *mCurrentVoxel;
}
template <typename VoxelType>
void SimpleVolume<VoxelType>::Sampler::setPosition(const Vector3DInt32& v3dNewPos)
{
setPosition(v3dNewPos.getX(), v3dNewPos.getY(), v3dNewPos.getZ());
}
template <typename VoxelType>
void SimpleVolume<VoxelType>::Sampler::setPosition(int32_t xPos, int32_t yPos, int32_t zPos)
{
mXPosInVolume = xPos;
mYPosInVolume = yPos;
mZPosInVolume = zPos;
const int32_t uXBlock = mXPosInVolume >> mVolume->m_uBlockSideLengthPower;
const int32_t uYBlock = mYPosInVolume >> mVolume->m_uBlockSideLengthPower;
const int32_t uZBlock = mZPosInVolume >> mVolume->m_uBlockSideLengthPower;
const uint16_t uXPosInBlock = mXPosInVolume - (uXBlock << mVolume->m_uBlockSideLengthPower);
const uint16_t uYPosInBlock = mYPosInVolume - (uYBlock << mVolume->m_uBlockSideLengthPower);
const uint16_t uZPosInBlock = mZPosInVolume - (uZBlock << mVolume->m_uBlockSideLengthPower);
const uint32_t uVoxelIndexInBlock = uXPosInBlock +
uYPosInBlock * mVolume->m_uBlockSideLength +
uZPosInBlock * mVolume->m_uBlockSideLength * mVolume->m_uBlockSideLength;
if(mVolume->m_regValidRegionInBlocks.containsPoint(Vector3DInt32(uXBlock, uYBlock, uZBlock)))
{
Block<VoxelType>* pUncompressedCurrentBlock = mVolume->getUncompressedBlock(uXBlock, uYBlock, uZBlock);
mCurrentVoxel = pUncompressedCurrentBlock->m_tUncompressedData + uVoxelIndexInBlock;
}
else
{
mCurrentVoxel = mVolume->m_pUncompressedBorderData + uVoxelIndexInBlock;
}
}
template <typename VoxelType>
void SimpleVolume<VoxelType>::Sampler::movePositiveX(void)
{
//Note the *pre* increament here
if((++mXPosInVolume) % 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(mXPosInVolume, mYPosInVolume, mZPosInVolume);
}
}
template <typename VoxelType>
void SimpleVolume<VoxelType>::Sampler::movePositiveY(void)
{
//Note the *pre* increament here
if((++mYPosInVolume) % mVolume->m_uBlockSideLength != 0)
{
//No need to compute new block.
mCurrentVoxel += mVolume->m_uBlockSideLength;
}
else
{
//We've hit the block boundary. Just calling setPosition() is the easiest way to resolve this.
setPosition(mXPosInVolume, mYPosInVolume, mZPosInVolume);
}
}
template <typename VoxelType>
void SimpleVolume<VoxelType>::Sampler::movePositiveZ(void)
{
//Note the *pre* increament here
if((++mZPosInVolume) % mVolume->m_uBlockSideLength != 0)
{
//No need to compute new block.
mCurrentVoxel += mVolume->m_uBlockSideLength * mVolume->m_uBlockSideLength;
}
else
{
//We've hit the block boundary. Just calling setPosition() is the easiest way to resolve this.
setPosition(mXPosInVolume, mYPosInVolume, mZPosInVolume);
}
}
template <typename VoxelType>
void SimpleVolume<VoxelType>::Sampler::moveNegativeX(void)
{
//Note the *post* decreament here
if((mXPosInVolume--) % 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(mXPosInVolume, mYPosInVolume, mZPosInVolume);
}
}
template <typename VoxelType>
void SimpleVolume<VoxelType>::Sampler::moveNegativeY(void)
{
//Note the *post* decreament here
if((mYPosInVolume--) % mVolume->m_uBlockSideLength != 0)
{
//No need to compute new block.
mCurrentVoxel -= mVolume->m_uBlockSideLength;
}
else
{
//We've hit the block boundary. Just calling setPosition() is the easiest way to resolve this.
setPosition(mXPosInVolume, mYPosInVolume, mZPosInVolume);
}
}
template <typename VoxelType>
void SimpleVolume<VoxelType>::Sampler::moveNegativeZ(void)
{
//Note the *post* decreament here
if((mZPosInVolume--) % mVolume->m_uBlockSideLength != 0)
{
//No need to compute new block.
mCurrentVoxel -= mVolume->m_uBlockSideLength * mVolume->m_uBlockSideLength;
}
else
{
//We've hit the block boundary. Just calling setPosition() is the easiest way to resolve this.
setPosition(mXPosInVolume, mYPosInVolume, mZPosInVolume);
}
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel1nx1ny1nz(void) const
{
if( BORDER_LOW(mXPosInVolume) && BORDER_LOW(mYPosInVolume) && BORDER_LOW(mZPosInVolume) )
{
return *(mCurrentVoxel - 1 - mVolume->m_uBlockSideLength - mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength);
}
return mVolume->getVoxelAt(mXPosInVolume-1,mYPosInVolume-1,mZPosInVolume-1);
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel1nx1ny0pz(void) const
{
if( BORDER_LOW(mXPosInVolume) && BORDER_LOW(mYPosInVolume) )
{
return *(mCurrentVoxel - 1 - mVolume->m_uBlockSideLength);
}
return mVolume->getVoxelAt(mXPosInVolume-1,mYPosInVolume-1,mZPosInVolume);
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel1nx1ny1pz(void) const
{
if( BORDER_LOW(mXPosInVolume) && BORDER_LOW(mYPosInVolume) && BORDER_HIGH(mZPosInVolume) )
{
return *(mCurrentVoxel - 1 - mVolume->m_uBlockSideLength + mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength);
}
return mVolume->getVoxelAt(mXPosInVolume-1,mYPosInVolume-1,mZPosInVolume+1);
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel1nx0py1nz(void) const
{
if( BORDER_LOW(mXPosInVolume) && BORDER_LOW(mZPosInVolume) )
{
return *(mCurrentVoxel - 1 - mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength);
}
return mVolume->getVoxelAt(mXPosInVolume-1,mYPosInVolume,mZPosInVolume-1);
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel1nx0py0pz(void) const
{
if( BORDER_LOW(mXPosInVolume) )
{
return *(mCurrentVoxel - 1);
}
return mVolume->getVoxelAt(mXPosInVolume-1,mYPosInVolume,mZPosInVolume);
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel1nx0py1pz(void) const
{
if( BORDER_LOW(mXPosInVolume) && BORDER_HIGH(mZPosInVolume) )
{
return *(mCurrentVoxel - 1 + mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength);
}
return mVolume->getVoxelAt(mXPosInVolume-1,mYPosInVolume,mZPosInVolume+1);
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel1nx1py1nz(void) const
{
if( BORDER_LOW(mXPosInVolume) && BORDER_HIGH(mYPosInVolume) && BORDER_LOW(mYPosInVolume) )
{
return *(mCurrentVoxel - 1 + mVolume->m_uBlockSideLength - mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength);
}
return mVolume->getVoxelAt(mXPosInVolume-1,mYPosInVolume+1,mZPosInVolume-1);
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel1nx1py0pz(void) const
{
if( BORDER_LOW(mXPosInVolume) && BORDER_HIGH(mYPosInVolume) )
{
return *(mCurrentVoxel - 1 + mVolume->m_uBlockSideLength);
}
return mVolume->getVoxelAt(mXPosInVolume-1,mYPosInVolume+1,mZPosInVolume);
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel1nx1py1pz(void) const
{
if( BORDER_LOW(mXPosInVolume) && BORDER_HIGH(mYPosInVolume) && BORDER_HIGH(mZPosInVolume) )
{
return *(mCurrentVoxel - 1 + mVolume->m_uBlockSideLength + mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength);
}
return mVolume->getVoxelAt(mXPosInVolume-1,mYPosInVolume+1,mZPosInVolume+1);
}
//////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel0px1ny1nz(void) const
{
if( BORDER_LOW(mYPosInVolume) && BORDER_LOW(mZPosInVolume) )
{
return *(mCurrentVoxel - mVolume->m_uBlockSideLength - mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength);
}
return mVolume->getVoxelAt(mXPosInVolume,mYPosInVolume-1,mZPosInVolume-1);
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel0px1ny0pz(void) const
{
if( BORDER_LOW(mYPosInVolume) )
{
return *(mCurrentVoxel - mVolume->m_uBlockSideLength);
}
return mVolume->getVoxelAt(mXPosInVolume,mYPosInVolume-1,mZPosInVolume);
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel0px1ny1pz(void) const
{
if( BORDER_LOW(mYPosInVolume) && BORDER_HIGH(mZPosInVolume) )
{
return *(mCurrentVoxel - mVolume->m_uBlockSideLength + mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength);
}
return mVolume->getVoxelAt(mXPosInVolume,mYPosInVolume-1,mZPosInVolume+1);
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel0px0py1nz(void) const
{
if( BORDER_LOW(mZPosInVolume) )
{
return *(mCurrentVoxel - mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength);
}
return mVolume->getVoxelAt(mXPosInVolume,mYPosInVolume,mZPosInVolume-1);
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel0px0py0pz(void) const
{
return *mCurrentVoxel;
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel0px0py1pz(void) const
{
if( BORDER_HIGH(mZPosInVolume) )
{
return *(mCurrentVoxel + mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength);
}
return mVolume->getVoxelAt(mXPosInVolume,mYPosInVolume,mZPosInVolume+1);
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel0px1py1nz(void) const
{
if( BORDER_HIGH(mYPosInVolume) && BORDER_LOW(mZPosInVolume) )
{
return *(mCurrentVoxel + mVolume->m_uBlockSideLength - mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength);
}
return mVolume->getVoxelAt(mXPosInVolume,mYPosInVolume+1,mZPosInVolume-1);
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel0px1py0pz(void) const
{
if( BORDER_HIGH(mYPosInVolume) )
{
return *(mCurrentVoxel + mVolume->m_uBlockSideLength);
}
return mVolume->getVoxelAt(mXPosInVolume,mYPosInVolume+1,mZPosInVolume);
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel0px1py1pz(void) const
{
if( BORDER_HIGH(mYPosInVolume) && BORDER_HIGH(mZPosInVolume) )
{
return *(mCurrentVoxel + mVolume->m_uBlockSideLength + mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength);
}
return mVolume->getVoxelAt(mXPosInVolume,mYPosInVolume+1,mZPosInVolume+1);
}
//////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel1px1ny1nz(void) const
{
if( BORDER_HIGH(mXPosInVolume) && BORDER_LOW(mYPosInVolume) && BORDER_LOW(mZPosInVolume) )
{
return *(mCurrentVoxel + 1 - mVolume->m_uBlockSideLength - mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength);
}
return mVolume->getVoxelAt(mXPosInVolume+1,mYPosInVolume-1,mZPosInVolume-1);
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel1px1ny0pz(void) const
{
if( BORDER_HIGH(mXPosInVolume) && BORDER_LOW(mYPosInVolume) )
{
return *(mCurrentVoxel + 1 - mVolume->m_uBlockSideLength);
}
return mVolume->getVoxelAt(mXPosInVolume+1,mYPosInVolume-1,mZPosInVolume);
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel1px1ny1pz(void) const
{
if( BORDER_HIGH(mXPosInVolume) && BORDER_LOW(mYPosInVolume) && BORDER_HIGH(mZPosInVolume) )
{
return *(mCurrentVoxel + 1 - mVolume->m_uBlockSideLength + mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength);
}
return mVolume->getVoxelAt(mXPosInVolume+1,mYPosInVolume-1,mZPosInVolume+1);
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel1px0py1nz(void) const
{
if( BORDER_HIGH(mXPosInVolume) && BORDER_LOW(mZPosInVolume) )
{
return *(mCurrentVoxel + 1 - mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength);
}
return mVolume->getVoxelAt(mXPosInVolume+1,mYPosInVolume,mZPosInVolume-1);
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel1px0py0pz(void) const
{
if( BORDER_HIGH(mXPosInVolume) )
{
return *(mCurrentVoxel + 1);
}
return mVolume->getVoxelAt(mXPosInVolume+1,mYPosInVolume,mZPosInVolume);
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel1px0py1pz(void) const
{
if( BORDER_HIGH(mXPosInVolume) && BORDER_HIGH(mZPosInVolume) )
{
return *(mCurrentVoxel + 1 + mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength);
}
return mVolume->getVoxelAt(mXPosInVolume+1,mYPosInVolume,mZPosInVolume+1);
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel1px1py1nz(void) const
{
if( BORDER_HIGH(mXPosInVolume) && BORDER_HIGH(mYPosInVolume) && BORDER_LOW(mZPosInVolume) )
{
return *(mCurrentVoxel + 1 + mVolume->m_uBlockSideLength - mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength);
}
return mVolume->getVoxelAt(mXPosInVolume+1,mYPosInVolume+1,mZPosInVolume-1);
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel1px1py0pz(void) const
{
if( BORDER_HIGH(mXPosInVolume) && BORDER_HIGH(mYPosInVolume) )
{
return *(mCurrentVoxel + 1 + mVolume->m_uBlockSideLength);
}
return mVolume->getVoxelAt(mXPosInVolume+1,mYPosInVolume+1,mZPosInVolume);
}
template <typename VoxelType>
VoxelType SimpleVolume<VoxelType>::Sampler::peekVoxel1px1py1pz(void) const
{
if( BORDER_HIGH(mXPosInVolume) && BORDER_HIGH(mYPosInVolume) && BORDER_HIGH(mZPosInVolume) )
{
return *(mCurrentVoxel + 1 + mVolume->m_uBlockSideLength + mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength);
}
return mVolume->getVoxelAt(mXPosInVolume+1,mYPosInVolume+1,mZPosInVolume+1);
}
}