Merge branch 'develop' into feature/cmake-cxx11-detect

Conflicts:
	examples/Basic/CMakeLists.txt
	examples/OpenGL/CMakeLists.txt
	examples/Paging/CMakeLists.txt
	examples/SmoothLOD/CMakeLists.txt
	library/PolyVoxCore/include/PolyVoxCore/Impl/TypeDef.h
This commit is contained in:
David Williams
2012-12-24 20:08:31 +00:00
90 changed files with 2808 additions and 114487 deletions

View File

@ -51,6 +51,7 @@ REMOVE_DEFINITIONS(-DQT_GUI_LIB) #Make sure the tests don't link to the QtGui
# Python tests
IF(BUILD_BINDINGS)
ADD_TEST(PythonSurfaceExtractorTest python ${CMAKE_CURRENT_SOURCE_DIR}/TestSurfaceExtractor.py)
ADD_TEST(PythonRaycastTest python ${CMAKE_CURRENT_SOURCE_DIR}/TestRaycast.py)
ENDIF()
# AmbientOcclusionGenerator tests
@ -72,10 +73,6 @@ ADD_TEST(CubicSurfaceExtractorExecuteTest ${LATEST_TEST} testExecute)
CREATE_TEST(TestLowPassFilter.h TestLowPassFilter.cpp TestLowPassFilter)
ADD_TEST(LowPassFilterExecuteTest ${LATEST_TEST} testExecute)
# LargeVolume tests
CREATE_TEST(testvolume.h testvolume.cpp testvolume)
ADD_TEST(VolumeSizeTest ${LATEST_TEST} testSize)
# Material tests
CREATE_TEST(testmaterial.h testmaterial.cpp testmaterial)
ADD_TEST(MaterialTestCompile ${LATEST_TEST} testCompile)
@ -97,6 +94,17 @@ ADD_TEST(VectorLengthTest ${LATEST_TEST} testLength)
ADD_TEST(VectorDotProductTest ${LATEST_TEST} testDotProduct)
ADD_TEST(VectorEqualityTest ${LATEST_TEST} testEquality)
# Volume tests
CREATE_TEST(testvolume.h testvolume.cpp testvolume)
ADD_TEST(RawVolumeDirectAccessTest ${LATEST_TEST} testRawVolumeDirectAccess)
ADD_TEST(SimpleVolumeDirectAccessTest ${LATEST_TEST} testSimpleVolumeDirectAccess)
ADD_TEST(LargeVolumeDirectAccessTest ${LATEST_TEST} testLargeVolumeDirectAccess)
ADD_TEST(RawVolumeSamplersTest ${LATEST_TEST} testRawVolumeSamplers)
ADD_TEST(SimpleVolumeSamplersTest ${LATEST_TEST} testSimpleVolumeSamplers)
ADD_TEST(LargeVolumeSamplersTest ${LATEST_TEST} testLargeVolumeSamplers)
# Volume subclass tests
CREATE_TEST(TestVolumeSubclass.h TestVolumeSubclass.cpp TestVolumeSubclass)
ADD_TEST(VolumeSubclassExtractSurfaceTest ${LATEST_TEST} testExtractSurface)

View File

