Merged in RLE branch.

This commit is contained in:
David Williams 2011-04-05 20:55:20 +00:00
commit 076ab9865e
54 changed files with 29210 additions and 730 deletions

View File

@ -40,8 +40,10 @@ ADD_SUBDIRECTORY(library)
OPTION(ENABLE_EXAMPLES "Should the examples be built" ON)
IF(ENABLE_EXAMPLES)
ADD_SUBDIRECTORY(examples/Basic)
ADD_SUBDIRECTORY(examples/Paging)
ADD_SUBDIRECTORY(examples/OpenGL)
ADD_DEPENDENCIES(BasicExample PolyVoxCore PolyVoxUtil)
ADD_DEPENDENCIES(PagingExample PolyVoxCore PolyVoxUtil)
ADD_DEPENDENCIES(OpenGLExample PolyVoxCore PolyVoxUtil)
ENDIF(ENABLE_EXAMPLES)

3
CREDITS.txt Normal file
View File

@ -0,0 +1,3 @@
David Williams - Lead programmer.
Matthew Williams - Linux port, build system, support and maintainence.
Oliver Schneider - Very large Volumes

View File

@ -78,7 +78,7 @@ int main(int argc, char *argv[])
openGLWidget.show();
//Create an empty volume and then place a sphere in it
Volume<MaterialDensityPair44> volData(64, 64, 64);
Volume<MaterialDensityPair44> volData(PolyVox::Region(Vector3DInt32(0,0,0), Vector3DInt32(63, 63, 63)));
createSphereInVolume(volData, 30);
//Extract the surface

View File

@ -74,16 +74,16 @@ void OpenGLWidget::setVolume(PolyVox::Volume<MaterialDensityPair44>* volData)
//Compute the extents of the current region
//FIXME - This is a little complex? PolyVox could
//provide more functions for dealing with regions?
uint16_t regionStartX = uRegionX * m_uRegionSideLength;
uint16_t regionStartY = uRegionY * m_uRegionSideLength;
uint16_t regionStartZ = uRegionZ * m_uRegionSideLength;
int32_t regionStartX = uRegionX * m_uRegionSideLength;
int32_t regionStartY = uRegionY * m_uRegionSideLength;
int32_t regionStartZ = uRegionZ * m_uRegionSideLength;
uint16_t regionEndX = regionStartX + m_uRegionSideLength;
uint16_t regionEndY = regionStartY + m_uRegionSideLength;
uint16_t regionEndZ = regionStartZ + m_uRegionSideLength;
int32_t regionEndX = regionStartX + m_uRegionSideLength;
int32_t regionEndY = regionStartY + m_uRegionSideLength;
int32_t regionEndZ = regionStartZ + m_uRegionSideLength;
Vector3DInt16 regLowerCorner(regionStartX, regionStartY, regionStartZ);
Vector3DInt16 regUpperCorner(regionEndX, regionEndY, regionEndZ);
Vector3DInt32 regLowerCorner(regionStartX, regionStartY, regionStartZ);
Vector3DInt32 regUpperCorner(regionEndX, regionEndY, regionEndZ);
//Extract the surface for this region
//extractSurface(m_volData, 0, PolyVox::Region(regLowerCorner, regUpperCorner), meshCurrent);

View File

@ -37,7 +37,7 @@ freely, subject to the following restrictions:
#include "OpenGLVertexBufferObjectSupport.h"
#include "Shapes.h"
const uint16_t g_uVolumeSideLength = 128;
const int32_t g_uVolumeSideLength = 128;
class OpenGLWidget : public QGLWidget
{

View File

@ -55,7 +55,7 @@ void createSphereInVolume(Volume<MaterialDensityPair44>& volData, float fRadius,
}
}
void createCubeInVolume(Volume<MaterialDensityPair44>& volData, Vector3DUint16 lowerCorner, Vector3DUint16 upperCorner, uint8_t uValue)
void createCubeInVolume(Volume<MaterialDensityPair44>& volData, Vector3DInt32 lowerCorner, Vector3DInt32 upperCorner, uint8_t uValue)
{
//This three-level for loop iterates over every voxel between the specified corners
for (int z = lowerCorner.getZ(); z <= upperCorner.getZ(); z++)

View File

@ -28,6 +28,6 @@ freely, subject to the following restrictions:
#include "Volume.h"
void createSphereInVolume(PolyVox::Volume<PolyVox::MaterialDensityPair44>& volData, float fRadius, uint8_t uValue);
void createCubeInVolume(PolyVox::Volume<PolyVox::MaterialDensityPair44>& volData, PolyVox::Vector3DUint16 lowerCorner, PolyVox::Vector3DUint16 upperCorner, uint8_t uValue);
void createCubeInVolume(PolyVox::Volume<PolyVox::MaterialDensityPair44>& volData, PolyVox::Vector3DInt32 lowerCorner, PolyVox::Vector3DInt32 upperCorner, uint8_t uValue);
#endif //__OpenGLExample_Shapes_H__

View File

@ -72,12 +72,12 @@ void exampleLog(string message, int severity)
int main(int argc, char *argv[])
{
logHandler = &exampleLog;
Volume<MaterialDensityPair44> volData(g_uVolumeSideLength, g_uVolumeSideLength, g_uVolumeSideLength);
Volume<MaterialDensityPair44> volData(PolyVox::Region(Vector3DInt32(0,0,0), Vector3DInt32(g_uVolumeSideLength-1, g_uVolumeSideLength-1, g_uVolumeSideLength-1)));
//Make our volume contain a sphere in the center.
uint16_t minPos = 0;
uint16_t midPos = g_uVolumeSideLength / 2;
uint16_t maxPos = g_uVolumeSideLength - 1;
int32_t minPos = 0;
int32_t midPos = g_uVolumeSideLength / 2;
int32_t maxPos = g_uVolumeSideLength - 1;
cout << "Creating sphere 1" << std::endl;
createSphereInVolume(volData, 60.0f, 5);
@ -91,22 +91,18 @@ int main(int argc, char *argv[])
createSphereInVolume(volData, 20.0f, 1);
cout << "Creating cubes" << std::endl;
createCubeInVolume(volData, Vector3DUint16(minPos, minPos, minPos), Vector3DUint16(midPos-1, midPos-1, midPos-1), 0);
createCubeInVolume(volData, Vector3DUint16(midPos+1, midPos+1, minPos), Vector3DUint16(maxPos, maxPos, midPos-1), 0);
createCubeInVolume(volData, Vector3DUint16(midPos+1, minPos, midPos+1), Vector3DUint16(maxPos, midPos-1, maxPos), 0);
createCubeInVolume(volData, Vector3DUint16(minPos, midPos+1, midPos+1), Vector3DUint16(midPos-1, maxPos, maxPos), 0);
createCubeInVolume(volData, Vector3DInt32(minPos, minPos, minPos), Vector3DInt32(midPos-1, midPos-1, midPos-1), 0);
createCubeInVolume(volData, Vector3DInt32(midPos+1, midPos+1, minPos), Vector3DInt32(maxPos, maxPos, midPos-1), 0);
createCubeInVolume(volData, Vector3DInt32(midPos+1, minPos, midPos+1), Vector3DInt32(maxPos, midPos-1, maxPos), 0);
createCubeInVolume(volData, Vector3DInt32(minPos, midPos+1, midPos+1), Vector3DInt32(midPos-1, maxPos, maxPos), 0);
createCubeInVolume(volData, Vector3DUint16(1, midPos-10, midPos-10), Vector3DUint16(maxPos-1, midPos+10, midPos+10), MaterialDensityPair44::getMaxDensity());
createCubeInVolume(volData, Vector3DUint16(midPos-10, 1, midPos-10), Vector3DUint16(midPos+10, maxPos-1, midPos+10), MaterialDensityPair44::getMaxDensity());
createCubeInVolume(volData, Vector3DUint16(midPos-10, midPos-10 ,1), Vector3DUint16(midPos+10, midPos+10, maxPos-1), MaterialDensityPair44::getMaxDensity());
createCubeInVolume(volData, Vector3DInt32(1, midPos-10, midPos-10), Vector3DInt32(maxPos-1, midPos+10, midPos+10), MaterialDensityPair44::getMaxDensity());
createCubeInVolume(volData, Vector3DInt32(midPos-10, 1, midPos-10), Vector3DInt32(midPos+10, maxPos-1, midPos+10), MaterialDensityPair44::getMaxDensity());
createCubeInVolume(volData, Vector3DInt32(midPos-10, midPos-10 ,1), Vector3DInt32(midPos+10, midPos+10, maxPos-1), MaterialDensityPair44::getMaxDensity());
//Smooth part of the volume
smoothRegion<MaterialDensityPair44>(volData, PolyVox::Region(Vector3DInt16(62, 62, 62), Vector3DInt16(130, 130, 130)));
smoothRegion<MaterialDensityPair44>(volData, PolyVox::Region(Vector3DInt16(62, 62, 62), Vector3DInt16(130, 130, 130)));
cout << "Tidying memory...";
volData.tidyUpMemory(0);
cout << "done." << endl;
smoothRegion<MaterialDensityPair44>(volData, PolyVox::Region(Vector3DInt32(62, 62, 62), Vector3DInt32(130, 130, 130)));
smoothRegion<MaterialDensityPair44>(volData, PolyVox::Region(Vector3DInt32(62, 62, 62), Vector3DInt32(130, 130, 130)));
QApplication app(argc, argv);

View File

@ -0,0 +1,56 @@
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
PROJECT(PagingExample)
#Projects source files
SET(SRC_FILES
glew/glew.c
main.cpp
OpenGLWidget.cpp
Perlin.cpp
)
#Projects headers files
SET(INC_FILES
glew/glew.h
glew/glxew.h
glew/wglew.h
OpenGLWidget.h
Perlin.h
)
ADD_DEFINITIONS(-DGLEW_STATIC)
#"Sources" and "Headers" are the group names in Visual Studio.
#They may have other uses too...
SOURCE_GROUP("Sources" FILES ${SRC_FILES})
SOURCE_GROUP("Headers" FILES ${INC_FILES})
FIND_PACKAGE(OpenGL REQUIRED)
#Tell CMake the paths for OpenGL and for PolyVox (which is just relative to our current location)
INCLUDE_DIRECTORIES(${OPENGL_INCLUDE_DIR} ${PolyVoxCore_SOURCE_DIR}/include)
#There has to be a better way!
LINK_DIRECTORIES(${PolyVoxCore_BINARY_DIR}/debug ${PolyVoxCore_BINARY_DIR}/release)
#Build
ADD_EXECUTABLE(PagingExample ${SRC_FILES})
TARGET_LINK_LIBRARIES(PagingExample ${QT_LIBRARIES} ${OPENGL_gl_LIBRARY} ${OPENGL_glu_LIBRARY} PolyVoxCore)
#Install - Only install the example in Windows
IF(WIN32)
INSTALL(TARGETS PagingExample
RUNTIME DESTINATION Examples/OpenGL/bin
LIBRARY DESTINATION Examples/OpenGL/lib
ARCHIVE DESTINATION Examples/OpenGL/lib
COMPONENT example
)
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/../../release/PolyVoxCore.dll DESTINATION Examples/OpenGL/bin CONFIGURATIONS Release)
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/../../release/PolyVoxUtil.dll DESTINATION Examples/OpenGL/bin CONFIGURATIONS Release)
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/../../debug/PolyVoxCore.dll DESTINATION Examples/OpenGL/bin CONFIGURATIONS Debug)
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/../../debug/PolyVoxUtil.dll DESTINATION Examples/OpenGL/bin CONFIGURATIONS Debug)
ENDIF(WIN32)

View File

@ -0,0 +1,135 @@
#include "OpenGLWidget.h"
#include <QMouseEvent>
using namespace PolyVox;
using namespace std;
OpenGLWidget::OpenGLWidget(QWidget *parent)
:QGLWidget(parent)
,m_uBeginIndex(0)
,m_uEndIndex(0)
,noOfIndices(0)
,m_xRotation(0)
,m_yRotation(0)
{
}
void OpenGLWidget::setSurfaceMeshToRender(const PolyVox::SurfaceMesh<PositionMaterialNormal>& surfaceMesh)
{
if((surfaceMesh.getNoOfIndices() == 0) || (surfaceMesh.getNoOfVertices() == 0))
{
//We don't have a valid mesh
return;
}
//Convienient access to the vertices and indices
const vector<uint32_t>& vecIndices = surfaceMesh.getIndices();
const vector<PositionMaterialNormal>& vecVertices = surfaceMesh.getVertices();
//Build an OpenGL index buffer
glGenBuffers(1, &indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
const GLvoid* pIndices = static_cast<const GLvoid*>(&(vecIndices[0]));
glBufferData(GL_ELEMENT_ARRAY_BUFFER, vecIndices.size() * sizeof(uint32_t), pIndices, GL_STATIC_DRAW);
//Build an OpenGL vertex buffer
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
const GLvoid* pVertices = static_cast<const GLvoid*>(&(vecVertices[0]));
glBufferData(GL_ARRAY_BUFFER, vecVertices.size() * sizeof(PositionMaterialNormal), pVertices, GL_STATIC_DRAW);
m_uBeginIndex = 0;
m_uEndIndex = vecIndices.size();
noOfIndices = surfaceMesh.getNoOfIndices();
}
void OpenGLWidget::initializeGL()
{
//We need GLEW to access recent OpenGL functionality
GLenum err = glewInit();
if (GLEW_OK != err)
{
/* Problem: glewInit failed, something is seriously wrong. */
std::cout << "GLEW Error: " << glewGetErrorString(err) << std::endl;
}
//Set up the clear colour
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClearDepth(1.0f);
//Enable the depth buffer
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
//Anable smooth lighting
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glShadeModel(GL_SMOOTH);
//We'll be rendering with index/vertex arrays
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
}
void OpenGLWidget::resizeGL(int w, int h)
{
//Setup the viewport
glViewport(0, 0, w, h);
//Set up the projection matrix
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
float frustumSize = 128.0f * 1.7f; //Half the volume diagonal
float aspect = static_cast<float>(width()) / static_cast<float>(height());
glOrtho(frustumSize*aspect, -frustumSize*aspect, frustumSize, -frustumSize, 10.0, 10000);
}
void OpenGLWidget::paintGL()
{
if(noOfIndices == 0)
{
//Nothing to render
return;
}
//Clear the screen
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//Set up the viewing transformation
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0f,0.0f,-5000.0f); //Centre volume and move back
glRotatef(m_xRotation, 1.0f, 0.0f, 0.0f);
glRotatef(m_yRotation, 0.0f, 1.0f, 0.0f);
glTranslatef(-128.0f,-128.0f,-128.0f); //Centre volume and move back
//Bind the index buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
//Bind the vertex buffer
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glVertexPointer(3, GL_FLOAT, sizeof(PositionMaterialNormal), 0);
glNormalPointer(GL_FLOAT, sizeof(PositionMaterialNormal), (GLvoid*)12);
glDrawRangeElements(GL_TRIANGLES, m_uBeginIndex, m_uEndIndex-1, m_uEndIndex - m_uBeginIndex, GL_UNSIGNED_INT, 0);
}
void OpenGLWidget::mousePressEvent(QMouseEvent* event)
{
m_CurrentMousePos = event->pos();
m_LastFrameMousePos = m_CurrentMousePos;
update();
}
void OpenGLWidget::mouseMoveEvent(QMouseEvent* event)
{
m_CurrentMousePos = event->pos();
QPoint diff = m_CurrentMousePos - m_LastFrameMousePos;
m_xRotation += diff.x();
m_yRotation += diff.y();
m_LastFrameMousePos = m_CurrentMousePos;
update();
}

View File

