From d9063b199ea3fe9284ebba4465314d745320b7a3 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 25 Apr 2016 18:02:50 -0700 Subject: [PATCH 1/2] GeometryUtils: coneSphereAngle test --- libraries/shared/src/GeometryUtil.cpp | 14 +++++++ libraries/shared/src/GeometryUtil.h | 2 + tests/shared/src/GeometryUtilTests.cpp | 58 ++++++++++++++++++++++++++ tests/shared/src/GeometryUtilTests.h | 1 + 4 files changed, 75 insertions(+) diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index 96c4d3bdea..92fe138021 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -564,3 +564,17 @@ void swingTwistDecomposition(const glm::quat& rotation, // rotation = swing * twist --> swing = rotation * invTwist swing = rotation * glm::inverse(twist); } + +// calculate the minimum angle between a point and a sphere. +float coneSphereAngle(const glm::vec3& coneCenter, const glm::vec3& coneDirection, const glm::vec3& sphereCenter, float sphereRadius) { + glm::vec3 d = sphereCenter - coneCenter; + float dLen = glm::length(d); + + // theta is the angle between the coneDirection normal and the center of the sphere. + float theta = acosf(glm::dot(d, coneDirection) / dLen); + + // phi is the deflection angle from the center of the sphere to a point tangent to the sphere. + float phi = atanf(sphereRadius / dLen); + + return glm::max(0.0f, theta - phi); +} diff --git a/libraries/shared/src/GeometryUtil.h b/libraries/shared/src/GeometryUtil.h index 7224aaacff..a66b16b0fd 100644 --- a/libraries/shared/src/GeometryUtil.h +++ b/libraries/shared/src/GeometryUtil.h @@ -110,6 +110,8 @@ bool doLineSegmentsIntersect(glm::vec2 r1p1, glm::vec2 r1p2, glm::vec2 r2p1, glm bool isOnSegment(float xi, float yi, float xj, float yj, float xk, float yk); int computeDirection(float xi, float yi, float xj, float yj, float xk, float yk); +// calculate the angle between a point on a sphere that is closest to the cone. +float coneSphereAngle(const glm::vec3& coneCenter, const glm::vec3& coneDirection, const glm::vec3& sphereCenter, float sphereRadius); typedef glm::vec2 LineSegment2[2]; diff --git a/tests/shared/src/GeometryUtilTests.cpp b/tests/shared/src/GeometryUtilTests.cpp index 289ac9a0a9..79472a1128 100644 --- a/tests/shared/src/GeometryUtilTests.cpp +++ b/tests/shared/src/GeometryUtilTests.cpp @@ -14,6 +14,7 @@ #include "GeometryUtilTests.h" #include +#include #include #include @@ -22,6 +23,63 @@ QTEST_MAIN(GeometryUtilTests) +static void testSphereVsCone(const glm::vec3 coneNormal, const glm::vec3 coneBiNormal, float coneAngle, float sphereRadius, float sphereDistance) { + + glm::vec3 u, v, w; + generateBasisVectors(coneNormal, coneBiNormal, u, v, w); + glm::vec3 coneEdge = u * cosf(coneAngle) + v * sinf(coneAngle); + glm::vec3 coneCenter = glm::vec3(0.0f, 0.0f, 0.0f); + glm::vec3 sphereCenter = coneCenter + coneEdge * sphereDistance; + float result = coneSphereAngle(coneCenter, u, sphereCenter, sphereRadius); + QCOMPARE(isnan(result), false); + QCOMPARE(result < coneAngle, true); + + // push sphere outward from edge so it is tangent to the cone. + glm::vec3 sphereOffset = glm::angleAxis(PI / 2.0f, w) * coneEdge; + sphereCenter += sphereOffset * sphereRadius; + result = coneSphereAngle(coneCenter, u, sphereCenter, sphereRadius); + QCOMPARE(isnan(result), false); + QCOMPARE_WITH_ABS_ERROR(result, coneAngle, 0.001f); + + // push sphere outward from edge a bit further, so it is outside of the cone. + sphereCenter += 0.1f * sphereOffset; + result = coneSphereAngle(coneCenter, u, sphereCenter, sphereRadius); + QCOMPARE(isnan(result), false); + QCOMPARE(result > coneAngle, true); +} + +void GeometryUtilTests::testConeSphereAngle() { + + // start with a 45 degree cone. + testSphereVsCone(glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), PI / 4.0f, 1.0f, 10.0f); + + // test 30 degree cone. + testSphereVsCone(glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), PI / 6.0f, 1.0f, 10.0f); + + // test 60 degree cone. + testSphereVsCone(glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), PI / 3.0f, 1.0f, 10.0f); + + // test 120 degree cone. + testSphereVsCone(glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), 2 * PI / 3.0f, 1.0f, 10.0f); + + // test skinny cone. + testSphereVsCone(glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), 0.0001f, 1.0f, 10.0f); + + // start again with a 45 off axis cone. + testSphereVsCone(glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3(0.0f, 1.0f, 0.0f), PI / 4.0f, 1.0f, 10.0f); + + // test 30 degree off axis cone + testSphereVsCone(glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3(0.0f, 1.0f, 0.0f), PI / 6.0f, 1.0f, 10.0f); + + // test 60 degree cone off axis cone + testSphereVsCone(glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3(0.0f, 1.0f, 0.0f), PI / 3.0f, 1.0f, 10.0f); + + // test 120 degree off axis cone. + testSphereVsCone(glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3(0.0f, 1.0f, 0.0f), 2 * PI / 3.0f, 1.0f, 10.0f); + + // test skinny off-axis cone. + testSphereVsCone(glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3(0.0f, 1.0f, 0.0f), 0.0001f, 1.0f, 10.0f); +} void GeometryUtilTests::testLocalRayRectangleIntersection() { glm::vec3 xAxis(1.0f, 0.0f, 0.0f); diff --git a/tests/shared/src/GeometryUtilTests.h b/tests/shared/src/GeometryUtilTests.h index b6340cd8cf..3b5ad3da51 100644 --- a/tests/shared/src/GeometryUtilTests.h +++ b/tests/shared/src/GeometryUtilTests.h @@ -18,6 +18,7 @@ class GeometryUtilTests : public QObject { Q_OBJECT private slots: + void testConeSphereAngle(); void testLocalRayRectangleIntersection(); void testWorldRayRectangleIntersection(); void testTwistSwingDecomposition(); From 71f67c99ccbd401810bcaa79ca8a736dfa28da90 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 26 Apr 2016 13:43:03 -0700 Subject: [PATCH 2/2] MyAvatar: use coneSphereAngle for eyeTracking look at targets --- interface/src/avatar/MyAvatar.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9a61c00712..2c4b8da5e1 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -911,7 +911,8 @@ void MyAvatar::updateLookAtTargetAvatar() { avatar->setIsLookAtTarget(false); if (!avatar->isMyAvatar() && avatar->isInitialized() && (distanceTo < GREATEST_LOOKING_AT_DISTANCE * getUniformScale())) { - float angleTo = glm::angle(lookForward, glm::normalize(avatar->getHead()->getEyePosition() - getHead()->getEyePosition())); + float radius = glm::length(avatar->getHead()->getEyePosition() - avatar->getHead()->getRightEyePosition()); + float angleTo = coneSphereAngle(getHead()->getEyePosition(), lookForward, avatar->getHead()->getEyePosition(), radius); if (angleTo < (smallestAngleTo * (isCurrentTarget ? KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR : 1.0f))) { _lookAtTargetAvatar = avatarPointer; _targetAvatarPosition = avatarPointer->getPosition();