@ -42,18 +42,30 @@ class RaycastTestFunctor
{
public:
RaycastTestFunctor()
:m_uTotalVoxelsTouched(0)
:m_uVoxelsTouched(0)
,m_bRayLeftVolume(false)
{
}
bool operator()(const SimpleVolume<int8_t>::Sampler& sampler)
{
m_uTotalVoxelsTouched++;
m_uVoxelsTouched++;
// For this particular test we know that we are always starting a ray inside the volume,
// so if it ever leaves the volume we know it can't go back in and so we can terminate early.
// This optimisation is worthwhile because samplers get slow once outside the volume.
if(!sampler.isCurrentPositionValid())
{
m_bRayLeftVolume = true;
return false;
}
// We are in the volume, so decide whether to continue based on the voxel value.
return sampler.getVoxel() <= 0;
}
uint32_t m_uTotalVoxelsTouched;
uint32_t m_uVoxelsTouched;
bool m_bRayLeftVolume;
};
void TestRaycast::testExecute()
@ -81,23 +93,25 @@ void TestRaycast::testExecute()
}
//Cast rays from the centre. Roughly 2/3 should escape.
Vector3DFloat start (uVolumeSideLength / 2, uVolumeSideLength / 2, uVolumeSideLength / 2);
// For demonstration purposes we are using the same function object for all raycasts.
// Therefore, the state it maintains (total voxels touched) is accumulated over all raycsts.
RaycastTestFunctor raycastTestFunctor;
Vector3DFloat start (uVolumeSideLength / 2, uVolumeSideLength / 2, uVolumeSideLength / 2);
// We could have counted the total number of hits in the same way as the total number of voxels
// touched, but for demonstration and testing purposes we are making use of the raycast return value
// and counting them seperatly in this variable.
int hits = 0;
uint32_t uTotalVoxelsTouched = 0;
// Cast a large number of random rays
for(int ct = 0; ct < 1000000; ct++)
{
RaycastTestFunctor raycastTestFunctor;
RaycastResult result = raycastWithDirection(&volData, start, randomUnitVectors[ct % 1024] * 1000.0f, raycastTestFunctor);
if(result == RaycastResults::Interupted)
uTotalVoxelsTouched += raycastTestFunctor.m_uVoxelsTouched;
// If the raycast completed then we know it did not hit anything.If it was interupted then it
// probably hit something, unless we noted that the reason it was interupted was that it left the volume.
if((result == RaycastResults::Interupted) && (raycastTestFunctor.m_bRayLeftVolume == false))
{
hits++;
}
@ -107,7 +121,7 @@ void TestRaycast::testExecute()
QCOMPARE(hits, 687494);
// Check the total number of voxels touched
QCOMPARE(raycastTestFunctor.m_uTotalVoxelsTouched, static_cast<uint32_t>(486219343));
QCOMPARE(uTotalVoxelsTouched, static_cast<uint32_t>(29783248));
}
QTEST_MAIN(TestRaycast)

28
tests/TestRaycast.py Normal file
View File

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
import sys
sys.path.append("../library/bindings/")
import unittest
import PolyVoxCore
def test_functor(sampler):
return sampler <= 0
class TestSurfaceExtractor(unittest.TestCase):
def setUp(self):
#Create a small volume
r = PolyVoxCore.Region(PolyVoxCore.Vector3DInt32(0,0,0), PolyVoxCore.Vector3DInt32(31,31,31))
self.vol = PolyVoxCore.SimpleVolumeuint8(r)
#Set one single voxel to have a reasonably high density
self.vol.setVoxelAt(PolyVoxCore.Vector3DInt32(5, 5, 5), 200)
def test_hit_voxel(self):
self.assertEqual(PolyVoxCore.raycastWithEndpointsSimpleVolumeuint8(self.vol, PolyVoxCore.Vector3DFloat(0,0,0), PolyVoxCore.Vector3DFloat(31,31,31), test_functor), 1)
def test_miss_voxel(self):
self.assertEqual(PolyVoxCore.raycastWithEndpointsSimpleVolumeuint8(self.vol, PolyVoxCore.Vector3DFloat(0,0,0), PolyVoxCore.Vector3DFloat(0,31,31), test_functor), 0)
if __name__ == '__main__':
unittest.main()

View File

@ -120,8 +120,9 @@ void testForType(SurfaceMesh<PositionMaterialNormal>& result)
}
}
DefaultMarchingCubesController<VoxelType> controller(50);
MarchingCubesSurfaceExtractor< SimpleVolume<VoxelType> > extractor(&volData, volData.getEnclosingRegion(), &result, controller);
DefaultMarchingCubesController<VoxelType> controller;
controller.setThreshold(50);
MarchingCubesSurfaceExtractor< SimpleVolume<VoxelType> > extractor(&volData, volData.getEnclosingRegion(), &result, WrapModes::Border, 0, controller);
extractor.execute();
}
@ -145,7 +146,7 @@ void testCustomController(SurfaceMesh<PositionMaterialNormal>& result)
}
CustomMarchingCubesController controller;
MarchingCubesSurfaceExtractor< SimpleVolume<float>, CustomMarchingCubesController > extractor(&volData, volData.getEnclosingRegion(), &result, controller);
MarchingCubesSurfaceExtractor< SimpleVolume<float>, CustomMarchingCubesController > extractor(&volData, volData.getEnclosingRegion(), &result, WrapModes::Border, 0, controller);
extractor.execute();
}