@ -0,0 +1,67 @@
/*******************************************************************************
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 __BasicExample_OpenGLWidget_H__
#define __BasicExample_OpenGLWidget_H__
#include "SurfaceMesh.h"
#include "glew/glew.h"
#include <QGLWidget>
class OpenGLWidget : public QGLWidget
{
public:
//Constructor
OpenGLWidget(QWidget *parent);
//Mouse handling
void mouseMoveEvent(QMouseEvent* event);
void mousePressEvent(QMouseEvent* event);
//Convert a SrfaceMesh to OpenGL index/vertex buffers
void setSurfaceMeshToRender(const PolyVox::SurfaceMesh<PolyVox::PositionMaterialNormal>& surfaceMesh);
protected:
//Qt OpenGL functions
void initializeGL();
void resizeGL(int w, int h);
void paintGL();
private:
//Index/vertex buffer data
GLuint m_uBeginIndex;
GLuint m_uEndIndex;
GLuint noOfIndices;
GLuint indexBuffer;
GLuint vertexBuffer;
//Mouse data
QPoint m_LastFrameMousePos;
QPoint m_CurrentMousePos;
int m_xRotation;
int m_yRotation;
};
#endif //__BasicExample_OpenGLWidget_H__

267
examples/Paging/Perlin.cpp Normal file
View File

@ -0,0 +1,267 @@
// Code from http://www.flipcode.com/archives/Perlin_Noise_Class.shtml
// This is only in PolyVox for the purpose of the examples. It was not written by
// the PolyVox authors and cannot be assumed to be under the same license as PolyVox.
/* coherent noise function over 1, 2 or 3 dimensions */
/* (copyright Ken Perlin) */
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "Perlin.h"
#define B SAMPLE_SIZE
#define BM (SAMPLE_SIZE-1)
#define N 0x1000
#define NP 12 /* 2^N */
#define NM 0xfff
#define s_curve(t) ( t * t * (3.0f - 2.0f * t) )
#define lerp(t, a, b) ( a + t * (b - a) )
#define setup(i,b0,b1,r0,r1)\
t = vec[i] + N;\
b0 = ((int)t) & BM;\
b1 = (b0+1) & BM;\
r0 = t - (int)t;\
r1 = r0 - 1.0f;
float Perlin::noise1(float arg)
{
int bx0, bx1;
float rx0, rx1, sx, t, u, v, vec[1];
vec[0] = arg;
if (mStart)
{
srand(mSeed);
mStart = false;
init();
}
setup(0, bx0,bx1, rx0,rx1);
sx = s_curve(rx0);
u = rx0 * g1[ p[ bx0 ] ];
v = rx1 * g1[ p[ bx1 ] ];
return lerp(sx, u, v);
}
float Perlin::noise2(float vec[2])
{
int bx0, bx1, by0, by1, b00, b10, b01, b11;
float rx0, rx1, ry0, ry1, *q, sx, sy, a, b, t, u, v;
int i, j;
if (mStart)
{
srand(mSeed);
mStart = false;
init();
}
setup(0,bx0,bx1,rx0,rx1);
setup(1,by0,by1,ry0,ry1);
i = p[bx0];
j = p[bx1];
b00 = p[i + by0];
b10 = p[j + by0];
b01 = p[i + by1];
b11 = p[j + by1];
sx = s_curve(rx0);
sy = s_curve(ry0);
#define at2(rx,ry) ( rx * q[0] + ry * q[1] )
q = g2[b00];
u = at2(rx0,ry0);
q = g2[b10];
v = at2(rx1,ry0);
a = lerp(sx, u, v);
q = g2[b01];
u = at2(rx0,ry1);
q = g2[b11];
v = at2(rx1,ry1);
b = lerp(sx, u, v);
return lerp(sy, a, b);
}
float Perlin::noise3(float vec[3])
{
int bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11;
float rx0, rx1, ry0, ry1, rz0, rz1, *q, sy, sz, a, b, c, d, t, u, v;
int i, j;
if (mStart)
{
srand(mSeed);
mStart = false;
init();
}
setup(0, bx0,bx1, rx0,rx1);
setup(1, by0,by1, ry0,ry1);
setup(2, bz0,bz1, rz0,rz1);
i = p[ bx0 ];
j = p[ bx1 ];
b00 = p[ i + by0 ];
b10 = p[ j + by0 ];
b01 = p[ i + by1 ];
b11 = p[ j + by1 ];
t = s_curve(rx0);
sy = s_curve(ry0);
sz = s_curve(rz0);
#define at3(rx,ry,rz) ( rx * q[0] + ry * q[1] + rz * q[2] )
q = g3[ b00 + bz0 ] ; u = at3(rx0,ry0,rz0);
q = g3[ b10 + bz0 ] ; v = at3(rx1,ry0,rz0);
a = lerp(t, u, v);
q = g3[ b01 + bz0 ] ; u = at3(rx0,ry1,rz0);
q = g3[ b11 + bz0 ] ; v = at3(rx1,ry1,rz0);
b = lerp(t, u, v);
c = lerp(sy, a, b);
q = g3[ b00 + bz1 ] ; u = at3(rx0,ry0,rz1);
q = g3[ b10 + bz1 ] ; v = at3(rx1,ry0,rz1);
a = lerp(t, u, v);
q = g3[ b01 + bz1 ] ; u = at3(rx0,ry1,rz1);
q = g3[ b11 + bz1 ] ; v = at3(rx1,ry1,rz1);
b = lerp(t, u, v);
d = lerp(sy, a, b);
return lerp(sz, c, d);
}
void Perlin::normalize2(float v[2])
{
float s;
s = (float)sqrt(v[0] * v[0] + v[1] * v[1]);
s = 1.0f/s;
v[0] = v[0] * s;
v[1] = v[1] * s;
}
void Perlin::normalize3(float v[3])
{
float s;
s = (float)sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
s = 1.0f/s;
v[0] = v[0] * s;
v[1] = v[1] * s;
v[2] = v[2] * s;
}
void Perlin::init(void)
{
int i, j, k;
for (i = 0 ; i < B ; i++)
{
p[i] = i;
g1[i] = (float)((rand() % (B + B)) - B) / B;
for (j = 0 ; j < 2 ; j++)
g2[i][j] = (float)((rand() % (B + B)) - B) / B;
normalize2(g2[i]);
for (j = 0 ; j < 3 ; j++)
g3[i][j] = (float)((rand() % (B + B)) - B) / B;
normalize3(g3[i]);
}
while (--i)
{
k = p[i];
p[i] = p[j = rand() % B];
p[j] = k;
}
for (i = 0 ; i < B + 2 ; i++)
{
p[B + i] = p[i];
g1[B + i] = g1[i];
for (j = 0 ; j < 2 ; j++)
g2[B + i][j] = g2[i][j];
for (j = 0 ; j < 3 ; j++)
g3[B + i][j] = g3[i][j];
}
}
float Perlin::perlin_noise_2D(float vec[2])
{
int terms = mOctaves;
float freq = mFrequency;
float result = 0.0f;
float amp = mAmplitude;
vec[0]*=mFrequency;
vec[1]*=mFrequency;
for( int i=0; i<terms; i++ )
{
result += noise2(vec)*amp;
vec[0] *= 2.0f;
vec[1] *= 2.0f;
amp*=0.5f;
}
return result;
}
float Perlin::perlin_noise_3D(float vec[3])
{
int terms = mOctaves;
float freq = mFrequency;
float result = 0.0f;
float amp = mAmplitude;
vec[0]*=mFrequency;
vec[1]*=mFrequency;
vec[2]*=mFrequency;
for( int i=0; i<terms; i++ )
{
result += noise3(vec)*amp;
vec[0] *= 2.0f;
vec[1] *= 2.0f;
vec[2] *= 2.0f;
amp*=0.5f;
}
return result;
}
Perlin::Perlin(int octaves,float freq,float amp,int seed)
{
mOctaves = octaves;
mFrequency = freq;
mAmplitude = amp;
mSeed = seed;
mStart = true;
}

64
examples/Paging/Perlin.h Normal file
View File

@ -0,0 +1,64 @@
// Code from http://www.flipcode.com/archives/Perlin_Noise_Class.shtml
// This is only in PolyVox for the purpose of the examples. It was not written by
// the PolyVox authors and cannot be assumed to be under the same license as PolyVox.
#ifndef PERLIN_H_
#define PERLIN_H_
#include <stdlib.h>
#define SAMPLE_SIZE 1024
class Perlin
{
public:
Perlin(int octaves,float freq,float amp,int seed);
float Get(float x,float y)
{
float vec[2];
vec[0] = x;
vec[1] = y;
return perlin_noise_2D(vec);
};
float Get3D(float x,float y,float z)
{
float vec[3];
vec[0] = x;
vec[1] = y;
vec[2] = z;
return perlin_noise_3D(vec);
};
private:
void init_perlin(int n,float p);
float perlin_noise_2D(float vec[2]);
float perlin_noise_3D(float vec[3]);
float noise1(float arg);
float noise2(float vec[2]);
float noise3(float vec[3]);
void normalize2(float v[2]);
void normalize3(float v[3]);
void init(void);
int mOctaves;
float mFrequency;
float mAmplitude;
int mSeed;
int p[SAMPLE_SIZE + SAMPLE_SIZE + 2];
float g3[SAMPLE_SIZE + SAMPLE_SIZE + 2][3];
float g2[SAMPLE_SIZE + SAMPLE_SIZE + 2][2];
float g1[SAMPLE_SIZE + SAMPLE_SIZE + 2];
bool mStart;
};
#endif

View File

@ -0,0 +1,73 @@
The OpenGL Extension Wrangler Library
Copyright (C) 2002-2007, Milan Ikits <milan ikits[]ieee org>
Copyright (C) 2002-2007, Marcelo E. Magallon <mmagallo[]debian org>
Copyright (C) 2002, Lev Povalahev
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* The name of the author may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
Mesa 3-D graphics library
Version: 7.0
Copyright (C) 1999-2007 Brian Paul All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Copyright (c) 2007 The Khronos Group Inc.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and/or associated documentation files (the
"Materials"), to deal in the Materials without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Materials, and to
permit persons to whom the Materials are furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Materials.
THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.

12180
examples/Paging/glew/glew.c Normal file

File diff suppressed because it is too large Load Diff

12262
examples/Paging/glew/glew.h Normal file

File diff suppressed because it is too large Load Diff

1397
examples/Paging/glew/glxew.h Normal file

File diff suppressed because it is too large Load Diff

1165
examples/Paging/glew/wglew.h Normal file

File diff suppressed because it is too large Load Diff

300
examples/Paging/main.cpp Normal file
View File

@ -0,0 +1,300 @@
/*******************************************************************************
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 "OpenGLWidget.h"
#include "Perlin.h"
#include "MaterialDensityPair.h"
#include "CubicSurfaceExtractorWithNormals.h"
#include "SurfaceExtractor.h"
#include "SurfaceMesh.h"
#include "Volume.h"
#include "Filters.h"
#include <QApplication>
//Use the PolyVox namespace
using namespace PolyVox;
void createPerlinVolumeSlow(Volume<MaterialDensityPair44>& volData)
{
Perlin perlin(2,8,1,234);
for(int z = 1; z < 256-1; z++)
{
std::cout << z << std::endl;
for(int y = 1; y < 256-1; y++)
{
for(int x = 1; x < 256-1; x++)
{
float perlinVal = perlin.Get3D(x /static_cast<float>(256-1), (y) / static_cast<float>(256-1), z / static_cast<float>(256-1));
perlinVal += 1.0f;
perlinVal *= 0.5f;
perlinVal *= MaterialDensityPair44::getMaxDensity();
MaterialDensityPair44 voxel;
voxel.setMaterial(245);
voxel.setDensity(perlinVal);
/*if(perlinVal < 0.0f)
{
voxel.setMaterial(245);
voxel.setDensity(MaterialDensityPair44::getMaxDensity());
}
else
{
voxel.setMaterial(0);
voxel.setDensity(MaterialDensityPair44::getMinDensity());
}*/
volData.setVoxelAt(x, y, z, voxel);
}
}
}
}
/*void createPerlinVolumeFast(Volume<MaterialDensityPair44>& volData)
{
Perlin perlin(2,8,1,234);
for(int blockZ = 0; blockZ < volData.m_uDepthInBlocks; blockZ++)
{
std::cout << blockZ << std::endl;
for(int blockY = 0; blockY < volData.m_uHeightInBlocks; blockY++)
{
for(int blockX = 0; blockX < volData.m_uWidthInBlocks; blockX++)
{
for(int offsetz = 0; offsetz < volData.m_uBlockSideLength; offsetz++)
{
for(int offsety = 0; offsety < volData.m_uBlockSideLength; offsety++)
{
for(int offsetx = 0; offsetx < volData.m_uBlockSideLength; offsetx++)
{
int x = blockX * volData.m_uBlockSideLength + offsetx;
int y = blockY * volData.m_uBlockSideLength + offsety;
int z = blockZ * volData.m_uBlockSideLength + offsetz;
if((x == 0) || (x == volData.getWidth()-1)) continue;
if((y == 0) || (y == volData.getHeight()-1)) continue;
if((z == 0) || (z == volData.getDepth()-1)) continue;
float perlinVal = perlin.Get3D(x /static_cast<float>(volData.getWidth()-1), (y) / static_cast<float>(volData.getHeight()-1), z / static_cast<float>(volData.getDepth()-1));
MaterialDensityPair44 voxel;
if(perlinVal < 0.0f)
{
voxel.setMaterial(245);
voxel.setDensity(MaterialDensityPair44::getMaxDensity());
}
else
{
voxel.setMaterial(0);
voxel.setDensity(MaterialDensityPair44::getMinDensity());
}
volData.setVoxelAt(x, y, z, voxel);
}
}
}
}
}
}
}*/
void createPerlinTerrain(Volume<MaterialDensityPair44>& volData)
{
Perlin perlin(2,2,1,234);
for(int x = 1; x < 255-1; x++)
{
if(x%(255/100) == 0) {
std::cout << "." << std::flush;
}
for(int y = 1; y < 255-1; y++)
{
float perlinVal = perlin.Get(x / static_cast<float>(255-1), y / static_cast<float>(255-1));
perlinVal += 1.0f;
perlinVal *= 0.5f;
perlinVal *= 255;
for(int z = 1; z < 255-1; z++)
{
MaterialDensityPair44 voxel;
if(z < perlinVal)
{
voxel.setMaterial(245);
voxel.setDensity(MaterialDensityPair44::getMaxDensity());
}
else
{
voxel.setMaterial(0);
voxel.setDensity(MaterialDensityPair44::getMinDensity());
}
volData.setVoxelAt(x, y, z, voxel);
}
}
}
std::cout << std::endl;
}
void createSphereInVolume(Volume<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);
int iRadius = fRadius;
//This three-level for loop iterates over every voxel in the volume
for (int z = v3dVolCenter.getZ() - iRadius; z <= v3dVolCenter.getZ() + iRadius; z++)
{
for (int y = v3dVolCenter.getY() - iRadius; y <= v3dVolCenter.getY() + iRadius; y++)
{
for (int x = v3dVolCenter.getX() - iRadius; x <= v3dVolCenter.getX() + iRadius; x++)
{
//Store our current position as a vector...
Vector3DFloat v3dCurrentPos(x,y,z);
//And compute how far the current position is from the center of the volume
float fDistToCenter = (v3dCurrentPos - v3dVolCenter).length();
//If the current voxel is less than 'radius' units from the center then we make it solid.
if(fDistToCenter <= fRadius)
{
//Our new density value
uint8_t uDensity = MaterialDensityPair44::getMaxDensity();
//Get the old voxel
MaterialDensityPair44 voxel = volData.getVoxelAt(x,y,z);
//Modify the density
voxel.setDensity(uDensity);
//Wrte the voxel value into the volume
volData.setVoxelAt(x, y, z, voxel);
}
}
}
}
}
void load(const ConstVolumeProxy<MaterialDensityPair44>& volume, const 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<float>(255-1), y / static_cast<float>(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.setVoxelAt(x, y, z, voxel);
}
}
}
}
void unload(const ConstVolumeProxy<MaterialDensityPair44>& vol, const PolyVox::Region& reg)
{
std::cout << "warning unloading region: " << reg.getLowerCorner() << " -> " << reg.getUpperCorner() << std::endl;
}
int main(int argc, char *argv[])
{
//Create and show the Qt OpenGL window
QApplication app(argc, argv);
OpenGLWidget openGLWidget(0);
openGLWidget.show();
//If these lines don't compile, please try commenting them out and using the two lines after
//(you will need Boost for this). If you have to do this then please let us know in the forums as
//we rely on community feedback to keep the Boost version running.
Volume<MaterialDensityPair44> volData(&load, &unload, 256);
//Volume<MaterialDensityPair44> volData(polyvox_bind(&load, polyvox_placeholder_1, polyvox_placeholder_2),
// polyvox_bind(&unload, polyvox_placeholder_1, polyvox_placeholder_2), 256);
volData.setMaxNumberOfBlocksInMemory(4096);
volData.setMaxNumberOfUncompressedBlocks(64);
//volData.dataRequiredHandler = &load;
//volData.dataOverflowHandler = &unload;
//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);
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;
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 << "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;
//Extract the surface
SurfaceMesh<PositionMaterialNormal> mesh;
//CubicSurfaceExtractorWithNormals<MaterialDensityPair44> surfaceExtractor(&volData, volData.getEnclosingRegion(), &mesh);
SurfaceExtractor<MaterialDensityPair44> surfaceExtractor(&volData, reg, &mesh);
//CubicSurfaceExtractorWithNormals<MaterialDensityPair44> surfaceExtractor(&volData, reg, &mesh);
surfaceExtractor.execute();
std::cout << "#vertices: " << mesh.getNoOfVertices() << std::endl;
//Pass the surface to the OpenGL window
openGLWidget.setSurfaceMeshToRender(mesh);
//Run the message pump.
return app.exec();
}

View File

@ -2,7 +2,7 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
PROJECT(PolyVox)
add_subdirectory(bindings)
#add_subdirectory(bindings)
add_subdirectory(PolyVoxCore)
add_subdirectory(PolyVoxUtil)

View File

