From 23184e6924ff717f59535292273385a4edd4ea6e Mon Sep 17 00:00:00 2001 From: David Williams Date: Tue, 2 Oct 2012 16:03:39 +0200 Subject: [PATCH] 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);