From 11fea386b68554138c48e7d408fa76c39b0afdc2 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 16 Oct 2013 14:07:36 -0700 Subject: [PATCH 1/4] First stab at chat circling mechanism. --- interface/src/avatar/MyAvatar.cpp | 77 +++++++++++++++++++++++++++++++ interface/src/avatar/MyAvatar.h | 1 + 2 files changed, 78 insertions(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 8cbcabe6e8..d4664168a7 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -351,6 +351,8 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter) { } } + updateChatCircle(deltaTime); + _position += _velocity * deltaTime; // Zero thrust out now that we've added it to velocity in this frame @@ -1092,6 +1094,81 @@ void MyAvatar::applyCollisionWithOtherAvatar(Avatar * otherAvatar, float deltaTi _velocity += bodyPushForce; } +void MyAvatar::updateChatCircle(float deltaTime) { + // find the number of members, their center of mass, and the average up vector + glm::vec3 center = _position; + glm::vec3 up = getWorldAlignedOrientation() * IDENTITY_UP; + int memberCount = 1; + NodeList* nodeList = NodeList::getInstance(); + const float MAX_CHAT_DISTANCE = 10.0f; + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { + if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) { + Avatar* otherAvatar = (Avatar*)node->getLinkedData(); + if (glm::distance(_position, otherAvatar->getPosition()) < MAX_CHAT_DISTANCE) { + center += otherAvatar->getPosition(); + up += otherAvatar->getWorldAlignedOrientation() * IDENTITY_UP; + memberCount++; + } + } + } + if (memberCount == 1) { + return; + } + center /= memberCount; + up = glm::normalize(up); + + // find reasonable corresponding right/front vectors + glm::vec3 front = glm::cross(up, IDENTITY_RIGHT); + if (glm::length(front) < EPSILON) { + front = glm::cross(up, IDENTITY_FRONT); + } + front = glm::normalize(front); + glm::vec3 right = glm::cross(front, up); + + // find our angle and the angular distances to our closest neighbors + glm::vec3 delta = _position - center; + glm::vec3 projected = glm::vec3(glm::dot(right, delta), glm::dot(front, delta), 0.0f); + float myAngle = glm::length(projected) > EPSILON ? atan2f(projected.y, projected.x) : 0.0f; + float leftDistance = PIf; + float rightDistance = PIf; + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { + if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) { + Avatar* otherAvatar = (Avatar*)node->getLinkedData(); + if (glm::distance(_position, otherAvatar->getPosition()) < MAX_CHAT_DISTANCE) { + delta = otherAvatar->getPosition() - center; + projected = glm::vec3(glm::dot(right, delta), glm::dot(front, delta), 0.0f); + float angle = glm::length(projected) > EPSILON ? atan2f(projected.y, projected.x) : 0.0f; + if (angle < myAngle) { + leftDistance = min(myAngle - angle, leftDistance); + rightDistance = min(PI_TIMES_TWO - (myAngle - angle), rightDistance); + + } else { + leftDistance = min(PI_TIMES_TWO - (angle - myAngle), leftDistance); + rightDistance = min(angle - myAngle, rightDistance); + } + } + } + } + + // determine the chat circle radius + const float CIRCUMFERENCE_PER_MEMBER = 2.0f; + float radius = (CIRCUMFERENCE_PER_MEMBER * memberCount) / PI_TIMES_TWO; + + // split the difference between our neighbors + float targetAngle = myAngle + (rightDistance - leftDistance) / 2.0f; + glm::vec3 targetPosition = center + (front * sinf(targetAngle) + right * cosf(targetAngle)) * radius; + + // face the center of the circle + glm::quat orientation = getOrientation(); + glm::quat targetOrientation = rotationBetween(orientation * IDENTITY_FRONT, center - targetPosition) * orientation; + targetOrientation = rotationBetween(targetOrientation * IDENTITY_UP, up) * targetOrientation; + + // approach the target position/orientation + const float APPROACH_RATE = 0.1f; + _position = glm::mix(_position, targetPosition, APPROACH_RATE); + setOrientation(safeMix(orientation, targetOrientation, APPROACH_RATE)); +} + void MyAvatar::setGravity(glm::vec3 gravity) { _gravity = gravity; _head.setGravity(_gravity); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 4dc04a978e..5e25a17fb8 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -94,6 +94,7 @@ private: void applyHardCollision(const glm::vec3& penetration, float elasticity, float damping); void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency); void applyCollisionWithOtherAvatar( Avatar * other, float deltaTime ); + void updateChatCircle(float deltaTime); void checkForMouseRayTouching(); }; From 50216ca4ade1e04cb22ceb5855c6b72bf4f8bc49 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 16 Oct 2013 15:37:59 -0700 Subject: [PATCH 2/4] Base the number of people we include in the circle on whether we would actually fall in its radius (plus an expansion factor). --- interface/src/avatar/MyAvatar.cpp | 92 +++++++++++++++++++------------ 1 file changed, 58 insertions(+), 34 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index d4664168a7..149aa504f5 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1094,27 +1094,60 @@ void MyAvatar::applyCollisionWithOtherAvatar(Avatar * otherAvatar, float deltaTi _velocity += bodyPushForce; } +class SortedAvatar { +public: + Avatar* avatar; + float distance; + glm::vec3 accumulatedCenter; +}; + +bool operator<(const SortedAvatar& s1, const SortedAvatar& s2) { + return s1.distance < s2.distance; +} + void MyAvatar::updateChatCircle(float deltaTime) { - // find the number of members, their center of mass, and the average up vector - glm::vec3 center = _position; - glm::vec3 up = getWorldAlignedOrientation() * IDENTITY_UP; - int memberCount = 1; + // find all members and sort by distance + QVector sortedAvatars; NodeList* nodeList = NodeList::getInstance(); - const float MAX_CHAT_DISTANCE = 10.0f; for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) { - Avatar* otherAvatar = (Avatar*)node->getLinkedData(); - if (glm::distance(_position, otherAvatar->getPosition()) < MAX_CHAT_DISTANCE) { - center += otherAvatar->getPosition(); - up += otherAvatar->getWorldAlignedOrientation() * IDENTITY_UP; - memberCount++; - } + SortedAvatar sortedAvatar; + sortedAvatar.avatar = (Avatar*)node->getLinkedData(); + sortedAvatar.distance = glm::distance(_position, sortedAvatar.avatar->getPosition()); + sortedAvatars.append(sortedAvatar); } } - if (memberCount == 1) { + qSort(sortedAvatars.begin(), sortedAvatars.end()); + + // compute the accumulated centers + glm::vec3 center = _position; + for (int i = 0; i < sortedAvatars.size(); i++) { + SortedAvatar& sortedAvatar = sortedAvatars[i]; + sortedAvatar.accumulatedCenter = (center += sortedAvatar.avatar->getPosition()) / (i + 2.0f); + } + + // remove members whose accumulated circles are too far away to influence us + const float CIRCUMFERENCE_PER_MEMBER = 2.0f; + const float CIRCLE_INFLUENCE_SCALE = 1.1f; + for (int i = sortedAvatars.size() - 1; i >= 0; i--) { + float radius = (CIRCUMFERENCE_PER_MEMBER * (i + 2)) / PI_TIMES_TWO; + if (glm::distance(_position, sortedAvatars[i].accumulatedCenter) > radius * CIRCLE_INFLUENCE_SCALE) { + sortedAvatars.remove(i); + } else { + break; + } + } + if (sortedAvatars.isEmpty()) { return; } - center /= memberCount; + center = sortedAvatars.last().accumulatedCenter; + float radius = (CIRCUMFERENCE_PER_MEMBER * (sortedAvatars.size() + 1)) / PI_TIMES_TWO; + + // compute the average up vector + glm::vec3 up = getWorldAlignedOrientation() * IDENTITY_UP; + foreach (const SortedAvatar& sortedAvatar, sortedAvatars) { + up += sortedAvatar.avatar->getWorldAlignedOrientation() * IDENTITY_UP; + } up = glm::normalize(up); // find reasonable corresponding right/front vectors @@ -1131,29 +1164,20 @@ void MyAvatar::updateChatCircle(float deltaTime) { float myAngle = glm::length(projected) > EPSILON ? atan2f(projected.y, projected.x) : 0.0f; float leftDistance = PIf; float rightDistance = PIf; - for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { - if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) { - Avatar* otherAvatar = (Avatar*)node->getLinkedData(); - if (glm::distance(_position, otherAvatar->getPosition()) < MAX_CHAT_DISTANCE) { - delta = otherAvatar->getPosition() - center; - projected = glm::vec3(glm::dot(right, delta), glm::dot(front, delta), 0.0f); - float angle = glm::length(projected) > EPSILON ? atan2f(projected.y, projected.x) : 0.0f; - if (angle < myAngle) { - leftDistance = min(myAngle - angle, leftDistance); - rightDistance = min(PI_TIMES_TWO - (myAngle - angle), rightDistance); - - } else { - leftDistance = min(PI_TIMES_TWO - (angle - myAngle), leftDistance); - rightDistance = min(angle - myAngle, rightDistance); - } - } + foreach (const SortedAvatar& sortedAvatar, sortedAvatars) { + delta = sortedAvatar.avatar->getPosition() - center; + projected = glm::vec3(glm::dot(right, delta), glm::dot(front, delta), 0.0f); + float angle = glm::length(projected) > EPSILON ? atan2f(projected.y, projected.x) : 0.0f; + if (angle < myAngle) { + leftDistance = min(myAngle - angle, leftDistance); + rightDistance = min(PI_TIMES_TWO - (myAngle - angle), rightDistance); + + } else { + leftDistance = min(PI_TIMES_TWO - (angle - myAngle), leftDistance); + rightDistance = min(angle - myAngle, rightDistance); } } - // determine the chat circle radius - const float CIRCUMFERENCE_PER_MEMBER = 2.0f; - float radius = (CIRCUMFERENCE_PER_MEMBER * memberCount) / PI_TIMES_TWO; - // split the difference between our neighbors float targetAngle = myAngle + (rightDistance - leftDistance) / 2.0f; glm::vec3 targetPosition = center + (front * sinf(targetAngle) + right * cosf(targetAngle)) * radius; @@ -1164,7 +1188,7 @@ void MyAvatar::updateChatCircle(float deltaTime) { targetOrientation = rotationBetween(targetOrientation * IDENTITY_UP, up) * targetOrientation; // approach the target position/orientation - const float APPROACH_RATE = 0.1f; + const float APPROACH_RATE = 0.05f; _position = glm::mix(_position, targetPosition, APPROACH_RATE); setOrientation(safeMix(orientation, targetOrientation, APPROACH_RATE)); } From 6df245908c702b107dbf993db39461581c91e755 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 16 Oct 2013 15:41:38 -0700 Subject: [PATCH 3/4] Reduce the approach rate. --- interface/src/avatar/MyAvatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 149aa504f5..10beec4a12 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1188,7 +1188,7 @@ void MyAvatar::updateChatCircle(float deltaTime) { targetOrientation = rotationBetween(targetOrientation * IDENTITY_UP, up) * targetOrientation; // approach the target position/orientation - const float APPROACH_RATE = 0.05f; + const float APPROACH_RATE = 0.01f; _position = glm::mix(_position, targetPosition, APPROACH_RATE); setOrientation(safeMix(orientation, targetOrientation, APPROACH_RATE)); } From 7a1f8451890626d5b673c91c34d5b836be6e23b3 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 16 Oct 2013 15:48:25 -0700 Subject: [PATCH 4/4] Another rate adjustment. --- interface/src/avatar/MyAvatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 10beec4a12..7fc625072e 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1188,7 +1188,7 @@ void MyAvatar::updateChatCircle(float deltaTime) { targetOrientation = rotationBetween(targetOrientation * IDENTITY_UP, up) * targetOrientation; // approach the target position/orientation - const float APPROACH_RATE = 0.01f; + const float APPROACH_RATE = 0.025f; _position = glm::mix(_position, targetPosition, APPROACH_RATE); setOrientation(safeMix(orientation, targetOrientation, APPROACH_RATE)); }