@ -23,6 +23,7 @@ SET(CORE_INC_FILES
include/ArraySizes.h
include/AStarPathfinder.h
include/AStarPathfinder.inl
include/ConstVolumeProxy.h
include/CubicSurfaceExtractor.h
include/CubicSurfaceExtractor.inl
include/CubicSurfaceExtractorWithNormals.h

View File

@ -38,14 +38,14 @@ namespace PolyVox
const float sqrt_2 = 1.4143f;
const float sqrt_3 = 1.7321f;
extern const POLYVOXCORE_API Vector3DInt16 arrayPathfinderFaces[6];
extern const POLYVOXCORE_API Vector3DInt16 arrayPathfinderEdges[12];
extern const POLYVOXCORE_API Vector3DInt16 arrayPathfinderCorners[8];
extern const POLYVOXCORE_API Vector3DInt32 arrayPathfinderFaces[6];
extern const POLYVOXCORE_API Vector3DInt32 arrayPathfinderEdges[12];
extern const POLYVOXCORE_API Vector3DInt32 arrayPathfinderCorners[8];
/// This function provides the default method for checking whether a given voxel
/// is vaid for the path computed by the AStarPathfinder.
template <typename VoxelType>
bool aStarDefaultVoxelValidator(const Volume<VoxelType>* volData, const Vector3DInt16& v3dPos);
bool aStarDefaultVoxelValidator(const Volume<VoxelType>* volData, const Vector3DInt32& v3dPos);
/// Provides a configuration for the AStarPathfinder.
////////////////////////////////////////////////////////////////////////////////
@ -65,13 +65,13 @@ namespace PolyVox
AStarPathfinderParams
(
Volume<VoxelType>* volData,
const Vector3DInt16& v3dStart,
const Vector3DInt16& v3dEnd,
std::list<Vector3DInt16>* listResult,
const Vector3DInt32& v3dStart,
const Vector3DInt32& v3dEnd,
std::list<Vector3DInt32>* listResult,
float fHBias = 1.0,
uint32_t uMaxNoOfNodes = 10000,
Connectivity connectivity = TwentySixConnected,
polyvox_function<bool (const Volume<VoxelType>*, const Vector3DInt16&)> funcIsVoxelValidForPath = &aStarDefaultVoxelValidator<VoxelType>,
polyvox_function<bool (const Volume<VoxelType>*, const Vector3DInt32&)> funcIsVoxelValidForPath = &aStarDefaultVoxelValidator<VoxelType>,
polyvox_function<void (float)> funcProgressCallback = 0
)
:volume(volData)
@ -90,14 +90,14 @@ namespace PolyVox
Volume<VoxelType>* volume;
/// The start point for the pathfinding algorithm.
Vector3DInt16 start;
Vector3DInt32 start;
/// The end point for the pathfinding algorithm.
Vector3DInt16 end;
Vector3DInt32 end;
/// The resulting path will be stored as a series of points in
/// this list. Any existing contents will be cleared.
std::list<Vector3DInt16>* result;
std::list<Vector3DInt32>* result;
/// The AStarPathfinder performs its search by examining the neighbours
/// of each voxel it encounters. This property controls the meaning of
@ -126,7 +126,7 @@ namespace PolyVox
/// you could check to ensure that the voxel above is empty and the voxel below is solid.
///
/// \sa aStarDefaultVoxelValidator
polyvox_function<bool (const Volume<VoxelType>*, const Vector3DInt16&)> isVoxelValidForPath;
polyvox_function<bool (const Volume<VoxelType>*, const Vector3DInt32&)> isVoxelValidForPath;
/// This function is called by the AStarPathfinder to report on its progress in getting to
/// the goal. The progress is reported by computing the distance from the closest node found
@ -169,12 +169,12 @@ namespace PolyVox
void execute();
private:
void processNeighbour(const Vector3DInt16& neighbourPos, float neighbourGVal);
void processNeighbour(const Vector3DInt32& neighbourPos, float neighbourGVal);
float SixConnectedCost(const Vector3DInt16& a, const Vector3DInt16& b);
float EighteenConnectedCost(const Vector3DInt16& a, const Vector3DInt16& b);
float TwentySixConnectedCost(const Vector3DInt16& a, const Vector3DInt16& b);
float computeH(const Vector3DInt16& a, const Vector3DInt16& b);
float SixConnectedCost(const Vector3DInt32& a, const Vector3DInt32& b);
float EighteenConnectedCost(const Vector3DInt32& a, const Vector3DInt32& b);
float TwentySixConnectedCost(const Vector3DInt32& a, const Vector3DInt32& b);
float computeH(const Vector3DInt32& a, const Vector3DInt32& b);
//Node containers
AllNodesContainer allNodes;

View File

@ -31,7 +31,7 @@ namespace PolyVox
/// \return true is the voxel is valid for the path
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
bool aStarDefaultVoxelValidator(const Volume<VoxelType>* volData, const Vector3DInt16& v3dPos)
bool aStarDefaultVoxelValidator(const Volume<VoxelType>* volData, const Vector3DInt32& v3dPos)
{
//Voxels are considered valid candidates for the path if they are inside the volume...
if(volData->getEnclosingRegion().containsPoint(v3dPos) == false)
@ -40,7 +40,7 @@ namespace PolyVox
}
//and if their density is below the threshold.
Material8 voxel = volData->getVoxelAt(static_cast<Vector3DUint16>(v3dPos));
Material8 voxel = volData->getVoxelAt(v3dPos);
if(voxel.getDensity() >= Material8::getThreshold())
{
return false;
@ -192,7 +192,7 @@ namespace PolyVox
}
template <typename VoxelType>
void AStarPathfinder<VoxelType>::processNeighbour(const Vector3DInt16& neighbourPos, float neighbourGVal)
void AStarPathfinder<VoxelType>::processNeighbour(const Vector3DInt32& neighbourPos, float neighbourGVal)
{
bool bIsVoxelValidForPath = m_params.isVoxelValidForPath(m_params.volume, neighbourPos);
if(!bIsVoxelValidForPath)
@ -248,16 +248,16 @@ namespace PolyVox
}
template <typename VoxelType>
float AStarPathfinder<VoxelType>::SixConnectedCost(const Vector3DInt16& a, const Vector3DInt16& b)
float AStarPathfinder<VoxelType>::SixConnectedCost(const Vector3DInt32& a, const Vector3DInt32& b)
{
//This is the only heuristic I'm sure of - just use the manhatten distance for the 6-connected case.
uint16_t faceSteps = abs(a.getX()-b.getX()) + abs(a.getY()-b.getY()) + abs(a.getZ()-b.getZ());
uint32_t faceSteps = abs(a.getX()-b.getX()) + abs(a.getY()-b.getY()) + abs(a.getZ()-b.getZ());
return faceSteps * 1.0f;
}
template <typename VoxelType>
float AStarPathfinder<VoxelType>::EighteenConnectedCost(const Vector3DInt16& a, const Vector3DInt16& b)
float AStarPathfinder<VoxelType>::EighteenConnectedCost(const Vector3DInt32& a, const Vector3DInt32& b)
{
//I'm not sure of the correct heuristic for the 18-connected case, so I'm just letting it fall through to the
//6-connected case. This means 'h' will be bigger than it should be, resulting in a faster path which may not
@ -267,11 +267,11 @@ namespace PolyVox
}
template <typename VoxelType>
float AStarPathfinder<VoxelType>::TwentySixConnectedCost(const Vector3DInt16& a, const Vector3DInt16& b)
float AStarPathfinder<VoxelType>::TwentySixConnectedCost(const Vector3DInt32& a, const Vector3DInt32& b)
{
//Can't say I'm certain about this heuristic - if anyone has
//a better idea of what it should be then please let me know.
uint16_t array[3];
uint32_t array[3];
array[0] = abs(a.getX() - b.getX());
array[1] = abs(a.getY() - b.getY());
array[2] = abs(a.getZ() - b.getZ());
@ -281,15 +281,15 @@ namespace PolyVox
//until the profiler says so.
std::sort(&array[0], &array[3]);
uint16_t cornerSteps = array[0];
uint16_t edgeSteps = array[1] - array[0];
uint16_t faceSteps = array[2] - array[1];
uint32_t cornerSteps = array[0];
uint32_t edgeSteps = array[1] - array[0];
uint32_t faceSteps = array[2] - array[1];
return cornerSteps * sqrt_3 + edgeSteps * sqrt_2 + faceSteps * sqrt_1;
}
template <typename VoxelType>
float AStarPathfinder<VoxelType>::computeH(const Vector3DInt16& a, const Vector3DInt16& b)
float AStarPathfinder<VoxelType>::computeH(const Vector3DInt32& a, const Vector3DInt32& b)
{
float hVal;

View File

@ -0,0 +1,74 @@
/*******************************************************************************
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_ConstVolumeProxy_H__
#define __PolyVox_ConstVolumeProxy_H__
#include "PolyVoxForwardDeclarations.h"
#include "Region.h"
namespace PolyVox
{
template <typename VoxelType>
class ConstVolumeProxy
{
//Volume is a friend so it can call the constructor.
friend class Volume<VoxelType>;
public:
VoxelType getVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos) const
{
assert(m_regValid.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos)));
return m_pVolume.getVoxelAt(uXPos, uYPos, uZPos);
}
VoxelType getVoxelAt(const Vector3DInt32& v3dPos) const
{
assert(m_regValid.containsPoint(v3dPos));
return getVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ());
}
void setVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue) const
{
assert(m_regValid.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos)));
m_pVolume.setVoxelAtConst(uXPos, uYPos, uZPos, tValue);
}
void setVoxelAt(const Vector3DInt32& v3dPos, VoxelType tValue) const
{
assert(m_regValid.containsPoint(v3dPos));
setVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ(), tValue);
}
private:
//Private constructor, so client code can't abuse this class.
ConstVolumeProxy(const Volume<VoxelType>& pVolume, const Region& regValid)
:m_pVolume(pVolume)
,m_regValid(regValid)
{
}
const Volume<VoxelType>& m_pVolume;
const Region& m_regValid;
};
}
#endif //__PolyVox_ConstVolumeProxy_H__

View File

@ -55,7 +55,6 @@ namespace PolyVox
//Information about the region we are currently processing
Region m_regSizeInVoxels;
Region m_regSizeInCells;
//The surface patch we are currently filling.
SurfaceMesh<PositionMaterial>* m_meshCurrent;

View File

@ -39,30 +39,30 @@ namespace PolyVox
,m_regSizeInVoxels(region)
,m_meshCurrent(result)
{
m_regSizeInCells = m_regSizeInVoxels;
m_regSizeInCells.setUpperCorner(m_regSizeInCells.getUpperCorner() - Vector3DInt16(1,1,1));
m_meshCurrent->clear();
}
template <typename VoxelType>
void CubicSurfaceExtractor<VoxelType>::execute()
{
uint32_t arraySize[3]= {m_regSizeInVoxels.width()+2, m_regSizeInVoxels.height()+2, MaxQuadsSharingVertex};
uint32_t uArrayWidth = m_regSizeInVoxels.getUpperCorner().getX() - m_regSizeInVoxels.getLowerCorner().getX() + 2;
uint32_t uArrayHeight = m_regSizeInVoxels.getUpperCorner().getY() - m_regSizeInVoxels.getLowerCorner().getY() + 2;
uint32_t arraySize[3]= {uArrayWidth, uArrayHeight, MaxQuadsSharingVertex};
m_previousSliceVertices.resize(arraySize);
m_currentSliceVertices.resize(arraySize);
memset(m_previousSliceVertices.getRawData(), 0xff, m_previousSliceVertices.getNoOfElements() * sizeof(IndexAndMaterial));
memset(m_currentSliceVertices.getRawData(), 0xff, m_currentSliceVertices.getNoOfElements() * sizeof(IndexAndMaterial));
for(int16_t z = m_regSizeInVoxels.getLowerCorner().getZ(); z <= m_regSizeInVoxels.getUpperCorner().getZ() + 1; z++)
for(int32_t z = m_regSizeInVoxels.getLowerCorner().getZ(); z <= m_regSizeInVoxels.getUpperCorner().getZ() + 1; z++)
{
for(int16_t y = m_regSizeInVoxels.getLowerCorner().getY(); y <= m_regSizeInVoxels.getUpperCorner().getY() + 1; y++)
for(int32_t y = m_regSizeInVoxels.getLowerCorner().getY(); y <= m_regSizeInVoxels.getUpperCorner().getY() + 1; y++)
{
for(int16_t x = m_regSizeInVoxels.getLowerCorner().getX(); x <= m_regSizeInVoxels.getUpperCorner().getX() + 1; x++)
for(int32_t x = m_regSizeInVoxels.getLowerCorner().getX(); x <= m_regSizeInVoxels.getUpperCorner().getX() + 1; x++)
{
uint16_t regX = x - m_regSizeInVoxels.getLowerCorner().getX();
uint16_t regY = y - m_regSizeInVoxels.getLowerCorner().getY();
uint16_t regZ = z - m_regSizeInVoxels.getLowerCorner().getZ();
// these are always positive anyway
uint32_t regX = x - m_regSizeInVoxels.getLowerCorner().getX();
uint32_t regY = y - m_regSizeInVoxels.getLowerCorner().getY();
uint32_t regZ = z - m_regSizeInVoxels.getLowerCorner().getZ();
bool finalX = (x == m_regSizeInVoxels.getUpperCorner().getX() + 1);
bool finalY = (y == m_regSizeInVoxels.getUpperCorner().getY() + 1);
@ -166,8 +166,8 @@ namespace PolyVox
template <typename VoxelType>
int32_t CubicSurfaceExtractor<VoxelType>::addVertex(float fX, float fY, float fZ, uint8_t uMaterialIn, Array<3, IndexAndMaterial>& existingVertices)
{
uint16_t uX = static_cast<uint16_t>(fX + 0.75f);
uint16_t uY = static_cast<uint16_t>(fY + 0.75f);
uint32_t uX = static_cast<uint32_t>(fX + 0.75f);
uint32_t uY = static_cast<uint32_t>(fY + 0.75f);
for(uint32_t ct = 0; ct < MaxQuadsSharingVertex; ct++)
{

View File

@ -49,7 +49,6 @@ namespace PolyVox
//Information about the region we are currently processing
Region m_regSizeInVoxels;
Region m_regSizeInCells;
};
}

View File

@ -36,24 +36,22 @@ namespace PolyVox
,m_regSizeInVoxels(region)
,m_meshCurrent(result)
{
m_regSizeInCells = m_regSizeInVoxels;
m_regSizeInCells.setUpperCorner(m_regSizeInCells.getUpperCorner() - Vector3DInt16(1,1,1));
m_meshCurrent->clear();
}
template <typename VoxelType>
void CubicSurfaceExtractorWithNormals<VoxelType>::execute()
{
for(int16_t z = m_regSizeInVoxels.getLowerCorner().getZ(); z < m_regSizeInVoxels.getUpperCorner().getZ(); z++)
for(int32_t z = m_regSizeInVoxels.getLowerCorner().getZ(); z < m_regSizeInVoxels.getUpperCorner().getZ(); z++)
{
for(int16_t y = m_regSizeInVoxels.getLowerCorner().getY(); y < m_regSizeInVoxels.getUpperCorner().getY(); y++)
for(int32_t y = m_regSizeInVoxels.getLowerCorner().getY(); y < m_regSizeInVoxels.getUpperCorner().getY(); y++)
{
for(int16_t x = m_regSizeInVoxels.getLowerCorner().getX(); x < m_regSizeInVoxels.getUpperCorner().getX(); x++)
for(int32_t x = m_regSizeInVoxels.getLowerCorner().getX(); x < m_regSizeInVoxels.getUpperCorner().getX(); x++)
{
uint16_t regX = x - m_regSizeInVoxels.getLowerCorner().getX();
uint16_t regY = y - m_regSizeInVoxels.getLowerCorner().getY();
uint16_t regZ = z - m_regSizeInVoxels.getLowerCorner().getZ();
// these are always positive anyway
uint32_t regX = x - m_regSizeInVoxels.getLowerCorner().getX();
uint32_t regY = y - m_regSizeInVoxels.getLowerCorner().getY();
uint32_t regZ = z - m_regSizeInVoxels.getLowerCorner().getZ();
int currentVoxel = m_volData->getVoxelAt(x,y,z).getDensity() >= VoxelType::getThreshold();

View File

@ -58,16 +58,6 @@ namespace PolyVox
return !(*this == rhs);
}
bool operator<(const Density& rhs) const throw()
{
if (m_uDensity < rhs.m_uDensity)
return true;
if (rhs.m_uDensity < m_uDensity)
return false;
return false;
}
Type getDensity() const throw() { return m_uDensity; }
Type getMaterial() const throw() { return 1; }

View File

@ -6,15 +6,17 @@ namespace PolyVox
void smoothRegion(Volume<VoxelType>& volData, const Region& regionToSmooth)
{
Region croppedRegion = regionToSmooth;
croppedRegion.cropTo(volData.getEnclosingRegion());
Array<3, uint16_t> temp(ArraySizes(croppedRegion.width())(croppedRegion.height())(croppedRegion.depth()));
uint32_t uArrayWidth = croppedRegion.getUpperCorner().getX() - croppedRegion.getLowerCorner().getX() + 1;
uint32_t uArrayHeight = croppedRegion.getUpperCorner().getY() - croppedRegion.getLowerCorner().getY() + 1;
uint32_t uArrayDepth = croppedRegion.getUpperCorner().getZ() - croppedRegion.getLowerCorner().getZ() + 1;
Array<3, uint16_t> temp(ArraySizes(uArrayWidth)(uArrayHeight)(uArrayDepth));
for (int z = croppedRegion.getLowerCorner().getZ(); z < croppedRegion.getUpperCorner().getZ(); z++)
for (int32_t z = croppedRegion.getLowerCorner().getZ(); z <= croppedRegion.getUpperCorner().getZ(); z++)
{
for (int y = croppedRegion.getLowerCorner().getY(); y < croppedRegion.getUpperCorner().getY(); y++)
for (int32_t y = croppedRegion.getLowerCorner().getY(); y <= croppedRegion.getUpperCorner().getY(); y++)
{
for (int x = croppedRegion.getLowerCorner().getX(); x < croppedRegion.getUpperCorner().getX(); x++)
for (int32_t x = croppedRegion.getLowerCorner().getX(); x <= croppedRegion.getUpperCorner().getX(); x++)
{
uint16_t& uDensity = temp[x-croppedRegion.getLowerCorner().getX()][y-croppedRegion.getLowerCorner().getY()][z-croppedRegion.getLowerCorner().getZ()];
@ -53,11 +55,11 @@ namespace PolyVox
}
}
for (int z = croppedRegion.getLowerCorner().getZ(); z < croppedRegion.getUpperCorner().getZ(); z++)
for (int32_t z = croppedRegion.getLowerCorner().getZ(); z < croppedRegion.getUpperCorner().getZ(); z++)
{
for (int y = croppedRegion.getLowerCorner().getY(); y < croppedRegion.getUpperCorner().getY(); y++)
for (int32_t y = croppedRegion.getLowerCorner().getY(); y < croppedRegion.getUpperCorner().getY(); y++)
{
for (int x = croppedRegion.getLowerCorner().getX(); x < croppedRegion.getUpperCorner().getX(); x++)
for (int32_t x = croppedRegion.getLowerCorner().getX(); x < croppedRegion.getUpperCorner().getX(); x++)
{
uint16_t& uDensity = temp[x-croppedRegion.getLowerCorner().getX()][y-croppedRegion.getLowerCorner().getY()][z-croppedRegion.getLowerCorner().getZ()];

View File

@ -49,9 +49,9 @@ namespace PolyVox
template <typename VoxelType>
Vector3DFloat computeDecimatedCentralDifferenceGradient(const VolumeSampler<VoxelType>& volIter)
{
const uint16_t x = volIter.getPosX();
const uint16_t y = volIter.getPosY();
const uint16_t z = volIter.getPosZ();
const int32_t x = volIter.getPosX();
const int32_t y = volIter.getPosY();
const int32_t z = volIter.getPosZ();
//FIXME - bitwise way of doing this?
VoxelType voxel1nx = volIter.getVoxelAt(x-2, y ,z ) > 0 ? 1: 0;
@ -74,9 +74,9 @@ namespace PolyVox
template <typename VoxelType>
Vector3DFloat computeSmoothCentralDifferenceGradient(VolumeSampler<VoxelType>& volIter)
{
uint16_t initialX = volIter.getPosX();
uint16_t initialY = volIter.getPosY();
uint16_t initialZ = volIter.getPosZ();
int32_t initialX = volIter.getPosX();
int32_t initialY = volIter.getPosY();
int32_t initialZ = volIter.getPosZ();
//FIXME - bitwise way of doing this?
volIter.setPosition(initialX-1, initialY, initialZ);
@ -191,9 +191,9 @@ namespace PolyVox
static const int weights[3][3][3] = { { {2,3,2}, {3,6,3}, {2,3,2} }, {
{3,6,3}, {6,0,6}, {3,6,3} }, { {2,3,2}, {3,6,3}, {2,3,2} } };
uint16_t initialX = volIter.getPosX();
uint16_t initialY = volIter.getPosY();
uint16_t initialZ = volIter.getPosZ();
int32_t initialX = volIter.getPosX();
int32_t initialY = volIter.getPosY();
int32_t initialZ = volIter.getPosZ();
volIter.setPosition(initialX-1, initialY-1, initialZ-1); const float pVoxel1nx1ny1nz = computeSmoothedVoxel(volIter);
volIter.setPosition(initialX-1, initialY-1, initialZ ); const float pVoxel1nx1ny0pz = computeSmoothedVoxel(volIter);

View File

@ -61,16 +61,6 @@ namespace PolyVox
return !(*this == rhs);
}
bool operator<(const Material& rhs) const throw()
{
if (m_uMaterial < rhs.m_uMaterial)
return true;
if (rhs.m_uMaterial < m_uMaterial)
return false;
return false;
}
Type getDensity() const throw()
{
//We don't actually have a density, so make one up based on the material.

View File

@ -60,20 +60,6 @@ namespace PolyVox
return !(*this == rhs);
}
bool operator<(const MaterialDensityPair& rhs) const throw()
{
if (m_uMaterial < rhs.m_uMaterial)
return true;
if (rhs.m_uMaterial < m_uMaterial)
return false;
if (m_uDensity < rhs.m_uDensity)
return true;
if (rhs.m_uDensity < m_uDensity)
return false;
return false;
}
Type getDensity() const throw() { return m_uDensity; }
Type getMaterial() const throw() { return m_uMaterial; }

View File

@ -60,11 +60,10 @@ namespace PolyVox
template <typename VoxelType> class Block;
template <typename VoxelType> class ConstVolumeProxy;
//---------- Volume ----------
template <typename VoxelType> class Volume;
typedef Volume<float> FloatVolume;
typedef Volume<uint8_t> UInt8Volume;
typedef Volume<uint16_t> UInt16Volume;
//---------------------------------
//---------- Mesh ----------

View File

@ -84,7 +84,7 @@ namespace PolyVox
return false;
}
PolyVox::Vector3DInt16 position;
PolyVox::Vector3DInt32 position;
Node* parent;
float gVal;
float hVal;

View File

@ -25,20 +25,29 @@ freely, subject to the following restrictions:
#define __PolyVox_Block_H__
#include "PolyVoxForwardDeclarations.h"
#include <limits>
#include <vector>
namespace PolyVox
{
template <typename VoxelType>
class Block
{
template <typename LengthType>
struct RunlengthEntry
{
LengthType length;
VoxelType value;
//We can parametise the length on anything up to uint32_t.
//This lets us experiment with the optimal size in the future.
static uint32_t maxRunlength(void) {return (std::numeric_limits<LengthType>::max)();}
};
//Make VolumeSampler a friend
friend class VolumeSampler<VoxelType>;
public:
Block(uint16_t uSideLength);
Block(const Block& rhs);
~Block();
Block& operator=(const Block& rhs);
Block(uint16_t uSideLength = 0);
uint16_t getSideLength(void) const;
VoxelType getVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos) const;
@ -48,13 +57,19 @@ namespace PolyVox
void setVoxelAt(const Vector3DUint16& v3dPos, VoxelType tValue);
void fill(VoxelType tValue);
bool isHomogeneous(void);
uint32_t sizeInChars(void);
void initialise(uint16_t uSideLength);
uint32_t calculateSizeInBytes(void);
private:
public:
void compress(void);
void uncompress(void);
std::vector< RunlengthEntry<uint16_t> > m_vecCompressedData;
VoxelType* m_tUncompressedData;
uint16_t m_uSideLength;
uint8_t m_uSideLengthPower;
VoxelType* m_tData;
bool m_bIsCompressed;
bool m_bIsUncompressedDataModified;
};
}

View File

@ -27,63 +27,23 @@ freely, subject to the following restrictions:
#include <cassert>
#include <cstring> //For memcpy
#include <limits>
#include <stdexcept> //for std::invalid_argument
namespace PolyVox
{
template <typename VoxelType>
Block<VoxelType>::Block(uint16_t uSideLength)
:m_tData(0)
:m_uSideLength(0)
,m_uSideLengthPower(0)
,m_tUncompressedData(0)
,m_bIsCompressed(true)
,m_bIsUncompressedDataModified(true)
{
//Debug mode validation
assert(isPowerOf2(uSideLength));
//Release mode validation
if(!isPowerOf2(uSideLength))
if(uSideLength != 0)
{
throw std::invalid_argument("Block side length must be a power of two.");
initialise(uSideLength);
}
//Compute the side length
m_uSideLength = uSideLength;
m_uSideLengthPower = logBase2(uSideLength);
//If this fails an exception will be thrown. Memory is not
//allocated and there is nothing else in this class to clean up
m_tData = new VoxelType[m_uSideLength * m_uSideLength * m_uSideLength];
}
template <typename VoxelType>
Block<VoxelType>::Block(const Block<VoxelType>& rhs)
{
*this = rhs;
}
template <typename VoxelType>
Block<VoxelType>::~Block()
{
delete[] m_tData;
m_tData = 0;
}
template <typename VoxelType>
Block<VoxelType>& Block<VoxelType>::operator=(const Block<VoxelType>& rhs)
{
if (this == &rhs)
{
return *this;
}
//If this fails an exception will be thrown. Memory is not
//allocated and there is nothing else in this class to clean up
m_tData = new VoxelType[rhs.m_uSideLength * rhs.m_uSideLength * rhs.m_uSideLength];
//Copy the data
m_uSideLength = rhs.m_uSideLength;
m_uSideLengthPower = rhs.m_uSideLengthPower;
memcpy(m_tData, rhs.m_tData, m_uSideLength * m_uSideLength * m_uSideLength * sizeof(VoxelType));
return *this;
}
template <typename VoxelType>
@ -99,7 +59,9 @@ namespace PolyVox
assert(uYPos < m_uSideLength);
assert(uZPos < m_uSideLength);
return m_tData
assert(m_tUncompressedData);
return m_tUncompressedData
[
uXPos +
uYPos * m_uSideLength +
@ -120,12 +82,16 @@ namespace PolyVox
assert(uYPos < m_uSideLength);
assert(uZPos < m_uSideLength);
m_tData
assert(m_tUncompressedData);
m_tUncompressedData
[
uXPos +
uYPos * m_uSideLength +
uZPos * m_uSideLength * m_uSideLength
] = tValue;
m_bIsUncompressedDataModified = true;
}
template <typename VoxelType>
@ -136,42 +102,114 @@ namespace PolyVox
template <typename VoxelType>
void Block<VoxelType>::fill(VoxelType tValue)
{
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.
//memset(m_tData, (int)tValue, m_uSideLength * m_uSideLength * m_uSideLength * sizeof(VoxelType));
const uint32_t uNoOfVoxels = m_uSideLength * m_uSideLength * m_uSideLength;
std::fill(m_tData, m_tData + uNoOfVoxels, tValue);
std::fill(m_tUncompressedData, m_tUncompressedData + uNoOfVoxels, tValue);
m_bIsUncompressedDataModified = true;
}
else
{
RunlengthEntry<uint16_t> rle;
rle.length = m_uSideLength*m_uSideLength*m_uSideLength;
rle.value = tValue;
m_vecCompressedData.clear();
m_vecCompressedData.push_back(rle);
}
}
template <typename VoxelType>
bool Block<VoxelType>::isHomogeneous(void)
void Block<VoxelType>::initialise(uint16_t uSideLength)
{
const VoxelType tFirstVoxel = m_tData[0];
const uint32_t uNoOfVoxels = m_uSideLength * m_uSideLength * m_uSideLength;
//Debug mode validation
assert(isPowerOf2(uSideLength));
//Release mode validation
if(!isPowerOf2(uSideLength))
{
throw std::invalid_argument("Block side length must be a power of two.");
}
//Compute the side length
m_uSideLength = uSideLength;
m_uSideLengthPower = logBase2(uSideLength);
Block<VoxelType>::fill(VoxelType());
}
template <typename VoxelType>
uint32_t Block<VoxelType>::calculateSizeInBytes(void)
{
uint32_t uSizeInBytes = sizeof(Block<VoxelType>);
uSizeInBytes += m_vecCompressedData.capacity() * sizeof(RunlengthEntry<uint16_t>);
return uSizeInBytes;
}
template <typename VoxelType>
void Block<VoxelType>::compress(void)
{
assert(m_bIsCompressed == false);
assert(m_tUncompressedData != 0);
//If the uncompressed data hasn't actually been
//modified then we don't need to redo the compression.
if(m_bIsUncompressedDataModified)
{
uint32_t uNoOfVoxels = m_uSideLength * m_uSideLength * m_uSideLength;
m_vecCompressedData.clear();
RunlengthEntry<uint16_t> entry;
entry.length = 1;
entry.value = m_tUncompressedData[0];
for(uint32_t ct = 1; ct < uNoOfVoxels; ++ct)
{
if(m_tData[ct] != tFirstVoxel)
VoxelType value = m_tUncompressedData[ct];
if((value == entry.value) && (entry.length < entry.maxRunlength()))
{
return false;
entry.length++;
}
else
{
m_vecCompressedData.push_back(entry);
entry.value = value;
entry.length = 1;
}
}
return true;
m_vecCompressedData.push_back(entry);
//Shrink the vectors to their contents (maybe slow?):
//http://stackoverflow.com/questions/1111078/reduce-the-capacity-of-an-stl-vector
//C++0x may have a shrink_to_fit() function?
std::vector< RunlengthEntry<uint16_t> >(m_vecCompressedData).swap(m_vecCompressedData);
}
//Flag the uncompressed data as no longer being used.
delete[] m_tUncompressedData;
m_tUncompressedData = 0;
m_bIsCompressed = true;
}
template <typename VoxelType>
uint32_t Block<VoxelType>::sizeInChars(void)
void Block<VoxelType>::uncompress(void)
{
uint32_t uSizeInChars = sizeof(Block<VoxelType>);
assert(m_bIsCompressed == true);
assert(m_tUncompressedData == 0);
m_tUncompressedData = new VoxelType[m_uSideLength * m_uSideLength * m_uSideLength];
if(m_tData != 0)
VoxelType* pUncompressedData = m_tUncompressedData;
for(uint32_t ct = 0; ct < m_vecCompressedData.size(); ++ct)
{
const uint32_t uNoOfVoxels = m_uSideLength * m_uSideLength * m_uSideLength;
uSizeInChars += uNoOfVoxels * sizeof(VoxelType);
std::fill(pUncompressedData, pUncompressedData + m_vecCompressedData[ct].length, m_vecCompressedData[ct].value);
pUncompressedData += m_vecCompressedData[ct].length;
}
return uSizeInChars;
m_bIsCompressed = false;
m_bIsUncompressedDataModified = false;
}
}

View File

@ -51,6 +51,11 @@ freely, subject to the following restrictions:
#include <boost/functional/hash.hpp>
#define polyvox_hash boost::hash
#include <boost/bind.hpp>
#define polyvox_bind boost::bind
#define polyvox_placeholder_1 _1
#define polyvox_placeholder_2 _2
//As long as we're requiring boost, we'll use it to compensate
//for the missing cstdint header too.
@ -68,6 +73,9 @@ freely, subject to the following restrictions:
#include <memory>
#define polyvox_shared_ptr std::shared_ptr
#define polyvox_function std::function
#define polyvox_bind std::bind
#define polyvox_placeholder_1 std::placeholders::_1
#define polyvox_placeholder_2 std::placeholders::_2
#define polyvox_hash std::hash
#endif

View File

@ -38,7 +38,7 @@ namespace PolyVox
///Indicates whether an intersection was found
bool foundIntersection;
///If an intersection was found then this field holds the intersecting voxel, otherwise it is undefined.
Vector3DInt16 intersectionVoxel;
Vector3DInt32 intersectionVoxel;
};
/// The Raycast class can be used to find the fist filled voxel along a given path.

View File

@ -139,7 +139,7 @@ namespace PolyVox
if(m_sampVolume.getVoxel().getDensity() > VoxelType::getThreshold())
{
m_result.foundIntersection = true;
m_result.intersectionVoxel = Vector3DInt16(i,j,k);
m_result.intersectionVoxel = Vector3DInt32(i,j,k);
return;
}
@ -172,6 +172,6 @@ namespace PolyVox
//Didn't hit anything
m_result.foundIntersection = false;
m_result.intersectionVoxel = Vector3DInt16(0,0,0);
m_result.intersectionVoxel = Vector3DInt32(0,0,0);
}
}

View File

@ -36,29 +36,36 @@ namespace PolyVox
#endif
{
public:
const static Region MaxRegion;
Region();
Region(const Vector3DInt16& v3dLowerCorner, const Vector3DInt16& v3dUpperCorner);
Region(const Vector3DInt32& v3dLowerCorner, const Vector3DInt32& v3dUpperCorner);
const Vector3DInt16& getLowerCorner(void) const;
const Vector3DInt16& getUpperCorner(void) const;
const Vector3DInt32& getLowerCorner(void) const;
const Vector3DInt32& getUpperCorner(void) const;
void setLowerCorner(const Vector3DInt16& v3dLowerCorner);
void setUpperCorner(const Vector3DInt16& v3dUpperCorner);
void setLowerCorner(const Vector3DInt32& v3dLowerCorner);
void setUpperCorner(const Vector3DInt32& v3dUpperCorner);
bool containsPoint(const Vector3DFloat& pos, float boundary = 0.0f) const;
bool containsPoint(const Vector3DInt16& pos, uint8_t boundary = 0) const;
bool containsPoint(const Vector3DInt32& pos, uint8_t boundary = 0) const;
void cropTo(const Region& other);
int16_t depth(void) const;
int16_t height(void) const;
void shift(const Vector3DInt16& amount);
void shiftLowerCorner(const Vector3DInt16& amount);
void shiftUpperCorner(const Vector3DInt16& amount);
Vector3DInt16 dimensions(void);
int16_t width(void) const;
/// Deprecated and misleading
int32_t depth(void) const;
/// Deprecated and misleading
int32_t height(void) const;
void shift(const Vector3DInt32& amount);
void shiftLowerCorner(const Vector3DInt32& amount);
void shiftUpperCorner(const Vector3DInt32& amount);
/// Deprecated and misleading
Vector3DInt32 dimensions(void);
/// Deprecated and misleading
int32_t width(void) const;
private:
Vector3DInt16 m_v3dLowerCorner;
Vector3DInt16 m_v3dUpperCorner;
Vector3DInt32 m_v3dLowerCorner;
Vector3DInt32 m_v3dUpperCorner;
//FIXME - This variable is unused, but without it the OpenGL example crashes in release mode
//when the volume size is 128^3 and the level of detail is 2. Very strange, but consistant.

View File

@ -71,14 +71,14 @@ namespace PolyVox
VolumeSampler<VoxelType> m_sampVolume;
//Holds a position in volume space.
int16_t iXVolSpace;
int16_t iYVolSpace;
int16_t iZVolSpace;
int32_t iXVolSpace;
int32_t iYVolSpace;
int32_t iZVolSpace;
//Holds a position in region space.
uint16_t uXRegSpace;
uint16_t uYRegSpace;
uint16_t uZRegSpace;
uint32_t uXRegSpace;
uint32_t uYRegSpace;
uint32_t uZRegSpace;
//Used to return the number of cells in a slice which contain triangles.
uint32_t m_uNoOfOccupiedCells;

View File

@ -38,7 +38,7 @@ namespace PolyVox
{
//m_regSizeInVoxels.cropTo(m_volData->getEnclosingRegion());
m_regSizeInCells = m_regSizeInVoxels;
m_regSizeInCells.setUpperCorner(m_regSizeInCells.getUpperCorner() - Vector3DInt16(1,1,1));
m_regSizeInCells.setUpperCorner(m_regSizeInCells.getUpperCorner() - Vector3DInt32(1,1,1));
m_meshCurrent->clear();
}
@ -46,7 +46,9 @@ namespace PolyVox
template <typename VoxelType>
void SurfaceExtractor<VoxelType>::execute()
{
uint32_t arraySizes[2]= {m_regSizeInVoxels.width()+1, m_regSizeInVoxels.height()+1}; // Array dimensions
uint32_t uArrayWidth = m_regSizeInVoxels.getUpperCorner().getX() - m_regSizeInVoxels.getLowerCorner().getX() + 1;
uint32_t uArrayHeight = m_regSizeInVoxels.getUpperCorner().getY() - m_regSizeInVoxels.getLowerCorner().getY() + 1;
uint32_t arraySizes[2]= {uArrayWidth, uArrayHeight}; // Array dimensions
//For edge indices
Array2DInt32 m_pPreviousVertexIndicesX(arraySizes);
@ -61,7 +63,7 @@ namespace PolyVox
//Create a region corresponding to the first slice
m_regSlicePrevious = m_regSizeInVoxels;
Vector3DInt16 v3dUpperCorner = m_regSlicePrevious.getUpperCorner();
Vector3DInt32 v3dUpperCorner = m_regSlicePrevious.getUpperCorner();
v3dUpperCorner.setZ(m_regSlicePrevious.getLowerCorner().getZ()); //Set the upper z to the lower z to make it one slice thick.
m_regSlicePrevious.setUpperCorner(v3dUpperCorner);
m_regSliceCurrent = m_regSlicePrevious;
@ -88,10 +90,10 @@ namespace PolyVox
m_pPreviousVertexIndicesZ.swap(m_pCurrentVertexIndicesZ);
m_regSlicePrevious = m_regSliceCurrent;
m_regSliceCurrent.shift(Vector3DInt16(0,0,1));
m_regSliceCurrent.shift(Vector3DInt32(0,0,1));
//Process the other slices (previous slice is available)
for(int16_t uSlice = 1; uSlice <= m_regSizeInVoxels.depth(); uSlice++)
for(int32_t uSlice = 1; uSlice <= m_regSizeInVoxels.getUpperCorner().getZ() - m_regSizeInVoxels.getLowerCorner().getZ(); uSlice++)
{
computeBitmaskForSlice<true>(pPreviousBitmask, pCurrentBitmask);
uNoOfNonEmptyCellsForSlice1 = m_uNoOfOccupiedCells;
@ -116,7 +118,7 @@ namespace PolyVox
m_pPreviousVertexIndicesZ.swap(m_pCurrentVertexIndicesZ);
m_regSlicePrevious = m_regSliceCurrent;
m_regSliceCurrent.shift(Vector3DInt16(0,0,1));
m_regSliceCurrent.shift(Vector3DInt32(0,0,1));
}
m_meshCurrent->m_Region = m_regSizeInVoxels;
@ -134,8 +136,8 @@ namespace PolyVox
{
m_uNoOfOccupiedCells = 0;
const int16_t iMaxXVolSpace = m_regSliceCurrent.getUpperCorner().getX();
const int16_t iMaxYVolSpace = m_regSliceCurrent.getUpperCorner().getY();
const int32_t iMaxXVolSpace = m_regSliceCurrent.getUpperCorner().getX();
const int32_t iMaxYVolSpace = m_regSliceCurrent.getUpperCorner().getY();
iZVolSpace = m_regSliceCurrent.getLowerCorner().getZ();
uZRegSpace = iZVolSpace - m_regSizeInVoxels.getLowerCorner().getZ();
@ -400,18 +402,18 @@ namespace PolyVox
Array2DInt32& m_pCurrentVertexIndicesY,
Array2DInt32& m_pCurrentVertexIndicesZ)
{
int16_t iZVolSpace = m_regSliceCurrent.getLowerCorner().getZ();
const uint16_t uZRegSpace = iZVolSpace - m_regSizeInVoxels.getLowerCorner().getZ();
int32_t iZVolSpace = m_regSliceCurrent.getLowerCorner().getZ();
const uint32_t uZRegSpace = iZVolSpace - m_regSizeInVoxels.getLowerCorner().getZ();
//Iterate over each cell in the region
for(int16_t iYVolSpace = m_regSliceCurrent.getLowerCorner().getY(); iYVolSpace <= m_regSliceCurrent.getUpperCorner().getY(); iYVolSpace++)
for(int32_t iYVolSpace = m_regSliceCurrent.getLowerCorner().getY(); iYVolSpace <= m_regSliceCurrent.getUpperCorner().getY(); iYVolSpace++)
{
const uint16_t uYRegSpace = iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY();
const uint32_t uYRegSpace = iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY();
for(int16_t iXVolSpace = m_regSliceCurrent.getLowerCorner().getX(); iXVolSpace <= m_regSliceCurrent.getUpperCorner().getX(); iXVolSpace++)
for(int32_t iXVolSpace = m_regSliceCurrent.getLowerCorner().getX(); iXVolSpace <= m_regSliceCurrent.getUpperCorner().getX(); iXVolSpace++)
{
//Current position
const uint16_t uXRegSpace = iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX();
const uint32_t uXRegSpace = iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX();
//Determine the index into the edge table which tells us which vertices are inside of the surface
uint8_t iCubeIndex = pCurrentBitmask[uXRegSpace][uYRegSpace];
@ -622,17 +624,17 @@ namespace PolyVox
indlist[i] = -1;
}
for(int16_t iYVolSpace = m_regSlicePrevious.getLowerCorner().getY(); iYVolSpace <= m_regSizeInCells.getUpperCorner().getY(); iYVolSpace++)
for(int32_t iYVolSpace = m_regSlicePrevious.getLowerCorner().getY(); iYVolSpace <= m_regSizeInCells.getUpperCorner().getY(); iYVolSpace++)
{
for(int16_t iXVolSpace = m_regSlicePrevious.getLowerCorner().getX(); iXVolSpace <= m_regSizeInCells.getUpperCorner().getX(); iXVolSpace++)
for(int32_t iXVolSpace = m_regSlicePrevious.getLowerCorner().getX(); iXVolSpace <= m_regSizeInCells.getUpperCorner().getX(); iXVolSpace++)
{
int16_t iZVolSpace = m_regSlicePrevious.getLowerCorner().getZ();
int32_t iZVolSpace = m_regSlicePrevious.getLowerCorner().getZ();
m_sampVolume.setPosition(iXVolSpace,iYVolSpace,iZVolSpace);
//Current position
const uint16_t uXRegSpace = m_sampVolume.getPosX() - m_regSizeInVoxels.getLowerCorner().getX();
const uint16_t uYRegSpace = m_sampVolume.getPosY() - m_regSizeInVoxels.getLowerCorner().getY();
const uint16_t uZRegSpace = m_sampVolume.getPosZ() - m_regSizeInVoxels.getLowerCorner().getZ();
const uint32_t uXRegSpace = m_sampVolume.getPosX() - m_regSizeInVoxels.getLowerCorner().getX();
const uint32_t uYRegSpace = m_sampVolume.getPosY() - m_regSizeInVoxels.getLowerCorner().getY();
const uint32_t uZRegSpace = m_sampVolume.getPosZ() - m_regSizeInVoxels.getLowerCorner().getZ();
//Determine the index into the edge table which tells us which vertices are inside of the surface
uint8_t iCubeIndex = pPreviousBitmask[uXRegSpace][uYRegSpace];

View File

@ -25,174 +25,269 @@ freely, subject to the following restrictions:
#define __PolyVox_Volume_H__
#include "PolyVoxImpl/Block.h"
#include "Region.h"
#include "PolyVoxForwardDeclarations.h"
#include <limits>
#include <map>
#include <set>
#include <memory>
#include <vector>
namespace PolyVox
{
///The Volume class provides a memory efficient method of storing voxel data while also allowing fast access and modification.
////////////////////////////////////////////////////////////////////////////////
/// A Volume is essentially a '3D image' in which each element (voxel) is identified
/// by a three dimensional (x,y,z) coordinate, rather than the two dimensional (x,y)
/// coordinate which is used to identify an element (pixel) in a normal image. Within
/// PolyVox, the Volume class is used to store and manipulate our data before we extract
/// our SurfaceMeshs from it.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// A Volume 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 Volume 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 Volume class is templatised so that different types of data can be stored within each voxel.
///
/// <b>Data Representaion - feel free to skip</b>
/// If stored carelessly, volume data can take up a huge amount of memory. For example, a
/// volume of dimensions 1024x1024x1024 with 1 byte per voxel will require 1GB of memory
/// if stored in an uncompressed form. Natuarally our Volume class is much more efficient
/// than this and it is worth understanding (at least at a high level) the approach
/// which is used.
///
/// Essentially, the Volume class stores its data as a collection of blocks. Each
/// of these block is much smaller than the whole volume, for example a typical size
/// might be 32x32x32 voxels (though is is configurable by the user). In this case,
/// a 256x512x1024 volume would contain 8x16x32 = 4096 blocks. However, it is unlikely that
/// all these blocks actually have to be stored because usually there are duplicates
/// in which case common data can be shared.
///
/// Identifying duplicate blocks is in general a difficult task which involves looking at pairs
/// of blocks and comparing all the voxels. This is a time consuming task which is not amiable
/// to being performed when the volume is being modified in real time. However, there are two
/// specific scenarios which are easily spotted and which PolyVox uses to identify block
/// sharing opportunities.
///
/// -# Homogeneous blocks (those which contain just a single voxel value) are easy to
/// spot and fairly common becuase volumes often contain large homogeous regions. Any time
/// you change the value of a voxel you have potentially made the block which contains
/// it homogeneous. PolyVox does not check the homogeneity immediately as this would slow
/// down the process of modifying voxels, but you can use the tidyUpMemory() function
/// to check for and remove duplicate homogeneous regions whenever you have spare
/// processing time.
///
/// -# Copying a volume naturally means that all the voxels in the second volume are
/// the same as the first. Therefore volume copying is a relatively fast operation in
/// which all the blocks in the second volume simply reference the first volume. Future
/// modifications to either volume will, of course, cause the blocks to become unshared.
///
/// Other advantages of breaking the volume down into blocks include enhancing data locality
/// (i.e. voxels which are spatially near to each other are also likely to be near in
/// memory) and the ability to load larger volumes as no large contiguous areas of
/// memory are needed. However, these advantages are more transparent to user code
/// so we will not dwell on them here.
///
/// <b>Usage</b>
/// Volumes are constructed by passing the desired width, height and depth to the
/// constructor. Note that for speed reasons only values which are a power of two
/// are permitted for these sidelengths.
/// <b> Basic usage</b>
/// The following code snippet shows how to construct a volume and demonstrates basic usage:
///
/// \code
/// Volume<uint8_t> volData(g_uVolumeSideLength, g_uVolumeSideLength, g_uVolumeSideLength);
/// Volume<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
///
/// Access to specific voxels is provided by the getVoxelAt() and setVoxelAt() fuctions.
/// Each of these has two forms so that voxels can be identified by integer triples
/// or by Vector3DUint16%s.
/// In this particular example each voxel in the Volume 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 Volume 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 VolumeSampler class for faster read-only access to a large number of voxels.
///
/// Lastly the example prints out some properties of the Volume. Note that the dimentsions width(), height(), and depth() are inclusive, such
/// that the width is 64 when the range of valid x coordinates goes from 0 to 63.
///
/// <b>Data Representaion</b>
/// If stored carelessly, volume data can take up a huge amount of memory. For example, a volume of dimensions 1024x1024x1024 with
/// 1 byte per voxel will require 1GB of memory if stored in an uncompressed form. Natuarally our Volume class is much more efficient
/// than this and it is worth understanding (at least at a high level) the approach which is used.
///
/// Essentially, the Volume class stores its data as a collection of blocks. Each of these block is much smaller than the whole volume,
/// for example a typical size might be 32x32x32 voxels (though is is configurable by the user). In this case, a 256x512x1024 volume
/// would contain 8x16x32 = 4096 blocks. The data for each block is stored in a compressed form, which uses only a small amout of
/// memory but it is hard to modify the data. Therefore, before any given voxel can be modified, its corresponding block must be uncompressed.
///
/// The compression and decompression of block is a relatively slow process and so we aim to do this as rarely as possible. In order
/// to achive this, the volume class stores a cache of recently used blocks and their associated uncompressed data. Each time a voxel
/// is touched a timestamp is updated on the corresponding block. When the cache becomes full the block with the oldest timestamp is
/// recompressed and moved out of the cache.
///
/// <b>Achieving high compression rates</b>
/// The compression rates which can be achieved can vary significantly depending the nature of the data you are storing, but you can
/// encourage high compression rates by making your data as homogenous as possible. If you are simply storing a material with each
/// voxel then this will probably happen naturally. Games such as Minecraft which use this approach will typically involve large areas
/// of the same material which will compress down well.
///
/// However, if you are storing density values then you may want to take some care. The advantage of storing smoothly changing values
/// is that you can get smooth surfaces extracted, but storing smoothly changing values inside or outside objects (rather than just
/// on the boundary) does not benefit the surface and is very hard to compress effectively. You may wish to apply some thresholding to
/// your density values to reduce this problem (this threasholding should only be applied to voxels who don't contribute to the surface).
///
/// <b>Paging large volumes</b>
/// The compression scheme described previously will typically allow you to load several billion voxels into a few hundred megabytes of memory,
/// though as explained the exact compression rate is highly dependant on your data. If you have more data than this then PolyVox provides a
/// mechanism by which parts of the volume can be paged out of memory by calling user supplied callback functions. This mechanism allows a
/// potentially unlimited amount of data to be loaded, provided the user is able to take responsibility for storing any data which PolyVox
/// cannot fit in memory, and then returning it back to PolyVox on demand. For example, the user might choose to temporarily store this data
/// on disk or stream it to a remote database.
///
/// You can construct such a Volume as follows:
///
/// \code
/// volData.setVoxelAt(12, 186, 281, 3);
/// uint8_t voxelValue = volData.getVoxelAt(12, 186, 281);
/// //voxelValue is now 3
/// 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.
/// }
///
/// Volume<Density>volData(&myDataRequiredHandler, &myDataOverflowHandler);
/// \endcode
///
/// The tidyUpMemory() function should normally be called after you first populate
/// the volume with data, and then at periodic intervals as the volume is modified.
/// However, you don't actually <i>have</i> to call it at all. See the function's
/// documentation for further details.
/// Essentially you are providing an extension to the Volume 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.
///
/// One further important point of note is that this class is templatised on the voxel
/// type. This allows you to store volumes of data types you might not normally expect,
/// for example the OpenGL example 'abuses' this class to store a 3D grid of pointers.
/// However, it is not guaranteed that all functionality works correctly with non-integer
/// voxel types.
////////////////////////////////////////////////////////////////////////////////
/// <b>Cache-aware traversal</b>
/// You might be suprised at just how many cache misses can occur when you traverse the volume in a naive manner. Consider a 1024x1024x1024 volume
/// with blocks of size 32x32x32. And imagine you iterate over this volume with a simple three-level for loop which iterates over x, the y, then z.
/// If you start at position (0,0,0) then ny the time you reach position (1023,0,0) you have touched 1024 voxels along one edge of the volume and
/// have pulled 32 blocks into the cache. By the time you reach (1023,1023,0) you have hit 1024x1024 voxels and pulled 32x32 blocks into the cache.
/// You are now ready to touch voxel (0,0,1) which is right nect to where you started, but unless your cache is at least 32x32 blocks large then this
/// initial block has already been cleared from the cache.
///
/// Ensuring you have a large enough cache size can obviously help the above situation, but you might also consider iterating over the voxels in a
/// different order. For example, if you replace your three-level loop with a six-level loop then you can first process all the voxels between (0,0,0)
/// and (31,31,31), then process all the voxels between (32,0,0) and (63,0,0), and so forth. Using this approach you will have no cache misses even
/// is your cache sise is only one. Of course the logic is more complex, but writing code in such a cache-aware manner may be beneficial in some situations.
///
/// <b>Threading</b>
/// The Volume 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 Volume
{
//Make VolumeSampler a friend
// Make VolumeSampler a friend
friend class VolumeSampler<VoxelType>;
// Make the ConstVolumeProxy a friend
friend class ConstVolumeProxy<VoxelType>;
struct LoadedBlock
{
public:
///Constructor
Volume(uint16_t uWidth, uint16_t uHeight, uint16_t uDepth, uint16_t uBlockSideLength = 32);
///Destructor
~Volume();
LoadedBlock(uint16_t uSideLength = 0)
:block(uSideLength)
,timestamp(0)
{
}
///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.
uint16_t getWidth(void) const;
///Gets the height of the volume in voxels.
uint16_t getHeight(void) const;
///Gets the depth of the volume in voxels.
uint16_t getDepth(void) const;
///Gets the length of the longest side in voxels
uint16_t getLongestSideLength(void) const;
///Gets the length of the shortest side in voxels
uint16_t getShortestSideLength(void) const;
///Gets the length of the diagonal in voxels
float getDiagonalLength(void) const;
///Gets a voxel by <tt>x,y,z</tt> position
VoxelType getVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos) const;
///Gets a voxel by 3D vector position
VoxelType getVoxelAt(const Vector3DUint16& v3dPos) const;
///Sets the value used for voxels which are outside the volume
void setBorderValue(const VoxelType& tBorder);
///Sets the voxel at an <tt>x,y,z</tt> position
bool setVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos, VoxelType tValue);
///Sets the voxel at a 3D vector position
bool setVoxelAt(const Vector3DUint16& v3dPos, VoxelType tValue);
///Resises the volume to the specified dimensions
void resize(uint16_t uWidth, uint16_t uHeight, uint16_t uDepth, uint16_t uBlockSideLength = 32);
void tidyUpMemory(uint32_t uNoOfBlocksToProcess = 1000000);
private:
polyvox_shared_ptr< Block<VoxelType> > getHomogenousBlock(VoxelType tHomogenousValue);
polyvox_shared_ptr< Block<VoxelType> > m_pBorderBlock;
std::vector< polyvox_shared_ptr< Block<VoxelType> > > m_pBlocks;
std::vector<bool> m_vecBlockIsPotentiallyHomogenous;
//Note: We were once storing weak_ptr's in this map, so that the blocks would be deleted once they
//were not being referenced by anyone else. However, this made it difficult to know when a block was
//shared. A call to shared_ptr::unique() from within setVoxel was not sufficient as weak_ptr's did
//not contribute to the reference count. Instead we store shared_ptr's here, and check if they
//are used by anyone else (i.e are non-unique) when we tidy the volume.
std::map<VoxelType, polyvox_shared_ptr< Block<VoxelType> > > m_pHomogenousBlock;
uint32_t m_uNoOfBlocksInVolume;
uint16_t m_uWidthInBlocks;
uint16_t m_uHeightInBlocks;
uint16_t m_uDepthInBlocks;
uint16_t m_uWidth;
uint16_t m_uHeight;
uint16_t m_uDepth;
uint8_t m_uBlockSideLengthPower;
uint16_t m_uBlockSideLength;
uint16_t m_uLongestSideLength;
uint16_t m_uShortestSideLength;
float m_fDiagonalLength;
uint32_t m_uCurrentBlockForTidying;
Block<VoxelType> block;
uint32_t timestamp;
};
//Some handy typedefs
typedef Volume<float> FloatVolume;
typedef Volume<uint8_t> UInt8Volume;
typedef Volume<uint16_t> UInt16Volume;
public:
/// Constructor for creting a very large paging volume.
Volume
(
polyvox_function<void(const ConstVolumeProxy<VoxelType>&, const Region&)> dataRequiredHandler,
polyvox_function<void(const ConstVolumeProxy<VoxelType>&, const Region&)> dataOverflowHandler,
uint16_t uBlockSideLength = 32
);
/// Constructor for creating a fixed size volume.
Volume
(
const Region& regValid,
polyvox_function<void(const ConstVolumeProxy<VoxelType>&, const Region&)> dataRequiredHandler = 0,
polyvox_function<void(const ConstVolumeProxy<VoxelType>&, const Region&)> dataOverflowHandler = 0,
bool bPagingEnabled = false,
uint16_t uBlockSideLength = 32
);
/// Deprecated constructor - do not use.
Volume
(
int32_t dont_use_this_constructor_1, int32_t dont_use_this_constructor_2, int32_t dont_use_this_constructor_3
);
/// Destructor
~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 at the position given by <tt>x,y,z</tt> coordinates
VoxelType getVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos) const;
/// Gets a voxel at the position given by a 3D vector
VoxelType getVoxelAt(const Vector3DInt32& v3dPos) const;
//Sets whether or not blocks are compressed in memory
void setCompressionEnabled(bool bCompressionEnabled);
/// Sets the number of blocks for which uncompressed data is stored
void setMaxNumberOfUncompressedBlocks(uint16_t uMaxNumberOfUncompressedBlocks);
/// Sets the number of blocks which can be in memory before the paging system starts unloading them
void setMaxNumberOfBlocksInMemory(uint16_t uMaxNumberOfBlocksInMemory);
/// Sets the value used for voxels which are outside the volume
void setBorderValue(const VoxelType& tBorder);
/// Sets the voxel at the position given by <tt>x,y,z</tt> coordinates
bool setVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue);
/// Sets the voxel at the position given by a 3D vector
bool setVoxelAt(const Vector3DInt32& v3dPos, VoxelType tValue);
/// Tries to ensure that the voxels within the specified Region are loaded into memory.
void prefetch(Region regPrefetch);
/// Ensures that any voxels within the specified Region are removed from memory.
void flush(Region regFlush);
/// Removes all voxels from memory
void flushAll();
/// Empties the cache of uncompressed blocks
void clearBlockCache(void);
/// Calculates the approximate compression ratio of the store volume data
float calculateCompressionRatio(void);
/// Calculates approximatly how many bytes of memory the volume is currently using.
uint32_t calculateSizeInBytes(void);
/// Deprecated - I don't think we should expose this function? Let us know if you disagree...
void resize(const Region& regValidRegion, uint16_t uBlockSideLength);
private:
/// gets called when a new region is allocated and needs to be filled
/// NOTE: accessing ANY voxels outside this region during the process of this function
/// is absolutely unsafe
polyvox_function<void(const ConstVolumeProxy<VoxelType>&, const Region&)> m_funcDataRequiredHandler;
/// gets called when a Region needs to be stored by the user, because 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
polyvox_function<void(const ConstVolumeProxy<VoxelType>&, const Region&)> m_funcDataOverflowHandler;
Block<VoxelType>* getUncompressedBlock(int32_t uBlockX, int32_t uBlockY, int32_t uBlockZ) const;
void eraseBlock(typename std::map<Vector3DInt32, LoadedBlock >::iterator itBlock) const;
/// this function can be called by m_funcDataRequiredHandler without causing any weird effects
bool setVoxelAtConst(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue) const;
//The block data
mutable std::map<Vector3DInt32, LoadedBlock > m_pBlocks;
//The cache of uncompressed blocks. The uncompressed block data and the timestamps are stored here rather
//than in the Block class. This is so that in the future each VolumeIterator might to maintain its own cache
//of blocks. However, this could mean the same block data is uncompressed and modified in more than one
//location in memory... could be messy with threading.
mutable std::vector< LoadedBlock* > m_vecUncompressedBlockCache;
mutable uint32_t m_uTimestamper;
mutable Vector3DInt32 m_v3dLastAccessedBlockPos;
mutable Block<VoxelType>* m_pLastAccessedBlock;
uint32_t m_uMaxNumberOfUncompressedBlocks;
uint32_t m_uMaxNumberOfBlocksInMemory;
//We don't store an actual Block for the border, just the uncompressed data. This is partly because the border
//block does not have a position (so can't be passed to getUncompressedBlock()) and partly because there's a
//good chance we'll often hit it anyway. It's a chunk of homogenous data (rather than a single value) so that
//the VolumeIterator can do it's usual pointer arithmetic without needing to know it's gone outside the volume.
VoxelType* m_pUncompressedBorderData;
//The size of the volume
Region m_regValidRegion;
Region m_regValidRegionInBlocks;
//The size of the blocks
uint16_t m_uBlockSideLength;
uint8_t m_uBlockSideLengthPower;
//Some useful sizes
int32_t m_uLongestSideLength;
int32_t m_uShortestSideLength;
float m_fDiagonalLength;
bool m_bCompressionEnabled;
bool m_bPagingEnabled;
};
}
#include "Volume.inl"

View File

@ -21,12 +21,14 @@ freely, subject to the following restrictions:
distribution.
*******************************************************************************/
#include "ConstVolumeProxy.h"
#include "PolyVoxImpl/Block.h"
#include "Log.h"
#include "VolumeSampler.h"
#include "Region.h"
#include "Vector.h"
#include <limits>
#include <cassert>
#include <cstring> //For memcpy
#include <list>
@ -35,35 +37,80 @@ freely, subject to the following restrictions:
namespace PolyVox
{
////////////////////////////////////////////////////////////////////////////////
/// Builds a volume of the desired dimensions
/// \param uWidth The desired width in voxels. This must be a power of two.
/// \param uHeight The desired height in voxels. This must be a power of two.
/// \param uDepth The desired depth in voxels. This must be a power of two.
/// \param uBlockSideLength The size of the blocks which make up the volume. Small
/// blocks are more likely to be homogeneous (so more easily shared) and have better
/// cache behaviour. However, there is a memory overhead per block so if they are
/// not shared it could actually be less efficient (this will depend on the data).
/// The size of the volume may also be a factor when choosing block size. Accept
/// the default if you are not sure what to choose here.
/// 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 Volume class documentation for a full description of this process and the required function signatures. If you really don't want to handle these events then you can provide null pointers here, in which case the data will be discarded and/or filled with default values.
/// \param dataRequiredHandler The callback function which will be called when PolyVox tries to use data which is not currently in momory.
/// \param dataOverflowHandler The callback function which will be called when PolyVox has too much data and needs to remove some from memory.
/// \param uBlockSideLength The size of the blocks making up the volume. Small blocks will compress/decompress faster, but there will also be more of them meaning voxel access could be slower.
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
Volume<VoxelType>::Volume(uint16_t uWidth, uint16_t uHeight, uint16_t uDepth, uint16_t uBlockSideLength)
Volume<VoxelType>::Volume
(
polyvox_function<void(const ConstVolumeProxy<VoxelType>&, const Region&)> dataRequiredHandler,
polyvox_function<void(const ConstVolumeProxy<VoxelType>&, const Region&)> dataOverflowHandler,
uint16_t uBlockSideLength
)
{
m_funcDataRequiredHandler = dataRequiredHandler;
m_funcDataOverflowHandler = dataOverflowHandler;
m_bPagingEnabled = true;
//Create a volume of the right size.
resize(uWidth, uHeight, uDepth, uBlockSideLength);
//Create the border block
polyvox_shared_ptr< Block<VoxelType> > pTempBlock(new Block<VoxelType>(m_uBlockSideLength));
pTempBlock->fill(VoxelType());
m_pBorderBlock = pTempBlock;
resize(Region::MaxRegion,uBlockSideLength);
}
////////////////////////////////////////////////////////////////////////////////
/// Destroys the volume and frees any blocks which are not in use by other volumes.
/// Deprecated - do not use this constructor.
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
Volume<VoxelType>::Volume
(
int32_t dont_use_this_constructor_1, int32_t dont_use_this_constructor_2, int32_t dont_use_this_constructor_3
)
{
//In earlier verions of PolyVox the constructor took three values indicating width, height, and depth. However, this
//causes confusion because these three parameters can be interpreted as two function pointers and a block size instead,
//hence calling a different constructor. And simply removing this constructor will cause confusion because existing
//code with three parameters will then always resolve to the constructor with two function pointers and a block size.
//
//Eventually this constructor will be removed, it's just here to make people change their code to the new version.
//
//IF YOU HIT THIS ASSERT/ABORT, CHANGE YOUR CODE TO USE THE CONSTRUCTOR TAKING A 'Region' INSTEAD.
assert(false);
abort();
}
////////////////////////////////////////////////////////////////////////////////
/// This constructor creates a volume with a fixed size which is specified as a parameter. By default this constructor will not enable paging but you can override this if desired. If you do wish to enable paging then you are required to provide the call back function (see the other Volume 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 Volume.
/// \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>
Volume<VoxelType>::Volume
(
const Region& regValid,
polyvox_function<void(const ConstVolumeProxy<VoxelType>&, const Region&)> dataRequiredHandler,
polyvox_function<void(const ConstVolumeProxy<VoxelType>&, const Region&)> dataOverflowHandler,
bool bPagingEnabled,
uint16_t uBlockSideLength
)
{
m_funcDataRequiredHandler = dataRequiredHandler;
m_funcDataOverflowHandler = dataOverflowHandler;
m_bPagingEnabled = bPagingEnabled;
//Create a volume of the right size.
resize(regValid,uBlockSideLength);
}
////////////////////////////////////////////////////////////////////////////////
/// Destroys the volume The destructor will call flushAll() to ensure that a paging volume has the chance to save it's data via the dataOverflowHandler() if desired.
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
Volume<VoxelType>::~Volume()
{
flushAll();
}
////////////////////////////////////////////////////////////////////////////////
@ -74,49 +121,46 @@ namespace PolyVox
template <typename VoxelType>
VoxelType Volume<VoxelType>::getBorderValue(void) const
{
return m_pBorderBlock->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 <typename VoxelType>
Region Volume<VoxelType>::getEnclosingRegion(void) const
{
return Region(Vector3DInt16(0,0,0), Vector3DInt16(m_uWidth-1,m_uHeight-1,m_uDepth-1));
return m_regValidRegion;
}
////////////////////////////////////////////////////////////////////////////////
/// \return The width of the volume in voxels
/// \return The width of the volume in voxels. Note that this value is inclusive, so that if the valid range is e.g. 0 to 63 then the width is 64.
/// \sa getHeight(), getDepth()
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
uint16_t Volume<VoxelType>::getWidth(void) const
int32_t Volume<VoxelType>::getWidth(void) const
{
return m_uWidth;
return m_regValidRegion.getUpperCorner().getX() - m_regValidRegion.getLowerCorner().getX() + 1;
}
////////////////////////////////////////////////////////////////////////////////
/// \return The height of the volume in voxels
/// \return The height of the volume in voxels. Note that this value is inclusive, so that if the valid range is e.g. 0 to 63 then the height is 64.
/// \sa getWidth(), getDepth()
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
uint16_t Volume<VoxelType>::getHeight(void) const
int32_t Volume<VoxelType>::getHeight(void) const
{
return m_uHeight;
return m_regValidRegion.getUpperCorner().getY() - m_regValidRegion.getLowerCorner().getY() + 1;
}
////////////////////////////////////////////////////////////////////////////////
/// \return The depth of the volume in voxels
/// \return The depth of the volume in voxels. Note that this value is inclusive, so that if the valid range is e.g. 0 to 63 then the depth is 64.
/// \sa getWidth(), getHeight()
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
uint16_t Volume<VoxelType>::getDepth(void) const
int32_t Volume<VoxelType>::getDepth(void) const
{
return m_uDepth;
return m_regValidRegion.getUpperCorner().getZ() - m_regValidRegion.getLowerCorner().getZ() + 1;
}
////////////////////////////////////////////////////////////////////////////////
@ -125,7 +169,7 @@ namespace PolyVox
/// \sa getLongestSideLength(), getDiagonalLength()
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
uint16_t Volume<VoxelType>::getShortestSideLength(void) const
int32_t Volume<VoxelType>::getShortestSideLength(void) const
{
return m_uShortestSideLength;
}
@ -136,7 +180,7 @@ namespace PolyVox
/// \sa getShortestSideLength(), getDiagonalLength()
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
uint16_t Volume<VoxelType>::getLongestSideLength(void) const
int32_t Volume<VoxelType>::getLongestSideLength(void) const
{
return m_uLongestSideLength;
}
@ -154,34 +198,27 @@ namespace PolyVox
}
////////////////////////////////////////////////////////////////////////////////
/// \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
/// \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 Volume<VoxelType>::getVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos) const
VoxelType Volume<VoxelType>::getVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos) const
{
//We don't use getEnclosingRegion here because we care
//about speed and don't need to check the lower bound.
if((uXPos < getWidth()) && (uYPos < getHeight()) && (uZPos < getDepth()))
if(m_regValidRegion.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos)))
{
const uint16_t blockX = uXPos >> m_uBlockSideLengthPower;
const uint16_t blockY = uYPos >> m_uBlockSideLengthPower;
const uint16_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 polyvox_shared_ptr< Block< VoxelType > >& block = m_pBlocks
[
blockX +
blockY * m_uWidthInBlocks +
blockZ * m_uWidthInBlocks * m_uHeightInBlocks
];
Block<VoxelType>* pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ);
return block->getVoxelAt(xOffset,yOffset,zOffset);
return pUncompressedBlock->getVoxelAt(xOffset,yOffset,zOffset);
}
else
{
@ -190,22 +227,76 @@ namespace PolyVox
}
////////////////////////////////////////////////////////////////////////////////
/// \param v3dPos the 3D position of the voxel
/// \return the voxel value
/// \param v3dPos The 3D position of the voxel
/// \return The voxel value
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
VoxelType Volume<VoxelType>::getVoxelAt(const Vector3DUint16& v3dPos) const
VoxelType Volume<VoxelType>::getVoxelAt(const Vector3DInt32& v3dPos) const
{
return getVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ());
}
////////////////////////////////////////////////////////////////////////////////
/// Enabling compression allows significantly more data to be stored in memory.
/// \param bCompressionEnabled Specifies whether compression is enabled.
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
void Volume<VoxelType>::setCompressionEnabled(bool bCompressionEnabled)
{
//Early out - nothing to do
if(m_bCompressionEnabled == bCompressionEnabled)
{
return;
}
m_bCompressionEnabled = bCompressionEnabled;
if(m_bCompressionEnabled)
{
//If compression has been enabled then we need to start honouring the max number of
//uncompressed blocks. Because compression has been disabled for a while we might have
//gone above that limit. Easiest solution is just to clear the cache and start again.
clearBlockCache();
}
}
////////////////////////////////////////////////////////////////////////////////
/// Increasing the size of the block cache will increase memory but may improve performance.
/// You may want to set this to a large value (e.g. 1024) when you are first loading your
/// volume data and then set it to a smaller value (e.g.64) for general processing.
/// \param uBlockCacheSize The number of blocks for which uncompressed data can be cached.
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
void Volume<VoxelType>::setMaxNumberOfUncompressedBlocks(uint16_t uMaxNumberOfUncompressedBlocks)
{
clearBlockCache();
m_uMaxNumberOfUncompressedBlocks = uMaxNumberOfUncompressedBlocks;
}
////////////////////////////////////////////////////////////////////////////////
/// Increasing the number of blocks in memory causes fewer calls to dataRequiredHandler()/dataOverflowHandler()
/// \param uMaxBlocks The number of blocks
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
void Volume<VoxelType>::setMaxNumberOfBlocksInMemory(uint16_t uMaxNumberOfBlocksInMemory)
{
if(m_pBlocks.size() > uMaxNumberOfBlocksInMemory)
{
flushAll();
}
m_uMaxNumberOfBlocksInMemory = uMaxNumberOfBlocksInMemory;
}
////////////////////////////////////////////////////////////////////////////////
/// \param tBorder The value to use for voxels outside the volume.
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
void Volume<VoxelType>::setBorderValue(const VoxelType& tBorder)
{
return m_pBorderBlock->fill(tBorder);
/*Block<VoxelType>* pUncompressedBorderBlock = getUncompressedBlock(&m_pBorderBlock);
return pUncompressedBorderBlock->fill(tBorder);*/
std::fill(m_pUncompressedBorderData, m_pUncompressedBorderData + m_uBlockSideLength * m_uBlockSideLength * m_uBlockSideLength, tBorder);
}
////////////////////////////////////////////////////////////////////////////////
@ -216,56 +307,25 @@ namespace PolyVox
/// \return whether the requested position is inside the volume
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
bool Volume<VoxelType>::setVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos, VoxelType tValue)
bool Volume<VoxelType>::setVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue)
{
//We don't use getEnclosingRegion here because we care
//about speed and don't need to check the lower bound.
if((uXPos < getWidth()) && (uYPos < getHeight()) && (uZPos < getDepth()))
{
const uint16_t blockX = uXPos >> m_uBlockSideLengthPower;
const uint16_t blockY = uYPos >> m_uBlockSideLengthPower;
const uint16_t blockZ = uZPos >> m_uBlockSideLengthPower;
assert(m_regValidRegion.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos)));
const int32_t blockX = uXPos >> m_uBlockSideLengthPower;
const int32_t blockY = uYPos >> m_uBlockSideLengthPower;
const int32_t blockZ = uZPos >> m_uBlockSideLengthPower;
const uint16_t xOffset = uXPos - (blockX << m_uBlockSideLengthPower);
const uint16_t yOffset = uYPos - (blockY << m_uBlockSideLengthPower);
const uint16_t zOffset = uZPos - (blockZ << m_uBlockSideLengthPower);
uint32_t uBlockIndex =
blockX +
blockY * m_uWidthInBlocks +
blockZ * m_uWidthInBlocks * m_uHeightInBlocks;
Block<VoxelType>* pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ);
polyvox_shared_ptr< Block<VoxelType> >& block = m_pBlocks[uBlockIndex];
pUncompressedBlock->setVoxelAt(xOffset,yOffset,zOffset, tValue);
//It's quite possible that the user might attempt to set a voxel to it's current value.
//We test for this case firstly because it could help performance, but more importantly
//because it lets us avoid unsharing blocks unnecessarily.
if(block->getVoxelAt(xOffset, yOffset, zOffset) != tValue)
{
if(block.unique())
{
block->setVoxelAt(xOffset,yOffset,zOffset, tValue);
//There is a chance that setting this voxel makes the block homogenous and therefore shareable.
//But checking this will take some time, so for now just set a flag.
m_vecBlockIsPotentiallyHomogenous[uBlockIndex] = true;
}
else
{
polyvox_shared_ptr< Block<VoxelType> > pNewBlock(new Block<VoxelType>(*(block)));
block = pNewBlock;
m_vecBlockIsPotentiallyHomogenous[uBlockIndex] = false;
block->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;
}
}
////////////////////////////////////////////////////////////////////////////////
/// \param v3dPos the 3D position of the voxel
@ -273,32 +333,148 @@ namespace PolyVox
/// \return whether the requested position is inside the volume
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
bool Volume<VoxelType>::setVoxelAt(const Vector3DUint16& v3dPos, VoxelType tValue)
bool Volume<VoxelType>::setVoxelAt(const Vector3DInt32& v3dPos, VoxelType tValue)
{
return setVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ(), tValue);
}
////////////////////////////////////////////////////////////////////////////////
/// 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
/// not shared it could actually be less efficient (this will depend on the data).
/// The size of the volume may also be a factor when choosing block size. Accept
/// the default if you are not sure what to choose here.
/// Note that if MaxNumberOfBlocksInMemory is not large enough to support the region this function will only load part of the region. In this case it is undefined which parts will actually be loaded. If all the voxels in the given region are already loaded, this function will not do anything. Other voxels might be unloaded to make space for the new voxels.
/// \param regPrefetch The Region of voxels to prefetch into memory.
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
void Volume<VoxelType>::resize(uint16_t uWidth, uint16_t uHeight, uint16_t uDepth, uint16_t uBlockSideLength)
void Volume<VoxelType>::prefetch(Region regPrefetch)
{
Vector3DInt32 v3dStart;
for(int i = 0; i < 3; i++)
{
v3dStart.setElement(i, regPrefetch.getLowerCorner().getElement(i) >> m_uBlockSideLengthPower);
}
Vector3DInt32 v3dEnd;
for(int i = 0; i < 3; i++)
{
v3dEnd.setElement(i, regPrefetch.getUpperCorner().getElement(i) >> m_uBlockSideLengthPower);
}
Vector3DInt32 v3dSize = v3dEnd - v3dStart + Vector3DInt32(1,1,1);
int32_t numblocks = v3dSize.getX() * v3dSize.getY() * v3dSize.getZ();
if(numblocks > m_uMaxNumberOfBlocksInMemory)
{
// cannot support the amount of blocks... so only load the maximum possible
numblocks = m_uMaxNumberOfBlocksInMemory;
}
for(int32_t x = v3dStart.getX(); x <= v3dEnd.getX(); x++)
{
for(int32_t y = v3dStart.getY(); y <= v3dEnd.getY(); y++)
{
for(int32_t z = v3dStart.getZ(); z <= v3dEnd.getZ(); z++)
{
Vector3DInt32 pos(x,y,z);
typename std::map<Vector3DInt32, LoadedBlock>::iterator itBlock = m_pBlocks.find(pos);
if(itBlock != m_pBlocks.end())
{
// If the block is already loaded then we don't load it again. This means it does not get uncompressed,
// whereas if we were to call getUncompressedBlock() regardless then it would also get uncompressed.
// This might be nice, but on the prefetch region could be bigger than the uncompressed cache size.
// This would limit the amount of prefetching we could do.
continue;
}
if(numblocks == 0)
{
// Loading any more blocks would attempt to overflow the memory and therefore erase blocks
// we loaded in the beginning. This wouldn't cause logic problems but would be wasteful.
return;
}
// load a block
numblocks--;
Block<VoxelType>* block = getUncompressedBlock(x,y,z);
} // for z
} // for y
} // for x
}
////////////////////////////////////////////////////////////////////////////////
/// Removes all voxels from memory, and calls dataOverflowHandler() to ensure the application has a chance to store the data.
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
void Volume<VoxelType>::flushAll()
{
typename std::map<Vector3DInt32, LoadedBlock >::iterator i;
//Replaced the for loop here as the call to
//eraseBlock was invalidating the iterator.
while(m_pBlocks.size() > 0)
{
eraseBlock(m_pBlocks.begin());
}
}
////////////////////////////////////////////////////////////////////////////////
/// Removes all voxels in the specified Region from memory, and calls dataOverflowHandler() to ensure the application has a chance to store the data. It is possible that there are no voxels loaded in the Region, in which case the function will have no effect.
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
void Volume<VoxelType>::flush(Region regFlush)
{
Vector3DInt32 v3dStart;
for(int i = 0; i < 3; i++)
{
v3dStart.setElement(i, regFlush.getLowerCorner().getElement(i) >> m_uBlockSideLengthPower);
}
Vector3DInt32 v3dEnd;
for(int i = 0; i < 3; i++)
{
v3dEnd.setElement(i, regFlush.getUpperCorner().getElement(i) >> m_uBlockSideLengthPower);
}
for(int32_t x = v3dStart.getX(); x <= v3dEnd.getX(); x++)
{
for(int32_t y = v3dStart.getY(); y <= v3dEnd.getY(); y++)
{
for(int32_t z = v3dStart.getZ(); z <= v3dEnd.getZ(); z++)
{
Vector3DInt32 pos(x,y,z);
typename std::map<Vector3DInt32, LoadedBlock>::iterator itBlock = m_pBlocks.find(pos);
if(itBlock == m_pBlocks.end())
{
// not loaded, not unloading
continue;
}
eraseBlock(itBlock);
// eraseBlock might cause a call to getUncompressedBlock, which again sets m_pLastAccessedBlock
if(m_v3dLastAccessedBlockPos == pos)
{
m_pLastAccessedBlock = 0;
}
} // for z
} // for y
} // for x
}
////////////////////////////////////////////////////////////////////////////////
///
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
void Volume<VoxelType>::clearBlockCache(void)
{
for(uint32_t ct = 0; ct < m_vecUncompressedBlockCache.size(); ct++)
{
m_vecUncompressedBlockCache[ct]->block.compress();
}
m_vecUncompressedBlockCache.clear();
}
////////////////////////////////////////////////////////////////////////////////
/// This function should probably be made internal...
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
void Volume<VoxelType>::resize(const Region& regValidRegion, uint16_t uBlockSideLength)
{
//Debug mode validation
assert(uBlockSideLength > 0);
assert(isPowerOf2(uBlockSideLength));
assert(uBlockSideLength <= uWidth);
assert(uBlockSideLength <= uHeight);
assert(uBlockSideLength <= uDepth);
//Release mode validation
if(uBlockSideLength == 0)
@ -309,138 +485,251 @@ 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.");
}
m_uTimestamper = 0;
m_uMaxNumberOfUncompressedBlocks = 16;
m_uBlockSideLength = uBlockSideLength;
m_pUncompressedBorderData = 0;
m_uMaxNumberOfBlocksInMemory = 1024;
m_v3dLastAccessedBlockPos = Vector3DInt32(0,0,0); //There are no invalid positions, but initially the m_pLastAccessedBlock pointer will be null;
m_pLastAccessedBlock = 0;
m_bCompressionEnabled = true;
m_regValidRegion = regValidRegion;
m_regValidRegionInBlocks.setLowerCorner(m_regValidRegion.getLowerCorner() / static_cast<int32_t>(uBlockSideLength));
m_regValidRegionInBlocks.setUpperCorner(m_regValidRegion.getUpperCorner() / static_cast<int32_t>(uBlockSideLength));
setMaxNumberOfUncompressedBlocks(m_uMaxNumberOfUncompressedBlocks);
//Clear the previous data
m_pBlocks.clear();
m_vecBlockIsPotentiallyHomogenous.clear();
m_pHomogenousBlock.clear();
//Compute the volume side lengths
m_uWidth = uWidth;
m_uHeight = uHeight;
m_uDepth = uDepth;
//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;
//Clear the previous data
m_pBlocks.clear();
//Compute number of blocks in the volume
m_uNoOfBlocksInVolume = m_uWidthInBlocks * m_uHeightInBlocks * m_uDepthInBlocks;
//Create the blocks
m_pBlocks.resize(m_uNoOfBlocksInVolume);
m_vecBlockIsPotentiallyHomogenous.resize(m_uNoOfBlocksInVolume);
for(uint32_t i = 0; i < m_uNoOfBlocksInVolume; ++i)
{
m_pBlocks[i] = getHomogenousBlock(VoxelType());
m_vecBlockIsPotentiallyHomogenous[i] = false;
}
//Create the border block
m_pUncompressedBorderData = new VoxelType[m_uBlockSideLength * m_uBlockSideLength * m_uBlockSideLength];
std::fill(m_pUncompressedBorderData, m_pUncompressedBorderData + m_uBlockSideLength * m_uBlockSideLength * m_uBlockSideLength, VoxelType());
//Other properties we might find useful later
m_uLongestSideLength = (std::max)((std::max)(m_uWidth,m_uHeight),m_uDepth);
m_uShortestSideLength = (std::min)((std::min)(m_uWidth,m_uHeight),m_uDepth);
m_fDiagonalLength = sqrtf(static_cast<float>(m_uWidth * m_uWidth + m_uHeight * m_uHeight + m_uDepth * m_uDepth));
//Reset the counter for tidying
m_uCurrentBlockForTidying = 0;
}
////////////////////////////////////////////////////////////////////////////////
/// Clean up the memory usage of the volume. Checks for any blocks which are
/// homogeneous and flags them as such for faster processing and reduced memory
/// usage.
/// \param uNoOfBlocksToProcess the number of blocks to process
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
void Volume<VoxelType>::tidyUpMemory(uint32_t uNoOfBlocksToProcess)
{
//Track the number of blocks we have processed.
uint32_t m_uNoOfProcessedBlocks = 0;
//We will loop around, and finish if we get back to our start position
uint32_t uFinishBlock = m_uCurrentBlockForTidying;
//Increment the current block, looping around if necessary
++m_uCurrentBlockForTidying;
m_uCurrentBlockForTidying %= m_uNoOfBlocksInVolume;
//While we have not reached the user specified limit and there are more blocks to process...
while((m_uNoOfProcessedBlocks < uNoOfBlocksToProcess) && (m_uCurrentBlockForTidying != uFinishBlock))
{
//We only do any work if the block is flagged as potentially homogeneous.
if(m_vecBlockIsPotentiallyHomogenous[m_uCurrentBlockForTidying])
{
//Check if it's really homogeneous (this can be slow).
if(m_pBlocks[m_uCurrentBlockForTidying]->isHomogeneous())
{
//If so, replace is with a block from out homogeneous collection.
VoxelType homogeneousValue = m_pBlocks[m_uCurrentBlockForTidying]->getVoxelAt(0,0,0);
m_pBlocks[m_uCurrentBlockForTidying] = getHomogenousBlock(homogeneousValue);
}
//Either way, we have now determined whether the block was sharable. So it's not *potentially* sharable.
m_vecBlockIsPotentiallyHomogenous[m_uCurrentBlockForTidying] = false;
//We've processed a block. This is inside the 'if' because the path outside the 'if' is trivially fast.
++m_uNoOfProcessedBlocks;
}
//Increment the current block, looping around if necessary
++m_uCurrentBlockForTidying;
m_uCurrentBlockForTidying %= m_uNoOfBlocksInVolume;
}
//Identify and remove any homogeneous blocks which are not actually in use.
typename std::map<VoxelType, polyvox_shared_ptr< Block<VoxelType> > >::iterator iter = m_pHomogenousBlock.begin();
while(iter != m_pHomogenousBlock.end())
{
if(iter->second.unique())
{
m_pHomogenousBlock.erase(iter++); //Increments the iterator and returns the previous position to be erased.
}
else
{
++iter; //Just increments the iterator.
}
}
m_uLongestSideLength = (std::max)((std::max)(getWidth(),getHeight()),getDepth());
m_uShortestSideLength = (std::min)((std::min)(getWidth(),getHeight()),getDepth());
m_fDiagonalLength = sqrtf(static_cast<float>(getWidth() * getWidth() + getHeight() * getHeight() + getDepth() * getDepth()));
}
template <typename VoxelType>
polyvox_shared_ptr< Block<VoxelType> > Volume<VoxelType>::getHomogenousBlock(VoxelType tHomogenousValue)
void Volume<VoxelType>::eraseBlock(typename std::map<Vector3DInt32, LoadedBlock >::iterator itBlock) const
{
typename std::map<VoxelType, polyvox_shared_ptr< Block<VoxelType> > >::iterator iterResult = m_pHomogenousBlock.find(tHomogenousValue);
if(iterResult == m_pHomogenousBlock.end())
if(m_funcDataOverflowHandler)
{
//Block<VoxelType> block;
polyvox_shared_ptr< Block<VoxelType> > pHomogeneousBlock(new Block<VoxelType>(m_uBlockSideLength));
//block.m_pBlock = temp;
//block.m_uReferenceCount++;
pHomogeneousBlock->fill(tHomogenousValue);
m_pHomogenousBlock.insert(std::make_pair(tHomogenousValue, pHomogeneousBlock));
return pHomogeneousBlock;
Vector3DInt32 v3dPos = itBlock->first;
Vector3DInt32 v3dLower(v3dPos.getX() << m_uBlockSideLengthPower, v3dPos.getY() << m_uBlockSideLengthPower, v3dPos.getZ() << m_uBlockSideLengthPower);
Vector3DInt32 v3dUpper = v3dLower + Vector3DInt32(m_uBlockSideLength-1, m_uBlockSideLength-1, m_uBlockSideLength-1);
Region reg(v3dLower, v3dUpper);
ConstVolumeProxy<VoxelType> ConstVolumeProxy(*this, reg);
m_funcDataOverflowHandler(ConstVolumeProxy, reg);
}
if(m_bCompressionEnabled) {
for(uint32_t ct = 0; ct < m_vecUncompressedBlockCache.size(); ct++)
{
// find the block in the uncompressed cache
if(m_vecUncompressedBlockCache[ct] == &(itBlock->second))
{
// TODO: compression is unneccessary? or will not compressing this cause a memleak?
itBlock->second.block.compress();
// put last object in cache here
m_vecUncompressedBlockCache[ct] = m_vecUncompressedBlockCache.back();
// decrease cache size by one since last element is now in here twice
m_vecUncompressedBlockCache.resize(m_vecUncompressedBlockCache.size()-1);
break;
}
}
}
m_pBlocks.erase(itBlock);
}
template <typename VoxelType>
bool Volume<VoxelType>::setVoxelAtConst(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue) const
{
//We don't have any range checks in this function because it
//is a private function only called by the ConstVolumeProxy. The
//ConstVolumeProxy takes care of ensuring the range is appropriate.
const int32_t blockX = uXPos >> m_uBlockSideLengthPower;
const int32_t blockY = uYPos >> m_uBlockSideLengthPower;
const int32_t blockZ = uZPos >> m_uBlockSideLengthPower;
const uint16_t xOffset = uXPos - (blockX << m_uBlockSideLengthPower);
const uint16_t yOffset = uYPos - (blockY << m_uBlockSideLengthPower);
const uint16_t zOffset = uZPos - (blockZ << m_uBlockSideLengthPower);
Block<VoxelType>* pUncompressedBlock = getUncompressedBlock(blockX, blockY, blockZ);
pUncompressedBlock->setVoxelAt(xOffset,yOffset,zOffset, tValue);
//Return true to indicate that we modified a voxel.
return true;
}
template <typename VoxelType>
Block<VoxelType>* Volume<VoxelType>::getUncompressedBlock(int32_t uBlockX, int32_t uBlockY, int32_t uBlockZ) const
{
Vector3DInt32 v3dBlockPos(uBlockX, uBlockY, uBlockZ);
//Check if we have the same block as last time, if so there's no need to even update
//the time stamp. If we updated it everytime then that would be every time we touched
//a voxel, which would overflow a uint32_t and require us to use a uint64_t instead.
//This check should also provide a significant speed boost as usually it is true.
if((v3dBlockPos == m_v3dLastAccessedBlockPos) && (m_pLastAccessedBlock != 0))
{
assert(m_pLastAccessedBlock->m_tUncompressedData);
return m_pLastAccessedBlock;
}
typename std::map<Vector3DInt32, LoadedBlock >::iterator itBlock = m_pBlocks.find(v3dBlockPos);
// check whether the block is already loaded
if(itBlock == m_pBlocks.end())
{
//The block is not in the map, so we will have to create a new block and add it.
//Before we do so, we might want to dump some existing data to make space. We
//Only do this if paging is enabled.
if(m_bPagingEnabled)
{
// check wether another block needs to be unloaded before this one can be loaded
if(m_pBlocks.size() == m_uMaxNumberOfBlocksInMemory)
{
// find the least recently used block
typename std::map<Vector3DInt32, LoadedBlock >::iterator i;
typename std::map<Vector3DInt32, LoadedBlock >::iterator itUnloadBlock = m_pBlocks.begin();
for(i = m_pBlocks.begin(); i != m_pBlocks.end(); i++)
{
if(i->second.timestamp < itUnloadBlock->second.timestamp)
{
itUnloadBlock = i;
}
}
eraseBlock(itUnloadBlock);
}
}
// create the new block
LoadedBlock newBlock(m_uBlockSideLength);
itBlock = m_pBlocks.insert(std::make_pair(v3dBlockPos, newBlock)).first;
//We have created the new block. If paging is enabled it should be used to
//fill in the required data. Otherwise it is just left in the default state.
if(m_bPagingEnabled)
{
if(m_funcDataRequiredHandler)
{
// "load" will actually call setVoxel, which will in turn call this function again but the block will be found
// so this if(itBlock == m_pBlocks.end()) never is entered
//FIXME - can we pass the block around so that we don't have to find it again when we recursively call this function?
Vector3DInt32 v3dLower(v3dBlockPos.getX() << m_uBlockSideLengthPower, v3dBlockPos.getY() << m_uBlockSideLengthPower, v3dBlockPos.getZ() << m_uBlockSideLengthPower);
Vector3DInt32 v3dUpper = v3dLower + Vector3DInt32(m_uBlockSideLength-1, m_uBlockSideLength-1, m_uBlockSideLength-1);
Region reg(v3dLower, v3dUpper);
ConstVolumeProxy<VoxelType> ConstVolumeProxy(*this, reg);
m_funcDataRequiredHandler(ConstVolumeProxy, reg);
}
}
}
//Get the block and mark that we accessed it
LoadedBlock& loadedBlock = itBlock->second;
loadedBlock.timestamp = ++m_uTimestamper;
m_v3dLastAccessedBlockPos = v3dBlockPos;
m_pLastAccessedBlock = &(loadedBlock.block);
if(loadedBlock.block.m_bIsCompressed == false)
{
assert(m_pLastAccessedBlock->m_tUncompressedData);
return m_pLastAccessedBlock;
}
//If we are allowed to compress then check whether we need to
if((m_bCompressionEnabled) && (m_vecUncompressedBlockCache.size() == m_uMaxNumberOfUncompressedBlocks))
{
int32_t leastRecentlyUsedBlockIndex = -1;
uint32_t uLeastRecentTimestamp = (std::numeric_limits<uint32_t>::max)();
//Currently we find the oldest block by iterating over the whole array. Of course we could store the blocks sorted by
//timestamp (set, priority_queue, etc) but then we'll need to move them around as the timestamp changes. Can come back
//to this if it proves to be a bottleneck (compraed to the cost of actually doing the compression/decompression).
for(uint32_t ct = 0; ct < m_vecUncompressedBlockCache.size(); ct++)
{
if(m_vecUncompressedBlockCache[ct]->timestamp < uLeastRecentTimestamp)
{
uLeastRecentTimestamp = m_vecUncompressedBlockCache[ct]->timestamp;
leastRecentlyUsedBlockIndex = ct;
}
}
//Compress the least recently used block.
m_vecUncompressedBlockCache[leastRecentlyUsedBlockIndex]->block.compress();
//We don't actually remove any elements from this vector, we
//simply change the pointer to point at the new uncompressed bloack.
m_vecUncompressedBlockCache[leastRecentlyUsedBlockIndex] = &loadedBlock;
}
else
{
//iterResult->second.m_uReferenceCount++;
//polyvox_shared_ptr< Block<VoxelType> > result(iterResult->second);
return iterResult->second;
m_vecUncompressedBlockCache.push_back(&loadedBlock);
}
loadedBlock.block.uncompress();
m_pLastAccessedBlock = &(loadedBlock.block);
assert(m_pLastAccessedBlock->m_tUncompressedData);
return m_pLastAccessedBlock;
}
////////////////////////////////////////////////////////////////////////////////
/// Note: This function needs reviewing for accuracy...
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
float Volume<VoxelType>::calculateCompressionRatio(void)
{
float fRawSize = m_pBlocks.size() * m_uBlockSideLength * m_uBlockSideLength* m_uBlockSideLength * sizeof(VoxelType);
float fCompressedSize = calculateSizeInBytes();
return fCompressedSize/fRawSize;
}
////////////////////////////////////////////////////////////////////////////////
/// Note: This function needs reviewing for accuracy...
////////////////////////////////////////////////////////////////////////////////
template <typename VoxelType>
uint32_t Volume<VoxelType>::calculateSizeInBytes(void)
{
uint32_t uSizeInBytes = sizeof(Volume);
//Memory used by the blocks
typename std::map<Vector3DInt32, LoadedBlock >::iterator i;
for(i = m_pBlocks.begin(); i != m_pBlocks.end(); i++)
{
//Inaccurate - account for rest of loaded block.
uSizeInBytes += i->second.block.calculateSizeInBytes();
}
//Memory used by the block cache.
uSizeInBytes += m_vecUncompressedBlockCache.capacity() * sizeof(LoadedBlock);
uSizeInBytes += m_vecUncompressedBlockCache.size() * m_uBlockSideLength * m_uBlockSideLength * m_uBlockSideLength * sizeof(VoxelType);
//Memory used by border data.
if(m_pUncompressedBorderData)
{
uSizeInBytes += m_uBlockSideLength * m_uBlockSideLength * m_uBlockSideLength * sizeof(VoxelType);
}
return uSizeInBytes;
}
}

