From ca663966313a10e2ac59969bf26f27cfc387bc3a Mon Sep 17 00:00:00 2001 From: David Williams Date: Tue, 2 Oct 2012 13:15:07 +0200 Subject: [PATCH 01/12] Added info about CubicSurfaceExtractor changes to changelog. --- CHANGELOG.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 0ef77d83..5924cf3e 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -17,3 +17,9 @@ Some algothms assume that basic mathematical operations can be applied to voxel Changes to build system ----------------------- In order to make the build system easier to use, a number of CMake variables were changed to be more consistent. See :doc:`install` for details on the new variable naming. + +Changes to CubicSurfaceExtractor +-------------------------------- +The behaviour of the CubicSurfaceExtractor has been changed such that it no longer handles some edge cases. Because each generated quad lies between two voxels it can be unclear which region should 'own' a quad when the two voxels are from different regions. The previous version of the CubicSurfaceExtractor would attempt to handle this automatically, but as a result it was possible to get two quads existing at the same position in space. This can cause problems with transparency and with physics, as well as making it harder to decide which regions need to be updated when a voxel is changed. + +The new system simplifies the behaviour of this surface extractor but does require a bit of care on the part of the user. You should be clear on the rules controlling when quads are generated and to which regions they will belong. To aid with this we have significantly improved the API documentation for the CubicSurfaceExtractor so be sure to have a look. \ No newline at end of file From 23184e6924ff717f59535292273385a4edd4ea6e Mon Sep 17 00:00:00 2001 From: David Williams Date: Tue, 2 Oct 2012 16:03:39 +0200 Subject: [PATCH 02/12] Initial work on unclassing raycast. --- CHANGELOG.txt | 9 +- .../PolyVoxCore/include/PolyVoxCore/Raycast.h | 84 +++++++++++++++++++ tests/TestRaycast.cpp | 40 ++++++++- 3 files changed, 130 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 5924cf3e..ba4954eb 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -22,4 +22,11 @@ Changes to CubicSurfaceExtractor -------------------------------- The behaviour of the CubicSurfaceExtractor has been changed such that it no longer handles some edge cases. Because each generated quad lies between two voxels it can be unclear which region should 'own' a quad when the two voxels are from different regions. The previous version of the CubicSurfaceExtractor would attempt to handle this automatically, but as a result it was possible to get two quads existing at the same position in space. This can cause problems with transparency and with physics, as well as making it harder to decide which regions need to be updated when a voxel is changed. -The new system simplifies the behaviour of this surface extractor but does require a bit of care on the part of the user. You should be clear on the rules controlling when quads are generated and to which regions they will belong. To aid with this we have significantly improved the API documentation for the CubicSurfaceExtractor so be sure to have a look. \ No newline at end of file +The new system simplifies the behaviour of this surface extractor but does require a bit of care on the part of the user. You should be clear on the rules controlling when quads are generated and to which regions they will belong. To aid with this we have significantly improved the API documentation for the CubicSurfaceExtractor so be sure to have a look. + +Changes to Raycast +------------------ +It's been unclassed (makes sense, removes template parameters). +Switch from std::function to STL approach. +Note functor is passed by reference rather than by value. +Remove 0.5 offset? \ No newline at end of file diff --git a/library/PolyVoxCore/include/PolyVoxCore/Raycast.h b/library/PolyVoxCore/include/PolyVoxCore/Raycast.h index 2c569a01..4d77658e 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Raycast.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Raycast.h @@ -120,6 +120,90 @@ namespace PolyVox Vector3DFloat m_v3dDirectionAndLength; float m_fMaxDistance; }; + + template + void raycast(VolumeType* volData, /*const*/ Vector3DFloat/*&*/ v3dStart, const Vector3DFloat& v3dDirectionAndLength, Callback& callback) + { + VolumeType::Sampler sampler(volData); + + //The doRaycast function is assuming that it is iterating over the areas defined between + //voxels. We actually want to define the areas as being centered on voxels (as this is + //what the CubicSurfaceExtractor generates). We add (0.5,0.5,0.5) here to adjust for this. + v3dStart = v3dStart + Vector3DFloat(0.5f, 0.5f, 0.5f); + + //Compute the end point + Vector3DFloat v3dEnd = v3dStart + v3dDirectionAndLength; + + float x1 = v3dStart.getX(); + float y1 = v3dStart.getY(); + float z1 = v3dStart.getZ(); + float x2 = v3dEnd.getX(); + float y2 = v3dEnd.getY(); + float z2 = v3dEnd.getZ(); + + int i = (int)floorf(x1); + int j = (int)floorf(y1); + int k = (int)floorf(z1); + + int iend = (int)floorf(x2); + int jend = (int)floorf(y2); + int kend = (int)floorf(z2); + + int di = ((x1 < x2) ? 1 : ((x1 > x2) ? -1 : 0)); + int dj = ((y1 < y2) ? 1 : ((y1 > y2) ? -1 : 0)); + int dk = ((z1 < z2) ? 1 : ((z1 > z2) ? -1 : 0)); + + float deltatx = 1.0f / std::abs(x2 - x1); + float deltaty = 1.0f / std::abs(y2 - y1); + float deltatz = 1.0f / std::abs(z2 - z1); + + float minx = floorf(x1), maxx = minx + 1.0f; + float tx = ((x1 > x2) ? (x1 - minx) : (maxx - x1)) * deltatx; + float miny = floorf(y1), maxy = miny + 1.0f; + float ty = ((y1 > y2) ? (y1 - miny) : (maxy - y1)) * deltaty; + float minz = floorf(z1), maxz = minz + 1.0f; + float tz = ((z1 > z2) ? (z1 - minz) : (maxz - z1)) * deltatz; + + sampler.setPosition(i,j,k); + //m_result.previousVoxel = Vector3DInt32(i,j,k); + + for(;;) + { + if(!callback(sampler)) + { + //m_result.foundIntersection = true; + //m_result.intersectionVoxel = Vector3DInt32(i,j,k); + return; + } + //m_result.previousVoxel = Vector3DInt32(i,j,k); + + if(tx <= ty && tx <= tz) + { + if(i == iend) break; + tx += deltatx; + i += di; + + if(di == 1) sampler.movePositiveX(); + if(di == -1) sampler.moveNegativeX(); + } else if (ty <= tz) + { + if(j == jend) break; + ty += deltaty; + j += dj; + + if(dj == 1) sampler.movePositiveY(); + if(dj == -1) sampler.moveNegativeY(); + } else + { + if(k == kend) break; + tz += deltatz; + k += dk; + + if(dk == 1) sampler.movePositiveZ(); + if(dk == -1) sampler.moveNegativeZ(); + } + } + } } #include "PolyVoxCore/Raycast.inl" diff --git a/tests/TestRaycast.cpp b/tests/TestRaycast.cpp index 645a4542..0e49e79a 100644 --- a/tests/TestRaycast.cpp +++ b/tests/TestRaycast.cpp @@ -33,11 +33,33 @@ freely, subject to the following restrictions: using namespace PolyVox; +bool foundIntersection; + bool isPassableByRay(const SimpleVolume::Sampler& sampler) { + if(sampler.getVoxel() > 0) + { + foundIntersection = true; + } return sampler.getVoxel() <= 0; } +// This is the callback functor which is called by the raycast() function for every voxel it touches. +// It's primary purpose is to tell the raycast whether or not to continue (i.e. it tests whether the +// ray has hit a solid voxel). Because the instance of this class is passed to the raycast() function by reference we can also use it to encapsulate some state. +class MyFunctor +{ +public: + bool operator()(const SimpleVolume::Sampler& sampler) + { + if(sampler.getVoxel() > 0) + { + foundIntersection = true; + } + return sampler.getVoxel() <= 0; + } +}; + void TestRaycast::testExecute() { const int32_t uVolumeSideLength = 32; @@ -62,19 +84,33 @@ void TestRaycast::testExecute() } } + QTime timer; + timer.start(); + //Cast rays from the centre. Roughly 2/3 should escape. Vector3DFloat start (uVolumeSideLength / 2, uVolumeSideLength / 2, uVolumeSideLength / 2); int hits = 0; for(int ct = 0; ct < 1000000; ct++) { - RaycastResult result; + /*RaycastResult result; Raycast< SimpleVolume > raycast(&volData, start, randomUnitVectors[ct % 1024] * 1000.0f, result, isPassableByRay); raycast.execute(); if(result.foundIntersection) + { + hits++; + }*/ + + foundIntersection = false; + MyFunctor myFunctor; + raycast(&volData, start, randomUnitVectors[ct % 1024] * 1000.0f, myFunctor); + + if(foundIntersection) { hits++; } - } + } + + std::cout << "Finished in " << timer.elapsed() << "ms" << std::endl; //Check the number of hits. QCOMPARE(hits, 687494); From 7af38d83a33c9171aad0adaf5b917e41323cc9bd Mon Sep 17 00:00:00 2001 From: David Williams Date: Tue, 2 Oct 2012 16:52:43 +0200 Subject: [PATCH 03/12] Added meaningful raycast return values and improved tests. --- CHANGELOG.txt | 5 +- .../PolyVoxCore/include/PolyVoxCore/Raycast.h | 16 ++++- tests/TestRaycast.cpp | 62 +++++++++---------- 3 files changed, 46 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index ba4954eb..027e419c 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -29,4 +29,7 @@ Changes to Raycast It's been unclassed (makes sense, removes template parameters). Switch from std::function to STL approach. Note functor is passed by reference rather than by value. -Remove 0.5 offset? \ No newline at end of file +Remove 0.5 offset? +Resolved endpoints vs direction confusion +Prevent short directions +Prevent infinite loops (by preventing long directions? \ No newline at end of file diff --git a/library/PolyVoxCore/include/PolyVoxCore/Raycast.h b/library/PolyVoxCore/include/PolyVoxCore/Raycast.h index 4d77658e..e645dcbf 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Raycast.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Raycast.h @@ -121,8 +121,18 @@ namespace PolyVox float m_fMaxDistance; }; + namespace MyRaycastResults + { + enum MyRaycastResult + { + Completed, + Interupted + }; + } + typedef MyRaycastResults::MyRaycastResult MyRaycastResult; + template - void raycast(VolumeType* volData, /*const*/ Vector3DFloat/*&*/ v3dStart, const Vector3DFloat& v3dDirectionAndLength, Callback& callback) + MyRaycastResult raycast(VolumeType* volData, /*const*/ Vector3DFloat/*&*/ v3dStart, const Vector3DFloat& v3dDirectionAndLength, Callback& callback) { VolumeType::Sampler sampler(volData); @@ -173,7 +183,7 @@ namespace PolyVox { //m_result.foundIntersection = true; //m_result.intersectionVoxel = Vector3DInt32(i,j,k); - return; + return MyRaycastResults::Interupted; } //m_result.previousVoxel = Vector3DInt32(i,j,k); @@ -203,6 +213,8 @@ namespace PolyVox if(dk == -1) sampler.moveNegativeZ(); } } + + return MyRaycastResults::Completed; } } diff --git a/tests/TestRaycast.cpp b/tests/TestRaycast.cpp index 0e49e79a..ca79af7e 100644 --- a/tests/TestRaycast.cpp +++ b/tests/TestRaycast.cpp @@ -33,31 +33,27 @@ freely, subject to the following restrictions: using namespace PolyVox; -bool foundIntersection; - -bool isPassableByRay(const SimpleVolume::Sampler& sampler) -{ - if(sampler.getVoxel() > 0) - { - foundIntersection = true; - } - return sampler.getVoxel() <= 0; -} - // This is the callback functor which is called by the raycast() function for every voxel it touches. // It's primary purpose is to tell the raycast whether or not to continue (i.e. it tests whether the -// ray has hit a solid voxel). Because the instance of this class is passed to the raycast() function by reference we can also use it to encapsulate some state. +// ray has hit a solid voxel). Because the instance of this class is passed to the raycast() function +// by reference we can also use it to encapsulate some state. We're testing this by counting the total +// number of voxels touched. class MyFunctor { public: + MyFunctor() + :m_uTotalVoxelsTouched(0) + { + } + bool operator()(const SimpleVolume::Sampler& sampler) { - if(sampler.getVoxel() > 0) - { - foundIntersection = true; - } + m_uTotalVoxelsTouched++; + return sampler.getVoxel() <= 0; } + + uint32_t m_uTotalVoxelsTouched; }; void TestRaycast::testExecute() @@ -84,36 +80,34 @@ void TestRaycast::testExecute() } } - QTime timer; - timer.start(); - //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. + MyFunctor myFunctor; + + // 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; + + // Cast a large number of random rays for(int ct = 0; ct < 1000000; ct++) { - /*RaycastResult result; - Raycast< SimpleVolume > raycast(&volData, start, randomUnitVectors[ct % 1024] * 1000.0f, result, isPassableByRay); - raycast.execute(); - if(result.foundIntersection) - { - hits++; - }*/ + MyRaycastResult result = raycast(&volData, start, randomUnitVectors[ct % 1024] * 1000.0f, myFunctor); - foundIntersection = false; - MyFunctor myFunctor; - raycast(&volData, start, randomUnitVectors[ct % 1024] * 1000.0f, myFunctor); - - if(foundIntersection) + if(result == MyRaycastResults::Interupted) { hits++; } } - std::cout << "Finished in " << timer.elapsed() << "ms" << std::endl; - - //Check the number of hits. + // Check the number of hits. QCOMPARE(hits, 687494); + + // Check the total number of voxels touched + QCOMPARE(myFunctor.m_uTotalVoxelsTouched, static_cast(486219343)); } QTEST_MAIN(TestRaycast) From 191d04ec02f1472221d6afad159bea0d2b067dd7 Mon Sep 17 00:00:00 2001 From: David Williams Date: Tue, 2 Oct 2012 17:26:25 +0200 Subject: [PATCH 04/12] Split the raycast funtion into two versions. --- .../PolyVoxCore/include/PolyVoxCore/Raycast.h | 28 ++++++++++--------- tests/TestRaycast.cpp | 2 +- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/Raycast.h b/library/PolyVoxCore/include/PolyVoxCore/Raycast.h index e645dcbf..ddb9c36a 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Raycast.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Raycast.h @@ -132,24 +132,19 @@ namespace PolyVox typedef MyRaycastResults::MyRaycastResult MyRaycastResult; template - MyRaycastResult raycast(VolumeType* volData, /*const*/ Vector3DFloat/*&*/ v3dStart, const Vector3DFloat& v3dDirectionAndLength, Callback& callback) + MyRaycastResult raycastWithEndpoints(VolumeType* volData, const Vector3DFloat& v3dStart, const Vector3DFloat& v3dEnd, Callback& callback) { VolumeType::Sampler sampler(volData); //The doRaycast function is assuming that it is iterating over the areas defined between //voxels. We actually want to define the areas as being centered on voxels (as this is - //what the CubicSurfaceExtractor generates). We add (0.5,0.5,0.5) here to adjust for this. - v3dStart = v3dStart + Vector3DFloat(0.5f, 0.5f, 0.5f); - - //Compute the end point - Vector3DFloat v3dEnd = v3dStart + v3dDirectionAndLength; - - float x1 = v3dStart.getX(); - float y1 = v3dStart.getY(); - float z1 = v3dStart.getZ(); - float x2 = v3dEnd.getX(); - float y2 = v3dEnd.getY(); - float z2 = v3dEnd.getZ(); + //what the CubicSurfaceExtractor generates). We add 0.5 here to adjust for this. + float x1 = v3dStart.getX() + 0.5f; + float y1 = v3dStart.getY() + 0.5f; + float z1 = v3dStart.getZ() + 0.5f; + float x2 = v3dEnd.getX() + 0.5f; + float y2 = v3dEnd.getY() + 0.5f; + float z2 = v3dEnd.getZ() + 0.5f; int i = (int)floorf(x1); int j = (int)floorf(y1); @@ -216,6 +211,13 @@ namespace PolyVox return MyRaycastResults::Completed; } + + template + MyRaycastResult raycastWithDirection(VolumeType* volData, const Vector3DFloat& v3dStart, const Vector3DFloat& v3dDirectionAndLength, Callback& callback) + { + Vector3DFloat v3dEnd = v3dStart + v3dDirectionAndLength; + return raycastWithEndpoints(volData, v3dStart, v3dEnd, callback); + } } #include "PolyVoxCore/Raycast.inl" diff --git a/tests/TestRaycast.cpp b/tests/TestRaycast.cpp index ca79af7e..de658fee 100644 --- a/tests/TestRaycast.cpp +++ b/tests/TestRaycast.cpp @@ -95,7 +95,7 @@ void TestRaycast::testExecute() // Cast a large number of random rays for(int ct = 0; ct < 1000000; ct++) { - MyRaycastResult result = raycast(&volData, start, randomUnitVectors[ct % 1024] * 1000.0f, myFunctor); + MyRaycastResult result = raycastWithDirection(&volData, start, randomUnitVectors[ct % 1024] * 1000.0f, myFunctor); if(result == MyRaycastResults::Interupted) { From 419fa2e5d44f1046a95531554d1923f58bd495b9 Mon Sep 17 00:00:00 2001 From: David Williams Date: Tue, 2 Oct 2012 21:05:09 +0200 Subject: [PATCH 05/12] Work on unclassing the ambient occlusion calculation as it is currently making use of the old raycasts. --- .../PolyVoxCore/AmbientOcclusionCalculator.h | 125 ++++++++++++++++++ tests/TestAmbientOcclusionGenerator.cpp | 7 +- 2 files changed, 127 insertions(+), 5 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.h b/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.h index 0cfdef5b..f5b75bf4 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.h +++ b/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.h @@ -67,6 +67,131 @@ namespace PolyVox polyvox_function m_funcIsTransparent; }; + + class AmbientOcclusionCalculatorRaycastCallback + { + public: + AmbientOcclusionCalculatorRaycastCallback() + { + } + + bool operator()(const SimpleVolume::Sampler& sampler) + { + return sampler.getVoxel() == 0; + } + }; + + template + void calculateAmbientOcclusion(VolumeType* volInput, Array<3, uint8_t>* arrayResult, Region region, float fRayLength, uint8_t uNoOfSamplesPerOutputElement, polyvox_function funcIsTransparent) + { + Region m_region = region; + typename VolumeType::Sampler m_sampVolume(volInput); + VolumeType* m_volInput = volInput; + Array<3, uint8_t>* m_arrayResult = arrayResult; + float m_fRayLength = fRayLength; + + uint8_t m_uNoOfSamplesPerOutputElement = uNoOfSamplesPerOutputElement; + + uint16_t mRandomUnitVectorIndex = 0; + uint16_t mRandomVectorIndex = 0; + uint16_t mIndexIncreament; + + polyvox_function m_funcIsTransparent = funcIsTransparent; + + //Make sure that the size of the volume is an exact multiple of the size of the array. + assert(m_volInput->getWidth() % arrayResult->getDimension(0) == 0); + assert(m_volInput->getHeight() % arrayResult->getDimension(1) == 0); + assert(m_volInput->getDepth() % arrayResult->getDimension(2) == 0); + + //Our initial indices. It doesn't matter exactly what we set here, but the code below makes + //sure they are different for different regions which helps reduce tiling patterns in the results. + mRandomUnitVectorIndex += m_region.getLowerCorner().getX() + m_region.getLowerCorner().getY() + m_region.getLowerCorner().getZ(); + mRandomVectorIndex += m_region.getLowerCorner().getX() + m_region.getLowerCorner().getY() + m_region.getLowerCorner().getZ(); + + //This value helps us jump around in the array a bit more, so the + //nth 'random' value isn't always followed by the n+1th 'random' value. + mIndexIncreament = 1; + + const int iRatioX = m_volInput->getWidth() / m_arrayResult->getDimension(0); + const int iRatioY = m_volInput->getHeight() / m_arrayResult->getDimension(1); + const int iRatioZ = m_volInput->getDepth() / m_arrayResult->getDimension(2); + + const float fRatioX = iRatioX; + const float fRatioY = iRatioY; + const float fRatioZ = iRatioZ; + const Vector3DFloat v3dRatio(fRatioX, fRatioY, fRatioZ); + + const float fHalfRatioX = fRatioX * 0.5f; + const float fHalfRatioY = fRatioY * 0.5f; + const float fHalfRatioZ = fRatioZ * 0.5f; + const Vector3DFloat v3dHalfRatio(fHalfRatioX, fHalfRatioY, fHalfRatioZ); + + const Vector3DFloat v3dOffset(0.5f,0.5f,0.5f); + + //RaycastResult raycastResult; + //Raycast raycast(m_volInput, Vector3DFloat(0.0f,0.0f,0.0f), Vector3DFloat(1.0f,1.0f,1.0f), raycastResult, polyvox_bind(&PolyVox::AmbientOcclusionCalculator::raycastCallback, this, std::placeholders::_1)); + + //This loop iterates over the bottom-lower-left voxel in each of the cells in the output array + for(uint16_t z = m_region.getLowerCorner().getZ(); z <= m_region.getUpperCorner().getZ(); z += iRatioZ) + { + for(uint16_t y = m_region.getLowerCorner().getY(); y <= m_region.getUpperCorner().getY(); y += iRatioY) + { + for(uint16_t x = m_region.getLowerCorner().getX(); x <= m_region.getUpperCorner().getX(); x += iRatioX) + { + //Compute a start position corresponding to + //the centre of the cell in the output array. + Vector3DFloat v3dStart(x, y, z); + v3dStart -= v3dOffset; + v3dStart += v3dHalfRatio; + + //Keep track of how many rays did not hit anything + uint8_t uVisibleDirections = 0; + + for(int ct = 0; ct < m_uNoOfSamplesPerOutputElement; ct++) + { + //We take a random vector with components going from -1 to 1 and scale it to go from -halfRatio to +halfRatio. + //This jitter value moves our sample point from the center of the array cell to somewhere else in the array cell + Vector3DFloat v3dJitter = randomVectors[(mRandomVectorIndex += (++mIndexIncreament)) % 1019]; //Prime number helps avoid repetition on sucessive loops. + v3dJitter *= v3dHalfRatio; + const Vector3DFloat v3dRayStart = v3dStart + v3dJitter; + + Vector3DFloat v3dRayDirection = randomUnitVectors[(mRandomUnitVectorIndex += (++mIndexIncreament)) % 1021]; //Differenct prime number. + v3dRayDirection *= m_fRayLength; + + /*raycast.setStart(v3dRayStart); + raycast.setDirection(v3dRayDirection); + raycast.execute(); + + if(raycastResult.foundIntersection == false) + { + ++uVisibleDirections; + }*/ + + MyRaycastResult result = raycastWithDirection(m_volInput, v3dRayStart, v3dRayDirection, AmbientOcclusionCalculatorRaycastCallback()); + if(result == MyRaycastResults::Completed) + { + ++uVisibleDirections; + } + } + + float fVisibility; + if(m_uNoOfSamplesPerOutputElement == 0) + { + //The user might request zero samples (I've done this in the past while debugging - I don't want to + //wait for ambient occlusion but I do want as valid result for rendering). Avoid the divide by zero. + fVisibility = 1.0f; + } + else + { + fVisibility = static_cast(uVisibleDirections) / static_cast(m_uNoOfSamplesPerOutputElement); + assert((fVisibility >= 0.0f) && (fVisibility <= 1.0f)); + } + + (*m_arrayResult)[z / iRatioZ][y / iRatioY][x / iRatioX] = static_cast(255.0f * fVisibility); + } + } + } + } } #include "PolyVoxCore/AmbientOcclusionCalculator.inl" diff --git a/tests/TestAmbientOcclusionGenerator.cpp b/tests/TestAmbientOcclusionGenerator.cpp index 7491f7c5..18402c96 100644 --- a/tests/TestAmbientOcclusionGenerator.cpp +++ b/tests/TestAmbientOcclusionGenerator.cpp @@ -61,11 +61,8 @@ void TestAmbientOcclusionGenerator::testExecute() const int32_t g_uArraySideLength = g_uVolumeSideLength / 2; Array<3, uint8_t> ambientOcclusionResult(ArraySizes(g_uArraySideLength)(g_uArraySideLength)(g_uArraySideLength)); - //Create the ambient occlusion calculator - AmbientOcclusionCalculator< SimpleVolume > calculator(&volData, &ambientOcclusionResult, volData.getEnclosingRegion(), 32.0f, 255, isVoxelTransparent); - - //Execute the calculator - calculator.execute(); + // Calculate the ambient occlusion values + calculateAmbientOcclusion(&volData, &ambientOcclusionResult, volData.getEnclosingRegion(), 32.0f, 255, isVoxelTransparent); //Check the results by sampling along a line though the centre of the volume. Because //of the two walls we added, samples in the middle are darker than those at the edge. From 3b61adefaae3c746c437947af8b583c4c6f676a6 Mon Sep 17 00:00:00 2001 From: David Williams Date: Fri, 5 Oct 2012 15:42:16 +0200 Subject: [PATCH 06/12] Work unclassing ambientOcclusionCalculator --- .../PolyVoxCore/AmbientOcclusionCalculator.h | 29 +++++++++++++++---- tests/TestAmbientOcclusionGenerator.cpp | 7 ++--- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.h b/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.h index f5b75bf4..8bf66df8 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.h +++ b/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.h @@ -68,6 +68,16 @@ namespace PolyVox polyvox_function m_funcIsTransparent; }; + class IsVoxelTransparent + { + public: + bool operator()(uint8_t voxel) + { + return voxel == 0; + } + }; + + template class AmbientOcclusionCalculatorRaycastCallback { public: @@ -77,12 +87,21 @@ namespace PolyVox bool operator()(const SimpleVolume::Sampler& sampler) { - return sampler.getVoxel() == 0; + //return sampler.getVoxel() == 0; + //return IsTransparentCallback(sampler.getVoxel()); + + uint8_t sample = sampler.getVoxel(); + + bool direct = sample == 0; + IsTransparentCallback funcToCall; + bool func = funcToCall(sample); + assert(direct == func); + return direct; } }; - template - void calculateAmbientOcclusion(VolumeType* volInput, Array<3, uint8_t>* arrayResult, Region region, float fRayLength, uint8_t uNoOfSamplesPerOutputElement, polyvox_function funcIsTransparent) + template + void calculateAmbientOcclusion(VolumeType* volInput, Array<3, uint8_t>* arrayResult, Region region, float fRayLength, uint8_t uNoOfSamplesPerOutputElement/*, IsTransparentCallback funcIsTransparent*/) { Region m_region = region; typename VolumeType::Sampler m_sampVolume(volInput); @@ -96,7 +115,7 @@ namespace PolyVox uint16_t mRandomVectorIndex = 0; uint16_t mIndexIncreament; - polyvox_function m_funcIsTransparent = funcIsTransparent; + //polyvox_function m_funcIsTransparent = funcIsTransparent; //Make sure that the size of the volume is an exact multiple of the size of the array. assert(m_volInput->getWidth() % arrayResult->getDimension(0) == 0); @@ -167,7 +186,7 @@ namespace PolyVox ++uVisibleDirections; }*/ - MyRaycastResult result = raycastWithDirection(m_volInput, v3dRayStart, v3dRayDirection, AmbientOcclusionCalculatorRaycastCallback()); + MyRaycastResult result = raycastWithDirection(m_volInput, v3dRayStart, v3dRayDirection, AmbientOcclusionCalculatorRaycastCallback()); if(result == MyRaycastResults::Completed) { ++uVisibleDirections; diff --git a/tests/TestAmbientOcclusionGenerator.cpp b/tests/TestAmbientOcclusionGenerator.cpp index 18402c96..30dcdb16 100644 --- a/tests/TestAmbientOcclusionGenerator.cpp +++ b/tests/TestAmbientOcclusionGenerator.cpp @@ -30,10 +30,7 @@ freely, subject to the following restrictions: using namespace PolyVox; -bool isVoxelTransparent(uint8_t voxel) -{ - return voxel == 0; -} + void TestAmbientOcclusionGenerator::testExecute() { @@ -62,7 +59,7 @@ void TestAmbientOcclusionGenerator::testExecute() Array<3, uint8_t> ambientOcclusionResult(ArraySizes(g_uArraySideLength)(g_uArraySideLength)(g_uArraySideLength)); // Calculate the ambient occlusion values - calculateAmbientOcclusion(&volData, &ambientOcclusionResult, volData.getEnclosingRegion(), 32.0f, 255, isVoxelTransparent); + calculateAmbientOcclusion(&volData, &ambientOcclusionResult, volData.getEnclosingRegion(), 32.0f, 255/*, isVoxelTransparent*/); //Check the results by sampling along a line though the centre of the volume. Because //of the two walls we added, samples in the middle are darker than those at the edge. From b86a3552e61e0f7ace8925698f3180cf6dfe79f8 Mon Sep 17 00:00:00 2001 From: David Williams Date: Fri, 5 Oct 2012 15:57:41 +0200 Subject: [PATCH 07/12] Tidying up unclassed ambient occlusion calculator. --- .../PolyVoxCore/AmbientOcclusionCalculator.h | 35 ++++++++----------- tests/TestAmbientOcclusionGenerator.cpp | 12 +++++-- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.h b/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.h index 8bf66df8..d77458a7 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.h +++ b/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.h @@ -68,40 +68,31 @@ namespace PolyVox polyvox_function m_funcIsTransparent; }; - class IsVoxelTransparent - { - public: - bool operator()(uint8_t voxel) - { - return voxel == 0; - } - }; - - template + template class AmbientOcclusionCalculatorRaycastCallback { public: - AmbientOcclusionCalculatorRaycastCallback() + AmbientOcclusionCalculatorRaycastCallback(IsVoxelTransparentCallback isVoxelTransparentCallback) { + mIsVoxelTransparentCallback = isVoxelTransparentCallback; } bool operator()(const SimpleVolume::Sampler& sampler) { - //return sampler.getVoxel() == 0; - //return IsTransparentCallback(sampler.getVoxel()); - uint8_t sample = sampler.getVoxel(); - + bool direct = sample == 0; - IsTransparentCallback funcToCall; - bool func = funcToCall(sample); + bool func = mIsVoxelTransparentCallback(sample); assert(direct == func); + return direct; } + + IsVoxelTransparentCallback mIsVoxelTransparentCallback; }; - template - void calculateAmbientOcclusion(VolumeType* volInput, Array<3, uint8_t>* arrayResult, Region region, float fRayLength, uint8_t uNoOfSamplesPerOutputElement/*, IsTransparentCallback funcIsTransparent*/) + template + void calculateAmbientOcclusion(VolumeType* volInput, Array<3, uint8_t>* arrayResult, Region region, float fRayLength, uint8_t uNoOfSamplesPerOutputElement, IsVoxelTransparentCallback isVoxelTransparentCallback) { Region m_region = region; typename VolumeType::Sampler m_sampVolume(volInput); @@ -186,7 +177,11 @@ namespace PolyVox ++uVisibleDirections; }*/ - MyRaycastResult result = raycastWithDirection(m_volInput, v3dRayStart, v3dRayDirection, AmbientOcclusionCalculatorRaycastCallback()); + //IsVoxelTransparent isVoxelTransparent; + + AmbientOcclusionCalculatorRaycastCallback ambientOcclusionCalculatorRaycastCallback(isVoxelTransparentCallback); + + MyRaycastResult result = raycastWithDirection(m_volInput, v3dRayStart, v3dRayDirection, ambientOcclusionCalculatorRaycastCallback); if(result == MyRaycastResults::Completed) { ++uVisibleDirections; diff --git a/tests/TestAmbientOcclusionGenerator.cpp b/tests/TestAmbientOcclusionGenerator.cpp index 30dcdb16..65367da7 100644 --- a/tests/TestAmbientOcclusionGenerator.cpp +++ b/tests/TestAmbientOcclusionGenerator.cpp @@ -30,7 +30,14 @@ freely, subject to the following restrictions: using namespace PolyVox; - +class IsVoxelTransparent +{ +public: + bool operator()(uint8_t voxel) + { + return voxel == 0; + } +}; void TestAmbientOcclusionGenerator::testExecute() { @@ -59,7 +66,8 @@ void TestAmbientOcclusionGenerator::testExecute() Array<3, uint8_t> ambientOcclusionResult(ArraySizes(g_uArraySideLength)(g_uArraySideLength)(g_uArraySideLength)); // Calculate the ambient occlusion values - calculateAmbientOcclusion(&volData, &ambientOcclusionResult, volData.getEnclosingRegion(), 32.0f, 255/*, isVoxelTransparent*/); + IsVoxelTransparent isVoxelTransparent; + calculateAmbientOcclusion(&volData, &ambientOcclusionResult, volData.getEnclosingRegion(), 32.0f, 255, isVoxelTransparent); //Check the results by sampling along a line though the centre of the volume. Because //of the two walls we added, samples in the middle are darker than those at the edge. From bcea851f2c5a71e976e75407e11faeb0ee5fb367 Mon Sep 17 00:00:00 2001 From: David Williams Date: Fri, 5 Oct 2012 16:28:03 +0200 Subject: [PATCH 08/12] Removing class version of AmbientOcclusionCalculator. --- .../PolyVoxCore/AmbientOcclusionCalculator.h | 95 ++++---------- .../AmbientOcclusionCalculator.inl | 117 +----------------- 2 files changed, 24 insertions(+), 188 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.h b/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.h index d77458a7..5ff8247a 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.h +++ b/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.h @@ -41,33 +41,6 @@ freely, subject to the following restrictions: namespace PolyVox { - template - class AmbientOcclusionCalculator - { - public: - AmbientOcclusionCalculator(VolumeType* volInput, Array<3, uint8_t>* arrayResult, Region region, float fRayLength, uint8_t uNoOfSamplesPerOutputElement, polyvox_function funcIsTransparent); - ~AmbientOcclusionCalculator(); - - void execute(void); - - private: - bool raycastCallback(const typename VolumeType::Sampler& sampler); - - Region m_region; - typename VolumeType::Sampler m_sampVolume; - VolumeType* m_volInput; - Array<3, uint8_t>* m_arrayResult; - float m_fRayLength; - - uint8_t m_uNoOfSamplesPerOutputElement; - - uint16_t mRandomUnitVectorIndex; - uint16_t mRandomVectorIndex; - uint16_t mIndexIncreament; - - polyvox_function m_funcIsTransparent; - }; - template class AmbientOcclusionCalculatorRaycastCallback { @@ -94,37 +67,29 @@ namespace PolyVox template void calculateAmbientOcclusion(VolumeType* volInput, Array<3, uint8_t>* arrayResult, Region region, float fRayLength, uint8_t uNoOfSamplesPerOutputElement, IsVoxelTransparentCallback isVoxelTransparentCallback) { - Region m_region = region; typename VolumeType::Sampler m_sampVolume(volInput); - VolumeType* m_volInput = volInput; - Array<3, uint8_t>* m_arrayResult = arrayResult; - float m_fRayLength = fRayLength; - uint8_t m_uNoOfSamplesPerOutputElement = uNoOfSamplesPerOutputElement; - - uint16_t mRandomUnitVectorIndex = 0; - uint16_t mRandomVectorIndex = 0; - uint16_t mIndexIncreament; - - //polyvox_function m_funcIsTransparent = funcIsTransparent; + uint16_t uRandomUnitVectorIndex = 0; + uint16_t uRandomVectorIndex = 0; + uint16_t uIndexIncreament; //Make sure that the size of the volume is an exact multiple of the size of the array. - assert(m_volInput->getWidth() % arrayResult->getDimension(0) == 0); - assert(m_volInput->getHeight() % arrayResult->getDimension(1) == 0); - assert(m_volInput->getDepth() % arrayResult->getDimension(2) == 0); + assert(volInput->getWidth() % arrayResult->getDimension(0) == 0); + assert(volInput->getHeight() % arrayResult->getDimension(1) == 0); + assert(volInput->getDepth() % arrayResult->getDimension(2) == 0); //Our initial indices. It doesn't matter exactly what we set here, but the code below makes //sure they are different for different regions which helps reduce tiling patterns in the results. - mRandomUnitVectorIndex += m_region.getLowerCorner().getX() + m_region.getLowerCorner().getY() + m_region.getLowerCorner().getZ(); - mRandomVectorIndex += m_region.getLowerCorner().getX() + m_region.getLowerCorner().getY() + m_region.getLowerCorner().getZ(); + uRandomUnitVectorIndex += region.getLowerCorner().getX() + region.getLowerCorner().getY() + region.getLowerCorner().getZ(); + uRandomVectorIndex += region.getLowerCorner().getX() + region.getLowerCorner().getY() + region.getLowerCorner().getZ(); //This value helps us jump around in the array a bit more, so the //nth 'random' value isn't always followed by the n+1th 'random' value. - mIndexIncreament = 1; + uIndexIncreament = 1; - const int iRatioX = m_volInput->getWidth() / m_arrayResult->getDimension(0); - const int iRatioY = m_volInput->getHeight() / m_arrayResult->getDimension(1); - const int iRatioZ = m_volInput->getDepth() / m_arrayResult->getDimension(2); + const int iRatioX = volInput->getWidth() / arrayResult->getDimension(0); + const int iRatioY = volInput->getHeight() / arrayResult->getDimension(1); + const int iRatioZ = volInput->getDepth() / arrayResult->getDimension(2); const float fRatioX = iRatioX; const float fRatioY = iRatioY; @@ -138,15 +103,12 @@ namespace PolyVox const Vector3DFloat v3dOffset(0.5f,0.5f,0.5f); - //RaycastResult raycastResult; - //Raycast raycast(m_volInput, Vector3DFloat(0.0f,0.0f,0.0f), Vector3DFloat(1.0f,1.0f,1.0f), raycastResult, polyvox_bind(&PolyVox::AmbientOcclusionCalculator::raycastCallback, this, std::placeholders::_1)); - //This loop iterates over the bottom-lower-left voxel in each of the cells in the output array - for(uint16_t z = m_region.getLowerCorner().getZ(); z <= m_region.getUpperCorner().getZ(); z += iRatioZ) + for(uint16_t z = region.getLowerCorner().getZ(); z <= region.getUpperCorner().getZ(); z += iRatioZ) { - for(uint16_t y = m_region.getLowerCorner().getY(); y <= m_region.getUpperCorner().getY(); y += iRatioY) + for(uint16_t y = region.getLowerCorner().getY(); y <= region.getUpperCorner().getY(); y += iRatioY) { - for(uint16_t x = m_region.getLowerCorner().getX(); x <= m_region.getUpperCorner().getX(); x += iRatioX) + for(uint16_t x = region.getLowerCorner().getX(); x <= region.getUpperCorner().getX(); x += iRatioX) { //Compute a start position corresponding to //the centre of the cell in the output array. @@ -157,31 +119,20 @@ namespace PolyVox //Keep track of how many rays did not hit anything uint8_t uVisibleDirections = 0; - for(int ct = 0; ct < m_uNoOfSamplesPerOutputElement; ct++) + for(int ct = 0; ct < uNoOfSamplesPerOutputElement; ct++) { //We take a random vector with components going from -1 to 1 and scale it to go from -halfRatio to +halfRatio. //This jitter value moves our sample point from the center of the array cell to somewhere else in the array cell - Vector3DFloat v3dJitter = randomVectors[(mRandomVectorIndex += (++mIndexIncreament)) % 1019]; //Prime number helps avoid repetition on sucessive loops. + Vector3DFloat v3dJitter = randomVectors[(uRandomVectorIndex += (++uIndexIncreament)) % 1019]; //Prime number helps avoid repetition on sucessive loops. v3dJitter *= v3dHalfRatio; const Vector3DFloat v3dRayStart = v3dStart + v3dJitter; - Vector3DFloat v3dRayDirection = randomUnitVectors[(mRandomUnitVectorIndex += (++mIndexIncreament)) % 1021]; //Differenct prime number. - v3dRayDirection *= m_fRayLength; + Vector3DFloat v3dRayDirection = randomUnitVectors[(uRandomUnitVectorIndex += (++uIndexIncreament)) % 1021]; //Differenct prime number. + v3dRayDirection *= fRayLength; - /*raycast.setStart(v3dRayStart); - raycast.setDirection(v3dRayDirection); - raycast.execute(); - - if(raycastResult.foundIntersection == false) - { - ++uVisibleDirections; - }*/ - - //IsVoxelTransparent isVoxelTransparent; - AmbientOcclusionCalculatorRaycastCallback ambientOcclusionCalculatorRaycastCallback(isVoxelTransparentCallback); + MyRaycastResult result = raycastWithDirection(volInput, v3dRayStart, v3dRayDirection, ambientOcclusionCalculatorRaycastCallback); - MyRaycastResult result = raycastWithDirection(m_volInput, v3dRayStart, v3dRayDirection, ambientOcclusionCalculatorRaycastCallback); if(result == MyRaycastResults::Completed) { ++uVisibleDirections; @@ -189,7 +140,7 @@ namespace PolyVox } float fVisibility; - if(m_uNoOfSamplesPerOutputElement == 0) + if(uNoOfSamplesPerOutputElement == 0) { //The user might request zero samples (I've done this in the past while debugging - I don't want to //wait for ambient occlusion but I do want as valid result for rendering). Avoid the divide by zero. @@ -197,11 +148,11 @@ namespace PolyVox } else { - fVisibility = static_cast(uVisibleDirections) / static_cast(m_uNoOfSamplesPerOutputElement); + fVisibility = static_cast(uVisibleDirections) / static_cast(uNoOfSamplesPerOutputElement); assert((fVisibility >= 0.0f) && (fVisibility <= 1.0f)); } - (*m_arrayResult)[z / iRatioZ][y / iRatioY][x / iRatioX] = static_cast(255.0f * fVisibility); + (*arrayResult)[z / iRatioZ][y / iRatioY][x / iRatioX] = static_cast(255.0f * fVisibility); } } } diff --git a/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.inl b/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.inl index 766e4a73..461b5a23 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.inl @@ -23,120 +23,5 @@ freely, subject to the following restrictions: namespace PolyVox { - template - AmbientOcclusionCalculator::AmbientOcclusionCalculator(VolumeType* volInput, Array<3, uint8_t>* arrayResult, Region region, float fRayLength, uint8_t uNoOfSamplesPerOutputElement, polyvox_function funcIsTransparent) - :m_region(region) - ,m_sampVolume(volInput) - ,m_volInput(volInput) - ,m_arrayResult(arrayResult) - ,m_fRayLength(fRayLength) - ,m_uNoOfSamplesPerOutputElement(uNoOfSamplesPerOutputElement) - ,mRandomUnitVectorIndex(0) //Although these could be uninitialised, we - ,mRandomVectorIndex(0) //initialise for consistant results in the tests. - ,m_funcIsTransparent(funcIsTransparent) - { - //Make sure that the size of the volume is an exact multiple of the size of the array. - assert(m_volInput->getWidth() % arrayResult->getDimension(0) == 0); - assert(m_volInput->getHeight() % arrayResult->getDimension(1) == 0); - assert(m_volInput->getDepth() % arrayResult->getDimension(2) == 0); - - //Our initial indices. It doesn't matter exactly what we set here, but the code below makes - //sure they are different for different regions which helps reduce tiling patterns in the results. - mRandomUnitVectorIndex += m_region.getLowerCorner().getX() + m_region.getLowerCorner().getY() + m_region.getLowerCorner().getZ(); - mRandomVectorIndex += m_region.getLowerCorner().getX() + m_region.getLowerCorner().getY() + m_region.getLowerCorner().getZ(); - - //This value helps us jump around in the array a bit more, so the - //nth 'random' value isn't always followed by the n+1th 'random' value. - mIndexIncreament = 1; - } - - template - AmbientOcclusionCalculator::~AmbientOcclusionCalculator() - { - } - - template - void AmbientOcclusionCalculator::execute(void) - { - const int iRatioX = m_volInput->getWidth() / m_arrayResult->getDimension(0); - const int iRatioY = m_volInput->getHeight() / m_arrayResult->getDimension(1); - const int iRatioZ = m_volInput->getDepth() / m_arrayResult->getDimension(2); - - const float fRatioX = iRatioX; - const float fRatioY = iRatioY; - const float fRatioZ = iRatioZ; - const Vector3DFloat v3dRatio(fRatioX, fRatioY, fRatioZ); - - const float fHalfRatioX = fRatioX * 0.5f; - const float fHalfRatioY = fRatioY * 0.5f; - const float fHalfRatioZ = fRatioZ * 0.5f; - const Vector3DFloat v3dHalfRatio(fHalfRatioX, fHalfRatioY, fHalfRatioZ); - - const Vector3DFloat v3dOffset(0.5f,0.5f,0.5f); - - RaycastResult raycastResult; - Raycast raycast(m_volInput, Vector3DFloat(0.0f,0.0f,0.0f), Vector3DFloat(1.0f,1.0f,1.0f), raycastResult, polyvox_bind(&PolyVox::AmbientOcclusionCalculator::raycastCallback, this, std::placeholders::_1)); - - //This loop iterates over the bottom-lower-left voxel in each of the cells in the output array - for(uint16_t z = m_region.getLowerCorner().getZ(); z <= m_region.getUpperCorner().getZ(); z += iRatioZ) - { - for(uint16_t y = m_region.getLowerCorner().getY(); y <= m_region.getUpperCorner().getY(); y += iRatioY) - { - for(uint16_t x = m_region.getLowerCorner().getX(); x <= m_region.getUpperCorner().getX(); x += iRatioX) - { - //Compute a start position corresponding to - //the centre of the cell in the output array. - Vector3DFloat v3dStart(x, y, z); - v3dStart -= v3dOffset; - v3dStart += v3dHalfRatio; - - //Keep track of how many rays did not hit anything - uint8_t uVisibleDirections = 0; - - for(int ct = 0; ct < m_uNoOfSamplesPerOutputElement; ct++) - { - //We take a random vector with components going from -1 to 1 and scale it to go from -halfRatio to +halfRatio. - //This jitter value moves our sample point from the center of the array cell to somewhere else in the array cell - Vector3DFloat v3dJitter = randomVectors[(mRandomVectorIndex += (++mIndexIncreament)) % 1019]; //Prime number helps avoid repetition on sucessive loops. - v3dJitter *= v3dHalfRatio; - const Vector3DFloat v3dRayStart = v3dStart + v3dJitter; - - Vector3DFloat v3dRayDirection = randomUnitVectors[(mRandomUnitVectorIndex += (++mIndexIncreament)) % 1021]; //Differenct prime number. - v3dRayDirection *= m_fRayLength; - - raycast.setStart(v3dRayStart); - raycast.setDirection(v3dRayDirection); - raycast.execute(); - - if(raycastResult.foundIntersection == false) - { - ++uVisibleDirections; - } - } - - float fVisibility; - if(m_uNoOfSamplesPerOutputElement == 0) - { - //The user might request zero samples (I've done this in the past while debugging - I don't want to - //wait for ambient occlusion but I do want as valid result for rendering). Avoid the divide by zero. - fVisibility = 1.0f; - } - else - { - fVisibility = static_cast(uVisibleDirections) / static_cast(m_uNoOfSamplesPerOutputElement); - assert((fVisibility >= 0.0f) && (fVisibility <= 1.0f)); - } - - (*m_arrayResult)[z / iRatioZ][y / iRatioY][x / iRatioX] = static_cast(255.0f * fVisibility); - } - } - } - } - - template - bool AmbientOcclusionCalculator::raycastCallback(const typename VolumeType::Sampler& sampler) - { - typename VolumeType::VoxelType voxel = sampler.getVoxel(); - return m_funcIsTransparent(voxel); - } + } From 8c7e2671bebc2af12a8b593c1f81ce5adeafb620 Mon Sep 17 00:00:00 2001 From: David Williams Date: Fri, 5 Oct 2012 16:38:28 +0200 Subject: [PATCH 09/12] Rearranged ambient occlusion code. --- .../PolyVoxCore/AmbientOcclusionCalculator.h | 95 +------------------ .../AmbientOcclusionCalculator.inl | 94 +++++++++++++++++- 2 files changed, 96 insertions(+), 93 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.h b/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.h index 5ff8247a..2170ea60 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.h +++ b/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.h @@ -64,99 +64,10 @@ namespace PolyVox IsVoxelTransparentCallback mIsVoxelTransparentCallback; }; + // NOTE: The callback needs to be a functor not a function. I haven't been + // able to work the required template magic to get functions working as well. template - void calculateAmbientOcclusion(VolumeType* volInput, Array<3, uint8_t>* arrayResult, Region region, float fRayLength, uint8_t uNoOfSamplesPerOutputElement, IsVoxelTransparentCallback isVoxelTransparentCallback) - { - typename VolumeType::Sampler m_sampVolume(volInput); - - uint16_t uRandomUnitVectorIndex = 0; - uint16_t uRandomVectorIndex = 0; - uint16_t uIndexIncreament; - - //Make sure that the size of the volume is an exact multiple of the size of the array. - assert(volInput->getWidth() % arrayResult->getDimension(0) == 0); - assert(volInput->getHeight() % arrayResult->getDimension(1) == 0); - assert(volInput->getDepth() % arrayResult->getDimension(2) == 0); - - //Our initial indices. It doesn't matter exactly what we set here, but the code below makes - //sure they are different for different regions which helps reduce tiling patterns in the results. - uRandomUnitVectorIndex += region.getLowerCorner().getX() + region.getLowerCorner().getY() + region.getLowerCorner().getZ(); - uRandomVectorIndex += region.getLowerCorner().getX() + region.getLowerCorner().getY() + region.getLowerCorner().getZ(); - - //This value helps us jump around in the array a bit more, so the - //nth 'random' value isn't always followed by the n+1th 'random' value. - uIndexIncreament = 1; - - const int iRatioX = volInput->getWidth() / arrayResult->getDimension(0); - const int iRatioY = volInput->getHeight() / arrayResult->getDimension(1); - const int iRatioZ = volInput->getDepth() / arrayResult->getDimension(2); - - const float fRatioX = iRatioX; - const float fRatioY = iRatioY; - const float fRatioZ = iRatioZ; - const Vector3DFloat v3dRatio(fRatioX, fRatioY, fRatioZ); - - const float fHalfRatioX = fRatioX * 0.5f; - const float fHalfRatioY = fRatioY * 0.5f; - const float fHalfRatioZ = fRatioZ * 0.5f; - const Vector3DFloat v3dHalfRatio(fHalfRatioX, fHalfRatioY, fHalfRatioZ); - - const Vector3DFloat v3dOffset(0.5f,0.5f,0.5f); - - //This loop iterates over the bottom-lower-left voxel in each of the cells in the output array - for(uint16_t z = region.getLowerCorner().getZ(); z <= region.getUpperCorner().getZ(); z += iRatioZ) - { - for(uint16_t y = region.getLowerCorner().getY(); y <= region.getUpperCorner().getY(); y += iRatioY) - { - for(uint16_t x = region.getLowerCorner().getX(); x <= region.getUpperCorner().getX(); x += iRatioX) - { - //Compute a start position corresponding to - //the centre of the cell in the output array. - Vector3DFloat v3dStart(x, y, z); - v3dStart -= v3dOffset; - v3dStart += v3dHalfRatio; - - //Keep track of how many rays did not hit anything - uint8_t uVisibleDirections = 0; - - for(int ct = 0; ct < uNoOfSamplesPerOutputElement; ct++) - { - //We take a random vector with components going from -1 to 1 and scale it to go from -halfRatio to +halfRatio. - //This jitter value moves our sample point from the center of the array cell to somewhere else in the array cell - Vector3DFloat v3dJitter = randomVectors[(uRandomVectorIndex += (++uIndexIncreament)) % 1019]; //Prime number helps avoid repetition on sucessive loops. - v3dJitter *= v3dHalfRatio; - const Vector3DFloat v3dRayStart = v3dStart + v3dJitter; - - Vector3DFloat v3dRayDirection = randomUnitVectors[(uRandomUnitVectorIndex += (++uIndexIncreament)) % 1021]; //Differenct prime number. - v3dRayDirection *= fRayLength; - - AmbientOcclusionCalculatorRaycastCallback ambientOcclusionCalculatorRaycastCallback(isVoxelTransparentCallback); - MyRaycastResult result = raycastWithDirection(volInput, v3dRayStart, v3dRayDirection, ambientOcclusionCalculatorRaycastCallback); - - if(result == MyRaycastResults::Completed) - { - ++uVisibleDirections; - } - } - - float fVisibility; - if(uNoOfSamplesPerOutputElement == 0) - { - //The user might request zero samples (I've done this in the past while debugging - I don't want to - //wait for ambient occlusion but I do want as valid result for rendering). Avoid the divide by zero. - fVisibility = 1.0f; - } - else - { - fVisibility = static_cast(uVisibleDirections) / static_cast(uNoOfSamplesPerOutputElement); - assert((fVisibility >= 0.0f) && (fVisibility <= 1.0f)); - } - - (*arrayResult)[z / iRatioZ][y / iRatioY][x / iRatioX] = static_cast(255.0f * fVisibility); - } - } - } - } + void calculateAmbientOcclusion(VolumeType* volInput, Array<3, uint8_t>* arrayResult, Region region, float fRayLength, uint8_t uNoOfSamplesPerOutputElement, IsVoxelTransparentCallback isVoxelTransparentCallback); } #include "PolyVoxCore/AmbientOcclusionCalculator.inl" diff --git a/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.inl b/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.inl index 461b5a23..f7d40a9c 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.inl @@ -23,5 +23,97 @@ freely, subject to the following restrictions: namespace PolyVox { - + template + void calculateAmbientOcclusion(VolumeType* volInput, Array<3, uint8_t>* arrayResult, Region region, float fRayLength, uint8_t uNoOfSamplesPerOutputElement, IsVoxelTransparentCallback isVoxelTransparentCallback) + { + typename VolumeType::Sampler m_sampVolume(volInput); + + uint16_t uRandomUnitVectorIndex = 0; + uint16_t uRandomVectorIndex = 0; + uint16_t uIndexIncreament; + + //Make sure that the size of the volume is an exact multiple of the size of the array. + assert(volInput->getWidth() % arrayResult->getDimension(0) == 0); + assert(volInput->getHeight() % arrayResult->getDimension(1) == 0); + assert(volInput->getDepth() % arrayResult->getDimension(2) == 0); + + //Our initial indices. It doesn't matter exactly what we set here, but the code below makes + //sure they are different for different regions which helps reduce tiling patterns in the results. + uRandomUnitVectorIndex += region.getLowerCorner().getX() + region.getLowerCorner().getY() + region.getLowerCorner().getZ(); + uRandomVectorIndex += region.getLowerCorner().getX() + region.getLowerCorner().getY() + region.getLowerCorner().getZ(); + + //This value helps us jump around in the array a bit more, so the + //nth 'random' value isn't always followed by the n+1th 'random' value. + uIndexIncreament = 1; + + const int iRatioX = volInput->getWidth() / arrayResult->getDimension(0); + const int iRatioY = volInput->getHeight() / arrayResult->getDimension(1); + const int iRatioZ = volInput->getDepth() / arrayResult->getDimension(2); + + const float fRatioX = iRatioX; + const float fRatioY = iRatioY; + const float fRatioZ = iRatioZ; + const Vector3DFloat v3dRatio(fRatioX, fRatioY, fRatioZ); + + const float fHalfRatioX = fRatioX * 0.5f; + const float fHalfRatioY = fRatioY * 0.5f; + const float fHalfRatioZ = fRatioZ * 0.5f; + const Vector3DFloat v3dHalfRatio(fHalfRatioX, fHalfRatioY, fHalfRatioZ); + + const Vector3DFloat v3dOffset(0.5f,0.5f,0.5f); + + //This loop iterates over the bottom-lower-left voxel in each of the cells in the output array + for(uint16_t z = region.getLowerCorner().getZ(); z <= region.getUpperCorner().getZ(); z += iRatioZ) + { + for(uint16_t y = region.getLowerCorner().getY(); y <= region.getUpperCorner().getY(); y += iRatioY) + { + for(uint16_t x = region.getLowerCorner().getX(); x <= region.getUpperCorner().getX(); x += iRatioX) + { + //Compute a start position corresponding to + //the centre of the cell in the output array. + Vector3DFloat v3dStart(x, y, z); + v3dStart -= v3dOffset; + v3dStart += v3dHalfRatio; + + //Keep track of how many rays did not hit anything + uint8_t uVisibleDirections = 0; + + for(int ct = 0; ct < uNoOfSamplesPerOutputElement; ct++) + { + //We take a random vector with components going from -1 to 1 and scale it to go from -halfRatio to +halfRatio. + //This jitter value moves our sample point from the center of the array cell to somewhere else in the array cell + Vector3DFloat v3dJitter = randomVectors[(uRandomVectorIndex += (++uIndexIncreament)) % 1019]; //Prime number helps avoid repetition on sucessive loops. + v3dJitter *= v3dHalfRatio; + const Vector3DFloat v3dRayStart = v3dStart + v3dJitter; + + Vector3DFloat v3dRayDirection = randomUnitVectors[(uRandomUnitVectorIndex += (++uIndexIncreament)) % 1021]; //Differenct prime number. + v3dRayDirection *= fRayLength; + + AmbientOcclusionCalculatorRaycastCallback ambientOcclusionCalculatorRaycastCallback(isVoxelTransparentCallback); + MyRaycastResult result = raycastWithDirection(volInput, v3dRayStart, v3dRayDirection, ambientOcclusionCalculatorRaycastCallback); + + if(result == MyRaycastResults::Completed) + { + ++uVisibleDirections; + } + } + + float fVisibility; + if(uNoOfSamplesPerOutputElement == 0) + { + //The user might request zero samples (I've done this in the past while debugging - I don't want to + //wait for ambient occlusion but I do want as valid result for rendering). Avoid the divide by zero. + fVisibility = 1.0f; + } + else + { + fVisibility = static_cast(uVisibleDirections) / static_cast(uNoOfSamplesPerOutputElement); + assert((fVisibility >= 0.0f) && (fVisibility <= 1.0f)); + } + + (*arrayResult)[z / iRatioZ][y / iRatioY][x / iRatioX] = static_cast(255.0f * fVisibility); + } + } + } + } } From bedd09af1874634d293dd540a236f58fa66c6183 Mon Sep 17 00:00:00 2001 From: David Williams Date: Fri, 5 Oct 2012 16:49:39 +0200 Subject: [PATCH 10/12] Removing old raycasting code. --- library/PolyVoxCore/CMakeLists.txt | 2 - .../PolyVoxCore/include/PolyVoxCore/Raycast.h | 149 ++--------------- .../include/PolyVoxCore/Raycast.inl | 109 ++++-------- .../include/PolyVoxCore/RaycastWithCallback.h | 62 ------- .../PolyVoxCore/RaycastWithCallback.inl | 155 ------------------ 5 files changed, 43 insertions(+), 434 deletions(-) delete mode 100644 library/PolyVoxCore/include/PolyVoxCore/RaycastWithCallback.h delete mode 100644 library/PolyVoxCore/include/PolyVoxCore/RaycastWithCallback.inl diff --git a/library/PolyVoxCore/CMakeLists.txt b/library/PolyVoxCore/CMakeLists.txt index 660aa8c8..6e178ca0 100644 --- a/library/PolyVoxCore/CMakeLists.txt +++ b/library/PolyVoxCore/CMakeLists.txt @@ -79,8 +79,6 @@ SET(CORE_INC_FILES include/PolyVoxCore/RawVolumeSampler.inl include/PolyVoxCore/Raycast.h include/PolyVoxCore/Raycast.inl - include/PolyVoxCore/RaycastWithCallback.h - include/PolyVoxCore/RaycastWithCallback.inl include/PolyVoxCore/Region.h include/PolyVoxCore/SimpleInterface.h include/PolyVoxCore/SimpleVolume.h diff --git a/library/PolyVoxCore/include/PolyVoxCore/Raycast.h b/library/PolyVoxCore/include/PolyVoxCore/Raycast.h index ddb9c36a..1e95f60b 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Raycast.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Raycast.h @@ -28,23 +28,17 @@ freely, subject to the following restrictions: namespace PolyVox { - /// Stores the result of a raycast operation. - //////////////////////////////////////////////////////////////////////////////// - /// A instance of this structure is passed to a Raycast object, and is filled in - /// as the ray traverses the volume. The 'foundIntersection' field indicates whether - /// the ray hit any solid voxels, and if so the 'intersectionVoxel' field indicates - ///the voxel's position - //////////////////////////////////////////////////////////////////////////////// - struct RaycastResult + namespace MyRaycastResults { - ///Indicates whether an intersection was found - bool foundIntersection; - ///If an intersection was found then this field holds the intersecting voxel, otherwise it is undefined. - Vector3DInt32 intersectionVoxel; - Vector3DInt32 previousVoxel; - }; + enum MyRaycastResult + { + Completed, + Interupted + }; + } + typedef MyRaycastResults::MyRaycastResult MyRaycastResult; - /// The Raycast class can be used to find the fist filled voxel along a given path. + /// OUT OF DATE SINCE UNCLASSING //////////////////////////////////////////////////////////////////////////////// /// The principle behind raycasting is to fire a 'ray' through the volume and determine /// what (if anything) that ray hits. This simple test can be used for the purpose of @@ -91,133 +85,12 @@ namespace PolyVox /// surace extractors. It's behaviour with the Marching Cubes surface extractor has not /// been tested yet. //////////////////////////////////////////////////////////////////////////////// - template - class Raycast - { - public: - ///Constructor - Raycast(VolumeType* volData, const Vector3DFloat& v3dStart, const Vector3DFloat& v3dDirectionAndLength, RaycastResult& result, polyvox_function funcIsPassable); - - ///Sets the start position for the ray. - void setStart(const Vector3DFloat& v3dStart); - ///Set the direction for the ray. - void setDirection(const Vector3DFloat& v3dDirectionAndLength); - - ///Performs the raycast. - void execute(); - - private: - RaycastResult& m_result; - - polyvox_function m_funcIsPassable; - - void doRaycast(float x1, float y1, float z1, float x2, float y2, float z2); - - VolumeType* m_volData; - typename VolumeType::Sampler m_sampVolume; - - Vector3DFloat m_v3dStart; - Vector3DFloat m_v3dDirectionAndLength; - float m_fMaxDistance; - }; - - namespace MyRaycastResults - { - enum MyRaycastResult - { - Completed, - Interupted - }; - } - typedef MyRaycastResults::MyRaycastResult MyRaycastResult; template - MyRaycastResult raycastWithEndpoints(VolumeType* volData, const Vector3DFloat& v3dStart, const Vector3DFloat& v3dEnd, Callback& callback) - { - VolumeType::Sampler sampler(volData); - - //The doRaycast function is assuming that it is iterating over the areas defined between - //voxels. We actually want to define the areas as being centered on voxels (as this is - //what the CubicSurfaceExtractor generates). We add 0.5 here to adjust for this. - float x1 = v3dStart.getX() + 0.5f; - float y1 = v3dStart.getY() + 0.5f; - float z1 = v3dStart.getZ() + 0.5f; - float x2 = v3dEnd.getX() + 0.5f; - float y2 = v3dEnd.getY() + 0.5f; - float z2 = v3dEnd.getZ() + 0.5f; - - int i = (int)floorf(x1); - int j = (int)floorf(y1); - int k = (int)floorf(z1); - - int iend = (int)floorf(x2); - int jend = (int)floorf(y2); - int kend = (int)floorf(z2); - - int di = ((x1 < x2) ? 1 : ((x1 > x2) ? -1 : 0)); - int dj = ((y1 < y2) ? 1 : ((y1 > y2) ? -1 : 0)); - int dk = ((z1 < z2) ? 1 : ((z1 > z2) ? -1 : 0)); - - float deltatx = 1.0f / std::abs(x2 - x1); - float deltaty = 1.0f / std::abs(y2 - y1); - float deltatz = 1.0f / std::abs(z2 - z1); - - float minx = floorf(x1), maxx = minx + 1.0f; - float tx = ((x1 > x2) ? (x1 - minx) : (maxx - x1)) * deltatx; - float miny = floorf(y1), maxy = miny + 1.0f; - float ty = ((y1 > y2) ? (y1 - miny) : (maxy - y1)) * deltaty; - float minz = floorf(z1), maxz = minz + 1.0f; - float tz = ((z1 > z2) ? (z1 - minz) : (maxz - z1)) * deltatz; - - sampler.setPosition(i,j,k); - //m_result.previousVoxel = Vector3DInt32(i,j,k); - - for(;;) - { - if(!callback(sampler)) - { - //m_result.foundIntersection = true; - //m_result.intersectionVoxel = Vector3DInt32(i,j,k); - return MyRaycastResults::Interupted; - } - //m_result.previousVoxel = Vector3DInt32(i,j,k); - - if(tx <= ty && tx <= tz) - { - if(i == iend) break; - tx += deltatx; - i += di; - - if(di == 1) sampler.movePositiveX(); - if(di == -1) sampler.moveNegativeX(); - } else if (ty <= tz) - { - if(j == jend) break; - ty += deltaty; - j += dj; - - if(dj == 1) sampler.movePositiveY(); - if(dj == -1) sampler.moveNegativeY(); - } else - { - if(k == kend) break; - tz += deltatz; - k += dk; - - if(dk == 1) sampler.movePositiveZ(); - if(dk == -1) sampler.moveNegativeZ(); - } - } - - return MyRaycastResults::Completed; - } + MyRaycastResult raycastWithEndpoints(VolumeType* volData, const Vector3DFloat& v3dStart, const Vector3DFloat& v3dEnd, Callback& callback); template - MyRaycastResult raycastWithDirection(VolumeType* volData, const Vector3DFloat& v3dStart, const Vector3DFloat& v3dDirectionAndLength, Callback& callback) - { - Vector3DFloat v3dEnd = v3dStart + v3dDirectionAndLength; - return raycastWithEndpoints(volData, v3dStart, v3dEnd, callback); - } + MyRaycastResult raycastWithDirection(VolumeType* volData, const Vector3DFloat& v3dStart, const Vector3DFloat& v3dDirectionAndLength, Callback& callback); } #include "PolyVoxCore/Raycast.inl" diff --git a/library/PolyVoxCore/include/PolyVoxCore/Raycast.inl b/library/PolyVoxCore/include/PolyVoxCore/Raycast.inl index 77c79ad5..6ad915bc 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Raycast.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/Raycast.inl @@ -23,63 +23,6 @@ freely, subject to the following restrictions: namespace PolyVox { - //////////////////////////////////////////////////////////////////////////////// - /// Builds a Raycast object. - /// \param volData A pointer to the volume through which the ray will be cast. - /// \param v3dStart The starting position of the ray. - /// \param v3dDirectionAndLength The direction of the ray. The length of this vector also - /// represents the length of the ray. - /// \param result An instance of RaycastResult in which the result will be stored. - //////////////////////////////////////////////////////////////////////////////// - template - Raycast::Raycast(VolumeType* volData, const Vector3DFloat& v3dStart, const Vector3DFloat& v3dDirectionAndLength, RaycastResult& result, polyvox_function funcIsPassable) - :m_result(result) - ,m_funcIsPassable(funcIsPassable) - ,m_volData(volData) - ,m_sampVolume(volData) - ,m_v3dStart(v3dStart) - ,m_v3dDirectionAndLength(v3dDirectionAndLength) - { - } - - //////////////////////////////////////////////////////////////////////////////// - /// \param v3dStart The starting position of the ray. - //////////////////////////////////////////////////////////////////////////////// - template - void Raycast::setStart(const Vector3DFloat& v3dStart) - { - m_v3dStart = v3dStart; - } - - //////////////////////////////////////////////////////////////////////////////// - /// \param v3dDirectionAndLength The direction of the ray. The length of this vector also - /// represents the length of the ray. - //////////////////////////////////////////////////////////////////////////////// - template - void Raycast::setDirection(const Vector3DFloat& v3dDirectionAndLength) - { - //FIXME: We should add a warning when the ray direction is of length one, as this seems to be a common mistake. - m_v3dDirectionAndLength = v3dDirectionAndLength; - } - - //////////////////////////////////////////////////////////////////////////////// - /// The result is stored in the RaycastResult instance which was passed to the constructor. - //////////////////////////////////////////////////////////////////////////////// - template - void Raycast::execute(void) - { - //The doRaycast function is assuming that it is iterating over the areas defined between - //voxels. We actually want to define the areas as being centered on voxels (as this is - //what the CubicSurfaceExtractor generates). We add (0.5,0.5,0.5) here to adjust for this. - Vector3DFloat v3dStart = m_v3dStart + Vector3DFloat(0.5f, 0.5f, 0.5f); - - //Compute the end point - Vector3DFloat v3dEnd = v3dStart + m_v3dDirectionAndLength; - - //Do the raycast - doRaycast(v3dStart.getX(), v3dStart.getY(), v3dStart.getZ(), v3dEnd.getX(), v3dEnd.getY(), v3dEnd.getZ()); - } - // This function is based on Christer Ericson's code and description of the 'Uniform Grid Intersection Test' in // 'Real Time Collision Detection'. The following information from the errata on the book website is also relevent: // @@ -108,10 +51,22 @@ namespace PolyVox // page 328. The if-statement that reads "if (ty <= tx && ty <= tz)" has a superfluous condition. // It should simply read "if (ty <= tz)". // - // This error was reported by Joey Hammer (PixelActive). - template - void Raycast::doRaycast(float x1, float y1, float z1, float x2, float y2, float z2) + // This error was reported by Joey Hammer (PixelActive). + template + MyRaycastResult raycastWithEndpoints(VolumeType* volData, const Vector3DFloat& v3dStart, const Vector3DFloat& v3dEnd, Callback& callback) { + VolumeType::Sampler sampler(volData); + + //The doRaycast function is assuming that it is iterating over the areas defined between + //voxels. We actually want to define the areas as being centered on voxels (as this is + //what the CubicSurfaceExtractor generates). We add 0.5 here to adjust for this. + float x1 = v3dStart.getX() + 0.5f; + float y1 = v3dStart.getY() + 0.5f; + float z1 = v3dStart.getZ() + 0.5f; + float x2 = v3dEnd.getX() + 0.5f; + float y2 = v3dEnd.getY() + 0.5f; + float z2 = v3dEnd.getZ() + 0.5f; + int i = (int)floorf(x1); int j = (int)floorf(y1); int k = (int)floorf(z1); @@ -135,18 +90,14 @@ namespace PolyVox float minz = floorf(z1), maxz = minz + 1.0f; float tz = ((z1 > z2) ? (z1 - minz) : (maxz - z1)) * deltatz; - m_sampVolume.setPosition(i,j,k); - m_result.previousVoxel = Vector3DInt32(i,j,k); + sampler.setPosition(i,j,k); for(;;) { - if(!m_funcIsPassable(m_sampVolume)) + if(!callback(sampler)) { - m_result.foundIntersection = true; - m_result.intersectionVoxel = Vector3DInt32(i,j,k); - return; + return MyRaycastResults::Interupted; } - m_result.previousVoxel = Vector3DInt32(i,j,k); if(tx <= ty && tx <= tz) { @@ -154,30 +105,34 @@ namespace PolyVox tx += deltatx; i += di; - if(di == 1) m_sampVolume.movePositiveX(); - if(di == -1) m_sampVolume.moveNegativeX(); + if(di == 1) sampler.movePositiveX(); + if(di == -1) sampler.moveNegativeX(); } else if (ty <= tz) { if(j == jend) break; ty += deltaty; j += dj; - if(dj == 1) m_sampVolume.movePositiveY(); - if(dj == -1) m_sampVolume.moveNegativeY(); + if(dj == 1) sampler.movePositiveY(); + if(dj == -1) sampler.moveNegativeY(); } else { if(k == kend) break; tz += deltatz; k += dk; - if(dk == 1) m_sampVolume.movePositiveZ(); - if(dk == -1) m_sampVolume.moveNegativeZ(); + if(dk == 1) sampler.movePositiveZ(); + if(dk == -1) sampler.moveNegativeZ(); } } - //Didn't hit anything - m_result.foundIntersection = false; - m_result.intersectionVoxel = Vector3DInt32(0,0,0); - m_result.previousVoxel = Vector3DInt32(0,0,0); + return MyRaycastResults::Completed; + } + + template + MyRaycastResult raycastWithDirection(VolumeType* volData, const Vector3DFloat& v3dStart, const Vector3DFloat& v3dDirectionAndLength, Callback& callback) + { + Vector3DFloat v3dEnd = v3dStart + v3dDirectionAndLength; + return raycastWithEndpoints(volData, v3dStart, v3dEnd, callback); } } diff --git a/library/PolyVoxCore/include/PolyVoxCore/RaycastWithCallback.h b/library/PolyVoxCore/include/PolyVoxCore/RaycastWithCallback.h deleted file mode 100644 index 767972df..00000000 --- a/library/PolyVoxCore/include/PolyVoxCore/RaycastWithCallback.h +++ /dev/null @@ -1,62 +0,0 @@ -/******************************************************************************* -Copyright (c) 2005-2009 David Williams - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*******************************************************************************/ - -#ifndef __PolyVox_RaycastWithCallback_H__ -#define __PolyVox_RaycastWithCallback_H__ - -#include "PolyVoxCore/Vector.h" - -namespace PolyVox -{ - template - class RaycastWithCallback - { - public: - ///Constructor - RaycastWithCallback(VolumeType* volData, const Vector3DFloat& v3dStart, const Vector3DFloat& v3dDirectionAndLength, polyvox_function funcCallback); - - ///Sets the start position for the ray. - void setStart(const Vector3DFloat& v3dStart); - ///Set the direction for the ray. - void setDirection(const Vector3DFloat& v3dDirectionAndLength); - - ///Performs the raycast. - void execute(); - - private: - polyvox_function m_funcCallback; - - void doRaycast(float x1, float y1, float z1, float x2, float y2, float z2); - - VolumeType* m_volData; - typename VolumeType::Sampler m_sampVolume; - - Vector3DFloat m_v3dStart; - Vector3DFloat m_v3dDirectionAndLength; - float m_fMaxDistance; - }; -} - -#include "PolyVoxCore/RaycastWithCallback.inl" - -#endif //__PolyVox_RaycastWithCallback_H__ \ No newline at end of file diff --git a/library/PolyVoxCore/include/PolyVoxCore/RaycastWithCallback.inl b/library/PolyVoxCore/include/PolyVoxCore/RaycastWithCallback.inl deleted file mode 100644 index a8fc8def..00000000 --- a/library/PolyVoxCore/include/PolyVoxCore/RaycastWithCallback.inl +++ /dev/null @@ -1,155 +0,0 @@ -/******************************************************************************* -Copyright (c) 2005-2009 David Williams - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*******************************************************************************/ - -namespace PolyVox -{ - template - RaycastWithCallback::RaycastWithCallback(VolumeType* volData, const Vector3DFloat& v3dStart, const Vector3DFloat& v3dDirectionAndLength, polyvox_function funcCallback) - :m_volData(volData) - ,m_sampVolume(volData) - ,m_v3dStart(v3dStart) - ,m_v3dDirectionAndLength(v3dDirectionAndLength) - ,m_funcCallback(funcCallback) - { - //Check the user provided a callback, because it - //is used to determine when to finish the raycast. - assert(m_funcCallback); - } - - template - void RaycastWithCallback::setStart(const Vector3DFloat& v3dStart) - { - m_v3dStart = v3dStart; - } - - template - void RaycastWithCallback::setDirection(const Vector3DFloat& v3dDirectionAndLength) - { - m_v3dDirectionAndLength = v3dDirectionAndLength; - } - - template - void RaycastWithCallback::execute(void) - { - //The doRaycast function is assuming that it is iterating over the areas defined between - //voxels. We actually want to define the areas as being centered on voxels (as this is - //what the CubicSurfaceExtractor generates). We add (0.5,0.5,0.5) here to adjust for this. - Vector3DFloat v3dStart = m_v3dStart + Vector3DFloat(0.5f, 0.5f, 0.5f); - - //Compute the end point - Vector3DFloat v3dEnd = v3dStart + m_v3dDirectionAndLength; - - //Do the raycast - doRaycast(v3dStart.getX(), v3dStart.getY(), v3dStart.getZ(), v3dEnd.getX(), v3dEnd.getY(), v3dEnd.getZ()); - } - - // This function is based on Christer Ericson's code and description of the 'Uniform Grid Intersection Test' in - // 'Real Time Collision Detection'. The following information from the errata on the book website is also relevent: - // - // pages 326-327. In the function VisitCellsOverlapped() the two lines calculating tx and ty are incorrect. - // The less-than sign in each line should be a greater-than sign. That is, the two lines should read: - // - // float tx = ((x1 > x2) ? (x1 - minx) : (maxx - x1)) / Abs(x2 - x1); - // float ty = ((y1 > y2) ? (y1 - miny) : (maxy - y1)) / Abs(y2 - y1); - // - // Thanks to Jetro Lauha of Fathammer in Helsinki, Finland for reporting this error. - // - // Jetro also points out that the computations of i, j, iend, and jend are incorrectly rounded if the line - // coordinates are allowed to go negative. While that was not really the intent of the code — that is, I - // assumed grids to be numbered from (0, 0) to (m, n) — I'm at fault for not making my assumption clear. - // Where it is important to handle negative line coordinates the computation of these variables should be - // changed to something like this: - // - // // Determine start grid cell coordinates (i, j) - // int i = (int)floorf(x1 / CELL_SIDE); - // int j = (int)floorf(y1 / CELL_SIDE); - // - // // Determine end grid cell coordinates (iend, jend) - // int iend = (int)floorf(x2 / CELL_SIDE); - // int jend = (int)floorf(y2 / CELL_SIDE); - // - // page 328. The if-statement that reads "if (ty <= tx && ty <= tz)" has a superfluous condition. - // It should simply read "if (ty <= tz)". - // - // This error was reported by Joey Hammer (PixelActive). - template - void RaycastWithCallback::doRaycast(float x1, float y1, float z1, float x2, float y2, float z2) - { - int i = (int)floorf(x1); - int j = (int)floorf(y1); - int k = (int)floorf(z1); - - int iend = (int)floorf(x2); - int jend = (int)floorf(y2); - int kend = (int)floorf(z2); - - int di = ((x1 < x2) ? 1 : ((x1 > x2) ? -1 : 0)); - int dj = ((y1 < y2) ? 1 : ((y1 > y2) ? -1 : 0)); - int dk = ((z1 < z2) ? 1 : ((z1 > z2) ? -1 : 0)); - - float deltatx = 1.0f / std::abs(x2 - x1); - float deltaty = 1.0f / std::abs(y2 - y1); - float deltatz = 1.0f / std::abs(z2 - z1); - - float minx = floorf(x1), maxx = minx + 1.0f; - float tx = ((x1 > x2) ? (x1 - minx) : (maxx - x1)) * deltatx; - float miny = floorf(y1), maxy = miny + 1.0f; - float ty = ((y1 > y2) ? (y1 - miny) : (maxy - y1)) * deltaty; - float minz = floorf(z1), maxz = minz + 1.0f; - float tz = ((z1 > z2) ? (z1 - minz) : (maxz - z1)) * deltatz; - - m_sampVolume.setPosition(i,j,k); - - for(;;) - { - //Call the callback. If it returns false then finish the loop. - if(!m_funcCallback(Vector3DInt32(i,j,k))) - { - break; - } - - if(tx <= ty && tx <= tz) - { - tx += deltatx; - i += di; - - if(di == 1) m_sampVolume.movePositiveX(); - if(di == -1) m_sampVolume.moveNegativeX(); - } else if (ty <= tz) - { - ty += deltaty; - j += dj; - - if(dj == 1) m_sampVolume.movePositiveY(); - if(dj == -1) m_sampVolume.moveNegativeY(); - } else - { - tz += deltatz; - k += dk; - - if(dk == 1) m_sampVolume.movePositiveZ(); - if(dk == -1) m_sampVolume.moveNegativeZ(); - } - } - } -} \ No newline at end of file From c8ba433b88589ef628a9526d43639ddbd7d2a7fe Mon Sep 17 00:00:00 2001 From: David Williams Date: Fri, 5 Oct 2012 16:53:08 +0200 Subject: [PATCH 11/12] Replaced temporary use of MyClassname. --- .../PolyVoxCore/AmbientOcclusionCalculator.inl | 4 ++-- library/PolyVoxCore/include/PolyVoxCore/Raycast.h | 10 +++++----- library/PolyVoxCore/include/PolyVoxCore/Raycast.inl | 8 ++++---- tests/TestRaycast.cpp | 12 ++++++------ 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.inl b/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.inl index f7d40a9c..b5b814fd 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.inl @@ -90,9 +90,9 @@ namespace PolyVox v3dRayDirection *= fRayLength; AmbientOcclusionCalculatorRaycastCallback ambientOcclusionCalculatorRaycastCallback(isVoxelTransparentCallback); - MyRaycastResult result = raycastWithDirection(volInput, v3dRayStart, v3dRayDirection, ambientOcclusionCalculatorRaycastCallback); + RaycastResult result = raycastWithDirection(volInput, v3dRayStart, v3dRayDirection, ambientOcclusionCalculatorRaycastCallback); - if(result == MyRaycastResults::Completed) + if(result == RaycastResults::Completed) { ++uVisibleDirections; } diff --git a/library/PolyVoxCore/include/PolyVoxCore/Raycast.h b/library/PolyVoxCore/include/PolyVoxCore/Raycast.h index 1e95f60b..56aa6c96 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Raycast.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Raycast.h @@ -28,15 +28,15 @@ freely, subject to the following restrictions: namespace PolyVox { - namespace MyRaycastResults + namespace RaycastResults { - enum MyRaycastResult + enum RaycastResult { Completed, Interupted }; } - typedef MyRaycastResults::MyRaycastResult MyRaycastResult; + typedef RaycastResults::RaycastResult RaycastResult; /// OUT OF DATE SINCE UNCLASSING //////////////////////////////////////////////////////////////////////////////// @@ -87,10 +87,10 @@ namespace PolyVox //////////////////////////////////////////////////////////////////////////////// template - MyRaycastResult raycastWithEndpoints(VolumeType* volData, const Vector3DFloat& v3dStart, const Vector3DFloat& v3dEnd, Callback& callback); + RaycastResult raycastWithEndpoints(VolumeType* volData, const Vector3DFloat& v3dStart, const Vector3DFloat& v3dEnd, Callback& callback); template - MyRaycastResult raycastWithDirection(VolumeType* volData, const Vector3DFloat& v3dStart, const Vector3DFloat& v3dDirectionAndLength, Callback& callback); + RaycastResult raycastWithDirection(VolumeType* volData, const Vector3DFloat& v3dStart, const Vector3DFloat& v3dDirectionAndLength, Callback& callback); } #include "PolyVoxCore/Raycast.inl" diff --git a/library/PolyVoxCore/include/PolyVoxCore/Raycast.inl b/library/PolyVoxCore/include/PolyVoxCore/Raycast.inl index 6ad915bc..b0d61170 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Raycast.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/Raycast.inl @@ -53,7 +53,7 @@ namespace PolyVox // // This error was reported by Joey Hammer (PixelActive). template - MyRaycastResult raycastWithEndpoints(VolumeType* volData, const Vector3DFloat& v3dStart, const Vector3DFloat& v3dEnd, Callback& callback) + RaycastResult raycastWithEndpoints(VolumeType* volData, const Vector3DFloat& v3dStart, const Vector3DFloat& v3dEnd, Callback& callback) { VolumeType::Sampler sampler(volData); @@ -96,7 +96,7 @@ namespace PolyVox { if(!callback(sampler)) { - return MyRaycastResults::Interupted; + return RaycastResults::Interupted; } if(tx <= ty && tx <= tz) @@ -126,11 +126,11 @@ namespace PolyVox } } - return MyRaycastResults::Completed; + return RaycastResults::Completed; } template - MyRaycastResult raycastWithDirection(VolumeType* volData, const Vector3DFloat& v3dStart, const Vector3DFloat& v3dDirectionAndLength, Callback& callback) + RaycastResult raycastWithDirection(VolumeType* volData, const Vector3DFloat& v3dStart, const Vector3DFloat& v3dDirectionAndLength, Callback& callback) { Vector3DFloat v3dEnd = v3dStart + v3dDirectionAndLength; return raycastWithEndpoints(volData, v3dStart, v3dEnd, callback); diff --git a/tests/TestRaycast.cpp b/tests/TestRaycast.cpp index de658fee..80e74b61 100644 --- a/tests/TestRaycast.cpp +++ b/tests/TestRaycast.cpp @@ -38,10 +38,10 @@ using namespace PolyVox; // ray has hit a solid voxel). Because the instance of this class is passed to the raycast() function // by reference we can also use it to encapsulate some state. We're testing this by counting the total // number of voxels touched. -class MyFunctor +class RaycastTestFunctor { public: - MyFunctor() + RaycastTestFunctor() :m_uTotalVoxelsTouched(0) { } @@ -85,7 +85,7 @@ void TestRaycast::testExecute() // 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. - MyFunctor myFunctor; + RaycastTestFunctor raycastTestFunctor; // 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 @@ -95,9 +95,9 @@ void TestRaycast::testExecute() // Cast a large number of random rays for(int ct = 0; ct < 1000000; ct++) { - MyRaycastResult result = raycastWithDirection(&volData, start, randomUnitVectors[ct % 1024] * 1000.0f, myFunctor); + RaycastResult result = raycastWithDirection(&volData, start, randomUnitVectors[ct % 1024] * 1000.0f, raycastTestFunctor); - if(result == MyRaycastResults::Interupted) + if(result == RaycastResults::Interupted) { hits++; } @@ -107,7 +107,7 @@ void TestRaycast::testExecute() QCOMPARE(hits, 687494); // Check the total number of voxels touched - QCOMPARE(myFunctor.m_uTotalVoxelsTouched, static_cast(486219343)); + QCOMPARE(raycastTestFunctor.m_uTotalVoxelsTouched, static_cast(486219343)); } QTEST_MAIN(TestRaycast) From 5e25f80f8140cfb75a590da90cd2b9ae064ab841 Mon Sep 17 00:00:00 2001 From: David Williams Date: Fri, 5 Oct 2012 16:57:02 +0200 Subject: [PATCH 12/12] Updated changelog --- CHANGELOG.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 027e419c..33ec7bfc 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -29,7 +29,8 @@ Changes to Raycast It's been unclassed (makes sense, removes template parameters). Switch from std::function to STL approach. Note functor is passed by reference rather than by value. -Remove 0.5 offset? Resolved endpoints vs direction confusion -Prevent short directions -Prevent infinite loops (by preventing long directions? \ No newline at end of file + +Changes to AmbientOcclusionCalculator +------------------------------------- +It's been unclassed and is now called calculateAmbientOcclusion. \ No newline at end of file