Merge branch 'feature/volume-work' into develop
This commit is contained in:
commit
6a6e1e49e1
@ -27,8 +27,10 @@ C++ does provide the 'volatile' keyword which can be used to ensure a variable i
|
||||
|
||||
Lastly, note that PolyVox volumes are templatised which means the voxel type might be something other than a simple int. However we don't think this actually makes a difference given that so few guarantees are made anyway, and it should still be safe to perform multiple concurrent reads for more complex types.
|
||||
|
||||
LargeVolume
|
||||
PagedVolume
|
||||
-----------
|
||||
NOTE: The info below is based on LargeVolume, which PagedVolume has replaced. It is likely that the same limitations apply but this has not been well tested. We do intend to improve the thread safty of PagedVolume in the future.
|
||||
|
||||
The LargeVolume provides even less thread safety than the RawVolume, in that even concurrent read operations can cause problems. The reason for this is the more complex memory management which is performed behind the scenes, and which allows pieces of volume data to be moved around and deleted. For example, a read of a single voxel may mean that the block of data associated with that voxel has to be paged in to memory, which in turn may mean that another block of data has to be paged out of memory. If second thread was halfway through reading a voxel in this second block of data then a problem will occur.
|
||||
|
||||
In the future we may do a more comprehensive analysis of thread safety in the LargeVolume, but for now you should assume that any multithreaded access can cause problems.
|
||||
|
@ -26,14 +26,14 @@ freely, subject to the following restrictions:
|
||||
#include "PolyVoxCore/CubicSurfaceExtractor.h"
|
||||
#include "PolyVoxCore/MarchingCubesSurfaceExtractor.h"
|
||||
#include "PolyVoxCore/Mesh.h"
|
||||
#include "PolyVoxCore/SimpleVolume.h"
|
||||
#include "PolyVoxCore/PagedVolume.h"
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
//Use the PolyVox namespace
|
||||
using namespace PolyVox;
|
||||
|
||||
void createSphereInVolume(SimpleVolume<uint8_t>& volData, float fRadius)
|
||||
void createSphereInVolume(PagedVolume<uint8_t>& 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);
|
||||
@ -74,7 +74,7 @@ int main(int argc, char *argv[])
|
||||
openGLWidget.show();
|
||||
|
||||
//Create an empty volume and then place a sphere in it
|
||||
SimpleVolume<uint8_t> volData(PolyVox::Region(Vector3DInt32(0,0,0), Vector3DInt32(63, 63, 63)));
|
||||
PagedVolume<uint8_t> volData(PolyVox::Region(Vector3DInt32(0, 0, 0), Vector3DInt32(63, 63, 63)));
|
||||
createSphereInVolume(volData, 30);
|
||||
|
||||
// Extract the surface for the specified region of the volume. Uncomment the line for the kind of surface extraction you want to see.
|
||||
|
@ -26,14 +26,14 @@ freely, subject to the following restrictions:
|
||||
#include "PolyVoxCore/CubicSurfaceExtractor.h"
|
||||
#include "PolyVoxCore/MarchingCubesSurfaceExtractor.h"
|
||||
#include "PolyVoxCore/Mesh.h"
|
||||
#include "PolyVoxCore/SimpleVolume.h"
|
||||
#include "PolyVoxCore/PagedVolume.h"
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
//Use the PolyVox namespace
|
||||
using namespace PolyVox;
|
||||
|
||||
void createSphereInVolume(SimpleVolume<uint8_t>& volData, float fRadius)
|
||||
void createSphereInVolume(PagedVolume<uint8_t>& 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);
|
||||
@ -145,7 +145,7 @@ int main(int argc, char *argv[])
|
||||
openGLWidget.setShader(shader);
|
||||
|
||||
//Create an empty volume and then place a sphere in it
|
||||
SimpleVolume<uint8_t> volData(PolyVox::Region(Vector3DInt32(0,0,0), Vector3DInt32(63, 63, 63)));
|
||||
PagedVolume<uint8_t> volData(PolyVox::Region(Vector3DInt32(0, 0, 0), Vector3DInt32(63, 63, 63)));
|
||||
createSphereInVolume(volData, 30);
|
||||
|
||||
// Extract the surface for the specified region of the volume. Uncomment the line for the kind of surface extraction you want to see.
|
||||
|
@ -27,7 +27,7 @@ freely, subject to the following restrictions:
|
||||
|
||||
using namespace PolyVox;
|
||||
|
||||
void createSphereInVolume(LargeVolume<MaterialDensityPair88>& volData, float fRadius, uint8_t uValue)
|
||||
void createSphereInVolume(PagedVolume<MaterialDensityPair88>& volData, float fRadius, uint8_t uValue)
|
||||
{
|
||||
//This vector hold the position of the center of the volume
|
||||
Vector3DInt32 v3dVolCenter = (volData.getEnclosingRegion().getUpperCorner() - volData.getEnclosingRegion().getLowerCorner()) / static_cast<int32_t>(2);
|
||||
@ -55,7 +55,7 @@ void createSphereInVolume(LargeVolume<MaterialDensityPair88>& volData, float fRa
|
||||
}
|
||||
}
|
||||
|
||||
void createCubeInVolume(LargeVolume<MaterialDensityPair88>& volData, Vector3DInt32 lowerCorner, Vector3DInt32 upperCorner, uint8_t uValue)
|
||||
void createCubeInVolume(PagedVolume<MaterialDensityPair88>& volData, Vector3DInt32 lowerCorner, Vector3DInt32 upperCorner, uint8_t uValue)
|
||||
{
|
||||
uint8_t maxDen = MaterialDensityPair88::getMaxDensity();
|
||||
uint8_t minDen = MaterialDensityPair88::getMinDensity();
|
||||
|
@ -24,7 +24,7 @@ freely, subject to the following restrictions:
|
||||
#ifndef __OpenGLExample_Shapes_H__
|
||||
#define __OpenGLExample_Shapes_H__
|
||||
|
||||
#include "PolyVoxCore/LargeVolume.h"
|
||||
#include "PolyVoxCore/PagedVolume.h"
|
||||
#include "PolyVoxCore/MaterialDensityPair.h"
|
||||
|
||||
void createSphereInVolume(PolyVox::LargeVolume<PolyVox::MaterialDensityPair88>& volData, float fRadius, uint8_t uValue);
|
||||
|
@ -24,10 +24,8 @@ freely, subject to the following restrictions:
|
||||
#include "PolyVoxCore/FilePager.h"
|
||||
#include "PolyVoxCore/MarchingCubesSurfaceExtractor.h"
|
||||
#include "PolyVoxCore/MaterialDensityPair.h"
|
||||
#include "PolyVoxCore/LargeVolume.h"
|
||||
#include "PolyVoxCore/LowPassFilter.h"
|
||||
#include "PolyVoxCore/RawVolume.h"
|
||||
#include "PolyVoxCore/RLEBlockCompressor.h"
|
||||
#include "PolyVoxCore/Mesh.h"
|
||||
#include "PolyVoxCore/Impl/Utility.h"
|
||||
|
||||
@ -51,9 +49,8 @@ const int32_t g_uVolumeSideLength = 128;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
RLEBlockCompressor<MaterialDensityPair88>* compressor = new RLEBlockCompressor<MaterialDensityPair88>();
|
||||
FilePager<MaterialDensityPair88>* pager = new FilePager<MaterialDensityPair88>("./");
|
||||
LargeVolume<MaterialDensityPair88> volData(PolyVox::Region(Vector3DInt32(0, 0, 0), Vector3DInt32(g_uVolumeSideLength - 1, g_uVolumeSideLength - 1, g_uVolumeSideLength - 1)), compressor, pager);
|
||||
FilePager<MaterialDensityPair88>* pager = new FilePager<MaterialDensityPair88>(".");
|
||||
PagedVolume<MaterialDensityPair88> volData(PolyVox::Region(Vector3DInt32(0, 0, 0), Vector3DInt32(g_uVolumeSideLength - 1, g_uVolumeSideLength - 1, g_uVolumeSideLength - 1)), pager);
|
||||
|
||||
//Make our volume contain a sphere in the center.
|
||||
int32_t minPos = 0;
|
||||
|
@ -27,17 +27,15 @@ freely, subject to the following restrictions:
|
||||
#include "PolyVoxCore/MaterialDensityPair.h"
|
||||
#include "PolyVoxCore/CubicSurfaceExtractor.h"
|
||||
#include "PolyVoxCore/MarchingCubesSurfaceExtractor.h"
|
||||
#include "PolyVoxCore/Pager.h"
|
||||
#include "PolyVoxCore/RLEBlockCompressor.h"
|
||||
#include "PolyVoxCore/Mesh.h"
|
||||
#include "PolyVoxCore/LargeVolume.h"
|
||||
#include "PolyVoxCore/PagedVolume.h"
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
//Use the PolyVox namespace
|
||||
using namespace PolyVox;
|
||||
|
||||
void createSphereInVolume(LargeVolume<MaterialDensityPair44>& volData, Vector3DFloat v3dVolCenter, float fRadius)
|
||||
void createSphereInVolume(PagedVolume<MaterialDensityPair44>& volData, Vector3DFloat v3dVolCenter, float fRadius)
|
||||
{
|
||||
//This vector hold the position of the center of the volume
|
||||
//Vector3DFloat v3dVolCenter(volData.getWidth() / 2, volData.getHeight() / 2, volData.getDepth() / 2);
|
||||
@ -79,24 +77,20 @@ void createSphereInVolume(LargeVolume<MaterialDensityPair44>& volData, Vector3DF
|
||||
/**
|
||||
* Generates data using Perlin noise.
|
||||
*/
|
||||
class PerlinNoisePager : public PolyVox::Pager<MaterialDensityPair44>
|
||||
class PerlinNoisePager : public PolyVox::PagedVolume<MaterialDensityPair44>::Pager
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
PerlinNoisePager()
|
||||
:Pager<MaterialDensityPair44>()
|
||||
:PagedVolume<MaterialDensityPair44>::Pager()
|
||||
{
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
virtual ~PerlinNoisePager() {};
|
||||
|
||||
virtual void pageIn(const PolyVox::Region& region, CompressedBlock<MaterialDensityPair44>* pBlockData)
|
||||
virtual void pageIn(const PolyVox::Region& region, PagedVolume<MaterialDensityPair44>::Chunk* pChunk)
|
||||
{
|
||||
// FIXME - this isn't a great example... it's a shame we have to hard clode the block size and also create/destroy
|
||||
// a compressor each time. These could at least be moved outside somewhere if we can't fix it in a better way...
|
||||
UncompressedBlock<MaterialDensityPair44> block(256);
|
||||
|
||||
Perlin perlin(2,2,1,234);
|
||||
|
||||
for(int x = region.getLowerX(); x <= region.getUpperX(); x++)
|
||||
@ -130,21 +124,16 @@ public:
|
||||
voxel.setDensity(MaterialDensityPair44::getMinDensity());
|
||||
}
|
||||
|
||||
// Voxel position within a block always start from zero. So if a block represents region (4, 8, 12) to (11, 19, 15)
|
||||
// then the valid block voxels are from (0, 0, 0) to (7, 11, 3). Hence we subtract the lower corner position of the
|
||||
// region from the volume space position in order to get the block space position.
|
||||
block.setVoxelAt(x - region.getLowerX(), y - region.getLowerY(), z - region.getLowerZ(), voxel);
|
||||
// Voxel position within a chunk always start from zero. So if a chunk represents region (4, 8, 12) to (11, 19, 15)
|
||||
// then the valid chunk voxels are from (0, 0, 0) to (7, 11, 3). Hence we subtract the lower corner position of the
|
||||
// region from the volume space position in order to get the chunk space position.
|
||||
pChunk->setVoxelAt(x - region.getLowerX(), y - region.getLowerY(), z - region.getLowerZ(), voxel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now compress the computed data into the provided block.
|
||||
RLEBlockCompressor<MaterialDensityPair44>* compressor = new RLEBlockCompressor<MaterialDensityPair44>();
|
||||
compressor->compress(&block, pBlockData);
|
||||
delete compressor;
|
||||
}
|
||||
|
||||
virtual void pageOut(const PolyVox::Region& region, CompressedBlock<MaterialDensityPair44>* /*pBlockData*/)
|
||||
virtual void pageOut(const PolyVox::Region& region, PagedVolume<MaterialDensityPair44>::Chunk* /*pChunk*/)
|
||||
{
|
||||
std::cout << "warning unloading region: " << region.getLowerCorner() << " -> " << region.getUpperCorner() << std::endl;
|
||||
}
|
||||
@ -157,33 +146,29 @@ int main(int argc, char *argv[])
|
||||
OpenGLWidget openGLWidget(0);
|
||||
openGLWidget.show();
|
||||
|
||||
RLEBlockCompressor<MaterialDensityPair44>* compressor = new RLEBlockCompressor<MaterialDensityPair44>();
|
||||
PerlinNoisePager* pager = new PerlinNoisePager();
|
||||
LargeVolume<MaterialDensityPair44> volData(PolyVox::Region::MaxRegion, compressor, pager, 256);
|
||||
volData.setMaxNumberOfBlocksInMemory(4096);
|
||||
volData.setMaxNumberOfUncompressedBlocks(64);
|
||||
PagedVolume<MaterialDensityPair44> volData(PolyVox::Region::MaxRegion, pager, 64);
|
||||
volData.setMemoryUsageLimit(8 * 1024 * 1024); // 8Mb
|
||||
|
||||
//volData.setMaxNumberOfUncompressedBlocks(4096);
|
||||
//createSphereInVolume(volData, 30);
|
||||
//createPerlinTerrain(volData);
|
||||
//createPerlinVolumeSlow(volData);
|
||||
std::cout << "Memory usage: " << (volData.calculateSizeInBytes()/1024.0/1024.0) << "MB" << std::endl;
|
||||
std::cout << "Compression ratio: 1 to " << (1.0/(volData.calculateCompressionRatio())) << std::endl;
|
||||
//volData.setBlockCacheSize(64);
|
||||
//std::cout << "Compression ratio: 1 to " << (1.0/(volData.calculateCompressionRatio())) << std::endl;
|
||||
PolyVox::Region reg(Vector3DInt32(-255,0,0), Vector3DInt32(255,255,255));
|
||||
std::cout << "Prefetching region: " << reg.getLowerCorner() << " -> " << reg.getUpperCorner() << std::endl;
|
||||
volData.prefetch(reg);
|
||||
std::cout << "Memory usage: " << (volData.calculateSizeInBytes()/1024.0/1024.0) << "MB" << std::endl;
|
||||
std::cout << "Compression ratio: 1 to " << (1.0/(volData.calculateCompressionRatio())) << std::endl;
|
||||
//std::cout << "Compression ratio: 1 to " << (1.0/(volData.calculateCompressionRatio())) << std::endl;
|
||||
PolyVox::Region reg2(Vector3DInt32(0,0,0), Vector3DInt32(255,255,255));
|
||||
std::cout << "Flushing region: " << reg2.getLowerCorner() << " -> " << reg2.getUpperCorner() << std::endl;
|
||||
volData.flush(reg2);
|
||||
std::cout << "Memory usage: " << (volData.calculateSizeInBytes()/1024.0/1024.0) << "MB" << std::endl;
|
||||
std::cout << "Compression ratio: 1 to " << (1.0/(volData.calculateCompressionRatio())) << std::endl;
|
||||
//std::cout << "Compression ratio: 1 to " << (1.0/(volData.calculateCompressionRatio())) << std::endl;
|
||||
std::cout << "Flushing entire volume" << std::endl;
|
||||
volData.flushAll();
|
||||
std::cout << "Memory usage: " << (volData.calculateSizeInBytes()/1024.0/1024.0) << "MB" << std::endl;
|
||||
std::cout << "Compression ratio: 1 to " << (1.0/(volData.calculateCompressionRatio())) << std::endl;
|
||||
//std::cout << "Compression ratio: 1 to " << (1.0/(volData.calculateCompressionRatio())) << std::endl;
|
||||
|
||||
//Extract the surface
|
||||
auto mesh = extractCubicMesh(&volData, reg2);
|
||||
|
@ -27,7 +27,7 @@ freely, subject to the following restrictions:
|
||||
#include "PolyVoxCore/MarchingCubesSurfaceExtractor.h"
|
||||
#include "PolyVoxCore/Mesh.h"
|
||||
#include "PolyVoxCore/RawVolume.h"
|
||||
#include "PolyVoxCore/SimpleVolume.h"
|
||||
#include "PolyVoxCore/PagedVolume.h"
|
||||
#include "PolyVoxCore/VolumeResampler.h"
|
||||
|
||||
#include <QApplication>
|
||||
@ -35,7 +35,7 @@ freely, subject to the following restrictions:
|
||||
//Use the PolyVox namespace
|
||||
using namespace PolyVox;
|
||||
|
||||
void createSphereInVolume(SimpleVolume<uint8_t>& volData, float fRadius)
|
||||
void createSphereInVolume(PagedVolume<uint8_t>& 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);
|
||||
@ -76,17 +76,17 @@ int main(int argc, char *argv[])
|
||||
openGLWidget.show();
|
||||
|
||||
//Create an empty volume and then place a sphere in it
|
||||
SimpleVolume<uint8_t> volData(PolyVox::Region(Vector3DInt32(0,0,0), Vector3DInt32(63, 63, 63)));
|
||||
PagedVolume<uint8_t> volData(PolyVox::Region(Vector3DInt32(0, 0, 0), Vector3DInt32(63, 63, 63)));
|
||||
createSphereInVolume(volData, 28);
|
||||
|
||||
//Smooth the data - should reimplement this using LowPassFilter
|
||||
//smoothRegion<SimpleVolume, Density8>(volData, volData.getEnclosingRegion());
|
||||
//smoothRegion<SimpleVolume, Density8>(volData, volData.getEnclosingRegion());
|
||||
//smoothRegion<SimpleVolume, Density8>(volData, volData.getEnclosingRegion());
|
||||
//smoothRegion<PagedVolume, Density8>(volData, volData.getEnclosingRegion());
|
||||
//smoothRegion<PagedVolume, Density8>(volData, volData.getEnclosingRegion());
|
||||
//smoothRegion<PagedVolume, Density8>(volData, volData.getEnclosingRegion());
|
||||
|
||||
RawVolume<uint8_t> volDataLowLOD(PolyVox::Region(Vector3DInt32(0,0,0), Vector3DInt32(15, 31, 31)));
|
||||
|
||||
VolumeResampler< SimpleVolume<uint8_t>, RawVolume<uint8_t> > volumeResampler(&volData, PolyVox::Region(Vector3DInt32(0,0,0), Vector3DInt32(31, 63, 63)), &volDataLowLOD, volDataLowLOD.getEnclosingRegion());
|
||||
VolumeResampler< PagedVolume<uint8_t>, RawVolume<uint8_t> > volumeResampler(&volData, PolyVox::Region(Vector3DInt32(0, 0, 0), Vector3DInt32(31, 63, 63)), &volDataLowLOD, volDataLowLOD.getEnclosingRegion());
|
||||
volumeResampler.execute();
|
||||
|
||||
//Extract the surface
|
||||
|
@ -38,10 +38,6 @@ SET(CORE_INC_FILES
|
||||
include/PolyVoxCore/BaseVolume.h
|
||||
include/PolyVoxCore/BaseVolume.inl
|
||||
include/PolyVoxCore/BaseVolumeSampler.inl
|
||||
include/PolyVoxCore/Block.h
|
||||
include/PolyVoxCore/BlockCompressor.h
|
||||
include/PolyVoxCore/CompressedBlock.h
|
||||
include/PolyVoxCore/CompressedBlock.inl
|
||||
include/PolyVoxCore/CubicSurfaceExtractor.h
|
||||
include/PolyVoxCore/CubicSurfaceExtractor.inl
|
||||
include/PolyVoxCore/DefaultIsQuadNeeded.h
|
||||
@ -54,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
|
||||
@ -64,9 +58,10 @@ SET(CORE_INC_FILES
|
||||
include/PolyVoxCore/MaterialDensityPair.h
|
||||
include/PolyVoxCore/Mesh.h
|
||||
include/PolyVoxCore/Mesh.inl
|
||||
include/PolyVoxCore/MinizBlockCompressor.h
|
||||
include/PolyVoxCore/MinizBlockCompressor.inl
|
||||
include/PolyVoxCore/Pager.h
|
||||
include/PolyVoxCore/PagedVolume.h
|
||||
include/PolyVoxCore/PagedVolume.inl
|
||||
include/PolyVoxCore/PagedVolumeChunk.inl
|
||||
include/PolyVoxCore/PagedVolumeSampler.inl
|
||||
include/PolyVoxCore/PolyVoxForwardDeclarations.h
|
||||
include/PolyVoxCore/Picking.h
|
||||
include/PolyVoxCore/Picking.inl
|
||||
@ -76,14 +71,7 @@ SET(CORE_INC_FILES
|
||||
include/PolyVoxCore/Raycast.h
|
||||
include/PolyVoxCore/Raycast.inl
|
||||
include/PolyVoxCore/Region.h
|
||||
include/PolyVoxCore/RLEBlockCompressor.h
|
||||
include/PolyVoxCore/RLEBlockCompressor.inl
|
||||
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
|
||||
include/PolyVoxCore/Vector.inl
|
||||
include/PolyVoxCore/Vertex.h
|
||||
@ -97,7 +85,6 @@ SET(IMPL_SRC_FILES
|
||||
source/Impl/ErrorHandling.cpp
|
||||
source/Impl/Logging.cpp
|
||||
source/Impl/MarchingCubesTables.cpp
|
||||
source/Impl/MinizWrapper.cpp
|
||||
source/Impl/RandomUnitVectors.cpp
|
||||
source/Impl/RandomVectors.cpp
|
||||
source/Impl/Timer.cpp
|
||||
@ -110,7 +97,6 @@ SET(IMPL_INC_FILES
|
||||
include/PolyVoxCore/Impl/ErrorHandling.h
|
||||
include/PolyVoxCore/Impl/Logging.h
|
||||
include/PolyVoxCore/Impl/MarchingCubesTables.h
|
||||
include/PolyVoxCore/Impl/MinizWrapper.h
|
||||
include/PolyVoxCore/Impl/RandomUnitVectors.h
|
||||
include/PolyVoxCore/Impl/RandomVectors.h
|
||||
include/PolyVoxCore/Impl/Timer.h
|
||||
|
@ -33,7 +33,7 @@ freely, subject to the following restrictions:
|
||||
|
||||
//These two should not be here!
|
||||
#include "PolyVoxCore/Material.h"
|
||||
#include "PolyVoxCore/SimpleVolume.h"
|
||||
#include "PolyVoxCore/PagedVolume.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@ -53,7 +53,7 @@ namespace PolyVox
|
||||
{
|
||||
}
|
||||
|
||||
bool operator()(const SimpleVolume<uint8_t>::Sampler& sampler)
|
||||
bool operator()(const PagedVolume<uint8_t>::Sampler& sampler)
|
||||
{
|
||||
uint8_t sample = sampler.getVoxel();
|
||||
bool func = mIsVoxelTransparentCallback(sample);
|
||||
|
@ -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 or PagedVolume.
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// More details to come...
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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, PagedVolume
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename VoxelType>
|
||||
BaseVolume<VoxelType>::BaseVolume(const Region& regValid)
|
||||
|
@ -1,66 +0,0 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2005-2013 David Williams and Matthew 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_Block_H__
|
||||
#define __PolyVox_Block_H__
|
||||
|
||||
#include "PolyVoxCore/Impl/TypeDef.h"
|
||||
|
||||
#include "PolyVoxCore/PolyVoxForwardDeclarations.h"
|
||||
#include "PolyVoxCore/Vector.h"
|
||||
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
namespace PolyVox
|
||||
{
|
||||
template <typename VoxelType>
|
||||
class Block
|
||||
{
|
||||
friend class LargeVolume<VoxelType>;
|
||||
|
||||
public:
|
||||
Block()
|
||||
:m_uBlockLastAccessed(0)
|
||||
,m_bDataModified(true)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
// This is updated by the LargeVolume 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
|
||||
// a compressed block has to be paged back to disk, or whether they can just be discarded.
|
||||
bool m_bDataModified;
|
||||
|
||||
private:
|
||||
/// Private copy constructor to prevent accisdental copying
|
||||
Block(const Block& /*rhs*/) {};
|
||||
|
||||
/// Private assignment operator to prevent accisdental copying
|
||||
Block& operator=(const Block& /*rhs*/) {};
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -1,48 +0,0 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2005-2013 David Williams and Matthew 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_BlockCompressor_H__
|
||||
#define __PolyVox_BlockCompressor_H__
|
||||
|
||||
#include "PolyVoxCore/PolyVoxForwardDeclarations.h"
|
||||
#include "PolyVoxCore/CompressedBlock.h"
|
||||
#include "PolyVoxCore/UncompressedBlock.h"
|
||||
|
||||
namespace PolyVox
|
||||
{
|
||||
/**
|
||||
* Provides an interface for performing compression of blocks.
|
||||
*/
|
||||
template <typename VoxelType>
|
||||
class BlockCompressor
|
||||
{
|
||||
public:
|
||||
BlockCompressor() {};
|
||||
virtual ~BlockCompressor() {};
|
||||
|
||||
virtual void compress(UncompressedBlock<VoxelType>* pSrcBlock, CompressedBlock<VoxelType>* pDstBlock) = 0;
|
||||
virtual void decompress(CompressedBlock<VoxelType>* pSrcBlock, UncompressedBlock<VoxelType>* pDstBlock) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //__PolyVox_BlockCompressor_H__
|
@ -1,63 +0,0 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2005-2013 David Williams and Matthew 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_CompressedBlock_H__
|
||||
#define __PolyVox_CompressedBlock_H__
|
||||
|
||||
#include "PolyVoxCore/Block.h"
|
||||
|
||||
namespace PolyVox
|
||||
{
|
||||
template <typename VoxelType>
|
||||
class CompressedBlock : public Block<VoxelType>
|
||||
{
|
||||
friend class LargeVolume<VoxelType>;
|
||||
|
||||
public:
|
||||
CompressedBlock();
|
||||
~CompressedBlock();
|
||||
|
||||
const uint8_t* getData(void) const;
|
||||
uint32_t getDataSizeInBytes(void) const;
|
||||
|
||||
void setData(const uint8_t* const pData, uint32_t uDataSizeInBytes);
|
||||
|
||||
private:
|
||||
/// Private copy constructor to prevent accisdental copying
|
||||
CompressedBlock(const CompressedBlock& /*rhs*/) {};
|
||||
|
||||
/// Private assignment operator to prevent accisdental copying
|
||||
CompressedBlock& operator=(const CompressedBlock& /*rhs*/) {};
|
||||
|
||||
// Made this private to avoid any confusion with getDataSizeInBytes().
|
||||
// Users shouldn't really need this for CompressedBlock anyway.
|
||||
uint32_t calculateSizeInBytes(void);
|
||||
|
||||
uint8_t* m_pData;
|
||||
uint32_t m_uDataSizeInBytes;
|
||||
};
|
||||
}
|
||||
|
||||
#include "PolyVoxCore/CompressedBlock.inl"
|
||||
|
||||
#endif
|
@ -1,79 +0,0 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2005-2013 David Williams and Matthew 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.
|
||||
*******************************************************************************/
|
||||
|
||||
namespace PolyVox
|
||||
{
|
||||
template <typename VoxelType>
|
||||
CompressedBlock<VoxelType>::CompressedBlock()
|
||||
:m_pData(0)
|
||||
,m_uDataSizeInBytes(0)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
CompressedBlock<VoxelType>::~CompressedBlock()
|
||||
{
|
||||
delete[] m_pData;
|
||||
m_pData = 0;
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
const uint8_t* CompressedBlock<VoxelType>::getData(void) const
|
||||
{
|
||||
return m_pData;
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
uint32_t CompressedBlock<VoxelType>::getDataSizeInBytes(void) const
|
||||
{
|
||||
return m_uDataSizeInBytes;
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
void CompressedBlock<VoxelType>::setData(const uint8_t* const pData, uint32_t uDataSizeInBytes)
|
||||
{
|
||||
POLYVOX_THROW_IF(pData == 0, std::invalid_argument, "Pointer to data cannot be null");
|
||||
POLYVOX_THROW_IF(m_pData == pData, std::invalid_argument, "Attempting to copy data onto itself");
|
||||
|
||||
// Delete any existing data
|
||||
delete[] m_pData;
|
||||
|
||||
// Allocate new data
|
||||
m_uDataSizeInBytes = uDataSizeInBytes;
|
||||
m_pData = new uint8_t[uDataSizeInBytes];
|
||||
|
||||
// Copy the data across
|
||||
memcpy(m_pData, pData, uDataSizeInBytes);
|
||||
|
||||
// Flag as modified
|
||||
this->m_bDataModified = true;
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
uint32_t CompressedBlock<VoxelType>::calculateSizeInBytes(void)
|
||||
{
|
||||
// Returns the size of this class plus the size of the compressed data.
|
||||
uint32_t uSizeInBytes = sizeof(CompressedBlock<VoxelType>) + m_uDataSizeInBytes;
|
||||
return uSizeInBytes;
|
||||
}
|
||||
}
|
@ -206,7 +206,7 @@ namespace PolyVox
|
||||
///
|
||||
/// \image html CubicSurfaceExtractor3.png
|
||||
///
|
||||
/// We could choose to add the quads to *both* regions, but this can cause confusion when one of the region is modified (causing the face to disappear or a new one to be created) as *both* regions need to have their mesh regenerated to correctly represent the new state of the volume data. Such pairs of coplanar quads can also cause problems with physics engines, and may prevent transparent block from rendering correctly. Therefore we choose to instead only add the quad to one of the the regions and we always choose the one with the greater coordinate value in the direction in which they differ. In the above example the regions differ by the 'x' component of their position, and so the quad is added to the region with the greater 'x' value (the one marked in blue).
|
||||
/// We could choose to add the quads to *both* regions, but this can cause confusion when one of the region is modified (causing the face to disappear or a new one to be created) as *both* regions need to have their mesh regenerated to correctly represent the new state of the volume data. Such pairs of coplanar quads can also cause problems with physics engines, and may prevent transparent voxels from rendering correctly. Therefore we choose to instead only add the quad to one of the the regions and we always choose the one with the greater coordinate value in the direction in which they differ. In the above example the regions differ by the 'x' component of their position, and so the quad is added to the region with the greater 'x' value (the one marked in blue).
|
||||
///
|
||||
/// **Note:** *This behaviour has changed recently (September 2012). Earlier versions of PolyVox tried to be smart about this problem by looking beyond the region which was being processed, but this complicated the code and didn't work very well. Ultimatly we decided to simply stick with the convention outlined above.*
|
||||
///
|
||||
|
@ -26,7 +26,7 @@ freely, subject to the following restrictions:
|
||||
|
||||
#include "PolyVoxCore/Impl/TypeDef.h"
|
||||
|
||||
#include "PolyVoxCore/Pager.h"
|
||||
#include "PolyVoxCore/PagedVolume.h"
|
||||
#include "PolyVoxCore/Region.h"
|
||||
|
||||
#include <cstdlib>
|
||||
@ -34,6 +34,7 @@ freely, subject to the following restrictions:
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace PolyVox
|
||||
{
|
||||
@ -41,14 +42,21 @@ namespace PolyVox
|
||||
* Provides an interface for performing paging of data.
|
||||
*/
|
||||
template <typename VoxelType>
|
||||
class FilePager : public Pager<VoxelType>
|
||||
class FilePager : public PagedVolume<VoxelType>::Pager
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
FilePager(const std::string& strFolderName)
|
||||
:Pager<VoxelType>()
|
||||
FilePager(const std::string& strFolderName = ".")
|
||||
:PagedVolume<VoxelType>::Pager()
|
||||
,m_strFolderName(strFolderName)
|
||||
{
|
||||
// Add the trailing slash, assuming the user dind't already do it.
|
||||
if ((m_strFolderName.back() != '/') && (m_strFolderName.back() != '\\'))
|
||||
{
|
||||
m_strFolderName.append("/");
|
||||
}
|
||||
|
||||
// Build a unique prefix to avoid multiple pagers using the same filenames.
|
||||
srand(static_cast<unsigned int>(time(0)));
|
||||
int iRandomValue = rand();
|
||||
|
||||
@ -62,16 +70,16 @@ namespace PolyVox
|
||||
{
|
||||
for(std::vector<std::string>::iterator iter = m_vecCreatedFiles.begin(); iter < m_vecCreatedFiles.end(); iter++)
|
||||
{
|
||||
POLYVOX_LOG_WARNING_IF(!std::remove(iter->c_str()), "Failed to delete '" << *iter << "' when destroying FilePager");
|
||||
POLYVOX_LOG_WARNING_IF(std::remove(iter->c_str()) != 0, "Failed to delete '" << *iter << "' when destroying FilePager");
|
||||
}
|
||||
|
||||
m_vecCreatedFiles.clear();
|
||||
}
|
||||
|
||||
virtual void pageIn(const Region& region, CompressedBlock<VoxelType>* pBlockData)
|
||||
virtual void pageIn(const Region& region, typename PagedVolume<VoxelType>::Chunk* pChunk)
|
||||
{
|
||||
POLYVOX_ASSERT(pBlockData, "Attempting to page in NULL block");
|
||||
//POLYVOX_ASSERT(pBlockData->hasUncompressedData() == false, "Block should not have uncompressed data");
|
||||
POLYVOX_ASSERT(pChunk, "Attempting to page in NULL chunk");
|
||||
POLYVOX_ASSERT(pChunk->getData(), "Chunk must have valid data");
|
||||
|
||||
std::stringstream ssFilename;
|
||||
ssFilename << m_strFolderName << "/" << m_strRandomPrefix << "-"
|
||||
@ -88,18 +96,20 @@ namespace PolyVox
|
||||
{
|
||||
POLYVOX_LOG_TRACE("Paging in data for " << region);
|
||||
|
||||
fseek(pFile, 0L, SEEK_END);
|
||||
/*fseek(pFile, 0L, SEEK_END);
|
||||
size_t fileSizeInBytes = ftell(pFile);
|
||||
fseek(pFile, 0L, SEEK_SET);
|
||||
|
||||
uint8_t* buffer = new uint8_t[fileSizeInBytes];
|
||||
fread(buffer, sizeof(uint8_t), fileSizeInBytes, pFile);
|
||||
pBlockData->setData(buffer, fileSizeInBytes);
|
||||
delete[] buffer;
|
||||
pChunk->setData(buffer, fileSizeInBytes);
|
||||
delete[] buffer;*/
|
||||
|
||||
fread(pChunk->getData(), sizeof(uint8_t), pChunk->getDataSizeInBytes(), pFile);
|
||||
|
||||
if(ferror(pFile))
|
||||
{
|
||||
POLYVOX_THROW(std::runtime_error, "Error reading in block data, even though a file exists.");
|
||||
POLYVOX_THROW(std::runtime_error, "Error reading in chunk data, even though a file exists.");
|
||||
}
|
||||
|
||||
fclose(pFile);
|
||||
@ -110,10 +120,10 @@ namespace PolyVox
|
||||
}
|
||||
}
|
||||
|
||||
virtual void pageOut(const Region& region, CompressedBlock<VoxelType>* pBlockData)
|
||||
virtual void pageOut(const Region& region, typename PagedVolume<VoxelType>::Chunk* pChunk)
|
||||
{
|
||||
POLYVOX_ASSERT(pBlockData, "Attempting to page out NULL block");
|
||||
//POLYVOX_ASSERT(pBlockData->hasUncompressedData() == false, "Block should not have uncompressed data");
|
||||
POLYVOX_ASSERT(pChunk, "Attempting to page out NULL chunk");
|
||||
POLYVOX_ASSERT(pChunk->getData(), "Chunk must have valid data");
|
||||
|
||||
POLYVOX_LOG_TRACE("Paging out data for " << region);
|
||||
|
||||
@ -130,17 +140,17 @@ namespace PolyVox
|
||||
FILE* pFile = fopen(filename.c_str(), "wb");
|
||||
if(!pFile)
|
||||
{
|
||||
POLYVOX_THROW(std::runtime_error, "Unable to open file to write out block data.");
|
||||
POLYVOX_THROW(std::runtime_error, "Unable to open file to write out chunk data.");
|
||||
}
|
||||
|
||||
//The file has been created, so add it to the list to delete on shutdown.
|
||||
m_vecCreatedFiles.push_back(filename);
|
||||
|
||||
fwrite(pBlockData->getData(), sizeof(uint8_t), pBlockData->getDataSizeInBytes(), pFile);
|
||||
fwrite(pChunk->getData(), sizeof(uint8_t), pChunk->getDataSizeInBytes(), pFile);
|
||||
|
||||
if(ferror(pFile))
|
||||
{
|
||||
POLYVOX_THROW(std::runtime_error, "Error writing out block data.");
|
||||
POLYVOX_THROW(std::runtime_error, "Error writing out chunk data.");
|
||||
}
|
||||
|
||||
fclose(pFile);
|
||||
|
@ -1,49 +0,0 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2005-2013 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_MinizWrapper_H__
|
||||
#define __PolyVox_MinizWrapper_H__
|
||||
|
||||
// The miniz library is suplied as a single '.c' file, but this is messy for a project like PolyVox
|
||||
// because it consists mostly of headers. Many of our headers may want to make use of Miniz code which
|
||||
// means we need to be careful about #including 'miniz.c' multiple times and getting linker errors.
|
||||
// We simplify this situation with this 'MinizWrapper.h' and 'MinizWrapper.c' which include 'miniz.c'
|
||||
// with only declarations and definitions respectively. 'MinizWrapper.cpp' only gets compiled once,
|
||||
// and all other parts of PolyVox can simply include 'MinizWrapper.h' to get the declarations.
|
||||
|
||||
// Diable things we don't need, and in particular the zlib compatible names which
|
||||
// would cause conflicts if a user application is using both PolyVox and zlib.
|
||||
#define MINIZ_NO_STDIO
|
||||
#define MINIZ_NO_ARCHIVE_APIS
|
||||
#define MINIZ_NO_TIME
|
||||
#define MINIZ_NO_ZLIB_APIS
|
||||
#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
|
||||
#define MINIZ_NO_MALLOC
|
||||
|
||||
// Include only the declarations of the functions in miniz.c. Don't include
|
||||
// the actual definitions, as this 'MinizWrapper.h' may be included from multiple
|
||||
// locations and we want to avoid linker errors regarding multiple definitions.
|
||||
#define MINIZ_HEADER_FILE_ONLY
|
||||
#include "PolyVoxCore/Impl/miniz.c"
|
||||
|
||||
#endif //__PolyVox_MinizWrapper_H__
|
File diff suppressed because it is too large
Load Diff
@ -1,374 +1,9 @@
|
||||
/*******************************************************************************
|
||||
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/Block.h"
|
||||
#include "PolyVoxCore/BlockCompressor.h"
|
||||
#include "PolyVoxCore/Pager.h"
|
||||
#include "PolyVoxCore/Region.h"
|
||||
#include "PolyVoxCore/Vector.h"
|
||||
#pragma message("WARNING - The LargeVolume class has been replaced by PagedVolume. Please use that instead.")
|
||||
|
||||
#include <limits>
|
||||
#include <cstdlib> //For abort()
|
||||
#include <cstring> //For memcpy
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <stdexcept> //For invalid_argument
|
||||
#include <vector>
|
||||
#include "PagedVolume.h"
|
||||
#include "PolyVoxForwardDeclarations.h"
|
||||
|
||||
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 <i>voxel</i>) 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<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 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<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.
|
||||
/// }
|
||||
///
|
||||
/// LargeVolume<Density>volData(&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 <typename VoxelType>
|
||||
class LargeVolume : public BaseVolume<VoxelType>
|
||||
{
|
||||
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<VoxelType> VolumeOfVoxelType; //Workaround for GCC/VS2010 differences.
|
||||
//class Sampler : public VolumeOfVoxelType::template Sampler< LargeVolume<VoxelType> >
|
||||
#ifndef SWIG
|
||||
#if defined(_MSC_VER)
|
||||
class Sampler : public BaseVolume<VoxelType>::Sampler< LargeVolume<VoxelType> > //This line works on VS2010
|
||||
#else
|
||||
class Sampler : public BaseVolume<VoxelType>::template Sampler< LargeVolume<VoxelType> > //This line works on GCC
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
Sampler(LargeVolume<VoxelType>* 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,
|
||||
uint16_t uBlockSideLength = 32
|
||||
);
|
||||
/// Constructor for creating a fixed size volume.
|
||||
LargeVolume
|
||||
(
|
||||
const Region& regValid,
|
||||
BlockCompressor<VoxelType>* pBlockCompressor,
|
||||
Pager<VoxelType>* pPager ,
|
||||
uint16_t uBlockSideLength = 32
|
||||
);
|
||||
/// Destructor
|
||||
~LargeVolume();
|
||||
|
||||
/// Gets a voxel at the position given by <tt>x,y,z</tt> coordinates
|
||||
template <WrapMode eWrapMode>
|
||||
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 <WrapMode eWrapMode>
|
||||
VoxelType getVoxel(const Vector3DInt32& v3dPos, VoxelType tBorder = VoxelType()) const;
|
||||
|
||||
/// Gets a voxel at the position given by <tt>x,y,z</tt> 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 <tt>x,y,z</tt> 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 setMaxNumberOfUncompressedBlocks(uint32_t uMaxNumberOfUncompressedBlocks);
|
||||
/// Sets the number of blocks which can be in memory before the paging system starts unloading them
|
||||
void setMaxNumberOfBlocksInMemory(uint32_t uMaxNumberOfBlocksInMemory);
|
||||
/// Sets the voxel at the position given by <tt>x,y,z</tt> 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 <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);
|
||||
|
||||
protected:
|
||||
/// Copy constructor
|
||||
LargeVolume(const LargeVolume& rhs);
|
||||
|
||||
/// Assignment operator
|
||||
LargeVolume& operator=(const LargeVolume& rhs);
|
||||
|
||||
private:
|
||||
|
||||
struct BlockPositionCompare
|
||||
{
|
||||
bool operator() (const PolyVox::Vector3DInt32& a, const PolyVox::Vector3DInt32& b) const
|
||||
{
|
||||
const uint32_t size = 3;
|
||||
for(uint32_t ct = 0; ct < size; ++ct)
|
||||
{
|
||||
if (a.getElement(ct) < b.getElement(ct))
|
||||
return true;
|
||||
if (b.getElement(ct) < a.getElement(ct))
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map<Vector3DInt32, CompressedBlock<VoxelType>*, BlockPositionCompare> CompressedBlockMap;
|
||||
typedef std::map<Vector3DInt32, UncompressedBlock<VoxelType>*, BlockPositionCompare> UncompressedBlockMap;
|
||||
|
||||
uint32_t calculateBlockMemoryUsage(void) const;
|
||||
|
||||
void ensureCompressedBlockMapHasFreeSpace(void) const;
|
||||
void ensureUncompressedBlockMapHasFreeSpace(void) const;
|
||||
|
||||
void initialise();
|
||||
|
||||
// A trick to implement specialization of template member functions in template classes. See http://stackoverflow.com/a/4951057
|
||||
template <WrapMode eWrapMode>
|
||||
VoxelType getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<eWrapMode>, VoxelType tBorder) const;
|
||||
VoxelType getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<WrapModes::Validate>, VoxelType tBorder) const;
|
||||
VoxelType getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<WrapModes::Clamp>, VoxelType tBorder) const;
|
||||
VoxelType getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<WrapModes::Border>, VoxelType tBorder) const;
|
||||
VoxelType getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<WrapModes::AssumeValid>, VoxelType tBorder) const;
|
||||
|
||||
CompressedBlock<VoxelType>* getCompressedBlock(int32_t uBlockX, int32_t uBlockY, int32_t uBlockZ) const;
|
||||
UncompressedBlock<VoxelType>* getUncompressedBlock(int32_t uBlockX, int32_t uBlockY, int32_t uBlockZ) const;
|
||||
|
||||
void eraseBlock(typename CompressedBlockMap::iterator itCompressedBlock) const;
|
||||
void eraseBlock(typename UncompressedBlockMap::iterator itUncompressedBlock) const;
|
||||
|
||||
// The block data
|
||||
mutable CompressedBlockMap m_pBlocks;
|
||||
mutable UncompressedBlockMap m_pUncompressedBlockCache;
|
||||
|
||||
mutable uint32_t m_uTimestamper;
|
||||
mutable Vector3DInt32 m_v3dLastAccessedBlockPos;
|
||||
mutable UncompressedBlock<VoxelType>* m_pLastAccessedBlock;
|
||||
uint32_t m_uMaxNumberOfUncompressedBlocks;
|
||||
uint32_t m_uMaxNumberOfBlocksInMemory;
|
||||
|
||||
// The size of the volume
|
||||
Region m_regValidRegionInBlocks;
|
||||
|
||||
// The size of the blocks
|
||||
uint16_t m_uBlockSideLength;
|
||||
uint8_t m_uBlockSideLengthPower;
|
||||
|
||||
// The compressor used by the Blocks to compress their data if required.
|
||||
BlockCompressor<VoxelType>* m_pBlockCompressor;
|
||||
Pager<VoxelType>* m_pPager;
|
||||
|
||||
// Compressed data for an empty block (sometimes needed for initialisation).
|
||||
//CompressedBlock<VoxelType>* m_pCompressedEmptyBlock;
|
||||
|
||||
// Whether we created the compressor or whether it was provided
|
||||
// by the user. This controls whether we delete it on destruction.
|
||||
bool m_bIsOurCompressor;
|
||||
};
|
||||
}
|
||||
|
||||
#include "PolyVoxCore/LargeVolume.inl"
|
||||
#include "PolyVoxCore/LargeVolumeSampler.inl"
|
||||
|
||||
#endif //__PolyVox_LargeVolume_H__
|
||||
#endif //__PolyVox_LargeVolume_H__
|
@ -1,855 +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"
|
||||
|
||||
#include "PolyVoxCore/MinizBlockCompressor.h" // For creating a default compressor when none is provided.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
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 LargeVolume 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 memory.
|
||||
/// \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>
|
||||
LargeVolume<VoxelType>::LargeVolume
|
||||
(
|
||||
const Region& regValid,
|
||||
uint16_t uBlockSideLength
|
||||
)
|
||||
:BaseVolume<VoxelType>(regValid)
|
||||
{
|
||||
m_uBlockSideLength = uBlockSideLength;
|
||||
|
||||
m_pBlockCompressor = new MinizBlockCompressor<VoxelType>();
|
||||
m_bIsOurCompressor = true;
|
||||
|
||||
m_pPager = 0;
|
||||
|
||||
initialise();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// 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 <typename VoxelType>
|
||||
LargeVolume<VoxelType>::LargeVolume
|
||||
(
|
||||
const Region& regValid,
|
||||
BlockCompressor<VoxelType>* pBlockCompressor,
|
||||
Pager<VoxelType>* pPager,
|
||||
uint16_t uBlockSideLength
|
||||
)
|
||||
:BaseVolume<VoxelType>(regValid)
|
||||
{
|
||||
m_uBlockSideLength = uBlockSideLength;
|
||||
|
||||
m_pBlockCompressor = pBlockCompressor;
|
||||
m_bIsOurCompressor = false;
|
||||
|
||||
m_pPager = pPager;
|
||||
|
||||
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 <typename VoxelType>
|
||||
LargeVolume<VoxelType>::LargeVolume(const LargeVolume<VoxelType>& /*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 <typename VoxelType>
|
||||
LargeVolume<VoxelType>::~LargeVolume()
|
||||
{
|
||||
flushAll();
|
||||
|
||||
// Only delete the compressor if it was created by us (in the constructor), not by the user.
|
||||
if(m_bIsOurCompressor)
|
||||
{
|
||||
delete m_pBlockCompressor;
|
||||
}
|
||||
|
||||
//delete m_pCompressedEmptyBlock;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// 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 <typename VoxelType>
|
||||
LargeVolume<VoxelType>& LargeVolume<VoxelType>::operator=(const LargeVolume<VoxelType>& /*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 <typename VoxelType>
|
||||
template <WrapMode eWrapMode>
|
||||
VoxelType LargeVolume<VoxelType>::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<eWrapMode>(), 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 <typename VoxelType>
|
||||
template <WrapMode eWrapMode>
|
||||
VoxelType LargeVolume<VoxelType>::getVoxel(const Vector3DInt32& v3dPos, VoxelType tBorder) const
|
||||
{
|
||||
// Simply call through to the real implementation
|
||||
return getVoxel<eWrapMode>(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 <typename VoxelType>
|
||||
VoxelType LargeVolume<VoxelType>::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<WrapModes::Validate>(), tBorder);
|
||||
case WrapModes::Clamp:
|
||||
return getVoxelImpl(uXPos, uYPos, uZPos, WrapModeType<WrapModes::Clamp>(), tBorder);
|
||||
case WrapModes::Border:
|
||||
return getVoxelImpl(uXPos, uYPos, uZPos, WrapModeType<WrapModes::Border>(), tBorder);
|
||||
case WrapModes::AssumeValid:
|
||||
return getVoxelImpl(uXPos, uYPos, uZPos, WrapModeType<WrapModes::AssumeValid>(), 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 <typename VoxelType>
|
||||
VoxelType LargeVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType LargeVolume<VoxelType>::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<uint16_t>(uXPos - (blockX << m_uBlockSideLengthPower));
|
||||
const uint16_t yOffset = static_cast<uint16_t>(uYPos - (blockY << m_uBlockSideLengthPower));
|
||||
const uint16_t zOffset = static_cast<uint16_t>(uZPos - (blockZ << m_uBlockSideLengthPower));
|
||||
|
||||
const UncompressedBlock<VoxelType>* 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 <typename VoxelType>
|
||||
VoxelType LargeVolume<VoxelType>::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 <typename VoxelType>
|
||||
void LargeVolume<VoxelType>::setMaxNumberOfUncompressedBlocks(uint32_t uMaxNumberOfUncompressedBlocks)
|
||||
{
|
||||
clearBlockCache();
|
||||
|
||||
m_uMaxNumberOfUncompressedBlocks = uMaxNumberOfUncompressedBlocks;
|
||||
|
||||
uint32_t uUncompressedBlockSizeInBytes = m_uBlockSideLength * m_uBlockSideLength * m_uBlockSideLength * sizeof(VoxelType);
|
||||
POLYVOX_LOG_DEBUG("The maximum number of uncompresed blocks has been set to " << m_uMaxNumberOfUncompressedBlocks
|
||||
<< ", which is " << m_uMaxNumberOfUncompressedBlocks * uUncompressedBlockSizeInBytes << " bytes");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// Increasing the number of blocks in memory causes less paging to occur
|
||||
/// \param uMaxNumberOfBlocksInMemory The number of blocks
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename VoxelType>
|
||||
void LargeVolume<VoxelType>::setMaxNumberOfBlocksInMemory(uint32_t uMaxNumberOfBlocksInMemory)
|
||||
{
|
||||
if(m_pBlocks.size() > uMaxNumberOfBlocksInMemory)
|
||||
{
|
||||
flushAll();
|
||||
}
|
||||
|
||||
m_uMaxNumberOfBlocksInMemory = uMaxNumberOfBlocksInMemory;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \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 <typename VoxelType>
|
||||
void LargeVolume<VoxelType>::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<uint16_t>(uXPos - (blockX << m_uBlockSideLengthPower));
|
||||
const uint16_t yOffset = static_cast<uint16_t>(uYPos - (blockY << m_uBlockSideLengthPower));
|
||||
const uint16_t zOffset = static_cast<uint16_t>(uZPos - (blockZ << m_uBlockSideLengthPower));
|
||||
|
||||
UncompressedBlock<VoxelType>* 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 <typename VoxelType>
|
||||
void LargeVolume<VoxelType>::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 <typename VoxelType>
|
||||
bool LargeVolume<VoxelType>::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<uint16_t>(uXPos - (blockX << m_uBlockSideLengthPower));
|
||||
const uint16_t yOffset = static_cast<uint16_t>(uYPos - (blockY << m_uBlockSideLengthPower));
|
||||
const uint16_t zOffset = static_cast<uint16_t>(uZPos - (blockZ << m_uBlockSideLengthPower));
|
||||
|
||||
UncompressedBlock<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 LargeVolume<VoxelType>::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 <typename VoxelType>
|
||||
void LargeVolume<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);
|
||||
uint32_t numblocks = static_cast<uint32_t>(v3dSize.getX() * v3dSize.getY() * v3dSize.getZ());
|
||||
|
||||
// FIXME - reinstate some logic to handle when the prefetched region is too large.
|
||||
|
||||
/*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 CompressedBlockMap::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--;
|
||||
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 LargeVolume<VoxelType>::flushAll()
|
||||
{
|
||||
typename CompressedBlockMap::iterator i;
|
||||
|
||||
// Flushing will remove the most accessed block, so invalidate the pointer.
|
||||
m_pLastAccessedBlock = 0;
|
||||
|
||||
//Replaced the for loop here as the call to
|
||||
//eraseBlock was invalidating the iterator.
|
||||
while(m_pUncompressedBlockCache.size() > 0)
|
||||
{
|
||||
eraseBlock(m_pUncompressedBlockCache.begin());
|
||||
}
|
||||
|
||||
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 LargeVolume<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 CompressedBlockMap::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 LargeVolume<VoxelType>::clearBlockCache(void)
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// This function should probably be made internal...
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename VoxelType>
|
||||
void LargeVolume<VoxelType>::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.");
|
||||
}
|
||||
|
||||
if(!m_pBlockCompressor)
|
||||
{
|
||||
POLYVOX_THROW(std::invalid_argument, "You must provide a valid compressor for the LargeVolume to use.");
|
||||
}
|
||||
|
||||
m_uTimestamper = 0;
|
||||
//m_uMaxNumberOfUncompressedBlocks = 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_uMaxNumberOfUncompressedBlocks);
|
||||
|
||||
//Clear the previous data
|
||||
m_pBlocks.clear();
|
||||
|
||||
//Clear the previous data
|
||||
m_pBlocks.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<float>(this->getWidth() * this->getWidth() + this->getHeight() * this->getHeight() + this->getDepth() * this->getDepth()));
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
void LargeVolume<VoxelType>::eraseBlock(typename CompressedBlockMap::iterator itCompressedBlock) const
|
||||
{
|
||||
CompressedBlock<VoxelType>* pCompressedBlock = itCompressedBlock->second;
|
||||
|
||||
// Before deleting the block we may need to page out its data. We
|
||||
// only do this if the data has been modified since it was paged in.
|
||||
if(pCompressedBlock->m_bDataModified)
|
||||
{
|
||||
// The position of the block within the volume.
|
||||
Vector3DInt32 v3dBlockPos = itCompressedBlock->first;
|
||||
|
||||
// From the coordinates of the block we deduce the coordinates of the contained voxels.
|
||||
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);
|
||||
|
||||
// Page the data out
|
||||
m_pPager->pageOut(Region(v3dLower, v3dUpper), pCompressedBlock);
|
||||
|
||||
// The compressed data is no longer modified with respect to the data on disk
|
||||
pCompressedBlock->m_bDataModified = false;
|
||||
}
|
||||
|
||||
delete itCompressedBlock->second;
|
||||
|
||||
// We can now remove the block data from memory.
|
||||
m_pBlocks.erase(itCompressedBlock);
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
void LargeVolume<VoxelType>::eraseBlock(typename UncompressedBlockMap::iterator itUncompressedBlock) const
|
||||
{
|
||||
UncompressedBlock<VoxelType>* pUncompressedBlock = itUncompressedBlock->second;
|
||||
|
||||
// This should never happen as blocks are deleted based on being least recently used.
|
||||
// I the case that we are flushing we delete all blocks, but the flush function will
|
||||
// reset the 'm_pLastAccessedBlock' anyway to prevent it being accidentally reused.
|
||||
POLYVOX_ASSERT(pUncompressedBlock != m_pLastAccessedBlock, "Attempted to delete last accessed block.");
|
||||
|
||||
// Before deleting the block we may need to recompress its data. We
|
||||
// only do this if the data has been modified since it was decompressed.
|
||||
if(pUncompressedBlock->m_bDataModified)
|
||||
{
|
||||
// Get the compressed block which we will copy the data back in to.
|
||||
Vector3DInt32 v3dBlockPos = itUncompressedBlock->first;
|
||||
CompressedBlock<VoxelType>* pCompressedBlock = getCompressedBlock(v3dBlockPos.getX(), v3dBlockPos.getY(), v3dBlockPos.getZ());
|
||||
|
||||
m_pBlockCompressor->compress(pUncompressedBlock, pCompressedBlock);
|
||||
|
||||
// The compressed data has been updated, so the uncompressed data is no longer modified with respect to it.
|
||||
pUncompressedBlock->m_bDataModified = false;
|
||||
}
|
||||
|
||||
delete itUncompressedBlock->second;
|
||||
|
||||
// We can now remove the block data from memory.
|
||||
m_pUncompressedBlockCache.erase(itUncompressedBlock);
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
CompressedBlock<VoxelType>* LargeVolume<VoxelType>::getCompressedBlock(int32_t uBlockX, int32_t uBlockY, int32_t uBlockZ) const
|
||||
{
|
||||
Vector3DInt32 v3dBlockPos(uBlockX, uBlockY, uBlockZ);
|
||||
|
||||
CompressedBlock<VoxelType>* pBlock = 0;
|
||||
|
||||
typename CompressedBlockMap::iterator itBlock = m_pBlocks.find(v3dBlockPos);
|
||||
if(itBlock != m_pBlocks.end())
|
||||
{
|
||||
pBlock = itBlock->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
ensureCompressedBlockMapHasFreeSpace();
|
||||
|
||||
// The block wasn't found so we create a new one
|
||||
pBlock = new CompressedBlock<VoxelType>;
|
||||
|
||||
// Pass the block to the Pager to give it a chance to initialise it with any data
|
||||
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);
|
||||
m_pPager->pageIn(reg, pBlock);
|
||||
|
||||
// Add the block to the map
|
||||
itBlock = m_pBlocks.insert(std::make_pair(v3dBlockPos, pBlock)).first;
|
||||
}
|
||||
|
||||
pBlock->m_uBlockLastAccessed = ++m_uTimestamper;
|
||||
return pBlock;
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
UncompressedBlock<VoxelType>* LargeVolume<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))
|
||||
{
|
||||
return m_pLastAccessedBlock;
|
||||
}
|
||||
|
||||
UncompressedBlock<VoxelType>* pUncompressedBlock = 0;
|
||||
|
||||
typename UncompressedBlockMap::iterator itUncompressedBlock = m_pUncompressedBlockCache.find(v3dBlockPos);
|
||||
// check whether the block is already loaded
|
||||
if(itUncompressedBlock != m_pUncompressedBlockCache.end())
|
||||
{
|
||||
pUncompressedBlock = itUncompressedBlock->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
// At this point we know that the uncompresed block did not exist in the cache. We will
|
||||
// create it and add it to the cache, which means we need to make sure there is space.
|
||||
ensureUncompressedBlockMapHasFreeSpace();
|
||||
|
||||
// We can now create a new block.
|
||||
pUncompressedBlock = new UncompressedBlock<VoxelType>(m_uBlockSideLength);
|
||||
|
||||
// An uncompressed bock is always backed by a compressed one, and this is created by getCompressedBlock() if it doesn't
|
||||
// already exist. If it does already exist and has data then we bring this across into the ucompressed version.
|
||||
if(getCompressedBlock(uBlockX, uBlockY, uBlockZ)->getData() != 0)
|
||||
{
|
||||
// FIXME - multiple getCompressedBlock() calls (including the one above)
|
||||
CompressedBlock<VoxelType>* pBlock = getCompressedBlock(uBlockX, uBlockY, uBlockZ);
|
||||
|
||||
m_pBlockCompressor->decompress(pBlock, pUncompressedBlock);
|
||||
}
|
||||
|
||||
// Add our new block to the map.
|
||||
m_pUncompressedBlockCache.insert(std::make_pair(v3dBlockPos, pUncompressedBlock));
|
||||
}
|
||||
|
||||
pUncompressedBlock->m_uBlockLastAccessed = ++m_uTimestamper;
|
||||
m_pLastAccessedBlock = pUncompressedBlock;
|
||||
m_v3dLastAccessedBlockPos = v3dBlockPos;
|
||||
|
||||
return pUncompressedBlock;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// Note: This function needs reviewing for accuracy...
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename VoxelType>
|
||||
float LargeVolume<VoxelType>::calculateCompressionRatio(void)
|
||||
{
|
||||
float fRawSize = static_cast<float>(m_pBlocks.size() * m_uBlockSideLength * m_uBlockSideLength* m_uBlockSideLength * sizeof(VoxelType));
|
||||
float fCompressedSize = static_cast<float>(calculateSizeInBytes());
|
||||
return fCompressedSize/fRawSize;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// Note: This function needs reviewing for accuracy...
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename VoxelType>
|
||||
uint32_t LargeVolume<VoxelType>::calculateSizeInBytes(void)
|
||||
{
|
||||
uint32_t uSizeInBytes = sizeof(LargeVolume);
|
||||
|
||||
//Memory used by the blocks
|
||||
typename CompressedBlockMap::iterator i;
|
||||
for(i = m_pBlocks.begin(); i != m_pBlocks.end(); i++)
|
||||
{
|
||||
//Inaccurate - account for rest of loaded block.
|
||||
uSizeInBytes += i->second->calculateSizeInBytes();
|
||||
}
|
||||
|
||||
//Memory used by the block cache.
|
||||
//uSizeInBytes += m_vecBlocksWithUncompressedData.capacity() * sizeof(Block<VoxelType>);
|
||||
//uSizeInBytes += m_vecBlocksWithUncompressedData.size() * m_uBlockSideLength * m_uBlockSideLength * m_uBlockSideLength * sizeof(VoxelType);
|
||||
|
||||
return uSizeInBytes;
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
uint32_t LargeVolume<VoxelType>::calculateBlockMemoryUsage(void) const
|
||||
{
|
||||
uint32_t uMemoryUsage = 0;
|
||||
|
||||
typename CompressedBlockMap::iterator i;
|
||||
for(i = m_pBlocks.begin(); i != m_pBlocks.end(); i++)
|
||||
{
|
||||
//Inaccurate - account for rest of loaded block.
|
||||
uMemoryUsage += i->second->calculateSizeInBytes();
|
||||
}
|
||||
|
||||
return uMemoryUsage;
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
void LargeVolume<VoxelType>::ensureCompressedBlockMapHasFreeSpace(void) const
|
||||
{
|
||||
while(m_pBlocks.size() > m_uMaxNumberOfBlocksInMemory)
|
||||
{
|
||||
// Find the least recently used block. This is somewhat inefficient as it searches through
|
||||
// the map, so if it proves to be a bottleneck we may want some kind of sorted structure.
|
||||
typename CompressedBlockMap::iterator i;
|
||||
typename CompressedBlockMap::iterator itUnloadBlock = m_pBlocks.begin();
|
||||
for(i = m_pBlocks.begin(); i != m_pBlocks.end(); i++)
|
||||
{
|
||||
if(i->second->m_uBlockLastAccessed < itUnloadBlock->second->m_uBlockLastAccessed)
|
||||
{
|
||||
itUnloadBlock = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Erase the least recently used block
|
||||
eraseBlock(itUnloadBlock);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
void LargeVolume<VoxelType>::ensureUncompressedBlockMapHasFreeSpace(void) const
|
||||
{
|
||||
while(m_pUncompressedBlockCache.size() > m_uMaxNumberOfUncompressedBlocks)
|
||||
{
|
||||
// Find the least recently used block. The uncompressed block cache should be
|
||||
// much smaller than the total number of blocks, so hopefully this isn't too slow.
|
||||
typename UncompressedBlockMap::iterator i;
|
||||
typename UncompressedBlockMap::iterator itUnloadBlock = m_pUncompressedBlockCache.begin();
|
||||
for(i = m_pUncompressedBlockCache.begin(); i != m_pUncompressedBlockCache.end(); i++)
|
||||
{
|
||||
if(i->second->m_uBlockLastAccessed < itUnloadBlock->second->m_uBlockLastAccessed)
|
||||
{
|
||||
itUnloadBlock = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Erase the least recently used block
|
||||
eraseBlock(itUnloadBlock);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
template <WrapMode eWrapMode>
|
||||
VoxelType LargeVolume<VoxelType>::getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<eWrapMode>, 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 <typename VoxelType>
|
||||
VoxelType LargeVolume<VoxelType>::getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<WrapModes::Validate>, 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<WrapModes::AssumeValid>(), tBorder); // No wrapping as we've just validated the position.
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
VoxelType LargeVolume<VoxelType>::getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<WrapModes::Clamp>, 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<WrapModes::AssumeValid>(), tBorder); // No wrapping as we've just validated the position.
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
VoxelType LargeVolume<VoxelType>::getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<WrapModes::Border>, VoxelType tBorder) const
|
||||
{
|
||||
if(this->m_regValidRegion.containsPoint(uXPos, uYPos, uZPos))
|
||||
{
|
||||
return getVoxelImpl(uXPos, uYPos, uZPos, WrapModeType<WrapModes::AssumeValid>(), tBorder); // No wrapping as we've just validated the position.
|
||||
}
|
||||
else
|
||||
{
|
||||
return tBorder;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
VoxelType LargeVolume<VoxelType>::getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<WrapModes::AssumeValid>, 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<uint16_t>(uXPos - (blockX << m_uBlockSideLengthPower));
|
||||
const uint16_t yOffset = static_cast<uint16_t>(uYPos - (blockY << m_uBlockSideLengthPower));
|
||||
const uint16_t zOffset = static_cast<uint16_t>(uZPos - (blockZ << m_uBlockSideLengthPower));
|
||||
|
||||
UncompressedBlock<VoxelType>* pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ);
|
||||
return pUncompressedBlock->getVoxel(xOffset, yOffset, zOffset);
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,6 @@ namespace PolyVox
|
||||
|
||||
const uint32_t uArrayWidth = m_regSizeInVoxels.getUpperX() - m_regSizeInVoxels.getLowerX() + 1;
|
||||
const uint32_t uArrayHeight = m_regSizeInVoxels.getUpperY() - m_regSizeInVoxels.getLowerY() + 1;
|
||||
const uint32_t arraySizes[2]= {uArrayWidth, uArrayHeight}; // Array dimensions
|
||||
|
||||
//For edge indices
|
||||
Array2DInt32 m_pPreviousVertexIndicesX(uArrayWidth, uArrayHeight);
|
||||
|
@ -1,65 +0,0 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2005-2013 David Williams and Matthew 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_MinizBlockCompressor_H__
|
||||
#define __PolyVox_MinizBlockCompressor_H__
|
||||
|
||||
#include "PolyVoxCore/BlockCompressor.h"
|
||||
|
||||
#include "PolyVoxCore/Impl/MinizWrapper.h"
|
||||
|
||||
namespace PolyVox
|
||||
{
|
||||
/**
|
||||
* Provides an interface for performing paging of data.
|
||||
*/
|
||||
template <typename VoxelType>
|
||||
class MinizBlockCompressor : public BlockCompressor<VoxelType>
|
||||
{
|
||||
public:
|
||||
MinizBlockCompressor(int iCompressionLevel = 6); // Miniz defines MZ_DEFAULT_LEVEL = 6 so we use the same here
|
||||
~MinizBlockCompressor();
|
||||
|
||||
void compress(UncompressedBlock<VoxelType>* pSrcBlock, CompressedBlock<VoxelType>* pDstBlock);
|
||||
void decompress(CompressedBlock<VoxelType>* pSrcBlock, UncompressedBlock<VoxelType>* pDstBlock);
|
||||
|
||||
private:
|
||||
uint32_t getExpectedCompressedSize(uint32_t uUncompressedInputSize);
|
||||
uint32_t getMaxCompressedSize(uint32_t uUncompressedInputSize);
|
||||
uint32_t compressWithMiniz(const void* pSrcData, size_t uSrcLength, void* pDstData, size_t uDstLength);
|
||||
uint32_t decompressWithMiniz(const void* pSrcData, size_t uSrcLength, void* pDstData, size_t uDstLength);
|
||||
|
||||
unsigned int m_uCompressionFlags;
|
||||
|
||||
// Data gets compressed into this, then we check how big the result
|
||||
// is and copy the required number of bytes to the destination block.
|
||||
std::vector<uint8_t> m_vecTempBuffer;
|
||||
|
||||
// tdefl_compressor contains all the state needed by the low-level compressor so it's a pretty big struct (~300k).
|
||||
tdefl_compressor* m_pDeflator;
|
||||
};
|
||||
}
|
||||
|
||||
#include "PolyVoxCore/MinizBlockCompressor.inl"
|
||||
|
||||
#endif //__PolyVox_MinizBlockCompressor_H__
|
@ -1,210 +0,0 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2005-2013 David Williams and Matthew 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 <sstream>
|
||||
|
||||
namespace PolyVox
|
||||
{
|
||||
/**
|
||||
* You can specify a compression level when constructing this compressor. This controls the tradeoff between speed and compression
|
||||
* rate. Levels 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow).
|
||||
* \param iCompressionLevel The desired compression level.
|
||||
*/
|
||||
template <typename VoxelType>
|
||||
MinizBlockCompressor<VoxelType>::MinizBlockCompressor(int iCompressionLevel)
|
||||
:m_pDeflator(0)
|
||||
{
|
||||
// Create and store the deflator.
|
||||
m_pDeflator = new tdefl_compressor;
|
||||
|
||||
// The number of dictionary probes to use at each compression level (0-10). 0=implies fastest/minimal possible probing.
|
||||
// The discontinuity is unsettling but may be explained by the 'iCompressionLevel <= 3' check later?
|
||||
static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 };
|
||||
|
||||
// Create tdefl() compatible flags (we have to compose the low-level flags ourselves, or use tdefl_create_comp_flags_from_zip_params() but that means MINIZ_NO_ZLIB_APIS can't be defined).
|
||||
m_uCompressionFlags = TDEFL_WRITE_ZLIB_HEADER | s_tdefl_num_probes[(std::min)(10, iCompressionLevel)] | ((iCompressionLevel <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0);
|
||||
if (!iCompressionLevel)
|
||||
{
|
||||
m_uCompressionFlags |= TDEFL_FORCE_ALL_RAW_BLOCKS;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
MinizBlockCompressor<VoxelType>::~MinizBlockCompressor()
|
||||
{
|
||||
// Delete the deflator
|
||||
delete m_pDeflator;
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
void MinizBlockCompressor<VoxelType>::compress(UncompressedBlock<VoxelType>* pSrcBlock, CompressedBlock<VoxelType>* pDstBlock)
|
||||
{
|
||||
// The uncompressed data will be read straight out of the block
|
||||
void* pSrcData = reinterpret_cast<void*>(pSrcBlock->getData());
|
||||
size_t uSrcLength = pSrcBlock->getDataSizeInBytes();
|
||||
|
||||
// This compressor is expected to be used many times to compress a large number of blocks, but they are all
|
||||
// expected to have the same size. Therefore the resize() function below should only perform allocation once.
|
||||
uint32_t expectedCompressedSize = getExpectedCompressedSize(uSrcLength);
|
||||
if(m_vecTempBuffer.size() != expectedCompressedSize)
|
||||
{
|
||||
POLYVOX_LOG_INFO("Resizing temp buffer to " << expectedCompressedSize << "bytes. "
|
||||
<< "This should only happen the first time the MinizBlockCompressor is used");
|
||||
m_vecTempBuffer.resize(expectedCompressedSize);
|
||||
}
|
||||
|
||||
// The compressed data will be written into this temporary buffer.
|
||||
uint8_t* pDstData = &(m_vecTempBuffer[0]);
|
||||
size_t uDstLength = m_vecTempBuffer.size();
|
||||
|
||||
try
|
||||
{
|
||||
// Perform the compression
|
||||
uint32_t uCompressedLength = compressWithMiniz(pSrcData, uSrcLength, pDstData, uDstLength);
|
||||
|
||||
// Copy the resulting compressed data into the compressed block
|
||||
pDstBlock->setData(pDstData, uCompressedLength);
|
||||
}
|
||||
catch(std::exception&)
|
||||
{
|
||||
// It is possible for the compression to fail. A common cause for this would be if the destination
|
||||
// buffer is not big enough. So now we try again using a buffer that is definitely big enough.
|
||||
// Note that ideally we will choose our earlier buffer size so that this almost never happens.
|
||||
POLYVOX_LOG_WARNING("The compressor failed to compress the block, probabaly due to the buffer being too small.");
|
||||
POLYVOX_LOG_WARNING("The compression will be tried again with a larger buffer.");
|
||||
|
||||
std::vector<uint8_t> vecExtraBigBuffer;
|
||||
vecExtraBigBuffer.resize(getMaxCompressedSize(uSrcLength));
|
||||
|
||||
uint8_t* pDstData = &(vecExtraBigBuffer[0]);
|
||||
size_t uDstLength = vecExtraBigBuffer.size();
|
||||
|
||||
try
|
||||
{
|
||||
// Perform the compression
|
||||
uint32_t uCompressedLength = compressWithMiniz(pSrcData, uSrcLength, pDstData, uDstLength);
|
||||
|
||||
// Copy the resulting compressed data into the compressed block
|
||||
pDstBlock->setData(pDstData, uCompressedLength);
|
||||
}
|
||||
catch(std::exception&)
|
||||
{
|
||||
// At this point it didn't work even with a bigger buffer.
|
||||
// Not much more we can do so just rethrow the exception.
|
||||
POLYVOX_THROW(std::runtime_error, "Failed to compress block data");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
void MinizBlockCompressor<VoxelType>::decompress(CompressedBlock<VoxelType>* pSrcBlock, UncompressedBlock<VoxelType>* pDstBlock)
|
||||
{
|
||||
// Get raw pointers so that the data can be decompressed directly into the destination block.
|
||||
const void* pSrcData = reinterpret_cast<const void*>(pSrcBlock->getData());
|
||||
void* pDstData = reinterpret_cast<void*>(pDstBlock->getData());
|
||||
size_t uSrcLength = pSrcBlock->getDataSizeInBytes();
|
||||
size_t uDstLength = pDstBlock->getDataSizeInBytes();
|
||||
|
||||
// Perform the decompression
|
||||
uint32_t uUncompressedLength = decompressWithMiniz(pSrcData, uSrcLength, pDstData, uDstLength);
|
||||
|
||||
// We know we should have received exactly one block's worth of data. If not then something went wrong.
|
||||
POLYVOX_THROW_IF(uUncompressedLength != pDstBlock->getDataSizeInBytes(), std::runtime_error, "Decompressed data does not have the expected length");
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
uint32_t MinizBlockCompressor<VoxelType>::getExpectedCompressedSize(uint32_t uUncompressedInputSize)
|
||||
{
|
||||
// We expect this block compressor will be used for smoothly changing volume data such as density fields and so
|
||||
// the compression rate might not be great. The value beloew is basically a guess based on previous experience.
|
||||
uint32_t uExpectedCompressionRate = 4;
|
||||
return uUncompressedInputSize / uExpectedCompressionRate;
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
uint32_t MinizBlockCompressor<VoxelType>::getMaxCompressedSize(uint32_t uUncompressedInputSize)
|
||||
{
|
||||
// The contents of this function are copied from miniz's 'mz_deflateBound()'
|
||||
// (which we can't use because it is part of the zlib-style higher level API).
|
||||
unsigned long source_len = uUncompressedInputSize;
|
||||
|
||||
// This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.)
|
||||
return (std::max)(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5);
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
uint32_t MinizBlockCompressor<VoxelType>::compressWithMiniz(const void* pSrcData, size_t uSrcLength, void* pDstData, size_t uDstLength)
|
||||
{
|
||||
// It seems we have to reinitialise the deflator for each fresh dataset (it's probably intended for streaming, which we're not doing here)
|
||||
tdefl_status status = tdefl_init(m_pDeflator, NULL, NULL, m_uCompressionFlags);
|
||||
if (status != TDEFL_STATUS_OKAY)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "tdefl_init() failed with return code '" << status << "'";
|
||||
POLYVOX_THROW(std::runtime_error, ss.str());
|
||||
}
|
||||
|
||||
// Compress as much of the input as possible (or all of it) to the output buffer.
|
||||
status = tdefl_compress(m_pDeflator, pSrcData, &uSrcLength, pDstData, &uDstLength, TDEFL_FINISH);
|
||||
|
||||
//Check whether the compression was successful.
|
||||
if (status != TDEFL_STATUS_DONE)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "tdefl_compress() failed with return code '" << status << "'";
|
||||
POLYVOX_THROW(std::runtime_error, ss.str());
|
||||
}
|
||||
|
||||
// The compression modifies 'ulDstLength' to hold the new length.
|
||||
return uDstLength;
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
uint32_t MinizBlockCompressor<VoxelType>::decompressWithMiniz(const void* pSrcData, size_t uSrcLength, void* pDstData, size_t uDstLength)
|
||||
{
|
||||
// I don't know exactly why this limitation exists but it's an implementation detail of miniz. It shouldn't matter for our purposes
|
||||
// as our destination is a Block and those are always a power of two. If you need to use this code for other purposes then you'll
|
||||
// probably have to scale up your dst buffer to the nearest appropriate size. Alternatively you can use the mz_uncompress function,
|
||||
// but that means enabling parts of the miniz API which are #defined out at the top of this file.
|
||||
POLYVOX_THROW_IF(isPowerOf2(uDstLength) == false, std::invalid_argument, "Miniz decompressor requires the destination buffer to have a size which is a power of two.");
|
||||
|
||||
// Create and initialise the decompressor (I believe this is much small than the compressor).
|
||||
tinfl_decompressor inflator;
|
||||
tinfl_init(&inflator);
|
||||
|
||||
// Do the decompression. In some scenarios 'tinfl_decompress' would be called multiple times with the same dest buffer but
|
||||
// different locations within it. In our scenario it's only called once so the start and the location are the same (both pDstData).
|
||||
tinfl_status status = tinfl_decompress(&inflator, (const mz_uint8 *)pSrcData, &uSrcLength, (mz_uint8 *)pDstData, (mz_uint8 *)pDstData, &uDstLength, TINFL_FLAG_PARSE_ZLIB_HEADER);
|
||||
|
||||
//Check whether the decompression was successful.
|
||||
if (status != TINFL_STATUS_DONE)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "tinfl_decompress() failed with return code '" << status << "'";
|
||||
POLYVOX_THROW(std::runtime_error, ss.str());
|
||||
}
|
||||
|
||||
// The decompression modifies 'ulDstLength' to hold the new length.
|
||||
return uDstLength;
|
||||
}
|
||||
}
|
366
library/PolyVoxCore/include/PolyVoxCore/PagedVolume.h
Normal file
366
library/PolyVoxCore/include/PolyVoxCore/PagedVolume.h
Normal file
@ -0,0 +1,366 @@
|
||||
/*******************************************************************************
|
||||
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/Region.h"
|
||||
#include "PolyVoxCore/Vector.h"
|
||||
|
||||
#include <limits>
|
||||
#include <cstdlib> //For abort()
|
||||
#include <cstring> //For memcpy
|
||||
#include <unordered_map>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <stdexcept> //For invalid_argument
|
||||
#include <vector>
|
||||
|
||||
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 <i>voxel</i>) 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<int> volume(Region(Vector3DInt32(0,0,0), Vector3DInt32(63,127,255)));
|
||||
/// volume.setVoxel(15, 90, 42, int(5));
|
||||
/// std::cout << "Voxel at (15, 90, 42) has value: " << volume.getVoxel(15, 90, 42) << std::endl;
|
||||
/// std::cout << "Width = " << volume.getWidth() << ", Height = " << volume.getHeight() << ", Depth = " << volume.getDepth() << std::endl;
|
||||
/// \endcode
|
||||
///
|
||||
/// 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). The result of attempts to access voxels outside this range will result
|
||||
/// are defined by the WrapMode). PolyVox also has support for near infinite volumes which will be discussed later.
|
||||
///
|
||||
/// Access to individual voxels is provided via the setVoxel() and getVoxel() member functions. Advanced users may also be interested in
|
||||
/// the Sampler nested 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 chunks. Each of these chunk 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 chunks. Typically these chunks do not need to all be in memory all the time, and the Pager class can
|
||||
/// be used to control how they are loaded and unloaded. 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.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// Cache-aware traversal
|
||||
/// ---------------------
|
||||
/// *NOTE: This needs updating for PagedVolume rather than the old LargeVolume*
|
||||
/// 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 chunks 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 chunks into the cache. By the time you reach (1023,1023,0) you have hit 1024x1024 voxels and pulled 32x32 chunks into the cache.
|
||||
/// You are now ready to touch voxel (0,0,1) which is right next to where you started, but unless your cache is at least 32x32 chunks large then this
|
||||
/// initial chunk 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 size 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 chunk cache (amoung other problems).
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename VoxelType>
|
||||
class PagedVolume : public BaseVolume<VoxelType>
|
||||
{
|
||||
public:
|
||||
/// The PagedVolume stores it data as a set of Chunk instances which can be loaded and unloaded as memory requirements dictate.
|
||||
class Chunk;
|
||||
/// The Pager class is responsible for the loading and unloading of Chunks, and can be overridden by the user.
|
||||
class Pager;
|
||||
|
||||
class Chunk
|
||||
{
|
||||
friend class PagedVolume;
|
||||
|
||||
public:
|
||||
Chunk(Vector3DInt32 v3dPosition, uint16_t uSideLength, Pager* pPager = nullptr);
|
||||
~Chunk();
|
||||
|
||||
VoxelType* getData(void) const;
|
||||
uint32_t getDataSizeInBytes(void) const;
|
||||
|
||||
VoxelType getVoxel(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos) const;
|
||||
VoxelType getVoxel(const Vector3DUint16& v3dPos) const;
|
||||
|
||||
void setVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos, VoxelType tValue);
|
||||
void setVoxelAt(const Vector3DUint16& v3dPos, VoxelType tValue);
|
||||
|
||||
private:
|
||||
/// Private copy constructor to prevent accisdental copying
|
||||
Chunk(const Chunk& /*rhs*/) {};
|
||||
|
||||
/// Private assignment operator to prevent accisdental copying
|
||||
Chunk& operator=(const Chunk& /*rhs*/) {};
|
||||
|
||||
// This is updated by the PagedVolume and used to discard the least recently used chunks.
|
||||
uint32_t m_uChunkLastAccessed;
|
||||
|
||||
// This is so we can tell whether a uncompressed chunk has to be recompressed and whether
|
||||
// a compressed chunk has to be paged back to disk, or whether they can just be discarded.
|
||||
bool m_bDataModified;
|
||||
|
||||
uint32_t calculateSizeInBytes(void);
|
||||
static uint32_t calculateSizeInBytes(uint32_t uSideLength);
|
||||
|
||||
VoxelType* m_tData;
|
||||
uint16_t m_uSideLength;
|
||||
uint8_t m_uSideLengthPower;
|
||||
Pager* m_pPager;
|
||||
|
||||
// Note: Do we really need to store this position here as well as in the block maps?
|
||||
Vector3DInt32 m_v3dChunkSpacePosition;
|
||||
};
|
||||
|
||||
/**
|
||||
* Users can override this class and provide an instance of the derived class to the PagedVolume constructor. This derived class
|
||||
* could then perform tasks such as compression and decompression of the data, and read/writing it to a file, database, network,
|
||||
* or other storage as appropriate. See FilePager for a simple example of such a derived class.
|
||||
*/
|
||||
class Pager
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
Pager() {};
|
||||
/// Destructor
|
||||
virtual ~Pager() {};
|
||||
|
||||
virtual void pageIn(const Region& region, Chunk* pChunk) = 0;
|
||||
virtual void pageOut(const Region& region, Chunk* pChunk) = 0;
|
||||
};
|
||||
|
||||
//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<VoxelType> VolumeOfVoxelType; //Workaround for GCC/VS2010 differences.
|
||||
//class Sampler : public VolumeOfVoxelType::template Sampler< PagedVolume<VoxelType> >
|
||||
#ifndef SWIG
|
||||
#if defined(_MSC_VER)
|
||||
class Sampler : public BaseVolume<VoxelType>::Sampler< PagedVolume<VoxelType> > //This line works on VS2010
|
||||
#else
|
||||
class Sampler : public BaseVolume<VoxelType>::template Sampler< PagedVolume<VoxelType> > //This line works on GCC
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
Sampler(PagedVolume<VoxelType>* 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 uChunkSideLength = 32
|
||||
);
|
||||
/// Destructor
|
||||
~PagedVolume();
|
||||
|
||||
/// Gets a voxel at the position given by <tt>x,y,z</tt> coordinates
|
||||
template <WrapMode eWrapMode>
|
||||
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 <WrapMode eWrapMode>
|
||||
VoxelType getVoxel(const Vector3DInt32& v3dPos, VoxelType tBorder = VoxelType()) const;
|
||||
|
||||
/// Gets a voxel at the position given by <tt>x,y,z</tt> 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 <tt>x,y,z</tt> 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 chunks for which uncompressed data is stored
|
||||
void setMemoryUsageLimit(uint32_t uMemoryUsageInBytes);
|
||||
/// Sets the voxel at the position given by <tt>x,y,z</tt> 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 <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();
|
||||
|
||||
/// 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:
|
||||
|
||||
// FIXME - We can probably ove this into the constructor
|
||||
void initialise();
|
||||
|
||||
// A trick to implement specialization of template member functions in template classes. See http://stackoverflow.com/a/4951057
|
||||
template <WrapMode eWrapMode>
|
||||
VoxelType getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<eWrapMode>, VoxelType tBorder) const;
|
||||
VoxelType getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<WrapModes::Validate>, VoxelType tBorder) const;
|
||||
VoxelType getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<WrapModes::Clamp>, VoxelType tBorder) const;
|
||||
VoxelType getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<WrapModes::Border>, VoxelType tBorder) const;
|
||||
VoxelType getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<WrapModes::AssumeValid>, VoxelType tBorder) const;
|
||||
|
||||
std::shared_ptr<Chunk> getChunk(int32_t uChunkX, int32_t uChunkY, int32_t uChunkZ) const;
|
||||
|
||||
void purgeNullPtrsFromAllChunks(void) const;
|
||||
|
||||
// The chunk data is stored in the pair of maps below. This will often hold the same set of chunks but there are occasions
|
||||
// when they can differ (more on that in a moment). The main store is the set of 'recently used chunks' which holds shared_ptr's
|
||||
// to the chunk data. When memory is low chunks can be removed from this list and, assuming there are no more references to
|
||||
// them, they will be deleted. However, it is also possible that a Sampler is pointing at a given chunk, and in this case the
|
||||
// reference counting will ensure that the chunk survives until the sampler has finished with it.
|
||||
//
|
||||
// However, this leaves us open to a subtle bug. If a chunk is removed from the recently used set, continues to exist due to a
|
||||
// sampler using it, and then the user tries to access it through the volume interface, then the volume will page the chunk
|
||||
// back in (not knowing the sampler still has it). This would result in two chunks in memory with the same position is space.
|
||||
// To avoid this, the 'all chunks' set tracks chunks with are used by the volume *and/or* the samplers. It holds weak pointers
|
||||
// so does not cause chunks to persist.
|
||||
//
|
||||
// TODO: Do we really need maps here? It means we are storing the chunk positions in the map, but they are also stored in the
|
||||
// chunks themselves (so they can be passed to the pager from the chunks destructor). Can we use a set here? What is a better approach?
|
||||
typedef std::unordered_map<Vector3DInt32, std::weak_ptr< Chunk > > WeakPtrChunkMap;
|
||||
mutable WeakPtrChunkMap m_pAllChunks;
|
||||
typedef std::unordered_map<Vector3DInt32, std::shared_ptr< Chunk > > SharedPtrChunkMap;
|
||||
mutable SharedPtrChunkMap m_pRecentlyUsedChunks;
|
||||
|
||||
mutable uint32_t m_uTimestamper;
|
||||
mutable Vector3DInt32 m_v3dLastAccessedChunkPos;
|
||||
mutable std::shared_ptr<Chunk> m_pLastAccessedChunk;
|
||||
uint32_t m_uChunkCountLimit;
|
||||
|
||||
// The size of the volume
|
||||
Region m_regValidRegionInChunks;
|
||||
|
||||
// The size of the chunks
|
||||
uint16_t m_uChunkSideLength;
|
||||
uint8_t m_uChunkSideLengthPower;
|
||||
|
||||
Pager* m_pPager;
|
||||
|
||||
// Enough to make sure a chunks and it's neighbours can be loaded, with a few to spare.
|
||||
const uint32_t uMinPracticalNoOfChunks = 32;
|
||||
// Should prevent multi-gigabyte volumes when chunk sizes are reasonable.
|
||||
const uint32_t uMaxPracticalNoOfChunks = 32768;
|
||||
};
|
||||
}
|
||||
|
||||
#include "PolyVoxCore/PagedVolume.inl"
|
||||
#include "PolyVoxCore/PagedVolumeChunk.inl"
|
||||
#include "PolyVoxCore/PagedVolumeSampler.inl"
|
||||
|
||||
#endif //__PolyVox_PagedVolume_H__
|
696
library/PolyVoxCore/include/PolyVoxCore/PagedVolume.inl
Normal file
696
library/PolyVoxCore/include/PolyVoxCore/PagedVolume.inl
Normal file
@ -0,0 +1,696 @@
|
||||
/*******************************************************************************
|
||||
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 <algorithm>
|
||||
#include <limits>
|
||||
|
||||
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 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 uChunkSideLength The size of the chunks making up the volume. Small chunks will compress/decompress faster, but there will also be more of them meaning voxel access could be slower.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename VoxelType>
|
||||
PagedVolume<VoxelType>::PagedVolume
|
||||
(
|
||||
const Region& regValid,
|
||||
Pager* pPager,
|
||||
uint16_t uChunkSideLength
|
||||
)
|
||||
:BaseVolume<VoxelType>(regValid)
|
||||
{
|
||||
m_uChunkSideLength = uChunkSideLength;
|
||||
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 chunks to play around with.
|
||||
if (regValid == Region::MaxRegion)
|
||||
{
|
||||
m_uChunkCountLimit = 1024;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise we try to choose a chunk count to avoid too much thrashing, particularly when iterating
|
||||
// over the whole volume. This means at least enough chunks 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_uChunkSideLength;
|
||||
shortestSide /= m_uChunkSideLength;
|
||||
|
||||
m_uChunkCountLimit = longestSide * shortestSide;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there is no pager provided then we set the chunk limit to the maximum
|
||||
// value to ensure the system never attempts to page chunks out of memory.
|
||||
m_uChunkCountLimit = (std::numeric_limits<uint32_t>::max)();
|
||||
}
|
||||
|
||||
// Make sure the calculated chunk limit is within practical bounds
|
||||
m_uChunkCountLimit = (std::max)(m_uChunkCountLimit, uMinPracticalNoOfChunks);
|
||||
m_uChunkCountLimit = (std::min)(m_uChunkCountLimit, uMaxPracticalNoOfChunks);
|
||||
|
||||
uint32_t uChunkSizeInBytes = PagedVolume<VoxelType>::Chunk::calculateSizeInBytes(m_uChunkSideLength);
|
||||
POLYVOX_LOG_DEBUG("Memory usage limit for volume initially set to " << (m_uChunkCountLimit * uChunkSizeInBytes) / (1024 * 1024)
|
||||
<< "Mb (" << m_uChunkCountLimit << " chunks of " << uChunkSizeInBytes / 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 <typename VoxelType>
|
||||
PagedVolume<VoxelType>::PagedVolume(const PagedVolume<VoxelType>& /*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 <typename VoxelType>
|
||||
PagedVolume<VoxelType>::~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 <typename VoxelType>
|
||||
PagedVolume<VoxelType>& PagedVolume<VoxelType>::operator=(const PagedVolume<VoxelType>& /*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 <typename VoxelType>
|
||||
template <WrapMode eWrapMode>
|
||||
VoxelType PagedVolume<VoxelType>::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<eWrapMode>(), 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 <typename VoxelType>
|
||||
template <WrapMode eWrapMode>
|
||||
VoxelType PagedVolume<VoxelType>::getVoxel(const Vector3DInt32& v3dPos, VoxelType tBorder) const
|
||||
{
|
||||
// Simply call through to the real implementation
|
||||
return getVoxel<eWrapMode>(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 <typename VoxelType>
|
||||
VoxelType PagedVolume<VoxelType>::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<WrapModes::Validate>(), tBorder);
|
||||
case WrapModes::Clamp:
|
||||
return getVoxelImpl(uXPos, uYPos, uZPos, WrapModeType<WrapModes::Clamp>(), tBorder);
|
||||
case WrapModes::Border:
|
||||
return getVoxelImpl(uXPos, uYPos, uZPos, WrapModeType<WrapModes::Border>(), tBorder);
|
||||
case WrapModes::AssumeValid:
|
||||
return getVoxelImpl(uXPos, uYPos, uZPos, WrapModeType<WrapModes::AssumeValid>(), 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 <typename VoxelType>
|
||||
VoxelType PagedVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType PagedVolume<VoxelType>::getVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos) const
|
||||
{
|
||||
if(this->m_regValidRegion.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos)))
|
||||
{
|
||||
const int32_t chunkX = uXPos >> m_uChunkSideLengthPower;
|
||||
const int32_t chunkY = uYPos >> m_uChunkSideLengthPower;
|
||||
const int32_t chunkZ = uZPos >> m_uChunkSideLengthPower;
|
||||
|
||||
const uint16_t xOffset = static_cast<uint16_t>(uXPos - (chunkX << m_uChunkSideLengthPower));
|
||||
const uint16_t yOffset = static_cast<uint16_t>(uYPos - (chunkY << m_uChunkSideLengthPower));
|
||||
const uint16_t zOffset = static_cast<uint16_t>(uZPos - (chunkZ << m_uChunkSideLengthPower));
|
||||
|
||||
auto pChunk = getChunk(chunkX, chunkY, chunkZ);
|
||||
|
||||
return pChunk->getVoxel(xOffset, yOffset, zOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this->getBorderValue();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \param v3dPos The 3D position of the voxel
|
||||
/// \return The voxel value
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename VoxelType>
|
||||
VoxelType PagedVolume<VoxelType>::getVoxelAt(const Vector3DInt32& v3dPos) const
|
||||
{
|
||||
return getVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// Increasing the size of the chunk 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 uMaxNumberOfChunks The number of chunks for which uncompressed data can be cached.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename VoxelType>
|
||||
void PagedVolume<VoxelType>::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 chunks based on the memory limit and the size of each chunk.
|
||||
uint32_t uChunkSizeInBytes = PagedVolume<VoxelType>::Chunk::calculateSizeInBytes(m_uChunkSideLength);
|
||||
m_uChunkCountLimit = uMemoryUsageInBytes / uChunkSizeInBytes;
|
||||
|
||||
// We need at least a few chunks available to avoid thrashing, and in pratice there will probably be hundreds.
|
||||
POLYVOX_LOG_WARNING_IF(m_uChunkCountLimit < uMinPracticalNoOfChunks, "Requested memory usage limit of "
|
||||
<< uMemoryUsageInBytes / (1024 * 1024) << "Mb is too low and cannot be adhered to.");
|
||||
m_uChunkCountLimit = (std::max)(m_uChunkCountLimit, uMinPracticalNoOfChunks);
|
||||
m_uChunkCountLimit = (std::min)(m_uChunkCountLimit, uMaxPracticalNoOfChunks);
|
||||
|
||||
// If the new limit is less than the number of chunks already loaded then the easiest solution is to flush and start loading again.
|
||||
if (m_pRecentlyUsedChunks.size() > m_uChunkCountLimit)
|
||||
{
|
||||
flushAll();
|
||||
}
|
||||
|
||||
POLYVOX_LOG_DEBUG("Memory usage limit for volume now set to " << (m_uChunkCountLimit * uChunkSizeInBytes) / (1024 * 1024)
|
||||
<< "Mb (" << m_uChunkCountLimit << " chunks of " << uChunkSizeInBytes / 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 <typename VoxelType>
|
||||
void PagedVolume<VoxelType>::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 chunkX = uXPos >> m_uChunkSideLengthPower;
|
||||
const int32_t chunkY = uYPos >> m_uChunkSideLengthPower;
|
||||
const int32_t chunkZ = uZPos >> m_uChunkSideLengthPower;
|
||||
|
||||
const uint16_t xOffset = static_cast<uint16_t>(uXPos - (chunkX << m_uChunkSideLengthPower));
|
||||
const uint16_t yOffset = static_cast<uint16_t>(uYPos - (chunkY << m_uChunkSideLengthPower));
|
||||
const uint16_t zOffset = static_cast<uint16_t>(uZPos - (chunkZ << m_uChunkSideLengthPower));
|
||||
|
||||
auto pChunk = getChunk(chunkX, chunkY, chunkZ);
|
||||
pChunk->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 <typename VoxelType>
|
||||
void PagedVolume<VoxelType>::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 <typename VoxelType>
|
||||
bool PagedVolume<VoxelType>::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 chunkX = uXPos >> m_uChunkSideLengthPower;
|
||||
const int32_t chunkY = uYPos >> m_uChunkSideLengthPower;
|
||||
const int32_t chunkZ = uZPos >> m_uChunkSideLengthPower;
|
||||
|
||||
const uint16_t xOffset = static_cast<uint16_t>(uXPos - (chunkX << m_uChunkSideLengthPower));
|
||||
const uint16_t yOffset = static_cast<uint16_t>(uYPos - (chunkY << m_uChunkSideLengthPower));
|
||||
const uint16_t zOffset = static_cast<uint16_t>(uZPos - (chunkZ << m_uChunkSideLengthPower));
|
||||
|
||||
auto pChunk = getChunk(chunkX, chunkY, chunkZ);
|
||||
|
||||
pChunk->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 PagedVolume<VoxelType>::setVoxelAt(const Vector3DInt32& v3dPos, VoxelType tValue)
|
||||
{
|
||||
return setVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ(), tValue);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// Note that if the memory usage limit 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 PagedVolume<VoxelType>::prefetch(Region regPrefetch)
|
||||
{
|
||||
// Convert the start and end positions into chunk space coordinates
|
||||
Vector3DInt32 v3dStart;
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
v3dStart.setElement(i, regPrefetch.getLowerCorner().getElement(i) >> m_uChunkSideLengthPower);
|
||||
}
|
||||
|
||||
Vector3DInt32 v3dEnd;
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
v3dEnd.setElement(i, regPrefetch.getUpperCorner().getElement(i) >> m_uChunkSideLengthPower);
|
||||
}
|
||||
|
||||
// Ensure we don't page in more chunks than the volume can hold.
|
||||
Region region(v3dStart, v3dEnd);
|
||||
uint32_t uNoOfChunks = static_cast<uint32_t>(region.getWidthInVoxels() * region.getHeightInVoxels() * region.getDepthInVoxels());
|
||||
POLYVOX_LOG_WARNING_IF(uNoOfChunks > m_uChunkCountLimit, "Attempting to prefetch more than the maximum number of chunks.");
|
||||
uNoOfChunks = (std::min)(uNoOfChunks, m_uChunkCountLimit);
|
||||
|
||||
// Loops over the specified positions and touch the corresponding chunks.
|
||||
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++)
|
||||
{
|
||||
getChunk(x,y,z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// Removes all voxels from memory, and calls dataOverflowHandler() to ensure the application has a chance to store the data.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename VoxelType>
|
||||
void PagedVolume<VoxelType>::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 chunks.
|
||||
m_pLastAccessedChunk = nullptr;
|
||||
|
||||
// Erase all the most recently used chunks.
|
||||
m_pRecentlyUsedChunks.clear();
|
||||
|
||||
// Remove deleted chunks from the list of all loaded chunks.
|
||||
purgeNullPtrsFromAllChunks();
|
||||
|
||||
// If there are still some chunks left then this is a cause for concern. Perhaps samplers are holding on to them?
|
||||
POLYVOX_LOG_WARNING_IF(m_pAllChunks.size() > 0, "Chunks 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 <typename VoxelType>
|
||||
void PagedVolume<VoxelType>::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 chunks.
|
||||
m_pLastAccessedChunk = nullptr;
|
||||
|
||||
// Convert the start and end positions into chunk space coordinates
|
||||
Vector3DInt32 v3dStart;
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
v3dStart.setElement(i, regFlush.getLowerCorner().getElement(i) >> m_uChunkSideLengthPower);
|
||||
}
|
||||
|
||||
Vector3DInt32 v3dEnd;
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
v3dEnd.setElement(i, regFlush.getUpperCorner().getElement(i) >> m_uChunkSideLengthPower);
|
||||
}
|
||||
|
||||
// Loops over the specified positions and delete the corresponding chunks.
|
||||
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_pRecentlyUsedChunks.erase(Vector3DInt32(x, y, z)); m_pLastAccessedChunk = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We might now have so null pointers in the 'all chunks' list so clean them up.
|
||||
purgeNullPtrsFromAllChunks();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// This function should probably be made internal...
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename VoxelType>
|
||||
void PagedVolume<VoxelType>::initialise()
|
||||
{
|
||||
//Validate parameters
|
||||
if(m_uChunkSideLength == 0)
|
||||
{
|
||||
POLYVOX_THROW(std::invalid_argument, "Chunk side length cannot be zero.");
|
||||
}
|
||||
|
||||
if(!isPowerOf2(m_uChunkSideLength))
|
||||
{
|
||||
POLYVOX_THROW(std::invalid_argument, "Chunk side length must be a power of two.");
|
||||
}
|
||||
|
||||
m_uTimestamper = 0;
|
||||
m_v3dLastAccessedChunkPos = Vector3DInt32(0,0,0); //There are no invalid positions, but initially the m_pLastAccessedChunk pointer will be null;
|
||||
m_pLastAccessedChunk = 0;
|
||||
|
||||
//Compute the chunk side length
|
||||
m_uChunkSideLengthPower = logBase2(m_uChunkSideLength);
|
||||
|
||||
m_regValidRegionInChunks.setLowerX(this->m_regValidRegion.getLowerX() >> m_uChunkSideLengthPower);
|
||||
m_regValidRegionInChunks.setLowerY(this->m_regValidRegion.getLowerY() >> m_uChunkSideLengthPower);
|
||||
m_regValidRegionInChunks.setLowerZ(this->m_regValidRegion.getLowerZ() >> m_uChunkSideLengthPower);
|
||||
m_regValidRegionInChunks.setUpperX(this->m_regValidRegion.getUpperX() >> m_uChunkSideLengthPower);
|
||||
m_regValidRegionInChunks.setUpperY(this->m_regValidRegion.getUpperY() >> m_uChunkSideLengthPower);
|
||||
m_regValidRegionInChunks.setUpperZ(this->m_regValidRegion.getUpperZ() >> m_uChunkSideLengthPower);
|
||||
|
||||
//setMaxNumberOfChunks(m_uChunkCountLimit);
|
||||
|
||||
//Clear the previous data
|
||||
m_pRecentlyUsedChunks.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<float>(this->getWidth() * this->getWidth() + this->getHeight() * this->getHeight() + this->getDepth() * this->getDepth()));
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
std::shared_ptr<typename PagedVolume<VoxelType>::Chunk> PagedVolume<VoxelType>::getChunk(int32_t uChunkX, int32_t uChunkY, int32_t uChunkZ) const
|
||||
{
|
||||
Vector3DInt32 v3dChunkPos(uChunkX, uChunkY, uChunkZ);
|
||||
|
||||
//Check if we have the same chunk 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((v3dChunkPos == m_v3dLastAccessedChunkPos) && (m_pLastAccessedChunk != 0))
|
||||
{
|
||||
return m_pLastAccessedChunk;
|
||||
}
|
||||
|
||||
// The chunk was not the same as last time, but we can now hope it is in the set of most recently used chunks.
|
||||
std::shared_ptr<typename PagedVolume<VoxelType>::Chunk> pChunk = nullptr;
|
||||
typename SharedPtrChunkMap::iterator itChunk = m_pRecentlyUsedChunks.find(v3dChunkPos);
|
||||
|
||||
// Check whether the chunk was found.
|
||||
if ((itChunk) != m_pRecentlyUsedChunks.end())
|
||||
{
|
||||
// The chunk was found so we can use it.
|
||||
pChunk = itChunk->second;
|
||||
POLYVOX_ASSERT(pChunk, "Recent chunk list shold never contain a null pointer.");
|
||||
}
|
||||
|
||||
if (!pChunk)
|
||||
{
|
||||
// Although it's not in our recently use chunks, there's some (slim) chance that it
|
||||
// exists in the list of all loaded chunks, because a sampler may be holding on to it.
|
||||
typename WeakPtrChunkMap::iterator itWeakChunk = m_pAllChunks.find(v3dChunkPos);
|
||||
if (itWeakChunk != m_pAllChunks.end())
|
||||
{
|
||||
// We've found an entry in the 'all chunks' 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 (itWeakChunk->second.expired())
|
||||
{
|
||||
m_pAllChunks.erase(itWeakChunk);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The chunk is valid. We know it's not in the recently used list (we checked earlier) so it should be added.
|
||||
pChunk = std::shared_ptr< PagedVolume<VoxelType>::Chunk >(itWeakChunk->second);
|
||||
m_pRecentlyUsedChunks.insert(std::make_pair(v3dChunkPos, pChunk));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we still haven't found the chunk then it's time to create a new one and page it in from disk.
|
||||
if (!pChunk)
|
||||
{
|
||||
// The chunk was not found so we will create a new one.
|
||||
pChunk = std::make_shared< PagedVolume<VoxelType>::Chunk >(v3dChunkPos, m_uChunkSideLength, m_pPager);
|
||||
|
||||
// As we are loading a new chunk we should try to ensure we don't go over our target memory usage.
|
||||
bool erasedChunk = false;
|
||||
while (m_pRecentlyUsedChunks.size() + 1 > m_uChunkCountLimit) // +1 ready for new chunk we will add next.
|
||||
{
|
||||
// This should never hit, because it should not have been possible for
|
||||
// the user to limit the number of chunks if they did not provide a pager.
|
||||
POLYVOX_ASSERT(m_pPager, "A valid pager is required to limit number of chunks");
|
||||
|
||||
// Find the least recently used chunk. Hopefully this isn't too slow.
|
||||
typename SharedPtrChunkMap::iterator itUnloadChunk = m_pRecentlyUsedChunks.begin();
|
||||
for (typename SharedPtrChunkMap::iterator i = m_pRecentlyUsedChunks.begin(); i != m_pRecentlyUsedChunks.end(); i++)
|
||||
{
|
||||
if (i->second->m_uChunkLastAccessed < itUnloadChunk->second->m_uChunkLastAccessed)
|
||||
{
|
||||
itUnloadChunk = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Erase the least recently used chunk
|
||||
m_pRecentlyUsedChunks.erase(itUnloadChunk);
|
||||
erasedChunk = true;
|
||||
}
|
||||
|
||||
// If we've deleted any chunks from the recently used list then this
|
||||
// seems like a good place to purge the 'all chunks' list as well.
|
||||
if (erasedChunk)
|
||||
{
|
||||
purgeNullPtrsFromAllChunks();
|
||||
}
|
||||
|
||||
// Add our new chunk to the maps.
|
||||
m_pAllChunks.insert(std::make_pair(v3dChunkPos, pChunk));
|
||||
m_pRecentlyUsedChunks.insert(std::make_pair(v3dChunkPos, pChunk));
|
||||
}
|
||||
|
||||
pChunk->m_uChunkLastAccessed = ++m_uTimestamper;
|
||||
m_pLastAccessedChunk = pChunk;
|
||||
m_v3dLastAccessedChunkPos = v3dChunkPos;
|
||||
|
||||
return pChunk;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// Calculate the memory usage of the volume.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename VoxelType>
|
||||
uint32_t PagedVolume<VoxelType>::calculateSizeInBytes(void)
|
||||
{
|
||||
// Purge null chunks so we know that all chunks are used.
|
||||
purgeNullPtrsFromAllChunks();
|
||||
|
||||
// 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 PagedVolume<VoxelType>::Chunk::calculateSizeInBytes(m_uChunkSideLength) * m_pAllChunks.size();
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
void PagedVolume<VoxelType>::purgeNullPtrsFromAllChunks(void) const
|
||||
{
|
||||
for (auto chunkIter = m_pAllChunks.begin(); chunkIter != m_pAllChunks.end();)
|
||||
{
|
||||
if (chunkIter->second.expired())
|
||||
{
|
||||
chunkIter = m_pAllChunks.erase(chunkIter);
|
||||
}
|
||||
else
|
||||
{
|
||||
chunkIter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
template <WrapMode eWrapMode>
|
||||
VoxelType PagedVolume<VoxelType>::getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<eWrapMode>, 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 <typename VoxelType>
|
||||
VoxelType PagedVolume<VoxelType>::getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<WrapModes::Validate>, 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<WrapModes::AssumeValid>(), tBorder); // No wrapping as we've just validated the position.
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
VoxelType PagedVolume<VoxelType>::getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<WrapModes::Clamp>, 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<WrapModes::AssumeValid>(), tBorder); // No wrapping as we've just validated the position.
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
VoxelType PagedVolume<VoxelType>::getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<WrapModes::Border>, VoxelType tBorder) const
|
||||
{
|
||||
if(this->m_regValidRegion.containsPoint(uXPos, uYPos, uZPos))
|
||||
{
|
||||
return getVoxelImpl(uXPos, uYPos, uZPos, WrapModeType<WrapModes::AssumeValid>(), tBorder); // No wrapping as we've just validated the position.
|
||||
}
|
||||
else
|
||||
{
|
||||
return tBorder;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
VoxelType PagedVolume<VoxelType>::getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<WrapModes::AssumeValid>, VoxelType /*tBorder*/) const
|
||||
{
|
||||
const int32_t chunkX = uXPos >> m_uChunkSideLengthPower;
|
||||
const int32_t chunkY = uYPos >> m_uChunkSideLengthPower;
|
||||
const int32_t chunkZ = uZPos >> m_uChunkSideLengthPower;
|
||||
|
||||
const uint16_t xOffset = static_cast<uint16_t>(uXPos - (chunkX << m_uChunkSideLengthPower));
|
||||
const uint16_t yOffset = static_cast<uint16_t>(uYPos - (chunkY << m_uChunkSideLengthPower));
|
||||
const uint16_t zOffset = static_cast<uint16_t>(uZPos - (chunkZ << m_uChunkSideLengthPower));
|
||||
|
||||
auto pChunk = getChunk(chunkX, chunkY, chunkZ);
|
||||
return pChunk->getVoxel(xOffset, yOffset, zOffset);
|
||||
}
|
||||
}
|
||||
|
@ -26,10 +26,14 @@ freely, subject to the following restrictions:
|
||||
namespace PolyVox
|
||||
{
|
||||
template <typename VoxelType>
|
||||
UncompressedBlock<VoxelType>::UncompressedBlock(uint16_t uSideLength)
|
||||
:m_tData(0)
|
||||
PagedVolume<VoxelType>::Chunk::Chunk(Vector3DInt32 v3dPosition, uint16_t uSideLength, Pager* pPager)
|
||||
:m_uChunkLastAccessed(0)
|
||||
,m_bDataModified(true)
|
||||
,m_tData(0)
|
||||
,m_uSideLength(0)
|
||||
,m_uSideLengthPower(0)
|
||||
,m_pPager(pPager)
|
||||
,m_v3dChunkSpacePosition(v3dPosition)
|
||||
{
|
||||
// Compute the side length
|
||||
m_uSideLength = uSideLength;
|
||||
@ -37,37 +41,67 @@ namespace PolyVox
|
||||
|
||||
// Allocate the data
|
||||
const uint32_t uNoOfVoxels = m_uSideLength * m_uSideLength * m_uSideLength;
|
||||
m_tData = new VoxelType[uNoOfVoxels];
|
||||
m_tData = new VoxelType[uNoOfVoxels];
|
||||
|
||||
// Pass the chunk to the Pager to give it a chance to initialise it with any data
|
||||
if (m_pPager)
|
||||
{
|
||||
// From the coordinates of the chunk we deduce the coordinates of the contained voxels.
|
||||
Vector3DInt32 v3dLower = m_v3dChunkSpacePosition * static_cast<int32_t>(m_uSideLength);
|
||||
Vector3DInt32 v3dUpper = v3dLower + Vector3DInt32(m_uSideLength - 1, m_uSideLength - 1, m_uSideLength - 1);
|
||||
Region reg(v3dLower, v3dUpper);
|
||||
|
||||
// Page the data in
|
||||
m_pPager->pageIn(reg, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just fill with zeros
|
||||
std::fill(m_tData, m_tData + uNoOfVoxels, VoxelType());
|
||||
}
|
||||
|
||||
// We'll use this later to decide if data needs to be paged out again.
|
||||
m_bDataModified = false;
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
UncompressedBlock<VoxelType>::~UncompressedBlock()
|
||||
PagedVolume<VoxelType>::Chunk::~Chunk()
|
||||
{
|
||||
if (m_pPager && m_bDataModified)
|
||||
{
|
||||
// From the coordinates of the chunk we deduce the coordinates of the contained voxels.
|
||||
Vector3DInt32 v3dLower = m_v3dChunkSpacePosition * static_cast<int32_t>(m_uSideLength);
|
||||
Vector3DInt32 v3dUpper = v3dLower + Vector3DInt32(m_uSideLength - 1, m_uSideLength - 1, m_uSideLength - 1);
|
||||
|
||||
// Page the data out
|
||||
m_pPager->pageOut(Region(v3dLower, v3dUpper), this);
|
||||
}
|
||||
|
||||
delete[] m_tData;
|
||||
m_tData = 0;
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
VoxelType* UncompressedBlock<VoxelType>::getData(void) const
|
||||
VoxelType* PagedVolume<VoxelType>::Chunk::getData(void) const
|
||||
{
|
||||
return m_tData;
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
uint32_t UncompressedBlock<VoxelType>::getDataSizeInBytes(void) const
|
||||
uint32_t PagedVolume<VoxelType>::Chunk::getDataSizeInBytes(void) const
|
||||
{
|
||||
return m_uSideLength * m_uSideLength * m_uSideLength * sizeof(VoxelType);
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
VoxelType UncompressedBlock<VoxelType>::getVoxel(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos) const
|
||||
VoxelType PagedVolume<VoxelType>::Chunk::getVoxel(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos) const
|
||||
{
|
||||
// This code is not usually expected to be called by the user, with the exception of when implementing paging
|
||||
// of uncompressed data. It's a performance critical code path so we use asserts rather than exceptions.
|
||||
POLYVOX_ASSERT(uXPos < m_uSideLength, "Supplied position is outside of the block");
|
||||
POLYVOX_ASSERT(uYPos < m_uSideLength, "Supplied position is outside of the block");
|
||||
POLYVOX_ASSERT(uZPos < m_uSideLength, "Supplied position is outside of the block");
|
||||
POLYVOX_ASSERT(m_tData, "No uncompressed data - block must be decompressed before accessing voxels.");
|
||||
POLYVOX_ASSERT(uXPos < m_uSideLength, "Supplied position is outside of the chunk");
|
||||
POLYVOX_ASSERT(uYPos < m_uSideLength, "Supplied position is outside of the chunk");
|
||||
POLYVOX_ASSERT(uZPos < m_uSideLength, "Supplied position is outside of the chunk");
|
||||
POLYVOX_ASSERT(m_tData, "No uncompressed data - chunk must be decompressed before accessing voxels.");
|
||||
|
||||
return m_tData
|
||||
[
|
||||
@ -78,20 +112,20 @@ namespace PolyVox
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
VoxelType UncompressedBlock<VoxelType>::getVoxel(const Vector3DUint16& v3dPos) const
|
||||
VoxelType PagedVolume<VoxelType>::Chunk::getVoxel(const Vector3DUint16& v3dPos) const
|
||||
{
|
||||
return getVoxel(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ());
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
void UncompressedBlock<VoxelType>::setVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos, VoxelType tValue)
|
||||
void PagedVolume<VoxelType>::Chunk::setVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos, VoxelType tValue)
|
||||
{
|
||||
// This code is not usually expected to be called by the user, with the exception of when implementing paging
|
||||
// of uncompressed data. It's a performance critical code path so we use asserts rather than exceptions.
|
||||
POLYVOX_ASSERT(uXPos < m_uSideLength, "Supplied position is outside of the block");
|
||||
POLYVOX_ASSERT(uYPos < m_uSideLength, "Supplied position is outside of the block");
|
||||
POLYVOX_ASSERT(uZPos < m_uSideLength, "Supplied position is outside of the block");
|
||||
POLYVOX_ASSERT(m_tData, "No uncompressed data - block must be decompressed before accessing voxels.");
|
||||
POLYVOX_ASSERT(uXPos < m_uSideLength, "Supplied position is outside of the chunk");
|
||||
POLYVOX_ASSERT(uYPos < m_uSideLength, "Supplied position is outside of the chunk");
|
||||
POLYVOX_ASSERT(uZPos < m_uSideLength, "Supplied position is outside of the chunk");
|
||||
POLYVOX_ASSERT(m_tData, "No uncompressed data - chunk must be decompressed before accessing voxels.");
|
||||
|
||||
m_tData
|
||||
[
|
||||
@ -104,16 +138,24 @@ namespace PolyVox
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
void UncompressedBlock<VoxelType>::setVoxelAt(const Vector3DUint16& v3dPos, VoxelType tValue)
|
||||
void PagedVolume<VoxelType>::Chunk::setVoxelAt(const Vector3DUint16& v3dPos, VoxelType tValue)
|
||||
{
|
||||
setVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ(), tValue);
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
uint32_t UncompressedBlock<VoxelType>::calculateSizeInBytes(void)
|
||||
uint32_t PagedVolume<VoxelType>::Chunk::calculateSizeInBytes(void)
|
||||
{
|
||||
// Returns the size of this class plus the size of the uncompressed data.
|
||||
uint32_t uSizeInBytes = sizeof(UncompressedBlock<VoxelType>) + (m_uSideLength * m_uSideLength * m_uSideLength * sizeof(VoxelType));
|
||||
// Call through to the static version
|
||||
return calculateSizeInBytes(m_uSideLength);
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
uint32_t PagedVolume<VoxelType>::Chunk::calculateSizeInBytes(uint32_t uSideLength)
|
||||
{
|
||||
// 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.
|
||||
uint32_t uSizeInBytes = uSideLength * uSideLength * uSideLength * sizeof(VoxelType);
|
||||
return uSizeInBytes;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,49 +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.
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef __PolyVox_Pager_H__
|
||||
#define __PolyVox_Pager_H__
|
||||
|
||||
#include "PolyVoxCore/CompressedBlock.h"
|
||||
#include "PolyVoxCore/Impl/TypeDef.h"
|
||||
|
||||
namespace PolyVox
|
||||
{
|
||||
/**
|
||||
* Provides an interface for performing paging of data.
|
||||
*/
|
||||
template <typename VoxelType>
|
||||
class Pager
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
Pager() {};
|
||||
/// Destructor
|
||||
virtual ~Pager() {};
|
||||
|
||||
virtual void pageIn(const Region& region, CompressedBlock<VoxelType>* pBlockData) = 0;
|
||||
virtual void pageOut(const Region& region, CompressedBlock<VoxelType>* pBlockData) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //__PolyVox_Pager_H__
|
@ -62,11 +62,6 @@ namespace PolyVox
|
||||
typedef Array<3,int32_t> Array3DInt32;
|
||||
typedef Array<3,uint32_t> Array3DUint32;*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// BlockCompressor
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename VoxelType> class BlockCompressor;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Compressor
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -103,11 +98,6 @@ namespace PolyVox
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename VoxelType> class FilePager;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// LargeVolume
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename VoxelType> class LargeVolume;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// MarchingCubesSurfaceExtractor
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -145,6 +135,15 @@ namespace PolyVox
|
||||
typedef uint32_t DefaultIndexType;
|
||||
template <typename VertexType, typename IndexType = DefaultIndexType> class Mesh;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// PagedVolume
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename VoxelType> class PagedVolume;
|
||||
template <typename VoxelType>
|
||||
using LargeVolume = PagedVolume<VoxelType>;
|
||||
template <typename VoxelType>
|
||||
using SimpleVolume = PagedVolume<VoxelType>;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Pager
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -160,11 +159,6 @@ namespace PolyVox
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
class Region;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SimpleVolume
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename VoxelType> class SimpleVolume;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Vector
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1,59 +0,0 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2005-2013 David Williams and Matthew 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_RLEBlockCompressor_H__
|
||||
#define __PolyVox_RLEBlockCompressor_H__
|
||||
|
||||
#include "PolyVoxCore/BlockCompressor.h"
|
||||
|
||||
namespace PolyVox
|
||||
{
|
||||
template <typename VoxelType>
|
||||
class Run
|
||||
{
|
||||
public:
|
||||
typedef uint16_t LengthType;
|
||||
VoxelType value;
|
||||
LengthType length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Provides an interface for performing paging of data.
|
||||
*/
|
||||
template <typename VoxelType>
|
||||
class RLEBlockCompressor : public BlockCompressor<VoxelType>
|
||||
{
|
||||
|
||||
|
||||
public:
|
||||
RLEBlockCompressor();
|
||||
~RLEBlockCompressor();
|
||||
|
||||
void compress(UncompressedBlock<VoxelType>* pSrcBlock, CompressedBlock<VoxelType>* pDstBlock);
|
||||
void decompress(CompressedBlock<VoxelType>* pSrcBlock, UncompressedBlock<VoxelType>* pDstBlock);
|
||||
};
|
||||
}
|
||||
|
||||
#include "PolyVoxCore/RLEBlockCompressor.inl"
|
||||
|
||||
#endif //__PolyVox_RLEBlockCompressor_H__
|
@ -1,167 +0,0 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2005-2013 David Williams and Matthew 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 <vector>
|
||||
|
||||
namespace PolyVox
|
||||
{
|
||||
template <typename VoxelType>
|
||||
RLEBlockCompressor<VoxelType>::RLEBlockCompressor()
|
||||
{
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
RLEBlockCompressor<VoxelType>::~RLEBlockCompressor()
|
||||
{
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
void RLEBlockCompressor<VoxelType>::compress(UncompressedBlock<VoxelType>* pSrcBlock, CompressedBlock<VoxelType>* pDstBlock)
|
||||
{
|
||||
void* pSrcData = reinterpret_cast<void*>(pSrcBlock->getData());
|
||||
uint32_t uSrcLength = pSrcBlock->getDataSizeInBytes();
|
||||
|
||||
if(uSrcLength % sizeof(VoxelType) != 0)
|
||||
{
|
||||
POLYVOX_THROW(std::length_error, "Source length must be a integer multiple of the ValueType size");
|
||||
}
|
||||
|
||||
// Lengths provided are in bytes, so convert them to be in terms of our types.
|
||||
uSrcLength /= sizeof(VoxelType);
|
||||
//uDstLength /= sizeof(Run);
|
||||
|
||||
// Get data pointers in the appropriate type
|
||||
const VoxelType* pSrcDataAsType = reinterpret_cast<const VoxelType*>(pSrcData);
|
||||
//Run* pDstDataAsRun = reinterpret_cast<Run*>(pDstData);
|
||||
std::vector< Run<VoxelType> > vecDstDataAsRuns;
|
||||
|
||||
// Pointers to just past the end of the data
|
||||
const VoxelType* pSrcDataEnd = pSrcDataAsType + uSrcLength;
|
||||
//Run* pDstDataEnd = pDstDataAsRun + uDstLength;
|
||||
|
||||
//Counter for the output length
|
||||
//uint32_t uDstLengthInBytes = 0;
|
||||
|
||||
// Read the first element of the source and set up the first run based on it.
|
||||
/*pDstDataAsRun->value = *pSrcDataAsType;
|
||||
pSrcDataAsType++;
|
||||
pDstDataAsRun->length = 1;
|
||||
uDstLengthInBytes += sizeof(Run);*/
|
||||
Run<VoxelType> firstRun;
|
||||
firstRun.value = *pSrcDataAsType;
|
||||
firstRun.length = 1;
|
||||
vecDstDataAsRuns.push_back(firstRun);
|
||||
|
||||
pSrcDataAsType++;
|
||||
|
||||
//Now process all remaining elements of the source.
|
||||
while(pSrcDataAsType < pSrcDataEnd)
|
||||
{
|
||||
// If the value is the same as the current run (and we have not
|
||||
// reached the maximum run length) then extend the current run.
|
||||
typename std::vector< Run< VoxelType> >::iterator currentRun = (vecDstDataAsRuns.end() - 1);
|
||||
if((*pSrcDataAsType == currentRun->value) && (currentRun->length < (std::numeric_limits<typename Run<VoxelType>::LengthType>::max)()))
|
||||
{
|
||||
currentRun->length++;
|
||||
}
|
||||
// Otherwise we need to start a new Run.
|
||||
else
|
||||
{
|
||||
/*pDstDataAsRun++;
|
||||
|
||||
// Check if we have enough space in the destination buffer.
|
||||
if(pDstDataAsRun >= pDstDataEnd)
|
||||
{
|
||||
POLYVOX_THROW(std::runtime_error, "Insufficient space in destination buffer.");
|
||||
}*/
|
||||
|
||||
// Create the new run.
|
||||
/*pDstDataAsRun->value = *pSrcDataAsType;
|
||||
pDstDataAsRun->length = 1;
|
||||
uDstLengthInBytes += sizeof(Run);*/
|
||||
|
||||
Run<VoxelType> newRun;
|
||||
newRun.value = *pSrcDataAsType;
|
||||
newRun.length = 1;
|
||||
vecDstDataAsRuns.push_back(newRun);
|
||||
}
|
||||
|
||||
pSrcDataAsType++;
|
||||
}
|
||||
|
||||
//Now copy the data into the compressed block.
|
||||
|
||||
pDstBlock->setData(reinterpret_cast<uint8_t*>(&(vecDstDataAsRuns[0])), vecDstDataAsRuns.size() * sizeof(Run<VoxelType>));
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
void RLEBlockCompressor<VoxelType>::decompress(CompressedBlock<VoxelType>* pSrcBlock, UncompressedBlock<VoxelType>* pDstBlock)
|
||||
{
|
||||
const void* pSrcData = reinterpret_cast<const void*>(pSrcBlock->getData());
|
||||
uint32_t uSrcLength = pSrcBlock->getDataSizeInBytes();
|
||||
|
||||
void* pDstData = pDstBlock->getData();
|
||||
uint32_t uDstLength = pDstBlock->getDataSizeInBytes();
|
||||
|
||||
if(uSrcLength % sizeof(Run<VoxelType>) != 0)
|
||||
{
|
||||
POLYVOX_THROW(std::length_error, "Source length must be a integer multiple of the Run size");
|
||||
}
|
||||
|
||||
// Lengths provided are in bytes, so convert them to be in terms of our types.
|
||||
uSrcLength /= sizeof(Run<VoxelType>);
|
||||
uDstLength /= sizeof(VoxelType);
|
||||
|
||||
// Get data pointers in the appropriate type
|
||||
const Run<VoxelType>* pSrcDataAsRun = reinterpret_cast<const Run<VoxelType>*>(pSrcData);
|
||||
VoxelType* pDstDataAsType = reinterpret_cast<VoxelType*>(pDstData);
|
||||
|
||||
// Pointers to just past the end of the data
|
||||
const Run<VoxelType>* pSrcDataEnd = pSrcDataAsRun + uSrcLength;
|
||||
VoxelType* pDstDataEnd = pDstDataAsType + uDstLength;
|
||||
|
||||
//Counter for the output length
|
||||
uint32_t uDstLengthInBytes = 0;
|
||||
|
||||
while(pSrcDataAsRun < pSrcDataEnd)
|
||||
{
|
||||
// Check if we have enough space in the destination buffer.
|
||||
if(pDstDataAsType + pSrcDataAsRun->length > pDstDataEnd)
|
||||
{
|
||||
POLYVOX_THROW(std::runtime_error, "Insufficient space in destination buffer.");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// Write the run into the destination
|
||||
std::fill(pDstDataAsType, pDstDataAsType + pSrcDataAsRun->length, pSrcDataAsRun->value);
|
||||
pDstDataAsType += pSrcDataAsRun->length;
|
||||
|
||||
uDstLengthInBytes += pSrcDataAsRun->length * sizeof(VoxelType);
|
||||
}
|
||||
pSrcDataAsRun++;
|
||||
}
|
||||
|
||||
POLYVOX_ASSERT(uDstLengthInBytes == pDstBlock->getDataSizeInBytes(), "Uncompressed data does not have the correct length.");
|
||||
}
|
||||
}
|
@ -162,7 +162,7 @@ namespace PolyVox
|
||||
VoxelType getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<WrapModes::Border>, VoxelType tBorder) const;
|
||||
VoxelType getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<WrapModes::AssumeValid>, VoxelType tBorder) const;
|
||||
|
||||
//The block data
|
||||
//The voxel data
|
||||
VoxelType* m_pData;
|
||||
};
|
||||
}
|
||||
|
@ -1,229 +1,9 @@
|
||||
/*******************************************************************************
|
||||
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"
|
||||
#pragma message("WARNING - The SimpleVolume class has been replaced by PagedVolume. Please use that instead.")
|
||||
|
||||
#include "PolyVoxCore/BaseVolume.h"
|
||||
#include "PolyVoxCore/Region.h"
|
||||
#include "PolyVoxCore/Vector.h"
|
||||
#include "PagedVolume.h"
|
||||
#include "PolyVoxForwardDeclarations.h"
|
||||
|
||||
#include <cstdlib> //For abort()
|
||||
#include <cstring> //For memcpy
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <stdexcept> //For invalid_argument
|
||||
|
||||
namespace PolyVox
|
||||
{
|
||||
template <typename VoxelType>
|
||||
class SimpleVolume : public BaseVolume<VoxelType>
|
||||
{
|
||||
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<VoxelType> VolumeOfVoxelType; //Workaround for GCC/VS2010 differences.
|
||||
//class Sampler : public VolumeOfVoxelType::template Sampler< SimpleVolume<VoxelType> >
|
||||
#if defined(_MSC_VER)
|
||||
class Sampler : public BaseVolume<VoxelType>::Sampler< SimpleVolume<VoxelType> > //This line works on VS2010
|
||||
#else
|
||||
class Sampler : public BaseVolume<VoxelType>::template Sampler< SimpleVolume<VoxelType> > //This line works on GCC
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
/// Construct a new Sampler
|
||||
Sampler(SimpleVolume<VoxelType>* 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 <tt>x,y,z</tt> coordinates
|
||||
template <WrapMode eWrapMode>
|
||||
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 <WrapMode eWrapMode>
|
||||
VoxelType getVoxel(const Vector3DInt32& v3dPos, VoxelType tBorder = VoxelType()) const;
|
||||
|
||||
/// Gets a voxel at the position given by <tt>x,y,z</tt> 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 <tt>x,y,z</tt> 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 <tt>x,y,z</tt> 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 <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);
|
||||
|
||||
/// 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 <WrapMode eWrapMode>
|
||||
VoxelType getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<eWrapMode>, VoxelType tBorder) const;
|
||||
VoxelType getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<WrapModes::Validate>, VoxelType tBorder) const;
|
||||
VoxelType getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<WrapModes::Clamp>, VoxelType tBorder) const;
|
||||
VoxelType getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<WrapModes::Border>, VoxelType tBorder) const;
|
||||
VoxelType getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<WrapModes::AssumeValid>, 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__
|
@ -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 <typename VoxelType>
|
||||
SimpleVolume<VoxelType>::SimpleVolume(const Region& regValid, uint16_t uBlockSideLength)
|
||||
:BaseVolume<VoxelType>(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 <typename VoxelType>
|
||||
SimpleVolume<VoxelType>::SimpleVolume(const SimpleVolume<VoxelType>& /*rhs*/)
|
||||
{
|
||||
POLYVOX_THROW(not_implemented, "Volume copy constructor not implemented for performance reasons.");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// Destroys the volume
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename VoxelType>
|
||||
SimpleVolume<VoxelType>::~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 <typename VoxelType>
|
||||
SimpleVolume<VoxelType>& SimpleVolume<VoxelType>::operator=(const SimpleVolume<VoxelType>& /*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 <typename VoxelType>
|
||||
template <WrapMode eWrapMode>
|
||||
VoxelType SimpleVolume<VoxelType>::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<eWrapMode>(), 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 <typename VoxelType>
|
||||
template <WrapMode eWrapMode>
|
||||
VoxelType SimpleVolume<VoxelType>::getVoxel(const Vector3DInt32& v3dPos, VoxelType tBorder) const
|
||||
{
|
||||
// Simply call through to the real implementation
|
||||
return getVoxel<eWrapMode>(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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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<WrapModes::Validate>(), tBorder);
|
||||
case WrapModes::Clamp:
|
||||
return getVoxelImpl(uXPos, uYPos, uZPos, WrapModeType<WrapModes::Clamp>(), tBorder);
|
||||
case WrapModes::Border:
|
||||
return getVoxelImpl(uXPos, uYPos, uZPos, WrapModeType<WrapModes::Border>(), tBorder);
|
||||
case WrapModes::AssumeValid:
|
||||
return getVoxelImpl(uXPos, uYPos, uZPos, WrapModeType<WrapModes::AssumeValid>(), 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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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<uint16_t>(uXPos - (blockX << m_uBlockSideLengthPower));
|
||||
const uint16_t yOffset = static_cast<uint16_t>(uYPos - (blockY << m_uBlockSideLengthPower));
|
||||
const uint16_t zOffset = static_cast<uint16_t>(uZPos - (blockZ << m_uBlockSideLengthPower));
|
||||
|
||||
typename SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
void SimpleVolume<VoxelType>::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<VoxelType>::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 <typename VoxelType>
|
||||
void SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
bool SimpleVolume<VoxelType>::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<VoxelType>::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 <typename VoxelType>
|
||||
bool SimpleVolume<VoxelType>::setVoxelAt(const Vector3DInt32& v3dPos, VoxelType tValue)
|
||||
{
|
||||
return setVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ(), tValue);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// This function should probably be made internal...
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename VoxelType>
|
||||
void SimpleVolume<VoxelType>::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<float>(this->getWidth() * this->getWidth() + this->getHeight() * this->getHeight() + this->getDepth() * this->getDepth()));
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
typename SimpleVolume<VoxelType>::Block* SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
uint32_t SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
template <WrapMode eWrapMode>
|
||||
VoxelType SimpleVolume<VoxelType>::getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<eWrapMode>, 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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<WrapModes::Validate>, 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<WrapModes::AssumeValid>(), tBorder); // No wrapping as we've just validated the position.
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<WrapModes::Clamp>, 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<WrapModes::AssumeValid>(), tBorder); // No wrapping as we've just validated the position.
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<WrapModes::Border>, VoxelType tBorder) const
|
||||
{
|
||||
if(this->m_regValidRegion.containsPoint(uXPos, uYPos, uZPos))
|
||||
{
|
||||
return getVoxelImpl(uXPos, uYPos, uZPos, WrapModeType<WrapModes::AssumeValid>(), tBorder); // No wrapping as we've just validated the position.
|
||||
}
|
||||
else
|
||||
{
|
||||
return tBorder;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::getVoxelImpl(int32_t uXPos, int32_t uYPos, int32_t uZPos, WrapModeType<WrapModes::AssumeValid>, 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<uint16_t>(uXPos - (blockX << m_uBlockSideLengthPower));
|
||||
const uint16_t yOffset = static_cast<uint16_t>(uYPos - (blockY << m_uBlockSideLengthPower));
|
||||
const uint16_t zOffset = static_cast<uint16_t>(uZPos - (blockZ << m_uBlockSideLengthPower));
|
||||
|
||||
typename SimpleVolume<VoxelType>::Block* pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ);
|
||||
|
||||
return pUncompressedBlock->getVoxelAt(xOffset,yOffset,zOffset);
|
||||
}
|
||||
}
|
||||
|
@ -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 <typename VoxelType>
|
||||
SimpleVolume<VoxelType>::Block::Block(uint16_t uSideLength)
|
||||
:m_tUncompressedData(0)
|
||||
,m_uSideLength(0)
|
||||
,m_uSideLengthPower(0)
|
||||
{
|
||||
if(uSideLength != 0)
|
||||
{
|
||||
initialise(uSideLength);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
SimpleVolume<VoxelType>::Block::~Block()
|
||||
{
|
||||
delete[] m_tUncompressedData;
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
uint16_t SimpleVolume<VoxelType>::Block::getSideLength(void) const
|
||||
{
|
||||
return m_uSideLength;
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::Block::getVoxelAt(const Vector3DUint16& v3dPos) const
|
||||
{
|
||||
return getVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ());
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
void SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
void SimpleVolume<VoxelType>::Block::setVoxelAt(const Vector3DUint16& v3dPos, VoxelType tValue)
|
||||
{
|
||||
setVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ(), tValue);
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
void SimpleVolume<VoxelType>::Block::fill(VoxelType tValue)
|
||||
{
|
||||
const uint32_t uNoOfVoxels = m_uSideLength * m_uSideLength * m_uSideLength;
|
||||
std::fill(m_tUncompressedData, m_tUncompressedData + uNoOfVoxels, tValue);
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
void SimpleVolume<VoxelType>::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<VoxelType>::Block::fill(VoxelType());
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
uint32_t SimpleVolume<VoxelType>::Block::calculateSizeInBytes(void)
|
||||
{
|
||||
uint32_t uSizeInBytes = sizeof(Block);
|
||||
uSizeInBytes += sizeof(VoxelType) * m_uSideLength * m_uSideLength * m_uSideLength;
|
||||
return uSizeInBytes;
|
||||
}
|
||||
}
|
@ -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 <typename VoxelType>
|
||||
SimpleVolume<VoxelType>::Sampler::Sampler(SimpleVolume<VoxelType>* volume)
|
||||
:BaseVolume<VoxelType>::template Sampler< SimpleVolume<VoxelType> >(volume)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
SimpleVolume<VoxelType>::Sampler::~Sampler()
|
||||
{
|
||||
}
|
||||
|
||||
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, this->mVolume->getVoxelAt(this->mXPosInVolume + x, this->mYPosInVolume + y, this->mZPosInVolume + z));
|
||||
}
|
||||
}
|
||||
}
|
||||
return tValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \return The current voxel
|
||||
*/
|
||||
template <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
void SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
void SimpleVolume<VoxelType>::Sampler::setPosition(int32_t xPos, int32_t yPos, int32_t zPos)
|
||||
{
|
||||
// Base version updates position and validity flags.
|
||||
BaseVolume<VoxelType>::template Sampler< SimpleVolume<VoxelType> >::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<uint16_t>(this->mXPosInVolume - (uXBlock << this->mVolume->m_uBlockSideLengthPower));
|
||||
const uint16_t uYPosInBlock = static_cast<uint16_t>(this->mYPosInVolume - (uYBlock << this->mVolume->m_uBlockSideLengthPower));
|
||||
const uint16_t uZPosInBlock = static_cast<uint16_t>(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 <typename VoxelType>
|
||||
bool SimpleVolume<VoxelType>::Sampler::setVoxel(VoxelType tValue)
|
||||
{
|
||||
if(this->m_bIsCurrentPositionValidInX && this->m_bIsCurrentPositionValidInY && this->m_bIsCurrentPositionValidInZ)
|
||||
{
|
||||
*mCurrentVoxel = tValue;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
void SimpleVolume<VoxelType>::Sampler::movePositiveX(void)
|
||||
{
|
||||
// We'll need this in a moment...
|
||||
bool bIsOldPositionValid = this->isCurrentPositionValid();
|
||||
|
||||
// Base version updates position and validity flags.
|
||||
BaseVolume<VoxelType>::template Sampler< SimpleVolume<VoxelType> >::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 <typename VoxelType>
|
||||
void SimpleVolume<VoxelType>::Sampler::movePositiveY(void)
|
||||
{
|
||||
// We'll need this in a moment...
|
||||
bool bIsOldPositionValid = this->isCurrentPositionValid();
|
||||
|
||||
// Base version updates position and validity flags.
|
||||
BaseVolume<VoxelType>::template Sampler< SimpleVolume<VoxelType> >::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 <typename VoxelType>
|
||||
void SimpleVolume<VoxelType>::Sampler::movePositiveZ(void)
|
||||
{
|
||||
// We'll need this in a moment...
|
||||
bool bIsOldPositionValid = this->isCurrentPositionValid();
|
||||
|
||||
// Base version updates position and validity flags.
|
||||
BaseVolume<VoxelType>::template Sampler< SimpleVolume<VoxelType> >::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 <typename VoxelType>
|
||||
void SimpleVolume<VoxelType>::Sampler::moveNegativeX(void)
|
||||
{
|
||||
// We'll need this in a moment...
|
||||
bool bIsOldPositionValid = this->isCurrentPositionValid();
|
||||
|
||||
// Base version updates position and validity flags.
|
||||
BaseVolume<VoxelType>::template Sampler< SimpleVolume<VoxelType> >::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 <typename VoxelType>
|
||||
void SimpleVolume<VoxelType>::Sampler::moveNegativeY(void)
|
||||
{
|
||||
// We'll need this in a moment...
|
||||
bool bIsOldPositionValid = this->isCurrentPositionValid();
|
||||
|
||||
// Base version updates position and validity flags.
|
||||
BaseVolume<VoxelType>::template Sampler< SimpleVolume<VoxelType> >::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 <typename VoxelType>
|
||||
void SimpleVolume<VoxelType>::Sampler::moveNegativeZ(void)
|
||||
{
|
||||
// We'll need this in a moment...
|
||||
bool bIsOldPositionValid = this->isCurrentPositionValid();
|
||||
|
||||
// Base version updates position and validity flags.
|
||||
BaseVolume<VoxelType>::template Sampler< SimpleVolume<VoxelType> >::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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 <typename VoxelType>
|
||||
VoxelType SimpleVolume<VoxelType>::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
|
@ -1,68 +0,0 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2005-2013 David Williams and Matthew 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_UncompressedBlock_H__
|
||||
#define __PolyVox_UncompressedBlock_H__
|
||||
|
||||
#include "PolyVoxCore/Block.h"
|
||||
|
||||
namespace PolyVox
|
||||
{
|
||||
template <typename VoxelType>
|
||||
class UncompressedBlock : public Block<VoxelType>
|
||||
{
|
||||
friend class LargeVolume<VoxelType>;
|
||||
|
||||
public:
|
||||
UncompressedBlock(uint16_t uSideLength);
|
||||
~UncompressedBlock();
|
||||
|
||||
VoxelType* getData(void) const;
|
||||
uint32_t getDataSizeInBytes(void) const;
|
||||
|
||||
VoxelType getVoxel(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos) const;
|
||||
VoxelType getVoxel(const Vector3DUint16& v3dPos) const;
|
||||
|
||||
void setVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos, VoxelType tValue);
|
||||
void setVoxelAt(const Vector3DUint16& v3dPos, VoxelType tValue);
|
||||
|
||||
private:
|
||||
/// Private copy constructor to prevent accisdental copying
|
||||
UncompressedBlock(const UncompressedBlock& /*rhs*/) {};
|
||||
|
||||
/// Private assignment operator to prevent accisdental copying
|
||||
UncompressedBlock& operator=(const UncompressedBlock& /*rhs*/) {};
|
||||
|
||||
// Made this private for consistancy with CompressedBlock.
|
||||
// Users shouldn't really need this for UncompressedBlock anyway.
|
||||
uint32_t calculateSizeInBytes(void);
|
||||
|
||||
VoxelType* m_tData;
|
||||
uint16_t m_uSideLength;
|
||||
uint8_t m_uSideLengthPower;
|
||||
};
|
||||
}
|
||||
|
||||
#include "PolyVoxCore/UncompressedBlock.inl"
|
||||
|
||||
#endif
|
@ -31,6 +31,7 @@ freely, subject to the following restrictions:
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
|
||||
namespace PolyVox
|
||||
@ -228,6 +229,19 @@ namespace PolyVox
|
||||
|
||||
}//namespace PolyVox
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <>
|
||||
struct hash<PolyVox::Vector3DInt32>
|
||||
{
|
||||
std::size_t operator()(const PolyVox::Vector3DInt32& vec) const
|
||||
{
|
||||
return ((vec.getX() & 0xFF)) | ((vec.getY() & 0xFF) << 8) | ((vec.getZ() & 0xFF) << 16);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#include "PolyVoxCore/Vector.inl"
|
||||
|
||||
#endif
|
||||
|
@ -1,29 +0,0 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2005-2013 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.
|
||||
*******************************************************************************/
|
||||
|
||||
// Please see this header file for comment about the purpose of MinizWrapper.h/cpp
|
||||
#include "PolyVoxCore/Impl/MinizWrapper.h"
|
||||
|
||||
// Get the full definitions, not just the declarations.
|
||||
#undef MINIZ_HEADER_FILE_ONLY
|
||||
#include "PolyVoxCore/Impl/miniz.c"
|
@ -24,7 +24,7 @@ freely, subject to the following restrictions:
|
||||
#include "TestAmbientOcclusionGenerator.h"
|
||||
|
||||
#include "PolyVoxCore/AmbientOcclusionCalculator.h"
|
||||
#include "PolyVoxCore/SimpleVolume.h"
|
||||
#include "PolyVoxCore/PagedVolume.h"
|
||||
|
||||
#include <QtTest>
|
||||
|
||||
@ -49,7 +49,7 @@ void TestAmbientOcclusionGenerator::testExecute()
|
||||
const int32_t g_uVolumeSideLength = 64;
|
||||
|
||||
//Create empty volume
|
||||
SimpleVolume<uint8_t> volData(Region(Vector3DInt32(0,0,0), Vector3DInt32(g_uVolumeSideLength-1, g_uVolumeSideLength-1, g_uVolumeSideLength-1)));
|
||||
PagedVolume<uint8_t> volData(Region(Vector3DInt32(0, 0, 0), Vector3DInt32(g_uVolumeSideLength - 1, g_uVolumeSideLength - 1, g_uVolumeSideLength - 1)));
|
||||
|
||||
//Create two solid walls at opposite sides of the volume
|
||||
for (int32_t z = 0; z < g_uVolumeSideLength; z++)
|
||||
|
@ -27,7 +27,7 @@ freely, subject to the following restrictions:
|
||||
#include "PolyVoxCore/Material.h"
|
||||
#include "PolyVoxCore/MaterialDensityPair.h"
|
||||
#include "PolyVoxCore/RawVolume.h"
|
||||
#include "PolyVoxCore/SimpleVolume.h"
|
||||
#include "PolyVoxCore/PagedVolume.h"
|
||||
#include "PolyVoxCore/CubicSurfaceExtractor.h"
|
||||
|
||||
#include <QtTest>
|
||||
@ -128,26 +128,26 @@ VolumeType* createAndFillVolumeRealistic(int32_t iVolumeSideLength)
|
||||
void TestCubicSurfaceExtractor::testBehaviour()
|
||||
{
|
||||
// Test with default mesh and contoller types.
|
||||
auto uint8Vol = createAndFillVolumeWithNoise< SimpleVolume<uint8_t> >(32, 0, 2);
|
||||
auto uint8Vol = createAndFillVolumeWithNoise< PagedVolume<uint8_t> >(32, 0, 2);
|
||||
auto uint8Mesh = extractCubicMesh(uint8Vol, uint8Vol->getEnclosingRegion());
|
||||
QCOMPARE(uint8Mesh.getNoOfVertices(), uint32_t(57687));
|
||||
QCOMPARE(uint8Mesh.getNoOfIndices(), uint32_t(216234));
|
||||
|
||||
// Test with default mesh type but user-provided controller.
|
||||
auto int8Vol = createAndFillVolumeWithNoise< SimpleVolume<int8_t> >(32, 0, 2);
|
||||
auto int8Vol = createAndFillVolumeWithNoise< PagedVolume<int8_t> >(32, 0, 2);
|
||||
auto int8Mesh = extractCubicMesh(int8Vol, int8Vol->getEnclosingRegion(), CustomIsQuadNeeded<int8_t>());
|
||||
QCOMPARE(int8Mesh.getNoOfVertices(), uint32_t(29027));
|
||||
QCOMPARE(int8Mesh.getNoOfIndices(), uint32_t(178356));
|
||||
|
||||
// Test with default controller but user-provided mesh.
|
||||
auto uint32Vol = createAndFillVolumeWithNoise< SimpleVolume<uint32_t> >(32, 0, 2);
|
||||
auto uint32Vol = createAndFillVolumeWithNoise< PagedVolume<uint32_t> >(32, 0, 2);
|
||||
Mesh< CubicVertex< uint32_t >, uint16_t > uint32Mesh;
|
||||
extractCubicMeshCustom(uint32Vol, uint32Vol->getEnclosingRegion(), &uint32Mesh);
|
||||
QCOMPARE(uint32Mesh.getNoOfVertices(), uint16_t(57687));
|
||||
QCOMPARE(uint32Mesh.getNoOfIndices(), uint32_t(216234));
|
||||
|
||||
// Test with both mesh and controller being provided by the user.
|
||||
auto int32Vol = createAndFillVolumeWithNoise< SimpleVolume<int32_t> >(32, 0, 2);
|
||||
auto int32Vol = createAndFillVolumeWithNoise< PagedVolume<int32_t> >(32, 0, 2);
|
||||
Mesh< CubicVertex< int32_t >, uint16_t > int32Mesh;
|
||||
extractCubicMeshCustom(int32Vol, int32Vol->getEnclosingRegion(), &int32Mesh, CustomIsQuadNeeded<int32_t>());
|
||||
QCOMPARE(int32Mesh.getNoOfVertices(), uint16_t(29027));
|
||||
@ -156,7 +156,7 @@ void TestCubicSurfaceExtractor::testBehaviour()
|
||||
|
||||
void TestCubicSurfaceExtractor::testEmptyVolumePerformance()
|
||||
{
|
||||
auto emptyVol = createAndFillVolumeWithNoise< SimpleVolume<uint32_t> >(128, 0, 0);
|
||||
auto emptyVol = createAndFillVolumeWithNoise< PagedVolume<uint32_t> >(128, 0, 0);
|
||||
Mesh< CubicVertex< uint32_t >, uint16_t > emptyMesh;
|
||||
QBENCHMARK{ extractCubicMeshCustom(emptyVol, Region(32, 32, 32, 63, 63, 63), &emptyMesh); }
|
||||
QCOMPARE(emptyMesh.getNoOfVertices(), uint16_t(0));
|
||||
@ -164,7 +164,7 @@ void TestCubicSurfaceExtractor::testEmptyVolumePerformance()
|
||||
|
||||
void TestCubicSurfaceExtractor::testRealisticVolumePerformance()
|
||||
{
|
||||
auto realisticVol = createAndFillVolumeRealistic< SimpleVolume<uint32_t> >(128);
|
||||
auto realisticVol = createAndFillVolumeRealistic< PagedVolume<uint32_t> >(128);
|
||||
Mesh< CubicVertex< uint32_t >, uint16_t > realisticMesh;
|
||||
QBENCHMARK{ extractCubicMeshCustom(realisticVol, Region(32, 32, 32, 63, 63, 63), &realisticMesh); }
|
||||
QCOMPARE(realisticMesh.getNoOfVertices(), uint16_t(2176));
|
||||
@ -172,7 +172,7 @@ void TestCubicSurfaceExtractor::testRealisticVolumePerformance()
|
||||
|
||||
void TestCubicSurfaceExtractor::testNoiseVolumePerformance()
|
||||
{
|
||||
auto noiseVol = createAndFillVolumeWithNoise< SimpleVolume<uint32_t> >(128, 0, 2);
|
||||
auto noiseVol = createAndFillVolumeWithNoise< PagedVolume<uint32_t> >(128, 0, 2);
|
||||
Mesh< CubicVertex< uint32_t >, uint16_t > noiseMesh;
|
||||
QBENCHMARK{ extractCubicMeshCustom(noiseVol, Region(32, 32, 32, 63, 63, 63), &noiseMesh); }
|
||||
QCOMPARE(noiseMesh.getNoOfVertices(), uint16_t(57729));
|
||||
|
@ -24,7 +24,7 @@ freely, subject to the following restrictions:
|
||||
#include "TestPicking.h"
|
||||
|
||||
#include "PolyVoxCore/Picking.h"
|
||||
#include "PolyVoxCore/SimpleVolume.h"
|
||||
#include "PolyVoxCore/PagedVolume.h"
|
||||
|
||||
#include <QtTest>
|
||||
|
||||
@ -34,7 +34,7 @@ void TestPicking::testExecute()
|
||||
{
|
||||
const int32_t uVolumeSideLength = 32;
|
||||
|
||||
SimpleVolume<int8_t> volData(Region(Vector3DInt32(0,0,0), Vector3DInt32(uVolumeSideLength-1, uVolumeSideLength-1, uVolumeSideLength-1)));
|
||||
PagedVolume<int8_t> volData(Region(Vector3DInt32(0, 0, 0), Vector3DInt32(uVolumeSideLength - 1, uVolumeSideLength - 1, uVolumeSideLength - 1)));
|
||||
for (int32_t z = 0; z < uVolumeSideLength; z++)
|
||||
{
|
||||
for (int32_t y = 0; y < uVolumeSideLength; y++)
|
||||
|
@ -25,7 +25,7 @@ freely, subject to the following restrictions:
|
||||
|
||||
#include "PolyVoxCore/Density.h"
|
||||
#include "PolyVoxCore/Raycast.h"
|
||||
#include "PolyVoxCore/SimpleVolume.h"
|
||||
#include "PolyVoxCore/PagedVolume.h"
|
||||
|
||||
#include "PolyVoxCore/Impl/RandomUnitVectors.h"
|
||||
|
||||
@ -47,7 +47,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
bool operator()(const SimpleVolume<int8_t>::Sampler& sampler)
|
||||
bool operator()(const PagedVolume<int8_t>::Sampler& sampler)
|
||||
{
|
||||
m_uVoxelsTouched++;
|
||||
|
||||
@ -73,7 +73,7 @@ void TestRaycast::testExecute()
|
||||
const int32_t uVolumeSideLength = 32;
|
||||
|
||||
//Create a hollow volume, with solid sides on x and y but with open ends in z.
|
||||
SimpleVolume<int8_t> volData(Region(Vector3DInt32(0,0,0), Vector3DInt32(uVolumeSideLength-1, uVolumeSideLength-1, uVolumeSideLength-1)));
|
||||
PagedVolume<int8_t> volData(Region(Vector3DInt32(0, 0, 0), Vector3DInt32(uVolumeSideLength - 1, uVolumeSideLength - 1, uVolumeSideLength - 1)));
|
||||
for (int32_t z = 0; z < uVolumeSideLength; z++)
|
||||
{
|
||||
for (int32_t y = 0; y < uVolumeSideLength; y++)
|
||||
|
@ -1,231 +1,236 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2010 Matt 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 "TestSurfaceExtractor.h"
|
||||
|
||||
#include "PolyVoxCore/Density.h"
|
||||
#include "PolyVoxCore/MaterialDensityPair.h"
|
||||
#include "PolyVoxCore/RawVolume.h"
|
||||
#include "PolyVoxCore/SimpleVolume.h"
|
||||
#include "PolyVoxCore/MarchingCubesSurfaceExtractor.h"
|
||||
|
||||
#include <QtTest>
|
||||
|
||||
using namespace PolyVox;
|
||||
|
||||
// Test our ability to modify the behaviour of the MarchingCubesSurfaceExtractor. This simple example only modifies
|
||||
// the threshold (and actually this can be achieved by passing a parameter to the constructor of the
|
||||
// DefaultMarchingCubesController) but you could implement custom behaviour in the other members
|
||||
// if you wanted too. Actually, it's not clear if this ability is really useful because I can't think
|
||||
// what you'd modify apart from the threshold but the ability is at least available. Also, the
|
||||
// DefaultMarchingCubesController is templatised whereas this exmple shows that controllers don't
|
||||
// have to be.
|
||||
class CustomMarchingCubesController
|
||||
{
|
||||
public:
|
||||
typedef float DensityType;
|
||||
typedef float MaterialType;
|
||||
|
||||
float convertToDensity(float voxel)
|
||||
{
|
||||
return voxel;
|
||||
}
|
||||
|
||||
float convertToMaterial(float /*voxel*/)
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float blendMaterials(float /*a*/, float /*b*/, float /*weight*/)
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float getThreshold(void)
|
||||
{
|
||||
return 50.0f;
|
||||
}
|
||||
};
|
||||
|
||||
// These 'writeDensityValueToVoxel' functions provide a unified interface for writting densities to primative and class voxel types.
|
||||
// They are conceptually the inverse of the 'convertToDensity' function used by the MarchingCubesSurfaceExtractor. They probably shouldn't be part
|
||||
// of PolyVox, but they might be usful to other tests so we cold move them into a 'Tests.h' or something in the future.
|
||||
template<typename VoxelType>
|
||||
void writeDensityValueToVoxel(int valueToWrite, VoxelType& voxel)
|
||||
{
|
||||
voxel = valueToWrite;
|
||||
}
|
||||
|
||||
template<>
|
||||
void writeDensityValueToVoxel(int valueToWrite, MaterialDensityPair88& voxel)
|
||||
{
|
||||
voxel.setDensity(valueToWrite);
|
||||
}
|
||||
|
||||
template<typename VoxelType>
|
||||
void writeMaterialValueToVoxel(int /*valueToWrite*/, VoxelType& /*voxel*/)
|
||||
{
|
||||
//Most types don't have a material
|
||||
return;
|
||||
}
|
||||
|
||||
template<>
|
||||
void writeMaterialValueToVoxel(int valueToWrite, MaterialDensityPair88& voxel)
|
||||
{
|
||||
voxel.setMaterial(valueToWrite);
|
||||
}
|
||||
|
||||
template <typename VoxelType>
|
||||
SimpleVolume<VoxelType>* createAndFillVolume(void)
|
||||
{
|
||||
const int32_t uVolumeSideLength = 64;
|
||||
|
||||
//Create empty volume
|
||||
SimpleVolume<VoxelType>* volData = new SimpleVolume<VoxelType>(Region(Vector3DInt32(0, 0, 0), Vector3DInt32(uVolumeSideLength - 1, uVolumeSideLength - 1, uVolumeSideLength - 1)));
|
||||
|
||||
// Fill
|
||||
for (int32_t z = 0; z < uVolumeSideLength; z++)
|
||||
{
|
||||
for (int32_t y = 0; y < uVolumeSideLength; y++)
|
||||
{
|
||||
for (int32_t x = 0; x < uVolumeSideLength; x++)
|
||||
{
|
||||
// Create a density field which changes throughout the volume. It's
|
||||
// zero in the lower corner and increasing as the coordinates increase.
|
||||
VoxelType voxelValue;
|
||||
writeDensityValueToVoxel<VoxelType>(x + y + z, voxelValue);
|
||||
writeMaterialValueToVoxel<VoxelType>(z > uVolumeSideLength / 2 ? 42 : 79, voxelValue);
|
||||
volData->setVoxelAt(x, y, z, voxelValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return volData;
|
||||
}
|
||||
|
||||
// From http://stackoverflow.com/a/5289624
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2010 Matt 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 "TestSurfaceExtractor.h"
|
||||
|
||||
#include "PolyVoxCore/Density.h"
|
||||
#include "PolyVoxCore/FilePager.h"
|
||||
#include "PolyVoxCore/MaterialDensityPair.h"
|
||||
#include "PolyVoxCore/RawVolume.h"
|
||||
#include "PolyVoxCore/PagedVolume.h"
|
||||
#include "PolyVoxCore/MarchingCubesSurfaceExtractor.h"
|
||||
|
||||
#include <QtTest>
|
||||
|
||||
using namespace PolyVox;
|
||||
|
||||
// Test our ability to modify the behaviour of the MarchingCubesSurfaceExtractor. This simple example only modifies
|
||||
// the threshold (and actually this can be achieved by passing a parameter to the constructor of the
|
||||
// DefaultMarchingCubesController) but you could implement custom behaviour in the other members
|
||||
// if you wanted too. Actually, it's not clear if this ability is really useful because I can't think
|
||||
// what you'd modify apart from the threshold but the ability is at least available. Also, the
|
||||
// DefaultMarchingCubesController is templatised whereas this exmple shows that controllers don't
|
||||
// have to be.
|
||||
class CustomMarchingCubesController
|
||||
{
|
||||
public:
|
||||
typedef float DensityType;
|
||||
typedef float MaterialType;
|
||||
|
||||
float convertToDensity(float voxel)
|
||||
{
|
||||
return voxel;
|
||||
}
|
||||
|
||||
float convertToMaterial(float /*voxel*/)
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float blendMaterials(float /*a*/, float /*b*/, float /*weight*/)
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float getThreshold(void)
|
||||
{
|
||||
return 50.0f;
|
||||
}
|
||||
};
|
||||
|
||||
// These 'writeDensityValueToVoxel' functions provide a unified interface for writting densities to primative and class voxel types.
|
||||
// They are conceptually the inverse of the 'convertToDensity' function used by the MarchingCubesSurfaceExtractor. They probably shouldn't be part
|
||||
// of PolyVox, but they might be usful to other tests so we cold move them into a 'Tests.h' or something in the future.
|
||||
template<typename VoxelType>
|
||||
void writeDensityValueToVoxel(int valueToWrite, VoxelType& voxel)
|
||||
{
|
||||
voxel = valueToWrite;
|
||||
}
|
||||
|
||||
template<>
|
||||
void writeDensityValueToVoxel(int valueToWrite, MaterialDensityPair88& voxel)
|
||||
{
|
||||
voxel.setDensity(valueToWrite);
|
||||
}
|
||||
|
||||
template<typename VoxelType>
|
||||
void writeMaterialValueToVoxel(int /*valueToWrite*/, VoxelType& /*voxel*/)
|
||||
{
|
||||
//Most types don't have a material
|
||||
return;
|
||||
}
|
||||
|
||||
template<>
|
||||
void writeMaterialValueToVoxel(int valueToWrite, MaterialDensityPair88& voxel)
|
||||
{
|
||||
voxel.setMaterial(valueToWrite);
|
||||
}
|
||||
|
||||
template <typename VolumeType>
|
||||
VolumeType* createAndFillVolume(void)
|
||||
{
|
||||
const int32_t uVolumeSideLength = 64;
|
||||
|
||||
FilePager<typename VolumeType::VoxelType>* pager = new FilePager<typename VolumeType::VoxelType>(".");
|
||||
|
||||
//Create empty volume
|
||||
VolumeType* volData = new VolumeType(Region(Vector3DInt32(0, 0, 0), Vector3DInt32(uVolumeSideLength - 1, uVolumeSideLength - 1, uVolumeSideLength - 1)), pager);
|
||||
|
||||
// Fill
|
||||
for (int32_t z = 0; z < uVolumeSideLength; z++)
|
||||
{
|
||||
for (int32_t y = 0; y < uVolumeSideLength; y++)
|
||||
{
|
||||
for (int32_t x = 0; x < uVolumeSideLength; x++)
|
||||
{
|
||||
// Create a density field which changes throughout the volume. It's
|
||||
// zero in the lower corner and increasing as the coordinates increase.
|
||||
typename VolumeType::VoxelType voxelValue;
|
||||
writeDensityValueToVoxel<typename VolumeType::VoxelType>(x + y + z, voxelValue);
|
||||
writeMaterialValueToVoxel<typename VolumeType::VoxelType>(z > uVolumeSideLength / 2 ? 42 : 79, voxelValue);
|
||||
volData->setVoxelAt(x, y, z, voxelValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return volData;
|
||||
}
|
||||
|
||||
// From http://stackoverflow.com/a/5289624
|
||||
float randomFloat(float a, float b)
|
||||
{
|
||||
float random = ((float)rand()) / (float)RAND_MAX;
|
||||
float diff = b - a;
|
||||
float r = random * diff;
|
||||
return a + r;
|
||||
}
|
||||
|
||||
template <typename VolumeType>
|
||||
VolumeType* createAndFillVolumeWithNoise(int32_t iVolumeSideLength, float minValue, float maxValue)
|
||||
{
|
||||
//Create empty volume
|
||||
VolumeType* volData = new VolumeType(Region(Vector3DInt32(0, 0, 0), Vector3DInt32(iVolumeSideLength - 1, iVolumeSideLength - 1, iVolumeSideLength - 1)));
|
||||
|
||||
// Seed generator for consistency between runs.
|
||||
srand(12345);
|
||||
|
||||
// Fill
|
||||
for (int32_t z = 0; z < iVolumeSideLength; z++)
|
||||
{
|
||||
for (int32_t y = 0; y < iVolumeSideLength; y++)
|
||||
{
|
||||
for (int32_t x = 0; x < iVolumeSideLength; x++)
|
||||
{
|
||||
float voxelValue = randomFloat(minValue, maxValue);
|
||||
volData->setVoxelAt(x, y, z, voxelValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return volData;
|
||||
}
|
||||
|
||||
void TestSurfaceExtractor::testBehaviour()
|
||||
{
|
||||
// These tests apply the Marching Cubes surface extractor to volumes of various voxel types. In addition we sometimes make use of custom controllers
|
||||
// and user-provided meshes to make sure these various combinations work as expected.
|
||||
//
|
||||
// It is also noted that the number of indices and vertices is varying quite significantly based on the voxel type. This seems unexpected, but could
|
||||
// be explained if some overflow is occuring when writing data into the volume, causing volumes of different voxel types to have different distributions.
|
||||
// Of course, the use of a custom controller will also make a significant diference, but this probably does need investigating further in the future.
|
||||
|
||||
// This basic test just uses the default controller and automatically generates a mesh of the appropriate type.
|
||||
auto uintVol = createAndFillVolume<uint8_t>();
|
||||
auto uintMesh = extractMarchingCubesMesh(uintVol, uintVol->getEnclosingRegion());
|
||||
QCOMPARE(uintMesh.getNoOfVertices(), uint32_t(12096)); // Verifies size of mesh and that we have 32-bit indices
|
||||
QCOMPARE(uintMesh.getNoOfIndices(), uint32_t(35157)); // Verifies size of mesh
|
||||
QCOMPARE(uintMesh.getIndex(100), uint32_t(44)); // Verifies that we have 32-bit indices
|
||||
QCOMPARE(uintMesh.getVertex(100).data, uint8_t(1)); // Not really meaningful for a primative type
|
||||
|
||||
// This test makes use of a custom controller
|
||||
auto floatVol = createAndFillVolume<float>();
|
||||
CustomMarchingCubesController floatCustomController;
|
||||
auto floatMesh = extractMarchingCubesMesh(floatVol, floatVol->getEnclosingRegion(), floatCustomController);
|
||||
QCOMPARE(floatMesh.getNoOfVertices(), uint32_t(16113)); // Verifies size of mesh and that we have 32-bit indices
|
||||
QCOMPARE(floatMesh.getNoOfIndices(), uint32_t(22053)); // Verifies size of mesh
|
||||
QCOMPARE(floatMesh.getIndex(100), uint32_t(26)); // Verifies that we have 32-bit indices
|
||||
QCOMPARE(floatMesh.getVertex(100).data, float(1.0f)); // Not really meaningful for a primative type
|
||||
|
||||
// This test makes use of a user provided mesh. It uses the default controller, but we have to explicitly provide this because C++ won't let us
|
||||
// use a default for the second-to-last parameter but noot use a default for the last parameter.
|
||||
auto intVol = createAndFillVolume<int8_t>();
|
||||
Mesh< MarchingCubesVertex< int8_t >, uint16_t > intMesh;
|
||||
extractMarchingCubesMeshCustom(intVol, intVol->getEnclosingRegion(), &intMesh);
|
||||
QCOMPARE(intMesh.getNoOfVertices(), uint16_t(11718)); // Verifies size of mesh and that we have 16-bit indices
|
||||
QCOMPARE(intMesh.getNoOfIndices(), uint32_t(34041)); // Verifies size of mesh
|
||||
QCOMPARE(intMesh.getIndex(100), uint16_t(29)); // Verifies that we have 16-bit indices
|
||||
QCOMPARE(intMesh.getVertex(100).data, int8_t(1)); // Not really meaningful for a primative type
|
||||
|
||||
// This test makes use of a user-provided mesh and also a custom controller.
|
||||
auto doubleVol = createAndFillVolume<double>();
|
||||
CustomMarchingCubesController doubleCustomController;
|
||||
Mesh< MarchingCubesVertex< double >, uint16_t > doubleMesh;
|
||||
extractMarchingCubesMeshCustom(doubleVol, doubleVol->getEnclosingRegion(), &doubleMesh, doubleCustomController);
|
||||
QCOMPARE(doubleMesh.getNoOfVertices(), uint16_t(16113)); // Verifies size of mesh and that we have 32-bit indices
|
||||
QCOMPARE(doubleMesh.getNoOfIndices(), uint32_t(22053)); // Verifies size of mesh
|
||||
QCOMPARE(doubleMesh.getIndex(100), uint16_t(26)); // Verifies that we have 32-bit indices
|
||||
QCOMPARE(doubleMesh.getVertex(100).data, double(1.0f)); // Not really meaningful for a primative type
|
||||
|
||||
// This test ensures the extractor works on a non-primitive voxel type.
|
||||
auto materialVol = createAndFillVolume<MaterialDensityPair88>();
|
||||
auto materialMesh = extractMarchingCubesMesh(materialVol, materialVol->getEnclosingRegion());
|
||||
QCOMPARE(materialMesh.getNoOfVertices(), uint32_t(12096)); // Verifies size of mesh and that we have 32-bit indices
|
||||
QCOMPARE(materialMesh.getNoOfIndices(), uint32_t(35157)); // Verifies size of mesh
|
||||
QCOMPARE(materialMesh.getIndex(100), uint32_t(44)); // Verifies that we have 32-bit indices
|
||||
QCOMPARE(materialMesh.getVertex(100).data.getMaterial(), uint16_t(79)); // Verify the data attached to the vertex
|
||||
}
|
||||
|
||||
void TestSurfaceExtractor::testEmptyVolumePerformance()
|
||||
{
|
||||
auto emptyVol = createAndFillVolumeWithNoise< SimpleVolume<float> >(128, -2.0f, -1.0f);
|
||||
Mesh< MarchingCubesVertex< float >, uint16_t > emptyMesh;
|
||||
QBENCHMARK{ extractMarchingCubesMeshCustom(emptyVol, Region(32, 32, 32, 63, 63, 63), &emptyMesh); }
|
||||
QCOMPARE(emptyMesh.getNoOfVertices(), uint16_t(0));
|
||||
}
|
||||
|
||||
void TestSurfaceExtractor::testNoiseVolumePerformance()
|
||||
{
|
||||
auto noiseVol = createAndFillVolumeWithNoise< SimpleVolume<float> >(128, -1.0f, 1.0f);
|
||||
Mesh< MarchingCubesVertex< float >, uint16_t > noiseMesh;
|
||||
QBENCHMARK{ extractMarchingCubesMeshCustom(noiseVol, Region(32, 32, 32, 63, 63, 63), &noiseMesh); }
|
||||
QCOMPARE(noiseMesh.getNoOfVertices(), uint16_t(48967));
|
||||
}
|
||||
|
||||
QTEST_MAIN(TestSurfaceExtractor)
|
||||
}
|
||||
|
||||
template <typename VolumeType>
|
||||
VolumeType* createAndFillVolumeWithNoise(int32_t iVolumeSideLength, float minValue, float maxValue)
|
||||
{
|
||||
FilePager<float>* pager = new FilePager<float>(".");
|
||||
|
||||
//Create empty volume
|
||||
VolumeType* volData = new VolumeType(Region(Vector3DInt32(0, 0, 0), Vector3DInt32(iVolumeSideLength - 1, iVolumeSideLength - 1, iVolumeSideLength - 1)), pager);
|
||||
|
||||
// Seed generator for consistency between runs.
|
||||
srand(12345);
|
||||
|
||||
// Fill
|
||||
for (int32_t z = 0; z < iVolumeSideLength; z++)
|
||||
{
|
||||
for (int32_t y = 0; y < iVolumeSideLength; y++)
|
||||
{
|
||||
for (int32_t x = 0; x < iVolumeSideLength; x++)
|
||||
{
|
||||
float voxelValue = randomFloat(minValue, maxValue);
|
||||
volData->setVoxelAt(x, y, z, voxelValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return volData;
|
||||
}
|
||||
|
||||
void TestSurfaceExtractor::testBehaviour()
|
||||
{
|
||||
// These tests apply the Marching Cubes surface extractor to volumes of various voxel types. In addition we sometimes make use of custom controllers
|
||||
// and user-provided meshes to make sure these various combinations work as expected.
|
||||
//
|
||||
// It is also noted that the number of indices and vertices is varying quite significantly based on the voxel type. This seems unexpected, but could
|
||||
// be explained if some overflow is occuring when writing data into the volume, causing volumes of different voxel types to have different distributions.
|
||||
// Of course, the use of a custom controller will also make a significant diference, but this probably does need investigating further in the future.
|
||||
|
||||
// This basic test just uses the default controller and automatically generates a mesh of the appropriate type.
|
||||
auto uintVol = createAndFillVolume< PagedVolume<uint8_t> >();
|
||||
auto uintMesh = extractMarchingCubesMesh(uintVol, uintVol->getEnclosingRegion());
|
||||
QCOMPARE(uintMesh.getNoOfVertices(), uint32_t(12096)); // Verifies size of mesh and that we have 32-bit indices
|
||||
QCOMPARE(uintMesh.getNoOfIndices(), uint32_t(35157)); // Verifies size of mesh
|
||||
QCOMPARE(uintMesh.getIndex(100), uint32_t(44)); // Verifies that we have 32-bit indices
|
||||
QCOMPARE(uintMesh.getVertex(100).data, uint8_t(1)); // Not really meaningful for a primative type
|
||||
|
||||
// This test makes use of a custom controller
|
||||
auto floatVol = createAndFillVolume< PagedVolume<float> >();
|
||||
CustomMarchingCubesController floatCustomController;
|
||||
auto floatMesh = extractMarchingCubesMesh(floatVol, floatVol->getEnclosingRegion(), floatCustomController);
|
||||
QCOMPARE(floatMesh.getNoOfVertices(), uint32_t(16113)); // Verifies size of mesh and that we have 32-bit indices
|
||||
QCOMPARE(floatMesh.getNoOfIndices(), uint32_t(22053)); // Verifies size of mesh
|
||||
QCOMPARE(floatMesh.getIndex(100), uint32_t(26)); // Verifies that we have 32-bit indices
|
||||
QCOMPARE(floatMesh.getVertex(100).data, float(1.0f)); // Not really meaningful for a primative type
|
||||
|
||||
// This test makes use of a user provided mesh. It uses the default controller, but we have to explicitly provide this because C++ won't let us
|
||||
// use a default for the second-to-last parameter but noot use a default for the last parameter.
|
||||
auto intVol = createAndFillVolume< PagedVolume<int8_t> >();
|
||||
Mesh< MarchingCubesVertex< int8_t >, uint16_t > intMesh;
|
||||
extractMarchingCubesMeshCustom(intVol, intVol->getEnclosingRegion(), &intMesh);
|
||||
QCOMPARE(intMesh.getNoOfVertices(), uint16_t(11718)); // Verifies size of mesh and that we have 16-bit indices
|
||||
QCOMPARE(intMesh.getNoOfIndices(), uint32_t(34041)); // Verifies size of mesh
|
||||
QCOMPARE(intMesh.getIndex(100), uint16_t(29)); // Verifies that we have 16-bit indices
|
||||
QCOMPARE(intMesh.getVertex(100).data, int8_t(1)); // Not really meaningful for a primative type
|
||||
|
||||
// This test makes use of a user-provided mesh and also a custom controller.
|
||||
auto doubleVol = createAndFillVolume< PagedVolume<double> >();
|
||||
CustomMarchingCubesController doubleCustomController;
|
||||
Mesh< MarchingCubesVertex< double >, uint16_t > doubleMesh;
|
||||
extractMarchingCubesMeshCustom(doubleVol, doubleVol->getEnclosingRegion(), &doubleMesh, doubleCustomController);
|
||||
QCOMPARE(doubleMesh.getNoOfVertices(), uint16_t(16113)); // Verifies size of mesh and that we have 32-bit indices
|
||||
QCOMPARE(doubleMesh.getNoOfIndices(), uint32_t(22053)); // Verifies size of mesh
|
||||
QCOMPARE(doubleMesh.getIndex(100), uint16_t(26)); // Verifies that we have 32-bit indices
|
||||
QCOMPARE(doubleMesh.getVertex(100).data, double(1.0f)); // Not really meaningful for a primative type
|
||||
|
||||
// This test ensures the extractor works on a non-primitive voxel type.
|
||||
auto materialVol = createAndFillVolume< PagedVolume<MaterialDensityPair88> >();
|
||||
auto materialMesh = extractMarchingCubesMesh(materialVol, materialVol->getEnclosingRegion());
|
||||
QCOMPARE(materialMesh.getNoOfVertices(), uint32_t(12096)); // Verifies size of mesh and that we have 32-bit indices
|
||||
QCOMPARE(materialMesh.getNoOfIndices(), uint32_t(35157)); // Verifies size of mesh
|
||||
QCOMPARE(materialMesh.getIndex(100), uint32_t(44)); // Verifies that we have 32-bit indices
|
||||
QCOMPARE(materialMesh.getVertex(100).data.getMaterial(), uint16_t(79)); // Verify the data attached to the vertex
|
||||
}
|
||||
|
||||
void TestSurfaceExtractor::testEmptyVolumePerformance()
|
||||
{
|
||||
auto emptyVol = createAndFillVolumeWithNoise< PagedVolume<float> >(128, -2.0f, -1.0f);
|
||||
Mesh< MarchingCubesVertex< float >, uint16_t > emptyMesh;
|
||||
QBENCHMARK{ extractMarchingCubesMeshCustom(emptyVol, Region(32, 32, 32, 63, 63, 63), &emptyMesh); }
|
||||
QCOMPARE(emptyMesh.getNoOfVertices(), uint16_t(0));
|
||||
}
|
||||
|
||||
void TestSurfaceExtractor::testNoiseVolumePerformance()
|
||||
{
|
||||
auto noiseVol = createAndFillVolumeWithNoise< PagedVolume<float> >(128, -1.0f, 1.0f);
|
||||
Mesh< MarchingCubesVertex< float >, uint16_t > noiseMesh;
|
||||
QBENCHMARK{ extractMarchingCubesMeshCustom(noiseVol, Region(32, 32, 32, 63, 63, 63), &noiseMesh); }
|
||||
QCOMPARE(noiseMesh.getNoOfVertices(), uint16_t(48967));
|
||||
}
|
||||
|
||||
QTEST_MAIN(TestSurfaceExtractor)
|
||||
|
@ -24,10 +24,8 @@ freely, subject to the following restrictions:
|
||||
#include "testvolume.h"
|
||||
|
||||
#include "PolyVoxCore/FilePager.h"
|
||||
#include "PolyVoxCore/LargeVolume.h"
|
||||
#include "PolyVoxCore/MinizBlockCompressor.h"
|
||||
#include "PolyVoxCore/PagedVolume.h"
|
||||
#include "PolyVoxCore/RawVolume.h"
|
||||
#include "PolyVoxCore/SimpleVolume.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QtTest>
|
||||
@ -112,7 +110,7 @@ int32_t testSamplersWithWrappingForwards(VolumeType* volume, int lowXOffset, int
|
||||
xSampler = ySampler;
|
||||
for(int x = volume->getEnclosingRegion().getLowerX() + lowXOffset; x <= volume->getEnclosingRegion().getUpperX() + highXOffset; x++)
|
||||
{
|
||||
xSampler.setPosition(x, y, z); // HACK - Accessing a volume through multiple samplers currently breaks the LargeVolume.
|
||||
xSampler.setPosition(x, y, z); // HACK - Accessing a volume through multiple samplers currently breaks the PagedVolume.
|
||||
|
||||
result = cantorTupleFunction(result, xSampler.peekVoxel1nx1ny1nz());
|
||||
result = cantorTupleFunction(result, xSampler.peekVoxel0px1ny1nz());
|
||||
@ -225,7 +223,7 @@ int32_t testSamplersWithWrappingBackwards(VolumeType* volume, int lowXOffset, in
|
||||
xSampler = ySampler;
|
||||
for(int x = volume->getEnclosingRegion().getUpperX() + highXOffset; x >= volume->getEnclosingRegion().getLowerX() + lowXOffset; x--)
|
||||
{
|
||||
xSampler.setPosition(x, y, z); // HACK - Accessing a volume through multiple samplers currently breaks the LargeVolume.
|
||||
xSampler.setPosition(x, y, z); // HACK - Accessing a volume through multiple samplers currently breaks the PagedVolume.
|
||||
|
||||
result = cantorTupleFunction(result, xSampler.peekVoxel1nx1ny1nz());
|
||||
result = cantorTupleFunction(result, xSampler.peekVoxel0px1ny1nz());
|
||||
@ -271,17 +269,13 @@ TestVolume::TestVolume()
|
||||
{
|
||||
Region region(-57, -31, 12, 64, 96, 131); // Deliberatly awkward size
|
||||
|
||||
//m_pCompressor = new RLECompressor<int32_t, uint16_t>;
|
||||
m_pBlockCompressor = new MinizBlockCompressor<int32_t>;
|
||||
m_pFilePager = new FilePager<int32_t>("./");
|
||||
m_pFilePager = new FilePager<int32_t>(".");
|
||||
|
||||
//Create the volumes
|
||||
m_pRawVolume = new RawVolume<int32_t>(region);
|
||||
m_pSimpleVolume = new SimpleVolume<int32_t>(region);
|
||||
m_pLargeVolume = new LargeVolume<int32_t>(region, m_pBlockCompressor, m_pFilePager, 32);
|
||||
m_pPagedVolume = new PagedVolume<int32_t>(region, m_pFilePager, 32);
|
||||
|
||||
m_pLargeVolume->setMaxNumberOfBlocksInMemory(32);
|
||||
m_pLargeVolume->setMaxNumberOfUncompressedBlocks(16);
|
||||
m_pPagedVolume->setMemoryUsageLimit(1 * 1024 * 1024);
|
||||
|
||||
//Fill the volume with some data
|
||||
for(int z = region.getLowerZ(); z <= region.getUpperZ(); z++)
|
||||
@ -292,8 +286,7 @@ TestVolume::TestVolume()
|
||||
{
|
||||
int32_t value = x + y + z;
|
||||
m_pRawVolume->setVoxelAt(x, y, z, value);
|
||||
m_pSimpleVolume->setVoxelAt(x, y, z, value);
|
||||
m_pLargeVolume->setVoxelAt(x, y, z, value);
|
||||
m_pPagedVolume->setVoxelAt(x, y, z, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -302,11 +295,9 @@ TestVolume::TestVolume()
|
||||
TestVolume::~TestVolume()
|
||||
{
|
||||
delete m_pRawVolume;
|
||||
delete m_pSimpleVolume;
|
||||
delete m_pLargeVolume;
|
||||
delete m_pPagedVolume;
|
||||
|
||||
delete m_pFilePager;
|
||||
delete m_pBlockCompressor;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -402,173 +393,85 @@ void TestVolume::testRawVolumeSamplersWithExternalBackwards()
|
||||
}
|
||||
|
||||
/*
|
||||
* SimpleVolume Tests
|
||||
* PagedVolume Tests
|
||||
*/
|
||||
|
||||
void TestVolume::testSimpleVolumeDirectAccessAllInternalForwards()
|
||||
void TestVolume::testPagedVolumeDirectAccessAllInternalForwards()
|
||||
{
|
||||
int32_t result = 0;
|
||||
QBENCHMARK
|
||||
{
|
||||
result = testDirectAccessWithWrappingForwards(m_pSimpleVolume, 4, 2, 2, -3, -1, -2);
|
||||
result = testDirectAccessWithWrappingForwards(m_pPagedVolume, 4, 2, 2, -3, -1, -2);
|
||||
}
|
||||
QCOMPARE(result, static_cast<int32_t>(1004598054));
|
||||
}
|
||||
|
||||
void TestVolume::testSimpleVolumeSamplersAllInternalForwards()
|
||||
void TestVolume::testPagedVolumeSamplersAllInternalForwards()
|
||||
{
|
||||
int32_t result = 0;
|
||||
QBENCHMARK
|
||||
{
|
||||
result = testSamplersWithWrappingForwards(m_pSimpleVolume, 4, 2, 2, -3, -1, -2);
|
||||
result = testSamplersWithWrappingForwards(m_pPagedVolume, 4, 2, 2, -3, -1, -2);
|
||||
}
|
||||
QCOMPARE(result, static_cast<int32_t>(1004598054));
|
||||
}
|
||||
|
||||
void TestVolume::testSimpleVolumeDirectAccessWithExternalForwards()
|
||||
void TestVolume::testPagedVolumeDirectAccessWithExternalForwards()
|
||||
{
|
||||
int32_t result = 0;
|
||||
QBENCHMARK
|
||||
{
|
||||
result = testDirectAccessWithWrappingForwards(m_pSimpleVolume, -1, -3, -2, 2, 5, 4);
|
||||
result = testDirectAccessWithWrappingForwards(m_pPagedVolume, -1, -3, -2, 2, 5, 4);
|
||||
}
|
||||
QCOMPARE(result, static_cast<int32_t>(-928601007));
|
||||
}
|
||||
|
||||
void TestVolume::testSimpleVolumeSamplersWithExternalForwards()
|
||||
void TestVolume::testPagedVolumeSamplersWithExternalForwards()
|
||||
{
|
||||
int32_t result = 0;
|
||||
QBENCHMARK
|
||||
{
|
||||
result = testSamplersWithWrappingForwards(m_pSimpleVolume, -1, -3, -2, 2, 5, 4);
|
||||
result = testSamplersWithWrappingForwards(m_pPagedVolume, -1, -3, -2, 2, 5, 4);
|
||||
}
|
||||
QCOMPARE(result, static_cast<int32_t>(-928601007));
|
||||
}
|
||||
|
||||
void TestVolume::testSimpleVolumeDirectAccessAllInternalBackwards()
|
||||
void TestVolume::testPagedVolumeDirectAccessAllInternalBackwards()
|
||||
{
|
||||
int32_t result = 0;
|
||||
QBENCHMARK
|
||||
{
|
||||
result = testDirectAccessWithWrappingBackwards(m_pSimpleVolume, 4, 2, 2, -3, -1, -2);
|
||||
result = testDirectAccessWithWrappingBackwards(m_pPagedVolume, 4, 2, 2, -3, -1, -2);
|
||||
}
|
||||
QCOMPARE(result, static_cast<int32_t>(-269366578));
|
||||
}
|
||||
|
||||
void TestVolume::testSimpleVolumeSamplersAllInternalBackwards()
|
||||
void TestVolume::testPagedVolumeSamplersAllInternalBackwards()
|
||||
{
|
||||
int32_t result = 0;
|
||||
QBENCHMARK
|
||||
{
|
||||
result = testSamplersWithWrappingBackwards(m_pSimpleVolume, 4, 2, 2, -3, -1, -2);
|
||||
result = testSamplersWithWrappingBackwards(m_pPagedVolume, 4, 2, 2, -3, -1, -2);
|
||||
}
|
||||
QCOMPARE(result, static_cast<int32_t>(-269366578));
|
||||
}
|
||||
|
||||
void TestVolume::testSimpleVolumeDirectAccessWithExternalBackwards()
|
||||
void TestVolume::testPagedVolumeDirectAccessWithExternalBackwards()
|
||||
{
|
||||
int32_t result = 0;
|
||||
QBENCHMARK
|
||||
{
|
||||
result = testDirectAccessWithWrappingBackwards(m_pSimpleVolume, -1, -3, -2, 2, 5, 4);
|
||||
result = testDirectAccessWithWrappingBackwards(m_pPagedVolume, -1, -3, -2, 2, 5, 4);
|
||||
}
|
||||
QCOMPARE(result, static_cast<int32_t>(-769775893));
|
||||
}
|
||||
|
||||
void TestVolume::testSimpleVolumeSamplersWithExternalBackwards()
|
||||
void TestVolume::testPagedVolumeSamplersWithExternalBackwards()
|
||||
{
|
||||
int32_t result = 0;
|
||||
QBENCHMARK
|
||||
{
|
||||
result = testSamplersWithWrappingBackwards(m_pSimpleVolume, -1, -3, -2, 2, 5, 4);
|
||||
}
|
||||
QCOMPARE(result, static_cast<int32_t>(-769775893));
|
||||
}
|
||||
|
||||
/*
|
||||
* LargeVolume Tests
|
||||
*/
|
||||
|
||||
void TestVolume::testLargeVolumeDirectAccessAllInternalForwards()
|
||||
{
|
||||
int32_t result = 0;
|
||||
QBENCHMARK
|
||||
{
|
||||
result = testDirectAccessWithWrappingForwards(m_pLargeVolume, 4, 2, 2, -3, -1, -2);
|
||||
}
|
||||
QCOMPARE(result, static_cast<int32_t>(1004598054));
|
||||
}
|
||||
|
||||
void TestVolume::testLargeVolumeSamplersAllInternalForwards()
|
||||
{
|
||||
int32_t result = 0;
|
||||
|
||||
QBENCHMARK
|
||||
{
|
||||
result = testSamplersWithWrappingForwards(m_pLargeVolume, 4, 2, 2, -3, -1, -2);
|
||||
}
|
||||
QCOMPARE(result, static_cast<int32_t>(1004598054));
|
||||
}
|
||||
|
||||
void TestVolume::testLargeVolumeDirectAccessWithExternalForwards()
|
||||
{
|
||||
int32_t result = 0;
|
||||
QBENCHMARK
|
||||
{
|
||||
result = testDirectAccessWithWrappingForwards(m_pLargeVolume, -1, -3, -2, 2, 5, 4);
|
||||
}
|
||||
QCOMPARE(result, static_cast<int32_t>(-928601007));
|
||||
}
|
||||
|
||||
void TestVolume::testLargeVolumeSamplersWithExternalForwards()
|
||||
{
|
||||
int32_t result = 0;
|
||||
|
||||
QBENCHMARK
|
||||
{
|
||||
result = testSamplersWithWrappingForwards(m_pLargeVolume, -1, -3, -2, 2, 5, 4);
|
||||
}
|
||||
QCOMPARE(result, static_cast<int32_t>(-928601007));
|
||||
}
|
||||
|
||||
void TestVolume::testLargeVolumeDirectAccessAllInternalBackwards()
|
||||
{
|
||||
int32_t result = 0;
|
||||
QBENCHMARK
|
||||
{
|
||||
result = testDirectAccessWithWrappingBackwards(m_pLargeVolume, 4, 2, 2, -3, -1, -2);
|
||||
}
|
||||
QCOMPARE(result, static_cast<int32_t>(-269366578));
|
||||
}
|
||||
|
||||
void TestVolume::testLargeVolumeSamplersAllInternalBackwards()
|
||||
{
|
||||
int32_t result = 0;
|
||||
|
||||
QBENCHMARK
|
||||
{
|
||||
result = testSamplersWithWrappingBackwards(m_pLargeVolume, 4, 2, 2, -3, -1, -2);
|
||||
}
|
||||
QCOMPARE(result, static_cast<int32_t>(-269366578));
|
||||
}
|
||||
|
||||
void TestVolume::testLargeVolumeDirectAccessWithExternalBackwards()
|
||||
{
|
||||
int32_t result = 0;
|
||||
QBENCHMARK
|
||||
{
|
||||
result = testDirectAccessWithWrappingBackwards(m_pLargeVolume, -1, -3, -2, 2, 5, 4);
|
||||
}
|
||||
QCOMPARE(result, static_cast<int32_t>(-769775893));
|
||||
}
|
||||
|
||||
void TestVolume::testLargeVolumeSamplersWithExternalBackwards()
|
||||
{
|
||||
int32_t result = 0;
|
||||
|
||||
QBENCHMARK
|
||||
{
|
||||
result = testSamplersWithWrappingBackwards(m_pLargeVolume, -1, -3, -2, 2, 5, 4);
|
||||
result = testSamplersWithWrappingBackwards(m_pPagedVolume, -1, -3, -2, 2, 5, 4);
|
||||
}
|
||||
QCOMPARE(result, static_cast<int32_t>(-769775893));
|
||||
}
|
||||
|
@ -46,31 +46,20 @@ private slots:
|
||||
void testRawVolumeDirectAccessWithExternalBackwards();
|
||||
void testRawVolumeSamplersWithExternalBackwards();
|
||||
|
||||
void testSimpleVolumeDirectAccessAllInternalForwards();
|
||||
void testSimpleVolumeSamplersAllInternalForwards();
|
||||
void testSimpleVolumeDirectAccessWithExternalForwards();
|
||||
void testSimpleVolumeSamplersWithExternalForwards();
|
||||
void testSimpleVolumeDirectAccessAllInternalBackwards();
|
||||
void testSimpleVolumeSamplersAllInternalBackwards();
|
||||
void testSimpleVolumeDirectAccessWithExternalBackwards();
|
||||
void testSimpleVolumeSamplersWithExternalBackwards();
|
||||
|
||||
void testLargeVolumeDirectAccessAllInternalForwards();
|
||||
void testLargeVolumeSamplersAllInternalForwards();
|
||||
void testLargeVolumeDirectAccessWithExternalForwards();
|
||||
void testLargeVolumeSamplersWithExternalForwards();
|
||||
void testLargeVolumeDirectAccessAllInternalBackwards();
|
||||
void testLargeVolumeSamplersAllInternalBackwards();
|
||||
void testLargeVolumeDirectAccessWithExternalBackwards();
|
||||
void testLargeVolumeSamplersWithExternalBackwards();
|
||||
void testPagedVolumeDirectAccessAllInternalForwards();
|
||||
void testPagedVolumeSamplersAllInternalForwards();
|
||||
void testPagedVolumeDirectAccessWithExternalForwards();
|
||||
void testPagedVolumeSamplersWithExternalForwards();
|
||||
void testPagedVolumeDirectAccessAllInternalBackwards();
|
||||
void testPagedVolumeSamplersAllInternalBackwards();
|
||||
void testPagedVolumeDirectAccessWithExternalBackwards();
|
||||
void testPagedVolumeSamplersWithExternalBackwards();
|
||||
|
||||
private:
|
||||
PolyVox::BlockCompressor<int32_t>* m_pBlockCompressor;
|
||||
PolyVox::FilePager<int32_t>* m_pFilePager;
|
||||
|
||||
PolyVox::RawVolume<int32_t>* m_pRawVolume;
|
||||
PolyVox::SimpleVolume<int32_t>* m_pSimpleVolume;
|
||||
PolyVox::LargeVolume<int32_t>* m_pLargeVolume;
|
||||
PolyVox::PagedVolume<int32_t>* m_pPagedVolume;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user