View File

@ -37,15 +37,15 @@ namespace PolyVox
VolumeSampler<VoxelType>& operator=(const VolumeSampler<VoxelType>& rhs) throw();
uint16_t getPosX(void) const;
uint16_t getPosY(void) const;
uint16_t getPosZ(void) const;
int32_t getPosX(void) const;
int32_t getPosY(void) const;
int32_t getPosZ(void) const;
VoxelType getSubSampledVoxel(uint8_t uLevel) const;
const Volume<VoxelType>* getVolume(void) const;
inline VoxelType getVoxel(void) const;
void setPosition(const Vector3DInt16& v3dNewPos);
void setPosition(uint16_t xPos, uint16_t yPos, uint16_t zPos);
void setPosition(const Vector3DInt32& v3dNewPos);
void setPosition(int32_t xPos, int32_t yPos, int32_t zPos);
void movePositiveX(void);
void movePositiveY(void);
@ -91,9 +91,9 @@ namespace PolyVox
Volume<VoxelType>* mVolume;
//The current position in the volume
uint16_t mXPosInVolume;
uint16_t mYPosInVolume;
uint16_t mZPosInVolume;
int32_t mXPosInVolume;
int32_t mYPosInVolume;
int32_t mZPosInVolume;
//Other current position information
VoxelType* mCurrentVoxel;

