From 9cb53af023228cdb4cebcf70c216ea35eaac357e Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 19 Dec 2018 16:13:12 -0800 Subject: [PATCH 1/7] Correct set avatar-scale timestamp when changing in Avatar class --- libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index b4ea9c20f9..c05b79b8d0 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -295,6 +295,7 @@ void Avatar::setTargetScale(float targetScale) { if (_targetScale != newValue) { _targetScale = newValue; _scaleChanged = usecTimestampNow(); + _avatarScaleChanged = _scaleChanged; _isAnimatingScale = true; emit targetScaleChanged(targetScale); From a79bf783d2445c0616c81d64448dfd40cef6f5fd Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 17 Dec 2018 11:07:45 -0800 Subject: [PATCH 2/7] Fix MyAvatar::collisionWithEntity not firing --- interface/src/avatar/AvatarManager.cpp | 76 ++++++++++++++------------ 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 7ca18ca258..8583a8a1d1 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -28,7 +28,6 @@ #pragma GCC diagnostic pop #endif - #include #include #include @@ -529,6 +528,7 @@ void AvatarManager::handleChangedMotionStates(const VectorOfMotionStates& motion } void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents) { + bool playedCollisionSound { false }; for (Collision collision : collisionEvents) { // TODO: The plan is to handle MOTIONSTATE_TYPE_AVATAR, and then MOTIONSTATE_TYPE_MYAVATAR. As it is, other // people's avatars will have an id that doesn't match any entities, and one's own avatar will have @@ -536,43 +536,47 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents // my avatar. (Other user machines will make a similar analysis and inject sound for their collisions.) if (collision.idA.isNull() || collision.idB.isNull()) { auto myAvatar = getMyAvatar(); - auto collisionSound = myAvatar->getCollisionSound(); - if (collisionSound) { - const auto characterController = myAvatar->getCharacterController(); - const float avatarVelocityChange = (characterController ? glm::length(characterController->getVelocityChange()) : 0.0f); - const float velocityChange = glm::length(collision.velocityChange) + avatarVelocityChange; - const float MIN_AVATAR_COLLISION_ACCELERATION = 2.4f; // walking speed - const bool isSound = (collision.type == CONTACT_EVENT_TYPE_START) && (velocityChange > MIN_AVATAR_COLLISION_ACCELERATION); + myAvatar->collisionWithEntity(collision); - if (!isSound) { - return; // No sense iterating for others. We only have one avatar. + if (!playedCollisionSound) { + playedCollisionSound = true; + auto collisionSound = myAvatar->getCollisionSound(); + if (collisionSound) { + const auto characterController = myAvatar->getCharacterController(); + const float avatarVelocityChange = + (characterController ? glm::length(characterController->getVelocityChange()) : 0.0f); + const float velocityChange = glm::length(collision.velocityChange) + avatarVelocityChange; + const float MIN_AVATAR_COLLISION_ACCELERATION = 2.4f; // walking speed + const bool isSound = + (collision.type == CONTACT_EVENT_TYPE_START) && (velocityChange > MIN_AVATAR_COLLISION_ACCELERATION); + + if (!isSound) { + return; // No sense iterating for others. We only have one avatar. + } + // Your avatar sound is personal to you, so let's say the "mass" part of the kinetic energy is already accounted for. + const float energy = velocityChange * velocityChange; + const float COLLISION_ENERGY_AT_FULL_VOLUME = 10.0f; + const float energyFactorOfFull = fmin(1.0f, energy / COLLISION_ENERGY_AT_FULL_VOLUME); + + // For general entity collisionSoundURL, playSound supports changing the pitch for the sound based on the size of the object, + // but most avatars are roughly the same size, so let's not be so fancy yet. + const float AVATAR_STRETCH_FACTOR = 1.0f; + + _collisionInjectors.remove_if( + [](const AudioInjectorPointer& injector) { return !injector || injector->isFinished(); }); + + static const int MAX_INJECTOR_COUNT = 3; + if (_collisionInjectors.size() < MAX_INJECTOR_COUNT) { + AudioInjectorOptions options; + options.stereo = collisionSound->isStereo(); + options.position = myAvatar->getWorldPosition(); + options.volume = energyFactorOfFull; + options.pitch = 1.0f / AVATAR_STRETCH_FACTOR; + + auto injector = AudioInjector::playSoundAndDelete(collisionSound, options); + _collisionInjectors.emplace_back(injector); + } } - // Your avatar sound is personal to you, so let's say the "mass" part of the kinetic energy is already accounted for. - const float energy = velocityChange * velocityChange; - const float COLLISION_ENERGY_AT_FULL_VOLUME = 10.0f; - const float energyFactorOfFull = fmin(1.0f, energy / COLLISION_ENERGY_AT_FULL_VOLUME); - - // For general entity collisionSoundURL, playSound supports changing the pitch for the sound based on the size of the object, - // but most avatars are roughly the same size, so let's not be so fancy yet. - const float AVATAR_STRETCH_FACTOR = 1.0f; - - _collisionInjectors.remove_if([](const AudioInjectorPointer& injector) { - return !injector || injector->isFinished(); - }); - - static const int MAX_INJECTOR_COUNT = 3; - if (_collisionInjectors.size() < MAX_INJECTOR_COUNT) { - AudioInjectorOptions options; - options.stereo = collisionSound->isStereo(); - options.position = myAvatar->getWorldPosition(); - options.volume = energyFactorOfFull; - options.pitch = 1.0f / AVATAR_STRETCH_FACTOR; - - auto injector = AudioInjector::playSoundAndDelete(collisionSound, options); - _collisionInjectors.emplace_back(injector); - } - myAvatar->collisionWithEntity(collision); - return; } } } From d87bc3e898140cd494779b53a37b1d8e7c60b0f4 Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Mon, 31 Dec 2018 13:37:22 -0800 Subject: [PATCH 3/7] 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 4/7] 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 fe1a8a6b765103d97843a0b3b8c0389337213b10 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 3 Jan 2019 11:46:03 -0800 Subject: [PATCH 5/7] omit unknown device names from clear story --- libraries/networking/src/UserActivityLogger.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index c05daf0217..e743518965 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -139,11 +139,10 @@ void UserActivityLogger::connectedDevice(QString typeOfDevice, QString deviceNam "NullDisplayPlugin", "3D TV - Side by Side Stereo", "3D TV - Interleaved", - "Keyboard/Mouse" }; - if (DEVICE_BLACKLIST.contains(deviceName)) { + if (DEVICE_BLACKLIST.contains(deviceName) || deviceName.isEmpty()) { return; } @@ -151,7 +150,7 @@ void UserActivityLogger::connectedDevice(QString typeOfDevice, QString deviceNam QJsonObject actionDetails; const QString TYPE_OF_DEVICE = "type_of_device"; const QString DEVICE_NAME = "device_name"; - + qDebug() << "UserActivityLogger::connectedDevice -> type: " << typeOfDevice << " - deviceName: " << deviceName; actionDetails.insert(TYPE_OF_DEVICE, typeOfDevice); actionDetails.insert(DEVICE_NAME, deviceName); From f5de217b4c72cafc05c31c1eea29e9bf6cca9fc2 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 3 Jan 2019 13:40:44 -0800 Subject: [PATCH 6/7] removing debug statment --- libraries/networking/src/UserActivityLogger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index e743518965..948acd3d78 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -150,7 +150,7 @@ void UserActivityLogger::connectedDevice(QString typeOfDevice, QString deviceNam QJsonObject actionDetails; const QString TYPE_OF_DEVICE = "type_of_device"; const QString DEVICE_NAME = "device_name"; - qDebug() << "UserActivityLogger::connectedDevice -> type: " << typeOfDevice << " - deviceName: " << deviceName; + actionDetails.insert(TYPE_OF_DEVICE, typeOfDevice); actionDetails.insert(DEVICE_NAME, deviceName); From 7e68f539d323bccfe81e52d880a6db3295c5ac53 Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Thu, 3 Jan 2019 15:27:44 -0800 Subject: [PATCH 7/7] 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;