View File

@ -11,11 +11,11 @@ class TestSurfaceExtractor(unittest.TestCase):
#Create a small volume
r = PolyVoxCore.Region(PolyVoxCore.Vector3DInt32(0,0,0), PolyVoxCore.Vector3DInt32(31,31,31))
vol = PolyVoxCore.SimpleVolumeDensity8(r)
vol = PolyVoxCore.SimpleVolumeuint8(r)
#Set one single voxel to have a reasonably high density
vol.setVoxelAt(PolyVoxCore.Vector3DInt32(5, 5, 5), PolyVoxCore.Density8(200))
vol.setVoxelAt(PolyVoxCore.Vector3DInt32(5, 5, 5), 200)
self.mesh = PolyVoxCore.SurfaceMeshPositionMaterialNormal()
extractor = PolyVoxCore.SurfaceExtractorSimpleVolumeDensity8(vol, r, self.mesh)
extractor = PolyVoxCore.MarchingCubesSurfaceExtractorSimpleVolumeuint8(vol, r, self.mesh)
extractor.execute()
def test_num_vertices(self):

View File

@ -32,7 +32,7 @@ using namespace PolyVox;
void TestVector::testLength()
{
Vector3DInt8 vec(3, 4, 5);
QCOMPARE(vec.lengthSquared(), double(3*3+4*4+5*5));
QCOMPARE(vec.lengthSquared(), int32_t(3*3+4*4+5*5)); // QCOMPARE is strict on types. For an int8 vector, the OperationType is int32_t.
}
void TestVector::testDotProduct()
@ -40,7 +40,7 @@ void TestVector::testDotProduct()
Vector3DInt8 vecxy(3, 4, 0);
Vector3DInt8 vecz(0, 0, 1);
QCOMPARE(vecxy.dot(vecz), int8_t(0)); //QCOMPARE is very strict on the types matching
QCOMPARE(vecxy.dot(vecz), int32_t(0)); // QCOMPARE is strict on types. For an int8 vector, the OperationType is int32_t .
}
void TestVector::testEquality()

View File