View File

@ -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 <limits>
namespace PolyVox
{
@ -56,19 +61,19 @@ namespace PolyVox
}
template <typename VoxelType>
uint16_t VolumeSampler<VoxelType>::getPosX(void) const
int32_t VolumeSampler<VoxelType>::getPosX(void) const
{
return mXPosInVolume;
}
template <typename VoxelType>
uint16_t VolumeSampler<VoxelType>::getPosY(void) const
int32_t VolumeSampler<VoxelType>::getPosY(void) const
{
return mYPosInVolume;
}
template <typename VoxelType>
uint16_t VolumeSampler<VoxelType>::getPosZ(void) const
int32_t VolumeSampler<VoxelType>::getPosZ(void) const
{
return mZPosInVolume;
}
@ -124,21 +129,21 @@ namespace PolyVox
}
template <typename VoxelType>
void VolumeSampler<VoxelType>::setPosition(const Vector3DInt16& v3dNewPos)
void VolumeSampler<VoxelType>::setPosition(const Vector3DInt32& v3dNewPos)
{
setPosition(v3dNewPos.getX(), v3dNewPos.getY(), v3dNewPos.getZ());
}
template <typename VoxelType>
void VolumeSampler<VoxelType>::setPosition(uint16_t xPos, uint16_t yPos, uint16_t zPos)
void VolumeSampler<VoxelType>::setPosition(int32_t xPos, int32_t yPos, int32_t zPos)
{
mXPosInVolume = xPos;
mYPosInVolume = yPos;
mZPosInVolume = zPos;
const uint16_t uXBlock = mXPosInVolume >> mVolume->m_uBlockSideLengthPower;
const uint16_t uYBlock = mYPosInVolume >> mVolume->m_uBlockSideLengthPower;
const uint16_t uZBlock = mZPosInVolume >> mVolume->m_uBlockSideLengthPower;
const int32_t uXBlock = mXPosInVolume >> mVolume->m_uBlockSideLengthPower;
const int32_t uYBlock = mYPosInVolume >> mVolume->m_uBlockSideLengthPower;
const int32_t uZBlock = mZPosInVolume >> mVolume->m_uBlockSideLengthPower;
const uint16_t uXPosInBlock = mXPosInVolume - (uXBlock << mVolume->m_uBlockSideLengthPower);
const uint16_t uYPosInBlock = mYPosInVolume - (uYBlock << mVolume->m_uBlockSideLengthPower);
@ -148,18 +153,15 @@ 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))
if(mVolume->m_regValidRegionInBlocks.containsPoint(Vector3DInt32(uXBlock, uYBlock, uZBlock)))
{
const uint32_t uBlockIndexInVolume = uXBlock +
uYBlock * mVolume->m_uWidthInBlocks +
uZBlock * mVolume->m_uWidthInBlocks * mVolume->m_uHeightInBlocks;
const polyvox_shared_ptr< Block<VoxelType> >& currentBlock = mVolume->m_pBlocks[uBlockIndexInVolume];
Block<VoxelType>* pUncompressedCurrentBlock = mVolume->getUncompressedBlock(uXBlock, uYBlock, uZBlock);
mCurrentVoxel = currentBlock->m_tData + uVoxelIndexInBlock;
mCurrentVoxel = pUncompressedCurrentBlock->m_tUncompressedData + uVoxelIndexInBlock;
}
else
{
mCurrentVoxel = mVolume->m_pBorderBlock->m_tData + uVoxelIndexInBlock;
mCurrentVoxel = mVolume->m_pUncompressedBorderData + uVoxelIndexInBlock;
}
}
@ -262,7 +264,7 @@ namespace PolyVox
template <typename VoxelType>
VoxelType VolumeSampler<VoxelType>::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);
}
@ -272,7 +274,7 @@ namespace PolyVox
template <typename VoxelType>
VoxelType VolumeSampler<VoxelType>::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);
}
@ -282,7 +284,7 @@ namespace PolyVox
template <typename VoxelType>
VoxelType VolumeSampler<VoxelType>::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);
}
@ -292,7 +294,7 @@ namespace PolyVox
template <typename VoxelType>
VoxelType VolumeSampler<VoxelType>::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);
}
@ -302,7 +304,7 @@ namespace PolyVox
template <typename VoxelType>
VoxelType VolumeSampler<VoxelType>::peekVoxel1nx0py0pz(void) const
{
if((mXPosInVolume%mVolume->m_uBlockSideLength != 0))
if( BORDER_LOW(mXPosInVolume) )
{
return *(mCurrentVoxel - 1);
}
@ -312,7 +314,7 @@ namespace PolyVox
template <typename VoxelType>
VoxelType VolumeSampler<VoxelType>::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);
}
@ -322,7 +324,7 @@ namespace PolyVox
template <typename VoxelType>
VoxelType VolumeSampler<VoxelType>::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);
}
@ -332,7 +334,7 @@ namespace PolyVox
template <typename VoxelType>
VoxelType VolumeSampler<VoxelType>::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);
}
@ -342,7 +344,7 @@ namespace PolyVox
template <typename VoxelType>
VoxelType VolumeSampler<VoxelType>::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);
}
@ -354,7 +356,7 @@ namespace PolyVox
template <typename VoxelType>
VoxelType VolumeSampler<VoxelType>::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);
}
@ -364,7 +366,7 @@ namespace PolyVox
template <typename VoxelType>
VoxelType VolumeSampler<VoxelType>::peekVoxel0px1ny0pz(void) const
{
if((mYPosInVolume%mVolume->m_uBlockSideLength != 0))
if( BORDER_LOW(mYPosInVolume) )
{
return *(mCurrentVoxel - mVolume->m_uBlockSideLength);
}
@ -374,7 +376,7 @@ namespace PolyVox
template <typename VoxelType>
VoxelType VolumeSampler<VoxelType>::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);
}
@ -384,7 +386,7 @@ namespace PolyVox
template <typename VoxelType>
VoxelType VolumeSampler<VoxelType>::peekVoxel0px0py1nz(void) const
{
if((mZPosInVolume%mVolume->m_uBlockSideLength != 0))
if( BORDER_LOW(mZPosInVolume) )
{
return *(mCurrentVoxel - mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength);
}
@ -400,7 +402,7 @@ namespace PolyVox
template <typename VoxelType>
VoxelType VolumeSampler<VoxelType>::peekVoxel0px0py1pz(void) const
{
if((mZPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1))
if( BORDER_HIGH(mZPosInVolume) )
{
return *(mCurrentVoxel + mVolume->m_uBlockSideLength*mVolume->m_uBlockSideLength);
}
@ -410,7 +412,7 @@ namespace PolyVox
template <typename VoxelType>
VoxelType VolumeSampler<VoxelType>::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);
}
@ -420,7 +422,7 @@ namespace PolyVox
template <typename VoxelType>
VoxelType VolumeSampler<VoxelType>::peekVoxel0px1py0pz(void) const
{
if((mYPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1))
if( BORDER_HIGH(mYPosInVolume) )
{
return *(mCurrentVoxel + mVolume->m_uBlockSideLength);
}
@ -430,7 +432,7 @@ namespace PolyVox
template <typename VoxelType>
VoxelType VolumeSampler<VoxelType>::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);
}
@ -442,7 +444,7 @@ namespace PolyVox
template <typename VoxelType>
VoxelType VolumeSampler<VoxelType>::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);
}
@ -452,7 +454,7 @@ namespace PolyVox
template <typename VoxelType>
VoxelType VolumeSampler<VoxelType>::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);
}
@ -462,7 +464,7 @@ namespace PolyVox
template <typename VoxelType>
VoxelType VolumeSampler<VoxelType>::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);
}
@ -472,7 +474,7 @@ namespace PolyVox
template <typename VoxelType>
VoxelType VolumeSampler<VoxelType>::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);
}
@ -482,7 +484,7 @@ namespace PolyVox
template <typename VoxelType>
VoxelType VolumeSampler<VoxelType>::peekVoxel1px0py0pz(void) const
{
if((mXPosInVolume%mVolume->m_uBlockSideLength != mVolume->m_uBlockSideLength-1))
if( BORDER_HIGH(mXPosInVolume) )
{
return *(mCurrentVoxel + 1);
}
@ -492,7 +494,7 @@ namespace PolyVox
template <typename VoxelType>
VoxelType VolumeSampler<VoxelType>::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);
}
@ -502,7 +504,7 @@ namespace PolyVox
template <typename VoxelType>
VoxelType VolumeSampler<VoxelType>::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);
}
@ -512,7 +514,7 @@ namespace PolyVox
template <typename VoxelType>
VoxelType VolumeSampler<VoxelType>::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);
}
@ -522,7 +524,7 @@ namespace PolyVox
template <typename VoxelType>
VoxelType VolumeSampler<VoxelType>::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);
}

