From d87bc3e898140cd494779b53a37b1d8e7c60b0f4 Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Mon, 31 Dec 2018 13:37:22 -0800 Subject: [PATCH 1/3] Update eye look at interest calculation This now takes distance, your facing toward them, and their facing toward you into the interest calculation. Also, separate the logic for myAvatar look at from other avatars snapping their gaze toward myAvatar. Previously these two separate elements where merged together in a single loop. --- interface/src/avatar/MyAvatar.cpp | 91 +++++++++++++------ interface/src/avatar/MyAvatar.h | 2 + .../src/avatars-renderer/Avatar.h | 4 - 3 files changed, 65 insertions(+), 32 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 397817cf60..e6b11ce9d4 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1565,35 +1565,61 @@ ScriptAvatarData* MyAvatar::getTargetAvatar() const { } } -void MyAvatar::updateLookAtTargetAvatar() { - // - // Look at the avatar whose eyes are closest to the ray in direction of my avatar's head - // And set the correctedLookAt for all (nearby) avatars that are looking at me. - _lookAtTargetAvatar.reset(); - _targetAvatarPosition = glm::vec3(0.0f); +static float lookAtCostFunction(const glm::vec3& myForward, const glm::vec3& myPosition, const glm::vec3& otherForward, const glm::vec3& otherPosition) { + const float DISTANCE_FACTOR = 3.14f; + const float MY_ANGLE_FACTOR = 1.0f; + const float OTHER_ANGLE_FACTOR = 1.0f; + const float GREATEST_LOOKING_AT_DISTANCE = 10.0f; // meters - glm::vec3 lookForward = getHead()->getFinalOrientationInWorldFrame() * IDENTITY_FORWARD; - glm::vec3 cameraPosition = qApp->getCamera().getPosition(); + glm::vec3 d = otherPosition - myPosition; + float distance = glm::length(d); + glm::vec3 dUnit = d / distance; + float myAngle = acosf(glm::dot(myForward, dUnit)); + float otherAngle = acosf(glm::dot(otherForward, -dUnit)); - float smallestAngleTo = glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES) / 2.0f; - const float KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR = 1.3f; - const float GREATEST_LOOKING_AT_DISTANCE = 10.0f; + if (distance > GREATEST_LOOKING_AT_DISTANCE) { + return FLT_MAX; + } else { + return DISTANCE_FACTOR * distance + MY_ANGLE_FACTOR * myAngle + OTHER_ANGLE_FACTOR * otherAngle; + } +} - AvatarHash hash = DependencyManager::get()->getHashCopy(); +void MyAvatar::computeMyLookAtTarget(const AvatarHash& hash) { + glm::vec3 myForward = getHead()->getFinalOrientationInWorldFrame() * IDENTITY_FORWARD; + glm::vec3 myPosition = qApp->getCamera().getPosition(); + float bestCost = FLT_MAX; + std::shared_ptr bestAvatar; - foreach (const AvatarSharedPointer& avatarPointer, hash) { - auto avatar = static_pointer_cast(avatarPointer); - bool isCurrentTarget = avatar->getIsLookAtTarget(); - float distanceTo = glm::length(avatar->getHead()->getEyePosition() - cameraPosition); - avatar->setIsLookAtTarget(false); - if (!avatar->isMyAvatar() && avatar->isInitialized() && - (distanceTo < GREATEST_LOOKING_AT_DISTANCE * getModelScale())) { - 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->getWorldPosition(); + foreach (const AvatarSharedPointer& avatarData, hash) { + std::shared_ptr avatar = std::static_pointer_cast(avatarData); + if (!avatar->isMyAvatar() && avatar->isInitialized()) { + glm::vec3 otherForward = avatar->getHead()->getForwardDirection(); + glm::vec3 otherPosition = avatar->getHead()->getEyePosition(); + float cost = lookAtCostFunction(myForward, myPosition, otherForward, otherPosition); + if (_lookAtTargetAvatar.lock().get() == avatar.get()) { + const float COST_HYSTERESIS = 0.1f; + cost += COST_HYSTERESIS; } + + if (cost < bestCost) { + bestCost = cost; + bestAvatar = avatar; + } + } + } + + if (bestAvatar) { + _lookAtTargetAvatar = bestAvatar; + _targetAvatarPosition = bestAvatar->getWorldPosition(); + } else { + _lookAtTargetAvatar.reset(); + } +} + +void MyAvatar::snapOtherAvatarLookAtTargetsToMe(const AvatarHash& hash) { + foreach (const AvatarSharedPointer& avatarData, hash) { + std::shared_ptr avatar = std::static_pointer_cast(avatarData); + if (!avatar->isMyAvatar() && avatar->isInitialized()) { if (_lookAtSnappingEnabled && avatar->getLookAtSnappingEnabled() && isLookingAtMe(avatar)) { // Alter their gaze to look directly at my camera; this looks more natural than looking at my avatar's face. @@ -1648,10 +1674,19 @@ void MyAvatar::updateLookAtTargetAvatar() { avatar->getHead()->clearCorrectedLookAtPosition(); } } - auto avatarPointer = _lookAtTargetAvatar.lock(); - if (avatarPointer) { - static_pointer_cast(avatarPointer)->setIsLookAtTarget(true); - } +} + +void MyAvatar::updateLookAtTargetAvatar() { + + // The AvatarManager is a mutable class shared by many threads. We make a thread-safe deep copy of it, + // to avoid having to hold a lock while we iterate over all the avatars within. + AvatarHash hash = DependencyManager::get()->getHashCopy(); + + // determine what the best look at target for my avatar should be. + computeMyLookAtTarget(hash); + + // snap look at position for avatars that are looking at me. + snapOtherAvatarLookAtTargetsToMe(hash); } void MyAvatar::clearLookAtTargetAvatar() { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index b2381366bb..9a399c43c7 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -832,6 +832,8 @@ public: AvatarWeakPointer getLookAtTargetAvatar() const { return _lookAtTargetAvatar; } void updateLookAtTargetAvatar(); + void computeMyLookAtTarget(const AvatarHash& hash); + void snapOtherAvatarLookAtTargetsToMe(const AvatarHash& hash); void clearLookAtTargetAvatar(); virtual void setJointRotations(const QVector& jointRotations) override; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 8f70b12122..1164e0902a 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -156,9 +156,6 @@ public: virtual void postUpdate(float deltaTime, const render::ScenePointer& scene); - //setters - void setIsLookAtTarget(const bool isLookAtTarget) { _isLookAtTarget = isLookAtTarget; } - bool getIsLookAtTarget() const { return _isLookAtTarget; } //getters bool isInitialized() const { return _initialized; } SkeletonModelPointer getSkeletonModel() { return _skeletonModel; } @@ -592,7 +589,6 @@ protected: int _rightPointerGeometryID { 0 }; int _nameRectGeometryID { 0 }; bool _initialized { false }; - bool _isLookAtTarget { false }; bool _isAnimatingScale { false }; bool _mustFadeIn { false }; bool _isFading { false }; From 71991c2e2f1eed2b0d985791efcdcf37d0e8cf17 Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Wed, 2 Jan 2019 11:05:01 -0800 Subject: [PATCH 2/3] Added max angles and isTalking into the lookAtCostFunction --- interface/src/avatar/MyAvatar.cpp | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e6b11ce9d4..e168e182e9 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1565,11 +1565,17 @@ ScriptAvatarData* MyAvatar::getTargetAvatar() const { } } -static float lookAtCostFunction(const glm::vec3& myForward, const glm::vec3& myPosition, const glm::vec3& otherForward, const glm::vec3& otherPosition) { +static float lookAtCostFunction(const glm::vec3& myForward, const glm::vec3& myPosition, const glm::vec3& otherForward, const glm::vec3& otherPosition, + bool otherIsTalking, bool lookingAtOtherAlready) { const float DISTANCE_FACTOR = 3.14f; const float MY_ANGLE_FACTOR = 1.0f; const float OTHER_ANGLE_FACTOR = 1.0f; + const float OTHER_IS_TALKING_TERM = otherIsTalking ? 1.0f : 0.0f; + const float LOOKING_AT_OTHER_ALREADY_TERM = lookingAtOtherAlready ? -0.2f : 0.0f; + const float GREATEST_LOOKING_AT_DISTANCE = 10.0f; // meters + const float MAX_MY_ANGLE = PI / 8.0f; // 22.5 degrees, Don't look too far away from the head facing. + const float MAX_OTHER_ANGLE = (3.0f * PI) / 4.0f; // 135 degrees, Don't stare at the back of another avatars head. glm::vec3 d = otherPosition - myPosition; float distance = glm::length(d); @@ -1577,10 +1583,14 @@ static float lookAtCostFunction(const glm::vec3& myForward, const glm::vec3& myP float myAngle = acosf(glm::dot(myForward, dUnit)); float otherAngle = acosf(glm::dot(otherForward, -dUnit)); - if (distance > GREATEST_LOOKING_AT_DISTANCE) { + if (distance > GREATEST_LOOKING_AT_DISTANCE || myAngle > MAX_MY_ANGLE || otherAngle > MAX_OTHER_ANGLE) { return FLT_MAX; } else { - return DISTANCE_FACTOR * distance + MY_ANGLE_FACTOR * myAngle + OTHER_ANGLE_FACTOR * otherAngle; + return (DISTANCE_FACTOR * distance + + MY_ANGLE_FACTOR * myAngle + + OTHER_ANGLE_FACTOR * otherAngle + + OTHER_IS_TALKING_TERM + + LOOKING_AT_OTHER_ALREADY_TERM); } } @@ -1595,12 +1605,10 @@ void MyAvatar::computeMyLookAtTarget(const AvatarHash& hash) { if (!avatar->isMyAvatar() && avatar->isInitialized()) { glm::vec3 otherForward = avatar->getHead()->getForwardDirection(); glm::vec3 otherPosition = avatar->getHead()->getEyePosition(); - float cost = lookAtCostFunction(myForward, myPosition, otherForward, otherPosition); - if (_lookAtTargetAvatar.lock().get() == avatar.get()) { - const float COST_HYSTERESIS = 0.1f; - cost += COST_HYSTERESIS; - } - + const float TIME_WITHOUT_TALKING_THRESHOLD = 1.0f; + bool otherIsTalking = avatar->getHead()->getTimeWithoutTalking() <= TIME_WITHOUT_TALKING_THRESHOLD; + bool lookingAtOtherAlready = _lookAtTargetAvatar.lock().get() == avatar.get(); + float cost = lookAtCostFunction(myForward, myPosition, otherForward, otherPosition, otherIsTalking, lookingAtOtherAlready); if (cost < bestCost) { bestCost = cost; bestAvatar = avatar; From 7e68f539d323bccfe81e52d880a6db3295c5ac53 Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Thu, 3 Jan 2019 15:27:44 -0800 Subject: [PATCH 3/3] Only use the camera position for lookAt in first person. --- interface/src/avatar/MyAvatar.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 17361c9e5d..aecfb687e4 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1562,7 +1562,12 @@ static float lookAtCostFunction(const glm::vec3& myForward, const glm::vec3& myP void MyAvatar::computeMyLookAtTarget(const AvatarHash& hash) { glm::vec3 myForward = getHead()->getFinalOrientationInWorldFrame() * IDENTITY_FORWARD; - glm::vec3 myPosition = qApp->getCamera().getPosition(); + glm::vec3 myPosition = getHead()->getEyePosition(); + CameraMode mode = qApp->getCamera().getMode(); + if (mode == CAMERA_MODE_FIRST_PERSON) { + myPosition = qApp->getCamera().getPosition(); + } + float bestCost = FLT_MAX; std::shared_ptr bestAvatar;