@ -24,30 +24,330 @@ freely, subject to the following restrictions:
#include "testvolume.h"
#include "PolyVoxCore/LargeVolume.h"
#include "PolyVoxCore/RawVolume.h"
#include "PolyVoxCore/SimpleVolume.h"
#include <QtGlobal>
#include <QtTest>
using namespace PolyVox;
void TestVolume::testSize()
// This is used to compute a value from a list of integers. We use it to
// make sure we get the expected result from a series of volume accesses.
inline int32_t cantorTupleFunction(int32_t previousResult, int32_t value)
{
const int32_t g_uVolumeSideLength = 128;
LargeVolume<uint8_t> volData(Region(Vector3DInt32(0,0,0), Vector3DInt32(g_uVolumeSideLength-1, g_uVolumeSideLength-1, g_uVolumeSideLength-1)));
return (( previousResult + value ) * ( previousResult + value + 1 ) + value ) / 2;
}
for (int32_t z = 0; z < g_uVolumeSideLength; z++)
template <typename VolumeType>
VolumeType* createAndFillVolume(void)
{
//Create the volume
VolumeType* volume = new VolumeType(Region(-57, -31, 12, 64, 96, 131)); // Deliberatly awkward size
//Fill the volume with some data
for(int z = volume->getEnclosingRegion().getLowerZ(); z <= volume->getEnclosingRegion().getUpperZ(); z++)
{
for (int32_t y = 0; y < g_uVolumeSideLength; y++)
for(int y = volume->getEnclosingRegion().getLowerY(); y <= volume->getEnclosingRegion().getUpperY(); y++)
{
for (int32_t x = 0; x < g_uVolumeSideLength; x++)
for(int x = volume->getEnclosingRegion().getLowerX(); x <= volume->getEnclosingRegion().getUpperX(); x++)
{
volData.setVoxelAt(x,y,z,255);
volume->setVoxelAt(x, y, z, x + y + z);
}
}
}
return volume;
}
template <typename VolumeType>
int32_t testDirectAccessWithWrapping(const VolumeType* volume)
{
int32_t result = 0;
for(int z = volume->getEnclosingRegion().getLowerZ() - 2; z <= volume->getEnclosingRegion().getUpperZ() + 4; z++)
{
for(int y = volume->getEnclosingRegion().getLowerY() - 3; y <= volume->getEnclosingRegion().getUpperY() + 5; y++)
{
for(int x = volume->getEnclosingRegion().getLowerX() - 1; x <= volume->getEnclosingRegion().getUpperX() + 2; x++)
{
//Three level loop now processes 27 voxel neighbourhood
for(int innerZ = -1; innerZ <=1; innerZ++)
{
for(int innerY = -1; innerY <=1; innerY++)
{
for(int innerX = -1; innerX <=1; innerX++)
{
result = cantorTupleFunction(result, volume->getVoxelWithWrapping(x + innerX, y + innerY, z + innerZ, WrapModes::Border, 3));
}
}
}
//End of inner loops
}
}
}
return result;
}
template <typename VolumeType>
int32_t testSamplersWithWrapping(VolumeType* volume)
{
int32_t result = 0;
typename VolumeType::Sampler sampler(volume);
sampler.setWrapMode(WrapModes::Border, 3);
for(int z = volume->getEnclosingRegion().getLowerZ() - 2; z <= volume->getEnclosingRegion().getUpperZ() + 4; z++)
{
for(int y = volume->getEnclosingRegion().getLowerY() - 3; y <= volume->getEnclosingRegion().getUpperY() + 5; y++)
{
for(int x = volume->getEnclosingRegion().getLowerX() - 1; x <= volume->getEnclosingRegion().getUpperX() + 2; x++)
{
sampler.setPosition(x, y, z);
result = cantorTupleFunction(result, sampler.peekVoxel1nx1ny1nz());
result = cantorTupleFunction(result, sampler.peekVoxel0px1ny1nz());
result = cantorTupleFunction(result, sampler.peekVoxel1px1ny1nz());
result = cantorTupleFunction(result, sampler.peekVoxel1nx0py1nz());
result = cantorTupleFunction(result, sampler.peekVoxel0px0py1nz());
result = cantorTupleFunction(result, sampler.peekVoxel1px0py1nz());
result = cantorTupleFunction(result, sampler.peekVoxel1nx1py1nz());
result = cantorTupleFunction(result, sampler.peekVoxel0px1py1nz());
result = cantorTupleFunction(result, sampler.peekVoxel1px1py1nz());
result = cantorTupleFunction(result, sampler.peekVoxel1nx1ny0pz());
result = cantorTupleFunction(result, sampler.peekVoxel0px1ny0pz());
result = cantorTupleFunction(result, sampler.peekVoxel1px1ny0pz());
result = cantorTupleFunction(result, sampler.peekVoxel1nx0py0pz());
result = cantorTupleFunction(result, sampler.peekVoxel0px0py0pz());
result = cantorTupleFunction(result, sampler.peekVoxel1px0py0pz());
result = cantorTupleFunction(result, sampler.peekVoxel1nx1py0pz());
result = cantorTupleFunction(result, sampler.peekVoxel0px1py0pz());
result = cantorTupleFunction(result, sampler.peekVoxel1px1py0pz());
result = cantorTupleFunction(result, sampler.peekVoxel1nx1ny1pz());
result = cantorTupleFunction(result, sampler.peekVoxel0px1ny1pz());
result = cantorTupleFunction(result, sampler.peekVoxel1px1ny1pz());
result = cantorTupleFunction(result, sampler.peekVoxel1nx0py1pz());
result = cantorTupleFunction(result, sampler.peekVoxel0px0py1pz());
result = cantorTupleFunction(result, sampler.peekVoxel1px0py1pz());
result = cantorTupleFunction(result, sampler.peekVoxel1nx1py1pz());
result = cantorTupleFunction(result, sampler.peekVoxel0px1py1pz());
result = cantorTupleFunction(result, sampler.peekVoxel1px1py1pz());
}
}
}
return result;
}
template <typename VolumeType>
int32_t complexVolumeTest(void)
{
VolumeType* testVolume = createAndFillVolume<VolumeType>();
int32_t result = 0;
//Test the getVoxel function
for(int z = testVolume->getEnclosingRegion().getLowerZ(); z <= testVolume->getEnclosingRegion().getUpperZ(); z++)
{
for(int y = testVolume->getEnclosingRegion().getLowerY(); y <= testVolume->getEnclosingRegion().getUpperY(); y++)
{
for(int x = testVolume->getEnclosingRegion().getLowerX(); x <= testVolume->getEnclosingRegion().getUpperX(); x++)
{
result = cantorTupleFunction(result, testVolume->getVoxel(x, y, z));
}
}
}
//Test border wrap mode
for(int z = testVolume->getEnclosingRegion().getLowerZ(); z <= testVolume->getEnclosingRegion().getUpperZ(); z++)
{
//Extending outside in y
for(int y = testVolume->getEnclosingRegion().getLowerY() - 3; y <= testVolume->getEnclosingRegion().getUpperY() + 5; y++)
{
for(int x = testVolume->getEnclosingRegion().getLowerX(); x <= testVolume->getEnclosingRegion().getUpperX(); x++)
{
result = cantorTupleFunction(result, testVolume->getVoxelWithWrapping(x, y, z, WrapModes::Border, 3));
}
}
}
//Test clamp wrap mode
for(int z = testVolume->getEnclosingRegion().getLowerZ(); z <= testVolume->getEnclosingRegion().getUpperZ(); z++)
{
for(int y = testVolume->getEnclosingRegion().getLowerY(); y <= testVolume->getEnclosingRegion().getUpperY(); y++)
{
//Extending outside in x
for(int x = testVolume->getEnclosingRegion().getLowerX() - 2; x <= testVolume->getEnclosingRegion().getUpperX() + 4; x++)
{
result = cantorTupleFunction(result, testVolume->getVoxelWithWrapping(x, y, z, WrapModes::Clamp));
}
}
}
//Test the sampler setPosition
typename VolumeType::Sampler sampler(testVolume);
sampler.setWrapMode(WrapModes::Border, 1);
for(int z = testVolume->getEnclosingRegion().getLowerZ() - 2; z <= testVolume->getEnclosingRegion().getUpperZ() + 1; z++)
{
for(int y = testVolume->getEnclosingRegion().getLowerY() - 1; y <= testVolume->getEnclosingRegion().getUpperY() + 3; y++)
{
for(int x = testVolume->getEnclosingRegion().getLowerX() - 4; x <= testVolume->getEnclosingRegion().getUpperX() + 2; x++)
{
sampler.setPosition(x,y,z);
result = cantorTupleFunction(result, sampler.getVoxel());
}
}
}
//Test the sampler move functions
typename VolumeType::Sampler xSampler(testVolume);
typename VolumeType::Sampler ySampler(testVolume);
typename VolumeType::Sampler zSampler(testVolume);
xSampler.setWrapMode(WrapModes::Border, 1);
ySampler.setWrapMode(WrapModes::Clamp, 1);
zSampler.setWrapMode(WrapModes::Border, -3);
zSampler.setPosition(testVolume->getEnclosingRegion().getLowerX() - 4, testVolume->getEnclosingRegion().getLowerY() - 1, testVolume->getEnclosingRegion().getLowerZ() - 2);
for(int z = testVolume->getEnclosingRegion().getLowerZ() - 2; z <= testVolume->getEnclosingRegion().getUpperZ() + 1; z++)
{
ySampler = zSampler;
for(int y = testVolume->getEnclosingRegion().getLowerY() - 1; y <= testVolume->getEnclosingRegion().getUpperY() + 3; y++)
{
xSampler = ySampler;
for(int x = testVolume->getEnclosingRegion().getLowerX() - 4; x <= testVolume->getEnclosingRegion().getUpperX() + 2; x++)
{
result = cantorTupleFunction(result, xSampler.getVoxel());
xSampler.movePositiveX();
}
ySampler.movePositiveY();
}
zSampler.movePositiveZ();
}
xSampler.setWrapMode(WrapModes::Clamp);
ySampler.setWrapMode(WrapModes::Border, 1);
zSampler.setWrapMode(WrapModes::Clamp, -1);
zSampler.setPosition(testVolume->getEnclosingRegion().getUpperX() + 2, testVolume->getEnclosingRegion().getUpperY() + 3, testVolume->getEnclosingRegion().getUpperZ() + 1);
for(int z = 0; z < testVolume->getEnclosingRegion().getDepthInVoxels() + 8; z++)
{
ySampler = zSampler;
for(int y = 0; y < testVolume->getEnclosingRegion().getHeightInVoxels() + 3; y++)
{
xSampler = ySampler;
for(int x = 0; x < testVolume->getEnclosingRegion().getWidthInVoxels() + 5; x++)
{
result = cantorTupleFunction(result, xSampler.getVoxel());
xSampler.moveNegativeX();
}
ySampler.moveNegativeY();
}
zSampler.moveNegativeZ();
}
delete testVolume;
return result;
}
TestVolume::TestVolume()
{
Region region(-57, -31, 12, 64, 96, 131); // Deliberatly awkward size
//Create the volumes
m_pRawVolume = new RawVolume<int32_t>(region);
m_pSimpleVolume = new SimpleVolume<int32_t>(region);
m_pLargeVolume = new LargeVolume<int32_t>(region);
//Fill the volume with some data
for(int z = region.getLowerZ(); z <= region.getUpperZ(); z++)
{
for(int y = region.getLowerY(); y <= region.getUpperY(); y++)
{
for(int x = region.getLowerX(); x <= region.getUpperX(); x++)
{
int32_t value = x + y + z;
m_pRawVolume->setVoxelAt(x, y, z, value);
m_pSimpleVolume->setVoxelAt(x, y, z, value);
m_pLargeVolume->setVoxelAt(x, y, z, value);
}
}
}
}
TestVolume::~TestVolume()
{
delete m_pRawVolume;
delete m_pSimpleVolume;
delete m_pLargeVolume;
}
void TestVolume::testRawVolumeDirectAccess()
{
int32_t result = 0;
QBENCHMARK
{
result = testDirectAccessWithWrapping(m_pRawVolume);
}
QCOMPARE(result, static_cast<int32_t>(-928601007));
}
void TestVolume::testRawVolumeSamplers()
{
int32_t result = 0;
QBENCHMARK
{
result = testSamplersWithWrapping(m_pRawVolume);
}
QCOMPARE(result, static_cast<int32_t>(-928601007));
}
void TestVolume::testSimpleVolumeDirectAccess()
{
int32_t result = 0;
QBENCHMARK
{
result = testDirectAccessWithWrapping(m_pSimpleVolume);
}
QCOMPARE(result, static_cast<int32_t>(-928601007));
}
void TestVolume::testSimpleVolumeSamplers()
{
int32_t result = 0;
QBENCHMARK
{
result = testSamplersWithWrapping(m_pSimpleVolume);
}
QCOMPARE(result, static_cast<int32_t>(-928601007));
}
void TestVolume::testLargeVolumeDirectAccess()
{
int32_t result = 0;
QBENCHMARK
{
result = testDirectAccessWithWrapping(m_pLargeVolume);
}
QCOMPARE(result, static_cast<int32_t>(-928601007));
}
void TestVolume::testLargeVolumeSamplers()
{
int32_t result = 0;
QCOMPARE(volData.getWidth(), g_uVolumeSideLength);
QCOMPARE(volData.getHeight(), g_uVolumeSideLength);
QCOMPARE(volData.getDepth(), g_uVolumeSideLength);
QBENCHMARK
{
result = testSamplersWithWrapping(m_pLargeVolume);
}
QCOMPARE(result, static_cast<int32_t>(-928601007));
}
QTEST_MAIN(TestVolume)

View File

@ -24,14 +24,32 @@ freely, subject to the following restrictions:
#ifndef __PolyVox_TestVolume_H__
#define __PolyVox_TestVolume_H__
#include "PolyVoxCore/PolyVoxForwardDeclarations.h"
#include <QObject>
class TestVolume: public QObject
{
Q_OBJECT
public:
TestVolume();
~TestVolume();
private slots:
void testSize();
private slots:
void testRawVolumeDirectAccess();
void testRawVolumeSamplers();
void testSimpleVolumeDirectAccess();
void testSimpleVolumeSamplers();
void testLargeVolumeDirectAccess();
void testLargeVolumeSamplers();
private:
PolyVox::RawVolume<int32_t>* m_pRawVolume;
PolyVox::SimpleVolume<int32_t>* m_pSimpleVolume;
PolyVox::LargeVolume<int32_t>* m_pLargeVolume;
};
#endif