View File

@ -29,41 +29,41 @@ using namespace PolyVox;
namespace PolyVox
{
const Vector3DInt16 arrayPathfinderFaces[6] =
const Vector3DInt32 arrayPathfinderFaces[6] =
{
Vector3DInt16(0, 0, -1),
Vector3DInt16(0, 0, +1),
Vector3DInt16(0, -1, 0),
Vector3DInt16(0, +1, 0),
Vector3DInt16(-1, 0, 0),
Vector3DInt16(+1, 0, 0)
Vector3DInt32(0, 0, -1),
Vector3DInt32(0, 0, +1),
Vector3DInt32(0, -1, 0),
Vector3DInt32(0, +1, 0),
Vector3DInt32(-1, 0, 0),
Vector3DInt32(+1, 0, 0)
};
const Vector3DInt16 arrayPathfinderEdges[12] =
const Vector3DInt32 arrayPathfinderEdges[12] =
{
Vector3DInt16(0, -1, -1),
Vector3DInt16(0, -1, +1),
Vector3DInt16(0, +1, -1),
Vector3DInt16(0, +1, +1),
Vector3DInt16(-1, 0, -1),
Vector3DInt16(-1, 0, +1),
Vector3DInt16(+1, 0, -1),
Vector3DInt16(+1, 0, +1),
Vector3DInt16(-1, -1, 0),
Vector3DInt16(-1, +1, 0),
Vector3DInt16(+1, -1, 0),
Vector3DInt16(+1, +1, 0)
Vector3DInt32(0, -1, -1),
Vector3DInt32(0, -1, +1),
Vector3DInt32(0, +1, -1),
Vector3DInt32(0, +1, +1),
Vector3DInt32(-1, 0, -1),
Vector3DInt32(-1, 0, +1),
Vector3DInt32(+1, 0, -1),
Vector3DInt32(+1, 0, +1),
Vector3DInt32(-1, -1, 0),
Vector3DInt32(-1, +1, 0),
Vector3DInt32(+1, -1, 0),
Vector3DInt32(+1, +1, 0)
};
const Vector3DInt16 arrayPathfinderCorners[8] =
const Vector3DInt32 arrayPathfinderCorners[8] =
{
Vector3DInt16(-1, -1, -1),
Vector3DInt16(-1, -1, +1),
Vector3DInt16(-1, +1, -1),
Vector3DInt16(-1, +1, +1),
Vector3DInt16(+1, -1, -1),
Vector3DInt16(+1, -1, +1),
Vector3DInt16(+1, +1, -1),
Vector3DInt16(+1, +1, +1)
Vector3DInt32(-1, -1, -1),
Vector3DInt32(-1, -1, +1),
Vector3DInt32(-1, +1, -1),
Vector3DInt32(-1, +1, +1),
Vector3DInt32(+1, -1, -1),
Vector3DInt32(+1, -1, +1),
Vector3DInt32(+1, +1, -1),
Vector3DInt32(+1, +1, +1)
};
}

