From 94093392305cdf60b512a0f6582983f6773caabb Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 23 Feb 2016 09:16:14 -0800 Subject: [PATCH] add ViewFrustum::cubeInKeyhole() --- libraries/octree/src/ViewFrustum.cpp | 27 ++++ libraries/octree/src/ViewFrustum.h | 3 + tests/octree/src/ViewFrustumTests.cpp | 208 +++++++++++++++++++++++++- tests/octree/src/ViewFrustumTests.h | 1 + 4 files changed, 234 insertions(+), 5 deletions(-) diff --git a/libraries/octree/src/ViewFrustum.cpp b/libraries/octree/src/ViewFrustum.cpp index 7db80bffed..f720283d21 100644 --- a/libraries/octree/src/ViewFrustum.cpp +++ b/libraries/octree/src/ViewFrustum.cpp @@ -198,6 +198,33 @@ ViewFrustum::location ViewFrustum::boxInFrustum(const AABox& box) const { return result; } +const float HALF_SQRT_THREE = 0.8660254f; + +ViewFrustum::location ViewFrustum::cubeInKeyhole(const AACube& cube) const { + // check against centeral sphere + ViewFrustum::location sphereResult = INTERSECT; + glm::vec3 cubeOffset = cube.calcCenter() - _position; + float distance = glm::length(cubeOffset); + if (distance > EPSILON) { + glm::vec3 vertex = cube.getFarthestVertex(cubeOffset) - _position; + if (glm::dot(vertex, cubeOffset) < _keyholeRadius * distance) { + // the most outward cube vertex is inside central sphere + return INSIDE; + } + if (!cube.touchesSphere(_position, _keyholeRadius)) { + sphereResult = OUTSIDE; + } + } else if (_keyholeRadius > HALF_SQRT_THREE * cube.getScale()) { + // the cube is in center of sphere and its bounding radius is inside + return INSIDE; + } + + // check against frustum + ViewFrustum::location frustumResult = cubeInFrustum(cube); + + return (frustumResult == OUTSIDE) ? sphereResult : frustumResult; +} + bool ViewFrustum::sphereTouchesKeyhole(const glm::vec3& center, float radius) const { // check positive touch against central sphere if (glm::length(center - _position) <= (radius + _keyholeRadius)) { diff --git a/libraries/octree/src/ViewFrustum.h b/libraries/octree/src/ViewFrustum.h index 8b25b5cd4c..cd440676fe 100644 --- a/libraries/octree/src/ViewFrustum.h +++ b/libraries/octree/src/ViewFrustum.h @@ -96,6 +96,9 @@ public: ViewFrustum::location cubeInFrustum(const AACube& cube) const; ViewFrustum::location boxInFrustum(const AABox& box) const; + ViewFrustum::location cubeInKeyhole(const AACube& cube) const; + + // more efficient methods when only need boolean result bool sphereTouchesKeyhole(const glm::vec3& center, float radius) const; bool cubeTouchesKeyhole(const AACube& cube) const; bool boxTouchesKeyhole(const AABox& box) const; diff --git a/tests/octree/src/ViewFrustumTests.cpp b/tests/octree/src/ViewFrustumTests.cpp index 8d3908adb8..a5149d689c 100644 --- a/tests/octree/src/ViewFrustumTests.cpp +++ b/tests/octree/src/ViewFrustumTests.cpp @@ -198,7 +198,7 @@ void ViewFrustumTests::testSphereInFrustum() { glm::quat elevation, swing; glm::vec3 sphereCenter, localOffset; - float sphereRadius = 2.68f; // must be much smaller than sphereDistance + float sphereRadius = 2.68f; // must be much smaller than sphereDistance for small angle approx below float sphereDistance = farClip; float sphereAngle = sphereRadius / sphereDistance; // sine of small angles approximation @@ -327,7 +327,7 @@ void ViewFrustumTests::testCubeInFrustum() { glm::quat elevation, swing; glm::vec3 cubeCenter, localOffset; - float cubeScale = 2.68f; // must be much smaller than cubeDistance + float cubeScale = 2.68f; // must be much smaller than cubeDistance for small angle approx below glm::vec3 halfScaleOffset = 0.5f * glm::vec3(cubeScale); float cubeDistance = farClip; float cubeBoundingRadius = 0.5f * sqrtf(3.0f) * cubeScale; @@ -473,7 +473,7 @@ void ViewFrustumTests::testBoxInFrustum() { glm::quat elevation, swing; glm::vec3 boxCenter, localOffset; - glm::vec3 boxScale = glm::vec3(2.68f, 1.78f, 0.431f); // sides must be much smaller than boxDistance + glm::vec3 boxScale = glm::vec3(2.68f, 1.78f, 0.431f); float boxDistance = farClip; float boxBoundingRadius = 0.5f * glm::length(boxScale); float boxAngle = boxBoundingRadius / boxDistance; // sine of small angles approximation @@ -592,6 +592,204 @@ void ViewFrustumTests::testBoxInFrustum() { QCOMPARE(view.boxInFrustum(box), ViewFrustum::OUTSIDE); } +void ViewFrustumTests::testCubeInKeyhole() { + float aspect = 1.0f; + float fovX = PI / 2.0f; + float fovY = 2.0f * asinf(sinf(0.5f * fovX) / aspect); + float nearClip = 1.0f; + float farClip = 100.0f; + float holeRadius = 10.0f; + + glm::vec3 center = glm::vec3(12.3f, 4.56f, 89.7f); + + float angle = PI / 7.0f; + glm::vec3 axis = Vectors::UNIT_Y; + glm::quat rotation = glm::angleAxis(angle, axis); + + ViewFrustum view; + view.setProjection(glm::perspective(fovX, aspect, nearClip, farClip)); + view.setPosition(center); + view.setOrientation(rotation); + view.setKeyholeRadius(holeRadius); + view.calculate(); + + float delta = 0.1f; + float deltaAngle = 0.01f; + glm::quat elevation, swing; + glm::vec3 cubeCenter, localOffset; + + float cubeScale = 2.68f; // must be much smaller than cubeDistance for small angle approx below + glm::vec3 halfScaleOffset = 0.5f * glm::vec3(cubeScale); + float cubeDistance = farClip; + float cubeBoundingRadius = 0.5f * sqrtf(3.0f) * cubeScale; + float cubeAngle = cubeBoundingRadius / cubeDistance; // sine of small angles approximation + AACube cube(center, cubeScale); + + // farPlane + localOffset = (cubeDistance - cubeBoundingRadius - delta) * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::INSIDE); + + localOffset = cubeDistance * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::INTERSECT); + + localOffset = (cubeDistance + cubeBoundingRadius + delta) * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::OUTSIDE); + + // nearPlane + localOffset = (nearClip + 2.0f * cubeBoundingRadius + delta) * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::INSIDE); + + localOffset = (nearClip + delta) * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::INSIDE); + + // topPlane + angle = 0.5f * fovY; + elevation = glm::angleAxis(angle - cubeAngle - deltaAngle, localRight); + localOffset = elevation * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::INSIDE); + + elevation = glm::angleAxis(angle, localRight); + localOffset = elevation * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::INTERSECT); + + elevation = glm::angleAxis(angle + cubeAngle + deltaAngle, localRight); + localOffset = elevation * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::OUTSIDE); + + // bottom plane + angle = -0.5f * fovY; + elevation = glm::angleAxis(angle + cubeAngle + deltaAngle, localRight); + localOffset = elevation * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::INSIDE); + + elevation = glm::angleAxis(angle, localRight); + localOffset = elevation * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::INTERSECT); + + elevation = glm::angleAxis(angle - cubeAngle - deltaAngle, localRight); + localOffset = elevation * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::OUTSIDE); + + // right plane + angle = 0.5f * fovX; + swing = glm::angleAxis(angle - cubeAngle - deltaAngle, localUp); + localOffset = swing * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::INSIDE); + + swing = glm::angleAxis(angle, localUp); + localOffset = swing * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::INTERSECT); + + swing = glm::angleAxis(angle + cubeAngle + deltaAngle, localUp); + localOffset = swing * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::OUTSIDE); + + // left plane + angle = -0.5f * fovX; + swing = glm::angleAxis(angle + cubeAngle + deltaAngle, localUp); + localOffset = swing * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::INSIDE); + + swing = glm::angleAxis(angle, localUp); + localOffset = swing * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::INTERSECT); + + swing = glm::angleAxis(angle - cubeAngle - deltaAngle, localUp); + localOffset = swing * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::OUTSIDE); + + // central sphere right + localOffset = (holeRadius - cubeBoundingRadius - delta) * localRight; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::INSIDE); + + localOffset = holeRadius * localRight; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::INTERSECT); + + localOffset = (holeRadius + cubeBoundingRadius + delta) * localRight; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::OUTSIDE); + + // central sphere up + localOffset = (holeRadius - cubeBoundingRadius - delta) * localUp; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::INSIDE); + + localOffset = holeRadius * localUp; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::INTERSECT); + + localOffset = (holeRadius + cubeBoundingRadius + delta) * localUp; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::OUTSIDE); + + // central sphere back + localOffset = (-holeRadius + cubeBoundingRadius + delta) * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::INSIDE); + + localOffset = - holeRadius * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::INTERSECT); + + localOffset = (-holeRadius - cubeBoundingRadius - delta) * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::OUTSIDE); + + // central sphere center + float bigCubeScale = 2.0f * holeRadius / sqrtf(3.0f) - delta; + cube.setBox(center - glm::vec3(0.5f * bigCubeScale), bigCubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::INSIDE); // smaller than sphere + + bigCubeScale = 2.0f * holeRadius / sqrtf(3.0f) + delta; + cube.setBox(center - glm::vec3(0.5f * bigCubeScale), bigCubeScale); + QCOMPARE(view.cubeInKeyhole(cube), ViewFrustum::INTERSECT); // larger than sphere +} + void ViewFrustumTests::testSphereTouchesKeyhole() { float aspect = 1.0f; float fovX = PI / 2.0f; @@ -618,7 +816,7 @@ void ViewFrustumTests::testSphereTouchesKeyhole() { glm::quat elevation, swing; glm::vec3 sphereCenter, localOffset; - float sphereRadius = 2.68f; // must be much smaller than sphereDistance + float sphereRadius = 2.68f; // must be much smaller than sphereDistance for small angle approx below float sphereDistance = farClip; float sphereAngle = sphereRadius / sphereDistance; // sine of small angles approximation @@ -786,7 +984,7 @@ void ViewFrustumTests::testCubeTouchesKeyhole() { glm::quat elevation, swing; glm::vec3 cubeCenter, localOffset; - float cubeScale = 2.68f; // must be much smaller than cubeDistance + float cubeScale = 2.68f; // must be much smaller than cubeDistance for small angle approx below glm::vec3 halfScaleOffset = 0.5f * glm::vec3(cubeScale); float cubeDistance = farClip; float cubeBoundingRadius = 0.5f * sqrtf(3.0f) * cubeScale; diff --git a/tests/octree/src/ViewFrustumTests.h b/tests/octree/src/ViewFrustumTests.h index 8d53c71409..6d47348a04 100644 --- a/tests/octree/src/ViewFrustumTests.h +++ b/tests/octree/src/ViewFrustumTests.h @@ -23,6 +23,7 @@ private slots: void testSphereInFrustum(); void testCubeInFrustum(); void testBoxInFrustum(); + void testCubeInKeyhole(); void testSphereTouchesKeyhole(); void testCubeTouchesKeyhole(); void testBoxTouchesKeyhole();