From 8e6b1a3b7e7907cee7cdb835086ac66af41ab3d6 Mon Sep 17 00:00:00 2001 From: David Williams Date: Thu, 10 Mar 2011 20:31:11 +0000 Subject: [PATCH] Applying ker's patch unmodified. --- CREDITS.txt | 3 +- examples/Basic/main.cpp | 82 +++- examples/OpenGL/OpenGLWidget.cpp | 12 +- examples/OpenGL/Shapes.cpp | 10 +- examples/OpenGL/main.cpp | 2 +- library/PolyVoxCore/include/Filters.inl | 1 - .../PolyVoxCore/include/PolyVoxImpl/Block.h | 4 +- .../PolyVoxCore/include/PolyVoxImpl/Block.inl | 26 +- library/PolyVoxCore/include/Volume.h | 61 ++- library/PolyVoxCore/include/Volume.inl | 370 +++++++----------- library/PolyVoxCore/include/VolumeSampler.inl | 73 ++-- .../PolyVoxCore/source/GradientEstimators.cpp | 21 +- library/PolyVoxCore/source/VoxelFilters.cpp | 7 - tests/testvolume.cpp | 6 +- 14 files changed, 300 insertions(+), 378 deletions(-) diff --git a/CREDITS.txt b/CREDITS.txt index 2820faab..e7534c09 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1,2 +1,3 @@ David Williams - Lead programmer. -Matthew Williams - Linux port, build system, support and maintainence. \ No newline at end of file +Matthew Williams - Linux port, build system, support and maintainence. +Oliver Schneider - Very large Volumes \ No newline at end of file diff --git a/examples/Basic/main.cpp b/examples/Basic/main.cpp index d563f3cc..65990405 100644 --- a/examples/Basic/main.cpp +++ b/examples/Basic/main.cpp @@ -348,14 +348,14 @@ void createPerlinVolumeSlow(Volume& volData) { Perlin perlin(2,8,1,234); - for(int z = 1; z < volData.getDepth()-1; z++) + for(int z = 1; z < 256-1; z++) { std::cout << z << std::endl; - for(int y = 1; y < volData.getHeight()-1; y++) + for(int y = 1; y < 256-1; y++) { - for(int x = 1; x < volData.getWidth()-1; x++) + for(int x = 1; x < 256-1; x++) { - float perlinVal = perlin.Get3D(x /static_cast(volData.getWidth()-1), (y) / static_cast(volData.getHeight()-1), z / static_cast(volData.getDepth()-1)); + float perlinVal = perlin.Get3D(x /static_cast(256-1), (y) / static_cast(256-1), z / static_cast(256-1)); perlinVal += 1.0f; perlinVal *= 0.5f; @@ -435,18 +435,18 @@ void createPerlinTerrain(Volume& volData) { Perlin perlin(2,2,1,234); - for(int x = 1; x < volData.getWidth()-1; x++) + for(int x = 1; x < 255-1; x++) { - if(x%(volData.getWidth()/100) == 0) { + if(x%(255/100) == 0) { std::cout << "." << std::flush; } - for(int y = 1; y < volData.getHeight()-1; y++) + for(int y = 1; y < 255-1; y++) { - float perlinVal = perlin.Get(x / static_cast(volData.getHeight()-1), y / static_cast(volData.getDepth()-1)); + float perlinVal = perlin.Get(x / static_cast(255-1), y / static_cast(255-1)); perlinVal += 1.0f; perlinVal *= 0.5f; - perlinVal *= volData.getShortestSideLength(); - for(int z = 1; z < volData.getDepth()-1; z++) + perlinVal *= 255; + for(int z = 1; z < 255-1; z++) { MaterialDensityPair44 voxel; if(z < perlinVal) @@ -506,6 +506,53 @@ void createSphereInVolume(Volume& volData, Vector3DFloat } } +void load(const Volume& volume, PolyVox::Region reg) +{ + Perlin perlin(2,2,1,234); + + for(int x = reg.getLowerCorner().getX(); x <= reg.getUpperCorner().getX(); x++) + { + for(int y = reg.getLowerCorner().getY(); y <= reg.getUpperCorner().getY(); y++) + { + float perlinVal = perlin.Get(x / static_cast(255-1), y / static_cast(255-1)); + perlinVal += 1.0f; + perlinVal *= 0.5f; + perlinVal *= 255; + for(int z = reg.getLowerCorner().getZ(); z <= reg.getUpperCorner().getZ(); z++) + { + MaterialDensityPair44 voxel; + if(z < perlinVal) + { + const int xpos = 50; + const int zpos = 100; + if((x-xpos)*(x-xpos) + (z-zpos)*(z-zpos) < 200) { + // tunnel + voxel.setMaterial(0); + voxel.setDensity(MaterialDensityPair44::getMinDensity()); + } else { + // solid + voxel.setMaterial(245); + voxel.setDensity(MaterialDensityPair44::getMaxDensity()); + } + } + else + { + voxel.setMaterial(0); + voxel.setDensity(MaterialDensityPair44::getMinDensity()); + } + + volume.load_setVoxelAt(x, y, z, voxel); + } + } + } +} +void unload(const Volume& vol, PolyVox::Region reg) +{ + std::cout << "warning unloading region: " << reg.getLowerCorner() << " -> " << reg.getUpperCorner() << std::endl; +} + +#include + int main(int argc, char *argv[]) { //Create and show the Qt OpenGL window @@ -514,13 +561,15 @@ int main(int argc, char *argv[]) openGLWidget.show(); //Create an empty volume and then place a sphere in it - Volume volData(1024*128,16,16,16); - volData.useCompatibilityMode(); + Volume volData(128); + volData.m_LoadCallback = std::bind(&load, std::placeholders::_1, std::placeholders::_2); + volData.m_UnloadCallback = boost::bind(&unload, _1, _2); + volData.setBlockCacheSize(4096); //createSphereInVolume(volData, 30); - createPerlinTerrain(volData); + //createPerlinTerrain(volData); //createPerlinVolumeSlow(volData); std::cout << "Memory usage: " << (volData.calculateSizeInBytes()/1024.0/1024.0) << "MB" << std::endl; - volData.setBlockCacheSize(64); + //volData.setBlockCacheSize(64); 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; @@ -540,8 +589,11 @@ int main(int argc, char *argv[]) //Extract the surface SurfaceMesh mesh; //CubicSurfaceExtractorWithNormals surfaceExtractor(&volData, volData.getEnclosingRegion(), &mesh); - SurfaceExtractor surfaceExtractor(&volData, volData.getEnclosingRegion(), &mesh); + PolyVox::Region reg(Vector3DInt32(-255,0,0), Vector3DInt32(255,1024,255)); + SurfaceExtractor surfaceExtractor(&volData, reg, &mesh); + //CubicSurfaceExtractorWithNormals surfaceExtractor(&volData, reg, &mesh); surfaceExtractor.execute(); + std::cout << "#vertices: " << mesh.getNoOfVertices() << std::endl; //Pass the surface to the OpenGL window openGLWidget.setSurfaceMeshToRender(mesh); diff --git a/examples/OpenGL/OpenGLWidget.cpp b/examples/OpenGL/OpenGLWidget.cpp index 63192bc6..64279d1c 100644 --- a/examples/OpenGL/OpenGLWidget.cpp +++ b/examples/OpenGL/OpenGLWidget.cpp @@ -58,9 +58,9 @@ void OpenGLWidget::setVolume(PolyVox::Volume* volData) //If we have any volume data then generate the new surface patches. if(m_volData != 0) { - m_uVolumeWidthInRegions = volData->getWidth() / m_uRegionSideLength; - m_uVolumeHeightInRegions = volData->getHeight() / m_uRegionSideLength; - m_uVolumeDepthInRegions = volData->getDepth() / m_uRegionSideLength; + m_uVolumeWidthInRegions = g_uVolumeSideLength / m_uRegionSideLength; + m_uVolumeHeightInRegions = g_uVolumeSideLength / m_uRegionSideLength; + m_uVolumeDepthInRegions = g_uVolumeSideLength / m_uRegionSideLength; //Our volume is broken down into cuboid regions, and we create one mesh for each region. //This three-level for loop iterates over each region. @@ -180,8 +180,9 @@ void OpenGLWidget::paintGL() glMatrixMode(GL_MODELVIEW); // Select The Model View Matrix glLoadIdentity(); // Reset The Current Modelview Matrix + float diag_len = sqrtf(static_cast(255 * 255 * 3)); //Moves the camera back so we can see the volume - glTranslatef(0.0f, 0.0f, -m_volData->getDiagonalLength()); + glTranslatef(0.0f, 0.0f, -diag_len); glRotatef(m_xRotation, 1.0f, 0.0f, 0.0f); glRotatef(m_yRotation, 0.0f, 1.0f, 0.0f); @@ -248,7 +249,8 @@ void OpenGLWidget::setupProjectionMatrix(void) glMatrixMode(GL_PROJECTION); glLoadIdentity(); - float frustumSize = m_volData->getDiagonalLength() / 2.0f; + float diag_len = sqrtf(static_cast(255 * 255 * 3)); + float frustumSize = diag_len / 2.0f; float aspect = static_cast(width()) / static_cast(height()); glOrtho(frustumSize*aspect, -frustumSize*aspect, frustumSize, -frustumSize, 1.0, 5000); diff --git a/examples/OpenGL/Shapes.cpp b/examples/OpenGL/Shapes.cpp index 023f2d54..fdeec22e 100644 --- a/examples/OpenGL/Shapes.cpp +++ b/examples/OpenGL/Shapes.cpp @@ -27,17 +27,19 @@ freely, subject to the following restrictions: using namespace PolyVox; +const uint16_t g_uVolumeSideLength = 128; + void createSphereInVolume(Volume& volData, float fRadius, uint8_t uValue) { //This vector hold the position of the center of the volume - Vector3DFloat v3dVolCenter(volData.getWidth() / 2, volData.getHeight() / 2, volData.getDepth() / 2); + Vector3DFloat v3dVolCenter( g_uVolumeSideLength / 2, g_uVolumeSideLength / 2, g_uVolumeSideLength / 2); //This three-level for loop iterates over every voxel in the volume - for (int z = 0; z < volData.getWidth(); z++) + for (int z = 0; z < g_uVolumeSideLength ; z++) { - for (int y = 0; y < volData.getHeight(); y++) + for (int y = 0; y < g_uVolumeSideLength; y++) { - for (int x = 0; x < volData.getDepth(); x++) + for (int x = 0; x < g_uVolumeSideLength; x++) { //Store our current position as a vector... Vector3DFloat v3dCurrentPos(x,y,z); diff --git a/examples/OpenGL/main.cpp b/examples/OpenGL/main.cpp index 7e5e8fab..9129092f 100644 --- a/examples/OpenGL/main.cpp +++ b/examples/OpenGL/main.cpp @@ -72,7 +72,7 @@ void exampleLog(string message, int severity) int main(int argc, char *argv[]) { logHandler = &exampleLog; - Volume volData(g_uVolumeSideLength, g_uVolumeSideLength, g_uVolumeSideLength); + Volume volData; //Make our volume contain a sphere in the center. int32_t minPos = 0; diff --git a/library/PolyVoxCore/include/Filters.inl b/library/PolyVoxCore/include/Filters.inl index a3c2f958..f8db8a51 100644 --- a/library/PolyVoxCore/include/Filters.inl +++ b/library/PolyVoxCore/include/Filters.inl @@ -6,7 +6,6 @@ namespace PolyVox void smoothRegion(Volume& volData, const Region& regionToSmooth) { Region croppedRegion = regionToSmooth; - croppedRegion.cropTo(volData.getEnclosingRegion()); Array<3, uint16_t> temp(ArraySizes(croppedRegion.width())(croppedRegion.height())(croppedRegion.depth())); diff --git a/library/PolyVoxCore/include/PolyVoxImpl/Block.h b/library/PolyVoxCore/include/PolyVoxImpl/Block.h index 16e034ad..5503252b 100644 --- a/library/PolyVoxCore/include/PolyVoxImpl/Block.h +++ b/library/PolyVoxCore/include/PolyVoxImpl/Block.h @@ -69,7 +69,9 @@ namespace PolyVox uint16_t m_uSideLength; uint8_t m_uSideLengthPower; bool m_bIsCompressed; - bool m_bIsUncompressedDataModified; + bool m_bIsUncompressedDataModified; + uint32_t m_uTimestamp; + uint32_t m_uUncompressedIndex; }; } diff --git a/library/PolyVoxCore/include/PolyVoxImpl/Block.inl b/library/PolyVoxCore/include/PolyVoxImpl/Block.inl index 405e9132..62d4c4c6 100644 --- a/library/PolyVoxCore/include/PolyVoxImpl/Block.inl +++ b/library/PolyVoxCore/include/PolyVoxImpl/Block.inl @@ -36,9 +36,11 @@ namespace PolyVox Block::Block(uint16_t uSideLength) :m_uSideLength(0) ,m_uSideLengthPower(0) - ,m_tUncompressedData(0) + ,m_tUncompressedData(NULL) ,m_bIsCompressed(true) ,m_bIsUncompressedDataModified(true) + ,m_uTimestamp(0) + ,m_uUncompressedIndex((std::numeric_limits::max)()) { if(uSideLength != 0) { @@ -103,14 +105,20 @@ namespace PolyVox template void Block::fill(VoxelType tValue) { - assert(m_tUncompressedData); + if(!m_bIsCompressed) { + //The memset *may* be faster than the std::fill(), but it doesn't compile nicely + //in 64-bit mode as casting the pointer to an int causes a loss of precision. + const uint32_t uNoOfVoxels = m_uSideLength * m_uSideLength * m_uSideLength; + std::fill(m_tUncompressedData, m_tUncompressedData + uNoOfVoxels, tValue); - //The memset *may* be faster than the std::fill(), but it doesn't compile nicely - //in 64-bit mode as casting the pointer to an int causes a loss of precision. - const uint32_t uNoOfVoxels = m_uSideLength * m_uSideLength * m_uSideLength; - std::fill(m_tUncompressedData, m_tUncompressedData + uNoOfVoxels, tValue); - - m_bIsUncompressedDataModified = true; + m_bIsUncompressedDataModified = true; + } else { + RunlengthEntry rle; + rle.length = m_uSideLength*m_uSideLength*m_uSideLength; + rle.value = tValue; + m_vecCompressedData.clear(); + m_vecCompressedData.push_back(rle); + } } template @@ -128,6 +136,8 @@ namespace PolyVox //Compute the side length m_uSideLength = uSideLength; m_uSideLengthPower = logBase2(uSideLength); + + Block::fill(VoxelType()); } template diff --git a/library/PolyVoxCore/include/Volume.h b/library/PolyVoxCore/include/Volume.h index 744f189d..9e705091 100644 --- a/library/PolyVoxCore/include/Volume.h +++ b/library/PolyVoxCore/include/Volume.h @@ -32,6 +32,7 @@ freely, subject to the following restrictions: #include #include #include +#include namespace PolyVox { @@ -115,33 +116,17 @@ namespace PolyVox struct UncompressedBlock { - uint32_t uBlockIndex; + Vector3DInt32 v3dBlockIndex; VoxelType* data; }; public: /// Constructor - Volume(int32_t uWidth, int32_t uHeight, int32_t uDepth, uint16_t uBlockSideLength = 32); + Volume(uint16_t uBlockSideLength = 32); /// Destructor - ~Volume(); + ~Volume(); - /// Gets the value used for voxels which are outside the volume - VoxelType getBorderValue(void) const; - /// Gets a Region representing the extents of the Volume. - Region getEnclosingRegion(void) const; - /// Gets the width of the volume in voxels. - int32_t getWidth(void) const; - /// Gets the height of the volume in voxels. - int32_t getHeight(void) const; - /// Gets the depth of the volume in voxels. - int32_t getDepth(void) const; - /// Gets the length of the longest side in voxels - int32_t getLongestSideLength(void) const; - /// Gets the length of the shortest side in voxels - int32_t getShortestSideLength(void) const; - /// Gets the length of the diagonal in voxels - float getDiagonalLength(void) const; /// Gets a voxel by x,y,z position VoxelType getVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos) const; /// Gets a voxel by 3D vector position @@ -149,6 +134,8 @@ namespace PolyVox /// Sets the number of blocks for which uncompressed data is stored. void setBlockCacheSize(uint16_t uBlockCacheSize); + /// Sets the number of blocks which can be in memory before unload is called + void setMaxBlocksLoaded(uint16_t uBlockCacheSize); /// Sets the value used for voxels which are outside the volume void setBorderValue(const VoxelType& tBorder); /// Sets the voxel at an x,y,z position @@ -159,15 +146,25 @@ namespace PolyVox void clearBlockCache(void); float calculateCompressionRatio(void); uint32_t calculateSizeInBytes(void); - void useCompatibilityMode(void); /// Resizes the volume to the specified dimensions - void resize(int32_t uWidth, int32_t uHeight, int32_t uDepth, uint16_t uBlockSideLength = 32); - + void resize(uint16_t uBlockSideLength = 32); + /// gets called when a new region is allocated and needs to be filled + /// NOTE: accessing ANY voxels outside this region during the process of this function + /// is absolutely unsafe + std::function&, Region)> m_LoadCallback; + /// this function can be called by m_LoadCallback without causing any weird effects + bool load_setVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue) const; + /// gets called when a Region needs to be stored by the user, because Volume will erase it right after + /// this function returns + /// NOTE: accessing ANY voxels outside this region during the process of this function + /// is absolutely unsafe + std::function&, Region)> m_UnloadCallback; private: Block* getUncompressedBlock(int32_t uBlockX, int32_t uBlockY, int32_t uBlockZ) const; + void eraseBlock(typename std::map >::iterator itBlock) const; //The block data - mutable std::vector< Block > m_pBlocks; + mutable std::map > m_pBlocks; //The cache of uncompressed blocks. The uncompressed block data and the timestamps are stored here rather //than in the Block class. This is so that in the future each VolumeIterator might to maintain its own cache @@ -176,8 +173,10 @@ namespace PolyVox mutable std::vector< UncompressedBlock > m_vecUncompressedBlockCache; mutable std::vector m_pUncompressedTimestamps; mutable uint32_t m_uTimestamper; - uint32_t m_ulastAccessedBlockIndex; + mutable Vector3DInt32 m_v3dLastAccessedBlockPos; + mutable Vector3DInt32 m_v3dLoadBlockPos; uint32_t m_uMaxUncompressedBlockCacheSize; + uint32_t m_uMaxBlocksLoaded; //We don't store an actual Block for the border, just the uncompressed data. This is partly because the border //block does not have a position (so can't be passed to getUncompressedBlock()) and partly because there's a @@ -185,22 +184,8 @@ namespace PolyVox //the VolumeIterator can do it's usual pointer arithmetic without needing to know it's gone outside the volume. VoxelType* m_pUncompressedBorderData; - uint32_t m_uNoOfBlocksInVolume; - - int32_t m_uWidthInBlocks; - int32_t m_uHeightInBlocks; - int32_t m_uDepthInBlocks; - - int32_t m_uWidth; - int32_t m_uHeight; - int32_t m_uDepth; - uint8_t m_uBlockSideLengthPower; uint16_t m_uBlockSideLength; - - int32_t m_uLongestSideLength; - int32_t m_uShortestSideLength; - float m_fDiagonalLength; }; //Some handy typedefs diff --git a/library/PolyVoxCore/include/Volume.inl b/library/PolyVoxCore/include/Volume.inl index 0cad595a..fecef294 100644 --- a/library/PolyVoxCore/include/Volume.inl +++ b/library/PolyVoxCore/include/Volume.inl @@ -48,17 +48,19 @@ namespace PolyVox /// the default if you are not sure what to choose here. //////////////////////////////////////////////////////////////////////////////// template - Volume::Volume(int32_t uWidth, int32_t uHeight, int32_t uDepth, uint16_t uBlockSideLength) - :m_uTimestamper(0) + Volume::Volume(uint16_t uBlockSideLength) + : + m_uTimestamper(0) ,m_uMaxUncompressedBlockCacheSize(256) + ,m_uMaxBlocksLoaded(4096) ,m_uBlockSideLength(uBlockSideLength) ,m_pUncompressedBorderData(0) - ,m_ulastAccessedBlockIndex((std::numeric_limits::max)()) //An invalid index + ,m_v3dLastAccessedBlockPos((std::numeric_limits::max)(), (std::numeric_limits::max)(), (std::numeric_limits::max)()) //An invalid index { setBlockCacheSize(m_uMaxUncompressedBlockCacheSize); //Create a volume of the right size. - resize(uWidth, uHeight, uDepth, uBlockSideLength); + resize(uBlockSideLength); } //////////////////////////////////////////////////////////////////////////////// @@ -67,95 +69,10 @@ namespace PolyVox template Volume::~Volume() { - } - - //////////////////////////////////////////////////////////////////////////////// - /// The border value is returned whenever an atempt is made to read a voxel which - /// is outside the extents of the volume. - /// \return The value used for voxels outside of the volume - //////////////////////////////////////////////////////////////////////////////// - template - VoxelType Volume::getBorderValue(void) const - { - /*Block* pUncompressedBorderBlock = getUncompressedBlock(const_cast*>(&m_pBorderBlock)); - return pUncompressedBorderBlock->getVoxelAt(0,0,0);*/ - return *m_pUncompressedBorderData; - } - - //////////////////////////////////////////////////////////////////////////////// - /// The result will always have a lower corner at (0,0,0) and an upper corner at one - /// less than the side length. For example, if a volume has dimensions 256x512x1024 - /// then the upper corner of the enclosing region will be at (255,511,1023). - /// \return A Region representing the extent of the volume. - //////////////////////////////////////////////////////////////////////////////// - template - Region Volume::getEnclosingRegion(void) const - { - return Region(Vector3DInt32(0,0,0), Vector3DInt32(m_uWidth-1,m_uHeight-1,m_uDepth-1)); - } - - //////////////////////////////////////////////////////////////////////////////// - /// \return The width of the volume in voxels - /// \sa getHeight(), getDepth() - //////////////////////////////////////////////////////////////////////////////// - template - int32_t Volume::getWidth(void) const - { - return m_uWidth; - } - - //////////////////////////////////////////////////////////////////////////////// - /// \return The height of the volume in voxels - /// \sa getWidth(), getDepth() - //////////////////////////////////////////////////////////////////////////////// - template - int32_t Volume::getHeight(void) const - { - return m_uHeight; - } - - //////////////////////////////////////////////////////////////////////////////// - /// \return The depth of the volume in voxels - /// \sa getWidth(), getHeight() - //////////////////////////////////////////////////////////////////////////////// - template - int32_t Volume::getDepth(void) const - { - return m_uDepth; - } - - //////////////////////////////////////////////////////////////////////////////// - /// \return The length of the shortest side in voxels. For example, if a volume has - /// dimensions 256x512x1024 this function will return 256. - /// \sa getLongestSideLength(), getDiagonalLength() - //////////////////////////////////////////////////////////////////////////////// - template - int32_t Volume::getShortestSideLength(void) const - { - return m_uShortestSideLength; - } - - //////////////////////////////////////////////////////////////////////////////// - /// \return The length of the longest side in voxels. For example, if a volume has - /// dimensions 256x512x1024 this function will return 1024. - /// \sa getShortestSideLength(), getDiagonalLength() - //////////////////////////////////////////////////////////////////////////////// - template - int32_t Volume::getLongestSideLength(void) const - { - return m_uLongestSideLength; - } - - //////////////////////////////////////////////////////////////////////////////// - /// \return The length of the diagonal in voxels. For example, if a volume has - /// dimensions 256x512x1024 this function will return sqrt(256*256+512*512+1024*1024) - /// = 1173.139. This value is computed on volume creation so retrieving it is fast. - /// \sa getShortestSideLength(), getLongestSideLength() - //////////////////////////////////////////////////////////////////////////////// - template - float Volume::getDiagonalLength(void) const - { - return m_fDiagonalLength; + typename std::map >::iterator i; + for(i = m_pBlocks.begin(); i != m_pBlocks.end(); i = m_pBlocks.begin()) { + eraseBlock(i); + } } //////////////////////////////////////////////////////////////////////////////// @@ -167,26 +84,17 @@ namespace PolyVox template VoxelType Volume::getVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos) const { - //We don't use getEnclosingRegion here because we care - //about speed - if((uXPos >=0) && (uYPos >=0) && (uZPos >=0) && (uXPos < getWidth()) && (uYPos < getHeight()) && (uZPos < getDepth())) - { - const int32_t blockX = uXPos >> m_uBlockSideLengthPower; - const int32_t blockY = uYPos >> m_uBlockSideLengthPower; - const int32_t blockZ = uZPos >> m_uBlockSideLengthPower; + 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); + const uint16_t xOffset = uXPos - (blockX << m_uBlockSideLengthPower); + const uint16_t yOffset = uYPos - (blockY << m_uBlockSideLengthPower); + const uint16_t zOffset = uZPos - (blockZ << m_uBlockSideLengthPower); - Block* pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ); + Block* pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ); - return pUncompressedBlock->getVoxelAt(xOffset,yOffset,zOffset); - } - else - { - return getBorderValue(); - } + return pUncompressedBlock->getVoxelAt(xOffset,yOffset,zOffset); } //////////////////////////////////////////////////////////////////////////////// @@ -214,14 +122,27 @@ namespace PolyVox } //////////////////////////////////////////////////////////////////////////////// - /// \param tBorder The value to use for voxels outside the volume. + /// Increasing the number of blocks in memory causes fewer calls to load/unload + /// \param uMaxBlocks The number of blocks //////////////////////////////////////////////////////////////////////////////// template - void Volume::setBorderValue(const VoxelType& tBorder) + void Volume::setMaxBlocksLoaded(uint16_t uMaxBlocks) { - /*Block* pUncompressedBorderBlock = getUncompressedBlock(&m_pBorderBlock); - return pUncompressedBorderBlock->fill(tBorder);*/ - std::fill(m_pUncompressedBorderData, m_pUncompressedBorderData + m_uBlockSideLength * m_uBlockSideLength * m_uBlockSideLength, tBorder); + if(uMaxBlocks < m_pBlocks.size()) { + std::cout << uMaxBlocks << ", " << m_pBlocks.size() << ", " << m_pBlocks.size() - uMaxBlocks << std::endl; + // we need to unload some blocks + for(int j = 0; j < m_pBlocks.size() - uMaxBlocks; j++) { + typename std::map >::iterator i; + typename std::map >::iterator itUnloadBlock = m_pBlocks.begin(); + for(i = m_pBlocks.begin(); i != m_pBlocks.end(); i++) { + if(i->second.m_uTimestamp < itUnloadBlock->second.m_uTimestamp) { + itUnloadBlock = i; + } + } + eraseBlock(itUnloadBlock); + } + } + m_uMaxBlocksLoaded = uMaxBlocks; } //////////////////////////////////////////////////////////////////////////////// @@ -234,30 +155,20 @@ namespace PolyVox template bool Volume::setVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue) { - //We don't use getEnclosingRegion here because we care - //about speed - if((uXPos >=0) && (uYPos >=0) && (uZPos >=0) && (uXPos < getWidth()) && (uYPos < getHeight()) && (uZPos < getDepth())) - { - const int32_t blockX = uXPos >> m_uBlockSideLengthPower; - const int32_t blockY = uYPos >> m_uBlockSideLengthPower; - const int32_t blockZ = uZPos >> m_uBlockSideLengthPower; + 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); + const uint16_t xOffset = uXPos - (blockX << m_uBlockSideLengthPower); + const uint16_t yOffset = uYPos - (blockY << m_uBlockSideLengthPower); + const uint16_t zOffset = uZPos - (blockZ << m_uBlockSideLengthPower); - Block* pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ); + Block* pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ); - pUncompressedBlock->setVoxelAt(xOffset,yOffset,zOffset, tValue); + pUncompressedBlock->setVoxelAt(xOffset,yOffset,zOffset, tValue); - //Return true to indicate that we modified a voxel. - return true; - } - else - { - //Return false to indicate that no voxel was modified. - return false; - } + //Return true to indicate that we modified a voxel. + return true; } //////////////////////////////////////////////////////////////////////////////// @@ -276,7 +187,7 @@ namespace PolyVox { for(uint32_t ct = 0; ct < m_vecUncompressedBlockCache.size(); ct++) { - m_pBlocks[m_vecUncompressedBlockCache[ct].uBlockIndex].compress(); + m_pBlocks[m_vecUncompressedBlockCache[ct].v3dBlockIndex].compress(); delete[] m_vecUncompressedBlockCache[ct].data; } m_vecUncompressedBlockCache.clear(); @@ -284,9 +195,6 @@ namespace PolyVox //////////////////////////////////////////////////////////////////////////////// /// Note: Calling this function will destroy all existing data in the volume. - /// \param uWidth The desired width in voxels. This must be a power of two. - /// \param uHeight The desired height in voxels. This must be a power of two. - /// \param uDepth The desired depth in voxels. This must be a power of two. /// \param uBlockSideLength The size of the blocks which make up the volume. Small /// blocks are more likely to be homogeneous (so more easily shared) and have better /// cache behaviour. However, there is a memory overhead per block so if they are @@ -295,17 +203,11 @@ namespace PolyVox /// the default if you are not sure what to choose here. //////////////////////////////////////////////////////////////////////////////// template - void Volume::resize(int32_t uWidth, int32_t uHeight, int32_t uDepth, uint16_t uBlockSideLength) + void Volume::resize(uint16_t uBlockSideLength) { //Debug mode validation assert(uBlockSideLength > 0); assert(isPowerOf2(uBlockSideLength)); - assert(uBlockSideLength <= uWidth); - assert(uBlockSideLength <= uHeight); - assert(uBlockSideLength <= uDepth); - assert(0 < uWidth); - assert(0 < uHeight); - assert(0 < uDepth); //Release mode validation if(uBlockSideLength == 0) @@ -316,103 +218,112 @@ namespace PolyVox { throw std::invalid_argument("Block side length must be a power of two."); } - if(uBlockSideLength > uWidth) - { - throw std::invalid_argument("Block side length cannot be greater than volume width."); - } - if(uBlockSideLength > uHeight) - { - throw std::invalid_argument("Block side length cannot be greater than volume height."); - } - if(uBlockSideLength > uDepth) - { - throw std::invalid_argument("Block side length cannot be greater than volume depth."); - } - if(0 >= uWidth) - { - throw std::invalid_argument("Volume width cannot be smaller than 1."); - } - if(0 >= uHeight) - { - throw std::invalid_argument("Volume height cannot be smaller than 1."); - } - if(0 >= uDepth) - { - throw std::invalid_argument("Volume depth cannot be smaller than 1."); - } //Clear the previous data m_pBlocks.clear(); m_pUncompressedTimestamps.clear(); - //Compute the volume side lengths - m_uWidth = uWidth; - m_uHeight = uHeight; - m_uDepth = uDepth; + m_pUncompressedTimestamps.resize(m_uMaxUncompressedBlockCacheSize, 0); //Compute the block side length m_uBlockSideLength = uBlockSideLength; m_uBlockSideLengthPower = logBase2(m_uBlockSideLength); + } - //Compute the side lengths in blocks - m_uWidthInBlocks = m_uWidth / m_uBlockSideLength; - m_uHeightInBlocks = m_uHeight / m_uBlockSideLength; - m_uDepthInBlocks = m_uDepth / m_uBlockSideLength; + template + void Volume::eraseBlock(typename std::map >::iterator itBlock) const + { + Vector3DInt32 v3dPos = itBlock->first; + Vector3DInt32 v3dLower(v3dPos.getX() << m_uBlockSideLengthPower, v3dPos.getY() << m_uBlockSideLengthPower, v3dPos.getZ() << m_uBlockSideLengthPower); + Vector3DInt32 v3dUpper = v3dLower + Vector3DInt32(m_uBlockSideLength-1, m_uBlockSideLength-1, m_uBlockSideLength-1); + Region reg(v3dLower, v3dUpper); + if(m_UnloadCallback) { + m_UnloadCallback(std::ref(*this), reg); + } + m_pBlocks.erase(itBlock); + } - //Compute number of blocks in the volume - m_uNoOfBlocksInVolume = m_uWidthInBlocks * m_uHeightInBlocks * m_uDepthInBlocks; - - //Create the blocks - m_pBlocks.resize(m_uNoOfBlocksInVolume); - for(uint32_t i = 0; i < m_uNoOfBlocksInVolume; ++i) - { - m_pBlocks[i].initialise(m_uBlockSideLength); + template + bool Volume::load_setVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue) const + { + const int32_t blockX = uXPos >> m_uBlockSideLengthPower; + const int32_t blockY = uYPos >> m_uBlockSideLengthPower; + const int32_t blockZ = uZPos >> m_uBlockSideLengthPower; + assert(blockX == m_v3dLoadBlockPos.getX()); + assert(blockY == m_v3dLoadBlockPos.getY()); + assert(blockZ == m_v3dLoadBlockPos.getZ()); + if(blockX != m_v3dLoadBlockPos.getX() && blockY != m_v3dLoadBlockPos.getY() && blockZ != m_v3dLoadBlockPos.getZ()) { + throw(std::invalid_argument("you are not allowed to write to any voxels outside the designated region")); } - m_pUncompressedTimestamps.resize(m_uNoOfBlocksInVolume); - std::fill(m_pUncompressedTimestamps.begin(), m_pUncompressedTimestamps.end(), 0); + const uint16_t xOffset = uXPos - (blockX << m_uBlockSideLengthPower); + const uint16_t yOffset = uYPos - (blockY << m_uBlockSideLengthPower); + const uint16_t zOffset = uZPos - (blockZ << m_uBlockSideLengthPower); - //Create the border block - m_pUncompressedBorderData = new VoxelType[m_uBlockSideLength * m_uBlockSideLength * m_uBlockSideLength]; - std::fill(m_pUncompressedBorderData, m_pUncompressedBorderData + m_uBlockSideLength * m_uBlockSideLength * m_uBlockSideLength, VoxelType()); + Block* pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ); - //Other properties we might find useful later - m_uLongestSideLength = (std::max)((std::max)(m_uWidth,m_uHeight),m_uDepth); - m_uShortestSideLength = (std::min)((std::min)(m_uWidth,m_uHeight),m_uDepth); - m_fDiagonalLength = sqrtf(static_cast(m_uWidth * m_uWidth + m_uHeight * m_uHeight + m_uDepth * m_uDepth)); + pUncompressedBlock->setVoxelAt(xOffset,yOffset,zOffset, tValue); + + //Return true to indicate that we modified a voxel. + return true; } + template Block* Volume::getUncompressedBlock(int32_t uBlockX, int32_t uBlockY, int32_t uBlockZ) const { - assert(uBlockX >= 0); - assert(uBlockY >= 0); - assert(uBlockZ >= 0); - assert(uBlockX < m_uWidthInBlocks); - assert(uBlockY < m_uHeightInBlocks); - assert(uBlockZ < m_uDepthInBlocks); - //Compute the block's index from it's position. - const int32_t uBlockIndex = - uBlockX + - uBlockY * m_uWidthInBlocks + - uBlockZ * m_uWidthInBlocks * m_uHeightInBlocks; - assert(uBlockIndex < m_uNoOfBlocksInVolume); - assert(uBlockIndex >= 0); + Vector3DInt32 v3dBlockPos(uBlockX, uBlockY, uBlockZ); + typename std::map >::iterator itBlock = m_pBlocks.find(v3dBlockPos); + // check whether the block is already loaded + if(itBlock == m_pBlocks.end()) { + // it is not loaded + // check wether another block needs to be unloaded before this one can be loaded + if(m_pBlocks.size() == m_uMaxBlocksLoaded) { + // find the least recently used block + typename std::map >::iterator i; + typename std::map >::iterator itUnloadBlock = m_pBlocks.begin(); + for(i = m_pBlocks.begin(); i != m_pBlocks.end(); i++) { + if(i->second.m_uTimestamp < itUnloadBlock->second.m_uTimestamp) { + itUnloadBlock = i; + } + } + eraseBlock(itUnloadBlock); + } + 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); + // create the new block + m_pBlocks[v3dBlockPos] = Block(m_uBlockSideLength); + itBlock = m_pBlocks.find(v3dBlockPos); + // fill it with data (well currently fill it with nothingness) + // "load" will actually call setVoxel, which will in turn call this function again but the block will be found + // so this if(itBlock == m_pBlocks.end()) never is entered + + m_v3dLoadBlockPos = v3dBlockPos; + if(m_LoadCallback) { + m_LoadCallback(std::ref(*this), reg); + } + m_v3dLoadBlockPos = Vector3DInt32((std::numeric_limits::max)(), (std::numeric_limits::max)(), (std::numeric_limits::max)()); + } + //Get the block - Block* block = &(m_pBlocks[uBlockIndex]); + Block* block = &(itBlock->second); //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. - if(uBlockIndex == m_ulastAccessedBlockIndex) + if(v3dBlockPos == m_v3dLastAccessedBlockPos) { return block; } + m_v3dLastAccessedBlockPos = v3dBlockPos; - m_pUncompressedTimestamps[uBlockIndex] = ++m_uTimestamper; + m_uTimestamper++; + block->m_uTimestamp = m_uTimestamper; if(block->m_bIsCompressed == false) { + m_pUncompressedTimestamps[block->m_uUncompressedIndex] = m_uTimestamper; return block; } @@ -427,28 +338,27 @@ namespace PolyVox uint32_t uLeastRecentTimestamp = (std::numeric_limits::max)(); // you said not int64 ;) for(uint32_t ct = 0; ct < m_vecUncompressedBlockCache.size(); ct++) { - if(m_pUncompressedTimestamps[m_vecUncompressedBlockCache[ct].uBlockIndex] < uLeastRecentTimestamp) + if(m_pUncompressedTimestamps[ct] < uLeastRecentTimestamp) { - uLeastRecentTimestamp = m_pUncompressedTimestamps[m_vecUncompressedBlockCache[ct].uBlockIndex]; + uLeastRecentTimestamp = m_pUncompressedTimestamps[ct]; leastRecentlyUsedBlockIndex = ct; } } uUncompressedBlockIndex = leastRecentlyUsedBlockIndex; - m_pBlocks[m_vecUncompressedBlockCache[leastRecentlyUsedBlockIndex].uBlockIndex].compress(); - m_pBlocks[m_vecUncompressedBlockCache[leastRecentlyUsedBlockIndex].uBlockIndex].m_tUncompressedData = 0; - m_vecUncompressedBlockCache[leastRecentlyUsedBlockIndex].uBlockIndex = uBlockIndex; + m_pBlocks[m_vecUncompressedBlockCache[leastRecentlyUsedBlockIndex].v3dBlockIndex].compress(); + m_vecUncompressedBlockCache[leastRecentlyUsedBlockIndex].v3dBlockIndex = v3dBlockPos; } else { UncompressedBlock uncompressedBlock; //uncompressedBlock.block = block; - uncompressedBlock.uBlockIndex = uBlockIndex; + uncompressedBlock.v3dBlockIndex = v3dBlockPos; uncompressedBlock.data = new VoxelType[m_uBlockSideLength * m_uBlockSideLength * m_uBlockSideLength]; m_vecUncompressedBlockCache.push_back(uncompressedBlock); uUncompressedBlockIndex = m_vecUncompressedBlockCache.size() - 1; } - + block->m_uUncompressedIndex = uUncompressedBlockIndex; block->uncompress(m_vecUncompressedBlockCache[uUncompressedBlockIndex].data); return block; @@ -457,7 +367,7 @@ namespace PolyVox template float Volume::calculateCompressionRatio(void) { - float fRawSize = m_uWidth * m_uHeight * m_uDepth * sizeof(VoxelType); + float fRawSize = m_pBlocks.size() * m_uBlockSideLength * m_uBlockSideLength* m_uBlockSideLength * sizeof(VoxelType); float fCompressedSize = calculateSizeInBytes(); return fCompressedSize/fRawSize; } @@ -468,9 +378,9 @@ namespace PolyVox uint32_t uSizeInBytes = sizeof(Volume); //Memory used by the blocks - for(uint32_t i = 0; i < m_uNoOfBlocksInVolume; ++i) - { - uSizeInBytes += m_pBlocks[i].calculateSizeInBytes(); + typename std::map >::iterator i; + for(i = m_pBlocks.begin(); i != m_pBlocks.end(); i++) { + i->second.calculateSizeInBytes(); } //Memory used by the block cache. @@ -487,20 +397,4 @@ namespace PolyVox return uSizeInBytes; } - template - void Volume::useCompatibilityMode(void) - { - setBlockCacheSize(m_uNoOfBlocksInVolume * 2); //Times two gives space to spare - - for(int32_t z = 0; z < m_uDepthInBlocks; z++) - { - for(int32_t y = 0; y < m_uHeightInBlocks; y++) - { - for(int32_t x = 0; x < m_uWidthInBlocks; x++) - { - getUncompressedBlock(x,y,z); - } - } - } - } } diff --git a/library/PolyVoxCore/include/VolumeSampler.inl b/library/PolyVoxCore/include/VolumeSampler.inl index 2e797756..c5fc778d 100644 --- a/library/PolyVoxCore/include/VolumeSampler.inl +++ b/library/PolyVoxCore/include/VolumeSampler.inl @@ -26,6 +26,11 @@ freely, subject to the following restrictions: #include "Vector.h" #include "Region.h" +#define BORDER_LOW(x) ((( x >> mVolume->m_uBlockSideLengthPower) << mVolume->m_uBlockSideLengthPower) != x) +#define BORDER_HIGH(x) ((( (x+1) >> mVolume->m_uBlockSideLengthPower) << mVolume->m_uBlockSideLengthPower) != (x+1)) +//#define BORDER_LOW(x) (( x % mVolume->m_uBlockSideLength) != 0) +//#define BORDER_HIGH(x) (( x % mVolume->m_uBlockSideLength) != mVolume->m_uBlockSideLength - 1) + #include namespace PolyVox { @@ -148,21 +153,9 @@ namespace PolyVox uYPosInBlock * mVolume->m_uBlockSideLength + uZPosInBlock * mVolume->m_uBlockSideLength * mVolume->m_uBlockSideLength; - if((uXBlock < mVolume->m_uWidthInBlocks) && (uYBlock < mVolume->m_uHeightInBlocks) && (uZBlock < mVolume->m_uDepthInBlocks) && (uXBlock >= 0) && (uYBlock >= 0) && (uZBlock >=0)) - { - const uint32_t uBlockIndexInVolume = uXBlock + - uYBlock * mVolume->m_uWidthInBlocks + - uZBlock * mVolume->m_uWidthInBlocks * mVolume->m_uHeightInBlocks; - const Block& currentBlock = mVolume->m_pBlocks[uBlockIndexInVolume]; + Block* pUncompressedCurrentBlock = mVolume->getUncompressedBlock(uXBlock, uYBlock, uZBlock); - Block* pUncompressedCurrentBlock = mVolume->getUncompressedBlock(uXBlock, uYBlock, uZBlock); - - mCurrentVoxel = pUncompressedCurrentBlock->m_tUncompressedData + uVoxelIndexInBlock; - } - else - { - mCurrentVoxel = mVolume->m_pUncompressedBorderData + uVoxelIndexInBlock; - } + mCurrentVoxel = pUncompressedCurrentBlock->m_tUncompressedData + uVoxelIndexInBlock; } template @@ -264,7 +257,7 @@ namespace PolyVox template VoxelType VolumeSampler::peekVoxel1nx1ny1nz(void) const { - if((mXPosInVolume%mVolume->m_uBlockSideLength != 0) && (mYPosInVolume%mVolume->m_uBlockSideLength != 0) && (mZPosInVolume%mVolume->m_uBlockSideLength != 0)) + if( BORDER_LOW(mXPosInVolume) && BORDER_LOW(mYPosInVolume) && BORDER_LOW(mZPosInVolume) ) { return *(mCurrentVoxel - 1 - mVolume->m_uBlockSideLength - mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength); } @@ -274,7 +267,7 @@ namespace PolyVox template VoxelType VolumeSampler::peekVoxel1nx1ny0pz(void) const { - if((mXPosInVolume%mVolume->m_uBlockSideLength != 0) && (mYPosInVolume%mVolume->m_uBlockSideLength != 0)) + if( BORDER_LOW(mXPosInVolume) && BORDER_LOW(mYPosInVolume) ) { return *(mCurrentVoxel - 1 - mVolume->m_uBlockSideLength); } @@ -284,7 +277,7 @@ namespace PolyVox template VoxelType VolumeSampler::peekVoxel1nx1ny1pz(void) const { - if((mXPosInVolume%mVolume->m_uBlockSideLength != 0) && (mYPosInVolume%mVolume->m_uBlockSideLength != 0) && (mZPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1)) + if( BORDER_LOW(mXPosInVolume) && BORDER_LOW(mYPosInVolume) && BORDER_HIGH(mZPosInVolume) ) { return *(mCurrentVoxel - 1 - mVolume->m_uBlockSideLength + mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength); } @@ -294,7 +287,7 @@ namespace PolyVox template VoxelType VolumeSampler::peekVoxel1nx0py1nz(void) const { - if((mXPosInVolume%mVolume->m_uBlockSideLength != 0) && (mZPosInVolume%mVolume->m_uBlockSideLength != 0)) + if( BORDER_LOW(mXPosInVolume) && BORDER_LOW(mZPosInVolume) ) { return *(mCurrentVoxel - 1 - mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength); } @@ -304,7 +297,7 @@ namespace PolyVox template VoxelType VolumeSampler::peekVoxel1nx0py0pz(void) const { - if((mXPosInVolume%mVolume->m_uBlockSideLength != 0)) + if( BORDER_LOW(mXPosInVolume) ) { return *(mCurrentVoxel - 1); } @@ -314,7 +307,7 @@ namespace PolyVox template VoxelType VolumeSampler::peekVoxel1nx0py1pz(void) const { - if((mXPosInVolume%mVolume->m_uBlockSideLength != 0) && (mZPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1)) + if( BORDER_LOW(mXPosInVolume) && BORDER_HIGH(mZPosInVolume) ) { return *(mCurrentVoxel - 1 + mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength); } @@ -324,7 +317,7 @@ namespace PolyVox template VoxelType VolumeSampler::peekVoxel1nx1py1nz(void) const { - if((mXPosInVolume%mVolume->m_uBlockSideLength != 0) && (mYPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1) && (mZPosInVolume%mVolume->m_uBlockSideLength != 0)) + if( BORDER_LOW(mXPosInVolume) && BORDER_HIGH(mYPosInVolume) && BORDER_LOW(mYPosInVolume) ) { return *(mCurrentVoxel - 1 + mVolume->m_uBlockSideLength - mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength); } @@ -334,7 +327,7 @@ namespace PolyVox template VoxelType VolumeSampler::peekVoxel1nx1py0pz(void) const { - if((mXPosInVolume%mVolume->m_uBlockSideLength != 0) && (mYPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1)) + if( BORDER_LOW(mXPosInVolume) && BORDER_HIGH(mYPosInVolume) ) { return *(mCurrentVoxel - 1 + mVolume->m_uBlockSideLength); } @@ -344,7 +337,7 @@ namespace PolyVox template VoxelType VolumeSampler::peekVoxel1nx1py1pz(void) const { - if((mXPosInVolume%mVolume->m_uBlockSideLength != 0) && (mYPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1) && (mZPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1)) + if( BORDER_LOW(mXPosInVolume) && BORDER_HIGH(mYPosInVolume) && BORDER_HIGH(mZPosInVolume) ) { return *(mCurrentVoxel - 1 + mVolume->m_uBlockSideLength + mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength); } @@ -356,7 +349,7 @@ namespace PolyVox template VoxelType VolumeSampler::peekVoxel0px1ny1nz(void) const { - if((mYPosInVolume%mVolume->m_uBlockSideLength != 0) && (mZPosInVolume%mVolume->m_uBlockSideLength != 0)) + if( BORDER_LOW(mYPosInVolume) && BORDER_LOW(mZPosInVolume) ) { return *(mCurrentVoxel - mVolume->m_uBlockSideLength - mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength); } @@ -366,7 +359,7 @@ namespace PolyVox template VoxelType VolumeSampler::peekVoxel0px1ny0pz(void) const { - if((mYPosInVolume%mVolume->m_uBlockSideLength != 0)) + if( BORDER_LOW(mYPosInVolume) ) { return *(mCurrentVoxel - mVolume->m_uBlockSideLength); } @@ -376,7 +369,7 @@ namespace PolyVox template VoxelType VolumeSampler::peekVoxel0px1ny1pz(void) const { - if((mYPosInVolume%mVolume->m_uBlockSideLength != 0) && (mZPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1)) + if( BORDER_LOW(mYPosInVolume) && BORDER_HIGH(mZPosInVolume) ) { return *(mCurrentVoxel - mVolume->m_uBlockSideLength + mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength); } @@ -386,7 +379,7 @@ namespace PolyVox template VoxelType VolumeSampler::peekVoxel0px0py1nz(void) const { - if((mZPosInVolume%mVolume->m_uBlockSideLength != 0)) + if( BORDER_LOW(mZPosInVolume) ) { return *(mCurrentVoxel - mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength); } @@ -402,7 +395,7 @@ namespace PolyVox template VoxelType VolumeSampler::peekVoxel0px0py1pz(void) const { - if((mZPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1)) + if( BORDER_HIGH(mZPosInVolume) ) { return *(mCurrentVoxel + mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength); } @@ -412,7 +405,7 @@ namespace PolyVox template VoxelType VolumeSampler::peekVoxel0px1py1nz(void) const { - if((mYPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1) && (mZPosInVolume%mVolume->m_uBlockSideLength != 0)) + if( BORDER_HIGH(mYPosInVolume) && BORDER_LOW(mZPosInVolume) ) { return *(mCurrentVoxel + mVolume->m_uBlockSideLength - mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength); } @@ -422,7 +415,7 @@ namespace PolyVox template VoxelType VolumeSampler::peekVoxel0px1py0pz(void) const { - if((mYPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1)) + if( BORDER_HIGH(mYPosInVolume) ) { return *(mCurrentVoxel + mVolume->m_uBlockSideLength); } @@ -432,7 +425,7 @@ namespace PolyVox template VoxelType VolumeSampler::peekVoxel0px1py1pz(void) const { - if((mYPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1) && (mZPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1)) + if( BORDER_HIGH(mYPosInVolume) && BORDER_HIGH(mZPosInVolume) ) { return *(mCurrentVoxel + mVolume->m_uBlockSideLength + mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength); } @@ -444,7 +437,7 @@ namespace PolyVox template VoxelType VolumeSampler::peekVoxel1px1ny1nz(void) const { - if((mXPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1) && (mYPosInVolume%mVolume->m_uBlockSideLength != 0) && (mZPosInVolume%mVolume->m_uBlockSideLength != 0)) + if( BORDER_HIGH(mXPosInVolume) && BORDER_LOW(mYPosInVolume) && BORDER_LOW(mZPosInVolume) ) { return *(mCurrentVoxel + 1 - mVolume->m_uBlockSideLength - mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength); } @@ -454,7 +447,7 @@ namespace PolyVox template VoxelType VolumeSampler::peekVoxel1px1ny0pz(void) const { - if((mXPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1) && (mYPosInVolume%mVolume->m_uBlockSideLength != 0)) + if( BORDER_HIGH(mXPosInVolume) && BORDER_LOW(mYPosInVolume) ) { return *(mCurrentVoxel + 1 - mVolume->m_uBlockSideLength); } @@ -464,7 +457,7 @@ namespace PolyVox template VoxelType VolumeSampler::peekVoxel1px1ny1pz(void) const { - if((mXPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1) && (mYPosInVolume%mVolume->m_uBlockSideLength != 0) && (mZPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1)) + if( BORDER_HIGH(mXPosInVolume) && BORDER_LOW(mYPosInVolume) && BORDER_HIGH(mZPosInVolume) ) { return *(mCurrentVoxel + 1 - mVolume->m_uBlockSideLength + mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength); } @@ -474,7 +467,7 @@ namespace PolyVox template VoxelType VolumeSampler::peekVoxel1px0py1nz(void) const { - if((mXPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1) && (mZPosInVolume%mVolume->m_uBlockSideLength != 0)) + if( BORDER_HIGH(mXPosInVolume) && BORDER_LOW(mZPosInVolume) ) { return *(mCurrentVoxel + 1 - mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength); } @@ -484,7 +477,7 @@ namespace PolyVox template VoxelType VolumeSampler::peekVoxel1px0py0pz(void) const { - if((mXPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1)) + if( BORDER_HIGH(mXPosInVolume) ) { return *(mCurrentVoxel + 1); } @@ -494,7 +487,7 @@ namespace PolyVox template VoxelType VolumeSampler::peekVoxel1px0py1pz(void) const { - if((mXPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1) && (mZPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1)) + if( BORDER_HIGH(mXPosInVolume) && BORDER_HIGH(mZPosInVolume) ) { return *(mCurrentVoxel + 1 + mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength); } @@ -504,7 +497,7 @@ namespace PolyVox template VoxelType VolumeSampler::peekVoxel1px1py1nz(void) const { - if((mXPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1) && (mYPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1) && (mZPosInVolume%mVolume->m_uBlockSideLength != 0)) + if( BORDER_HIGH(mXPosInVolume) && BORDER_HIGH(mYPosInVolume) && BORDER_LOW(mZPosInVolume) ) { return *(mCurrentVoxel + 1 + mVolume->m_uBlockSideLength - mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength); } @@ -514,7 +507,7 @@ namespace PolyVox template VoxelType VolumeSampler::peekVoxel1px1py0pz(void) const { - if((mXPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1) && (mYPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1)) + if( BORDER_HIGH(mXPosInVolume) && BORDER_HIGH(mYPosInVolume) ) { return *(mCurrentVoxel + 1 + mVolume->m_uBlockSideLength); } @@ -524,7 +517,7 @@ namespace PolyVox template VoxelType VolumeSampler::peekVoxel1px1py1pz(void) const { - if((mXPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1) && (mYPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1) && (mZPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1)) + if( BORDER_HIGH(mXPosInVolume) && BORDER_HIGH(mYPosInVolume) && BORDER_HIGH(mZPosInVolume) ) { return *(mCurrentVoxel + 1 + mVolume->m_uBlockSideLength + mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength); } diff --git a/library/PolyVoxCore/source/GradientEstimators.cpp b/library/PolyVoxCore/source/GradientEstimators.cpp index ea94da05..ad264a2b 100644 --- a/library/PolyVoxCore/source/GradientEstimators.cpp +++ b/library/PolyVoxCore/source/GradientEstimators.cpp @@ -41,22 +41,15 @@ namespace PolyVox VolumeSampler volIter(volumeData); - //Check all corners are within the volume, allowing a boundary for gradient estimation - bool lowerCornerInside = volumeData->getEnclosingRegion().containsPoint(v3dFloor,2); - bool upperCornerInside = volumeData->getEnclosingRegion().containsPoint(v3dFloor+Vector3DInt32(1,1,1),2); + Vector3DFloat v3dGradient = computeNormal(volumeData, v3dPos, normalGenerationMethod); - if(lowerCornerInside && upperCornerInside) //If this test fails the vertex will be left as it was + if(v3dGradient.lengthSquared() > 0.0001) { - Vector3DFloat v3dGradient = computeNormal(volumeData, v3dPos, normalGenerationMethod); - - if(v3dGradient.lengthSquared() > 0.0001) - { - //If we got a normal of significant length then update it. - //Otherwise leave it as it was (should be the 'simple' version) - v3dGradient.normalise(); - iterSurfaceVertex->setNormal(v3dGradient); - } - } //(lowerCornerInside && upperCornerInside) + //If we got a normal of significant length then update it. + //Otherwise leave it as it was (should be the 'simple' version) + v3dGradient.normalise(); + iterSurfaceVertex->setNormal(v3dGradient); + } ++iterSurfaceVertex; } } diff --git a/library/PolyVoxCore/source/VoxelFilters.cpp b/library/PolyVoxCore/source/VoxelFilters.cpp index 572ab666..a2c6a968 100644 --- a/library/PolyVoxCore/source/VoxelFilters.cpp +++ b/library/PolyVoxCore/source/VoxelFilters.cpp @@ -29,13 +29,6 @@ namespace PolyVox { float computeSmoothedVoxel(VolumeSampler& volIter) { - assert(volIter.getPosX() >= 1); - assert(volIter.getPosY() >= 1); - assert(volIter.getPosZ() >= 1); - assert(volIter.getPosX() <= volIter.getVolume()->getWidth() - 2); - assert(volIter.getPosY() <= volIter.getVolume()->getHeight() - 2); - assert(volIter.getPosZ() <= volIter.getVolume()->getDepth() - 2); - float sum = 0.0; if(volIter.peekVoxel1nx1ny1nz() != 0) sum += 1.0f; diff --git a/tests/testvolume.cpp b/tests/testvolume.cpp index c3a5afd6..9f739950 100644 --- a/tests/testvolume.cpp +++ b/tests/testvolume.cpp @@ -32,7 +32,7 @@ using namespace PolyVox; void TestVolume::testSize() { const int32_t g_uVolumeSideLength = 128; - Volume volData(g_uVolumeSideLength, g_uVolumeSideLength, g_uVolumeSideLength); + Volume volData; //Note: Deliberatly go past each edge by one to test if the bounds checking works. for (int32_t z = 0; z < g_uVolumeSideLength + 1; z++) @@ -45,10 +45,6 @@ void TestVolume::testSize() } } } - - QCOMPARE(volData.getWidth(), g_uVolumeSideLength); - QCOMPARE(volData.getHeight(), g_uVolumeSideLength); - QCOMPARE(volData.getDepth(), g_uVolumeSideLength); } QTEST_MAIN(TestVolume)