View File

@ -37,13 +37,13 @@ namespace PolyVox
while(iterSurfaceVertex != vecVertices.end())
{
const Vector3DFloat& v3dPos = iterSurfaceVertex->getPosition() + static_cast<Vector3DFloat>(mesh.m_Region.getLowerCorner());
const Vector3DInt16 v3dFloor = static_cast<Vector3DInt16>(v3dPos);
const Vector3DInt32 v3dFloor = static_cast<Vector3DInt32>(v3dPos);
VolumeSampler<uint8_t> 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+Vector3DInt16(1,1,1),2);
bool upperCornerInside = volumeData->getEnclosingRegion().containsPoint(v3dFloor+Vector3DInt32(1,1,1),2);
if(lowerCornerInside && upperCornerInside) //If this test fails the vertex will be left as it was
{
@ -69,7 +69,7 @@ namespace PolyVox
const Vector3DInt32 v3dFloor = static_cast<Vector3DInt32>(v3dPos);
volIter.setPosition(static_cast<Vector3DInt16>(v3dFloor));
volIter.setPosition(static_cast<Vector3DInt32>(v3dFloor));
Vector3DFloat gradFloor;
switch(normalGenerationMethod)
{
@ -89,15 +89,15 @@ namespace PolyVox
if((v3dPos.getX() - v3dFloor.getX()) > 0.25) //The result should be 0.0 or 0.5
{
volIter.setPosition(static_cast<Vector3DInt16>(v3dFloor+Vector3DInt32(1,0,0)));
volIter.setPosition(static_cast<Vector3DInt32>(v3dFloor+Vector3DInt32(1,0,0)));
}
if((v3dPos.getY() - v3dFloor.getY()) > 0.25) //The result should be 0.0 or 0.5
{
volIter.setPosition(static_cast<Vector3DInt16>(v3dFloor+Vector3DInt32(0,1,0)));
volIter.setPosition(static_cast<Vector3DInt32>(v3dFloor+Vector3DInt32(0,1,0)));
}
if((v3dPos.getZ() - v3dFloor.getZ()) > 0.25) //The result should be 0.0 or 0.5
{
volIter.setPosition(static_cast<Vector3DInt16>(v3dFloor+Vector3DInt32(0,0,1)));
volIter.setPosition(static_cast<Vector3DInt32>(v3dFloor+Vector3DInt32(0,0,1)));
}
Vector3DFloat gradCeil;
@ -126,7 +126,7 @@ namespace PolyVox
if(normalGenerationMethod == SIMPLE)
{
volIter.setPosition(static_cast<Vector3DInt16>(v3dFloor));
volIter.setPosition(static_cast<Vector3DInt32>(v3dFloor));
const uint8_t uFloor = volIter.getVoxel() > 0 ? 1 : 0;
if((v3dPos.getX() - v3dFloor.getX()) > 0.25) //The result should be 0.0 or 0.5
{

View File

@ -62,7 +62,7 @@ namespace PolyVox
for(int ct = 0; ct < vecVertexMetadata.size(); ct++)
{
Region regTransformed = m_pOutputMesh->m_Region;
regTransformed.shift(regTransformed.getLowerCorner() * static_cast<int16_t>(-1));
regTransformed.shift(regTransformed.getLowerCorner() * static_cast<int32_t>(-1));
//Plus and minus X
vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_NEG_X, m_pOutputMesh->m_vecVertices[ct].getPosition().getX() < regTransformed.getLowerCorner().getX() + 0.001f);
@ -94,7 +94,7 @@ namespace PolyVox
for(int ct = 0; ct < vecVertexMetadata.size(); ct++)
{
Region regTransformed = m_pOutputMesh->m_Region;
regTransformed.shift(regTransformed.getLowerCorner() * static_cast<int16_t>(-1));
regTransformed.shift(regTransformed.getLowerCorner() * static_cast<int32_t>(-1));
//Plus and minus X
vecVertexMetadata[ct].isOnRegionFace.set(RFF_ON_REGION_FACE_NEG_X, m_pOutputMesh->m_vecVertices[ct].getPosition().getX() < regTransformed.getLowerCorner().getX() + 0.001f);

View File

@ -23,36 +23,49 @@ freely, subject to the following restrictions:
#include "Region.h"
#include <limits>
namespace PolyVox
{
const Region Region::MaxRegion
(
Vector3DInt32((std::numeric_limits<int32_t>::min)(), (std::numeric_limits<int32_t>::min)(), (std::numeric_limits<int32_t>::min)()),
Vector3DInt32((std::numeric_limits<int32_t>::max)(), (std::numeric_limits<int32_t>::max)(), (std::numeric_limits<int32_t>::max)())
);
Region::Region()
:m_v3dLowerCorner(0,0,0)
,m_v3dUpperCorner(0,0,0)
{
}
Region::Region(const Vector3DInt16& v3dLowerCorner, const Vector3DInt16& v3dUpperCorner)
Region::Region(const Vector3DInt32& v3dLowerCorner, const Vector3DInt32& v3dUpperCorner)
:m_v3dLowerCorner(v3dLowerCorner)
,m_v3dUpperCorner(v3dUpperCorner)
{
//Check the region is valid.
assert(m_v3dUpperCorner.getX() >= m_v3dLowerCorner.getX());
assert(m_v3dUpperCorner.getY() >= m_v3dLowerCorner.getY());
assert(m_v3dUpperCorner.getZ() >= m_v3dLowerCorner.getZ());
}
const Vector3DInt16& Region::getLowerCorner(void) const
const Vector3DInt32& Region::getLowerCorner(void) const
{
return m_v3dLowerCorner;
}
const Vector3DInt16& Region::getUpperCorner(void) const
const Vector3DInt32& Region::getUpperCorner(void) const
{
return m_v3dUpperCorner;
}
void Region::setLowerCorner(const Vector3DInt16& v3dLowerCorner)
void Region::setLowerCorner(const Vector3DInt32& v3dLowerCorner)
{
m_v3dLowerCorner = v3dLowerCorner;
}
void Region::setUpperCorner(const Vector3DInt16& v3dUpperCorner)
void Region::setUpperCorner(const Vector3DInt32& v3dUpperCorner)
{
m_v3dUpperCorner = v3dUpperCorner;
}
@ -67,7 +80,7 @@ namespace PolyVox
&& (pos.getZ() >= m_v3dLowerCorner.getZ() + boundary);
}
bool Region::containsPoint(const Vector3DInt16& pos, uint8_t boundary) const
bool Region::containsPoint(const Vector3DInt32& pos, uint8_t boundary) const
{
return (pos.getX() <= m_v3dUpperCorner.getX() - boundary)
&& (pos.getY() <= m_v3dUpperCorner.getY() - boundary)
@ -87,39 +100,47 @@ namespace PolyVox
m_v3dUpperCorner.setZ((std::min)(m_v3dUpperCorner.getZ(), other.m_v3dUpperCorner.getZ()));
}
int16_t Region::depth(void) const
int32_t Region::depth(void) const
{
//This function is deprecated and wrong.
assert(false);
return m_v3dUpperCorner.getZ() - m_v3dLowerCorner.getZ();
}
int16_t Region::height(void) const
int32_t Region::height(void) const
{
//This function is deprecated and wrong.
assert(false);
return m_v3dUpperCorner.getY() - m_v3dLowerCorner.getY();
}
void Region::shift(const Vector3DInt16& amount)
void Region::shift(const Vector3DInt32& amount)
{
m_v3dLowerCorner += amount;
m_v3dUpperCorner += amount;
}
void Region::shiftLowerCorner(const Vector3DInt16& amount)
void Region::shiftLowerCorner(const Vector3DInt32& amount)
{
m_v3dLowerCorner += amount;
}
void Region::shiftUpperCorner(const Vector3DInt16& amount)
void Region::shiftUpperCorner(const Vector3DInt32& amount)
{
m_v3dUpperCorner += amount;
}
Vector3DInt16 Region::dimensions(void)
Vector3DInt32 Region::dimensions(void)
{
//This function is deprecated and wrong.
assert(false);
return m_v3dUpperCorner - m_v3dLowerCorner;
}
int16_t Region::width(void) const
int32_t Region::width(void) const
{
//This function is deprecated and wrong.
assert(false);
return m_v3dUpperCorner.getX() - m_v3dLowerCorner.getX();
}
}

View File

@ -320,7 +320,8 @@ namespace PolyVox
stream.read(reinterpret_cast<char*>(&volumeDepth), sizeof(volumeDepth));
//Resize the volume
volume.resize(volumeWidth, volumeHeight, volumeDepth);
//HACK - Forces block size to 32. This functions needs reworking anyway due to large volume support.
volume.resize(Region(Vector3DInt32(0,0,0), Vector3DInt32(volumeWidth, volumeHeight, volumeDepth)), 32);
//Read data
bool firstTime = true;

View File

@ -35,8 +35,7 @@ void TestArray::testReadWrite()
int height = 10;
int depth = 20;
uint32_t dimensions[3] = {width, height, depth}; // Array dimensions
Array<3, int> myArray(dimensions);
Array<3, int> myArray(ArraySizes(width)(height)(depth));
int ct = 1;
int expectedTotal = 0;

View File

@ -31,23 +31,21 @@ using namespace PolyVox;
void TestVolume::testSize()
{
const uint16_t g_uVolumeSideLength = 128;
Volume<uint8_t> volData(g_uVolumeSideLength, g_uVolumeSideLength, g_uVolumeSideLength);
const int32_t g_uVolumeSideLength = 128;
Volume<uint8_t> volData(Region(Vector3DInt32(0,0,0), Vector3DInt32(g_uVolumeSideLength-1, g_uVolumeSideLength-1, g_uVolumeSideLength-1)));
//Note: Deliberatly go past each edge by one to test if the bounds checking works.
for (uint16_t z = 0; z < g_uVolumeSideLength + 1; z++)
for (int32_t z = 0; z < g_uVolumeSideLength + 1; z++)
{
for (uint16_t y = 0; y < g_uVolumeSideLength + 1; y++)
for (int32_t y = 0; y < g_uVolumeSideLength + 1; y++)
{
for (uint16_t x = 0; x < g_uVolumeSideLength + 1; x++)
for (int32_t x = 0; x < g_uVolumeSideLength + 1; x++)
{
volData.setVoxelAt(x,y,z,255);
}
}
}
volData.tidyUpMemory(0);
QCOMPARE(volData.getWidth(), g_uVolumeSideLength);
QCOMPARE(volData.getHeight(), g_uVolumeSideLength);
QCOMPARE(volData.getDepth(), g_uVolumeSideLength);