From db1acb87b8b2d6dcfa95bb526031947652f84ae6 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Thu, 13 Sep 2018 15:38:24 -0700 Subject: [PATCH 1/8] button was clipped; adding higher res img --- .../images/interstitialPage/goTo_button.png | Bin 0 -> 9189 bytes scripts/system/interstitialPage.js | 10 +++++----- 2 files changed, 5 insertions(+), 5 deletions(-) create mode 100644 interface/resources/images/interstitialPage/goTo_button.png diff --git a/interface/resources/images/interstitialPage/goTo_button.png b/interface/resources/images/interstitialPage/goTo_button.png new file mode 100644 index 0000000000000000000000000000000000000000..7c1b0d85003f4757522ee820d6bef0cff0561f6c GIT binary patch literal 9189 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 P{!7$S*Lzf^W)ty$o<}NJ literal 0 HcmV?d00001 diff --git a/scripts/system/interstitialPage.js b/scripts/system/interstitialPage.js index dcce721cd9..f47468bb40 100644 --- a/scripts/system/interstitialPage.js +++ b/scripts/system/interstitialPage.js @@ -137,10 +137,10 @@ var loadingToTheSpotID = Overlays.addOverlay("image3d", { name: "Loading-Destination-Card-Text", - localPosition: { x: 0.0 , y: -1.8, z: 0.0 }, - url: "http://hifi-content.s3.amazonaws.com/alexia/LoadingScreens/goTo_button.png", + localPosition: { x: 0.0 , y: -1.5, z: -0.3 }, + url: Script.resourcesPath() + "interstitialPage/goTo_button.png", alpha: 1, - dimensions: { x: 1.2, y: 0.6}, + dimensions: { x: 1.5, y: 1.0 }, visible: isVisible, emissive: true, ignoreRayIntersection: false, @@ -415,13 +415,13 @@ Overlays.mouseReleaseOnOverlay.connect(clickedOnOverlay); Overlays.hoverEnterOverlay.connect(function(overlayID, event) { if (overlayID === loadingToTheSpotID) { - Overlays.editOverlay(loadingToTheSpotID, { color: greyColor}); + Overlays.editOverlay(loadingToTheSpotID, { color: greyColor }); } }); Overlays.hoverLeaveOverlay.connect(function(overlayID, event) { if (overlayID === loadingToTheSpotID) { - Overlays.editOverlay(loadingToTheSpotID, { color: whiteColor}); + Overlays.editOverlay(loadingToTheSpotID, { color: whiteColor }); } }); From b49e3377b67ca58c239a475beeb0392ffd63ddc3 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Thu, 13 Sep 2018 15:40:01 -0700 Subject: [PATCH 2/8] Revert "adding fix for getting DomainHandler into error state" This reverts commit 49f26d968a4349d3665b40eaa552943b6ed4237a. --- libraries/networking/src/DomainHandler.cpp | 15 +++++---------- libraries/networking/src/DomainHandler.h | 5 ++--- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index a432b41a0d..59e3de922f 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -55,9 +55,6 @@ DomainHandler::DomainHandler(QObject* parent) : // stop the refresh timer if we connect to a domain connect(this, &DomainHandler::connectedToDomain, &_apiRefreshTimer, &QTimer::stop); - - // stop the refresh timer if we connect to a domain - connect(this, &DomainHandler::redirectToErrorDomainURL, &_apiRefreshTimer, &QTimer::stop); } void DomainHandler::disconnect() { @@ -102,6 +99,7 @@ void DomainHandler::softReset() { clearSettings(); + _isInErrorState = false; _connectionDenialsSinceKeypairRegen = 0; _checkInPacketsSinceLastReply = 0; @@ -109,14 +107,11 @@ void DomainHandler::softReset() { QMetaObject::invokeMethod(&_settingsTimer, "stop"); // restart the API refresh timer in case we fail to connect and need to refresh information - if (!_isInErrorState) - QMetaObject::invokeMethod(&_apiRefreshTimer, "start"); - _isInErrorState = false; + QMetaObject::invokeMethod(&_apiRefreshTimer, "start"); } void DomainHandler::hardReset() { - if (!_isInErrorState) - emit resetting(); + emit resetting(); softReset(); @@ -343,7 +338,6 @@ void DomainHandler::loadedErrorDomain(std::map namedPaths) { void DomainHandler::setRedirectErrorState(QUrl errorUrl, int reasonCode) { _errorDomainURL = errorUrl; _lastDomainConnectionError = reasonCode; - _isInErrorState = true; emit redirectToErrorDomainURL(_errorDomainURL); } @@ -486,8 +480,9 @@ void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer namedPaths); void loadedErrorDomain(std::map namedPaths); + // sets domain handler in error state. + void setRedirectErrorState(QUrl errorUrl, int reasonCode); QString getViewPointFromNamedPath(QString namedPath); @@ -170,9 +172,6 @@ public slots: void processICEResponsePacket(QSharedPointer icePacket); void processDomainServerConnectionDeniedPacket(QSharedPointer message); - // sets domain handler in error state. - void setRedirectErrorState(QUrl errorUrl, int reasonCode); - private slots: void completedHostnameLookup(const QHostInfo& hostInfo); void completedIceServerHostnameLookup(); From 77d5ebf34f180a9c17e87c072a6198805e98df2a Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Thu, 13 Sep 2018 15:47:25 -0700 Subject: [PATCH 3/8] changing button URL - typo --- scripts/system/interstitialPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/interstitialPage.js b/scripts/system/interstitialPage.js index f47468bb40..57726f397b 100644 --- a/scripts/system/interstitialPage.js +++ b/scripts/system/interstitialPage.js @@ -138,7 +138,7 @@ var loadingToTheSpotID = Overlays.addOverlay("image3d", { name: "Loading-Destination-Card-Text", localPosition: { x: 0.0 , y: -1.5, z: -0.3 }, - url: Script.resourcesPath() + "interstitialPage/goTo_button.png", + url: Script.resourcesPath() + "images/interstitialPage/goTo_button.png", alpha: 1, dimensions: { x: 1.5, y: 1.0 }, visible: isVisible, From c5468fd8e9169cf97a814e3db2db6d66ac1549ed Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Thu, 13 Sep 2018 17:18:49 -0700 Subject: [PATCH 4/8] Revert "Merge branch 'master' into fixGoToButtonInterstitial" This reverts commit b4b0c519f39ab543f77dc6e88ba5cb954a2b45e6, reversing changes made to 77d5ebf34f180a9c17e87c072a6198805e98df2a. --- .../src/avatars/AvatarMixerClientData.cpp | 22 +- .../src/avatars/AvatarMixerClientData.h | 13 +- .../src/avatars/AvatarMixerSlave.cpp | 211 ++++++++++-------- .../meshes/redirect/oopsDialog_timeout.fbx | Bin 31904 -> 0 bytes .../meshes/redirect/oopsDialog_timeout.png | Bin 4555 -> 0 bytes .../meshes/redirect/oopsDialog_vague.fbx | Bin 32448 -> 32480 bytes .../qml/LoginDialog/LinkAccountBody.qml | 25 +-- interface/src/Application.cpp | 14 +- interface/src/avatar/AvatarManager.cpp | 40 ++-- interface/src/octree/SafeLanding.cpp | 30 +-- libraries/avatars/src/AvatarData.cpp | 200 ++++++++++------- libraries/avatars/src/AvatarData.h | 4 +- libraries/networking/src/AccountManager.cpp | 7 + libraries/networking/src/AccountManager.h | 1 + libraries/networking/src/AddressManager.cpp | 4 +- libraries/networking/src/DomainHandler.cpp | 7 +- libraries/networking/src/DomainHandler.h | 5 - libraries/networking/src/udt/Socket.cpp | 92 +++----- libraries/networking/src/udt/Socket.h | 14 -- libraries/physics/src/PhysicsEngine.cpp | 1 - libraries/shared/src/PrioritySortUtil.h | 77 +++++-- scripts/defaultScripts.js | 5 +- scripts/system/edit.js | 4 +- .../system/libraries/entitySelectionTool.js | 168 +++++--------- 24 files changed, 458 insertions(+), 486 deletions(-) delete mode 100644 interface/resources/meshes/redirect/oopsDialog_timeout.fbx delete mode 100644 interface/resources/meshes/redirect/oopsDialog_timeout.png diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 6c01e6e02b..f524c071ec 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -11,7 +11,6 @@ #include "AvatarMixerClientData.h" -#include #include #include @@ -219,10 +218,6 @@ uint16_t AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& node } void AvatarMixerClientData::ignoreOther(SharedNodePointer self, SharedNodePointer other) { - ignoreOther(self.data(), other.data()); -} - -void AvatarMixerClientData::ignoreOther(const Node* self, const Node* other) { if (!isRadiusIgnoring(other->getUUID())) { addToRadiusIgnoringSet(other->getUUID()); auto killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason), true); @@ -240,20 +235,9 @@ void AvatarMixerClientData::ignoreOther(const Node* self, const Node* other) { } } -bool AvatarMixerClientData::isRadiusIgnoring(const QUuid& other) const { - return std::find(_radiusIgnoredOthers.cbegin(), _radiusIgnoredOthers.cend(), other) != _radiusIgnoredOthers.cend(); -} - -void AvatarMixerClientData::addToRadiusIgnoringSet(const QUuid& other) { - if (!isRadiusIgnoring(other)) { - _radiusIgnoredOthers.push_back(other); - } -} - -void AvatarMixerClientData::removeFromRadiusIgnoringSet(const QUuid& other) { - auto ignoredOtherIter = std::find(_radiusIgnoredOthers.cbegin(), _radiusIgnoredOthers.cend(), other); - if (ignoredOtherIter != _radiusIgnoredOthers.cend()) { - _radiusIgnoredOthers.erase(ignoredOtherIter); +void AvatarMixerClientData::removeFromRadiusIgnoringSet(SharedNodePointer self, const QUuid& other) { + if (isRadiusIgnoring(other)) { + _radiusIgnoredOthers.erase(other); } } diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index d38a90ef1f..a892455fe3 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include @@ -45,7 +45,6 @@ public: int parseData(ReceivedMessage& message) override; AvatarData& getAvatar() { return *_avatar; } - const AvatarData& getAvatar() const { return *_avatar; } const AvatarData* getConstAvatarData() const { return _avatar.get(); } AvatarSharedPointer getAvatarSharedPointer() const { return _avatar; } @@ -91,11 +90,11 @@ public: void loadJSONStats(QJsonObject& jsonObject) const; glm::vec3 getPosition() const { return _avatar ? _avatar->getWorldPosition() : glm::vec3(0); } - bool isRadiusIgnoring(const QUuid& other) const; - void addToRadiusIgnoringSet(const QUuid& other); - void removeFromRadiusIgnoringSet(const QUuid& other); + glm::vec3 getGlobalBoundingBoxCorner() const { return _avatar ? _avatar->getGlobalBoundingBoxCorner() : glm::vec3(0); } + bool isRadiusIgnoring(const QUuid& other) const { return _radiusIgnoredOthers.find(other) != _radiusIgnoredOthers.end(); } + void addToRadiusIgnoringSet(const QUuid& other) { _radiusIgnoredOthers.insert(other); } + void removeFromRadiusIgnoringSet(SharedNodePointer self, const QUuid& other); void ignoreOther(SharedNodePointer self, SharedNodePointer other); - void ignoreOther(const Node* self, const Node* other); void readViewFrustumPacket(const QByteArray& message); @@ -167,7 +166,7 @@ private: int _numOutOfOrderSends = 0; SimpleMovingAverage _avgOtherAvatarDataRate; - std::vector _radiusIgnoredOthers; + std::unordered_set _radiusIgnoredOthers; ConicalViewFrustums _currentViewFrustums; int _recentOtherAvatarsInView { 0 }; diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 7368db0c31..c434d82116 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -13,7 +13,6 @@ #include #include -#include #include #include @@ -34,8 +33,6 @@ #include "AvatarMixer.h" #include "AvatarMixerClientData.h" -namespace chrono = std::chrono; - void AvatarMixerSlave::configure(ConstIter begin, ConstIter end) { _begin = begin; _end = end; @@ -212,18 +209,7 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { _stats.jobElapsedTime += (end - start); } -AABox computeBubbleBox(const AvatarData& avatar, float bubbleExpansionFactor) { - AABox box = avatar.getGlobalBoundingBox(); - glm::vec3 scale = box.getScale(); - scale *= bubbleExpansionFactor; - const glm::vec3 MIN_BUBBLE_SCALE(0.3f, 1.3f, 0.3); - scale = glm::max(scale, MIN_BUBBLE_SCALE); - box.setScaleStayCentered(glm::max(scale, MIN_BUBBLE_SCALE)); - return box; -} - void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) { - const Node* destinationNode = node.data(); auto nodeList = DependencyManager::get(); @@ -234,7 +220,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) _stats.nodesBroadcastedTo++; - AvatarMixerClientData* nodeData = reinterpret_cast(destinationNode->getLinkedData()); + AvatarMixerClientData* nodeData = reinterpret_cast(node->getLinkedData()); nodeData->resetInViewStats(); @@ -256,8 +242,12 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) int traitBytesSent = 0; // max number of avatarBytes per frame - int maxAvatarBytesPerFrame = int(_maxKbpsPerNode * BYTES_PER_KILOBIT / AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND); + auto maxAvatarBytesPerFrame = (_maxKbpsPerNode * BYTES_PER_KILOBIT) / AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND; + // FIXME - find a way to not send the sessionID for every avatar + int minimumBytesPerAvatar = AvatarDataPacket::AVATAR_HAS_FLAGS_SIZE + NUM_BYTES_RFC4122_UUID; + + int overBudgetAvatars = 0; // keep track of the number of other avatars held back in this frame int numAvatarsHeldBack = 0; @@ -270,38 +260,66 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) bool PALIsOpen = nodeData->getRequestsDomainListData(); // When this is true, the AvatarMixer will send Avatar data to a client about avatars that have ignored them - bool getsAnyIgnored = PALIsOpen && destinationNode->getCanKick(); + bool getsAnyIgnored = PALIsOpen && node->getCanKick(); - // Bandwidth allowance for data that must be sent. - int minimumBytesPerAvatar = PALIsOpen ? AvatarDataPacket::AVATAR_HAS_FLAGS_SIZE + NUM_BYTES_RFC4122_UUID + - sizeof(AvatarDataPacket::AvatarGlobalPosition) + sizeof(AvatarDataPacket::AudioLoudness) : 0; + if (PALIsOpen) { + // Increase minimumBytesPerAvatar if the PAL is open + minimumBytesPerAvatar += sizeof(AvatarDataPacket::AvatarGlobalPosition) + + sizeof(AvatarDataPacket::AudioLoudness); + } // setup a PacketList for the avatarPackets auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData); - static auto maxAvatarDataBytes = avatarPacketList->getMaxSegmentSize() - NUM_BYTES_RFC4122_UUID; - // compute node bounding box - const float MY_AVATAR_BUBBLE_EXPANSION_FACTOR = 4.0f; // magic number determined emperically - AABox nodeBox = computeBubbleBox(avatar, MY_AVATAR_BUBBLE_EXPANSION_FACTOR); + // Define the minimum bubble size + static const glm::vec3 minBubbleSize = avatar.getSensorToWorldScale() * glm::vec3(0.3f, 1.3f, 0.3f); + // Define the scale of the box for the current node + glm::vec3 nodeBoxScale = (nodeData->getPosition() - nodeData->getGlobalBoundingBoxCorner()) * 2.0f * avatar.getSensorToWorldScale(); + // Set up the bounding box for the current node + AABox nodeBox(nodeData->getGlobalBoundingBoxCorner(), nodeBoxScale); + // Clamp the size of the bounding box to a minimum scale + if (glm::any(glm::lessThan(nodeBoxScale, minBubbleSize))) { + nodeBox.setScaleStayCentered(minBubbleSize); + } + // Quadruple the scale of both bounding boxes + nodeBox.embiggen(4.0f); + + + // setup list of AvatarData as well as maps to map betweeen the AvatarData and the original nodes + std::vector avatarsToSort; + std::unordered_map avatarDataToNodes; + std::unordered_map avatarEncodeTimes; + std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) { + // make sure this is an agent that we have avatar data for before considering it for inclusion + if (otherNode->getType() == NodeType::Agent + && otherNode->getLinkedData()) { + const AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); + + AvatarSharedPointer otherAvatar = otherNodeData->getAvatarSharedPointer(); + avatarsToSort.push_back(otherAvatar); + avatarDataToNodes[otherAvatar] = otherNode; + QUuid id = otherAvatar->getSessionUUID(); + avatarEncodeTimes[id] = nodeData->getLastOtherAvatarEncodeTime(id); + } + }); class SortableAvatar: public PrioritySortUtil::Sortable { public: SortableAvatar() = delete; - SortableAvatar(const AvatarData* avatar, const Node* avatarNode, uint64_t lastEncodeTime) - : _avatar(avatar), _node(avatarNode), _lastEncodeTime(lastEncodeTime) {} - glm::vec3 getPosition() const override { return _avatar->getClientGlobalPosition(); } + SortableAvatar(const AvatarSharedPointer& avatar, uint64_t lastEncodeTime) + : _avatar(avatar), _lastEncodeTime(lastEncodeTime) {} + glm::vec3 getPosition() const override { return _avatar->getWorldPosition(); } float getRadius() const override { - glm::vec3 nodeBoxScale = _avatar->getGlobalBoundingBox().getScale(); - return 0.5f * glm::max(nodeBoxScale.x, glm::max(nodeBoxScale.y, nodeBoxScale.z)); + glm::vec3 nodeBoxHalfScale = (_avatar->getWorldPosition() - _avatar->getGlobalBoundingBoxCorner() * _avatar->getSensorToWorldScale()); + return glm::max(nodeBoxHalfScale.x, glm::max(nodeBoxHalfScale.y, nodeBoxHalfScale.z)); } uint64_t getTimestamp() const override { return _lastEncodeTime; } - const Node* getNode() const { return _node; } + AvatarSharedPointer getAvatar() const { return _avatar; } private: - const AvatarData* _avatar; - const Node* _node; + AvatarSharedPointer _avatar; uint64_t _lastEncodeTime; }; @@ -311,18 +329,16 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) AvatarData::_avatarSortCoefficientSize, AvatarData::_avatarSortCoefficientCenter, AvatarData::_avatarSortCoefficientAge); - sortedAvatars.reserve(_end - _begin); + sortedAvatars.reserve(avatarsToSort.size()); - for (auto listedNode = _begin; listedNode != _end; ++listedNode) { - Node* otherNodeRaw = (*listedNode).data(); - if (otherNodeRaw->getType() != NodeType::Agent - || !otherNodeRaw->getLinkedData() - || otherNodeRaw == destinationNode) { + // ignore or sort + const AvatarSharedPointer& thisAvatar = nodeData->getAvatarSharedPointer(); + for (const auto& avatar : avatarsToSort) { + if (avatar == thisAvatar) { + // don't echo updates to self continue; } - auto avatarNode = otherNodeRaw; - bool shouldIgnore = false; // We ignore other nodes for a couple of reasons: // 1) ignore bubbles and ignore specific node @@ -330,39 +346,53 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // happen if for example the avatar is connected on a desktop and sending // updates at ~30hz. So every 3 frames we skip a frame. + auto avatarNode = avatarDataToNodes[avatar]; assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map - const AvatarMixerClientData* avatarClientNodeData = reinterpret_cast(avatarNode->getLinkedData()); - assert(avatarClientNodeData); // we can't have gotten here without avatarNode having valid data + const AvatarMixerClientData* avatarNodeData = reinterpret_cast(avatarNode->getLinkedData()); + assert(avatarNodeData); // we can't have gotten here without avatarNode having valid data quint64 startIgnoreCalculation = usecTimestampNow(); // make sure we have data for this avatar, that it isn't the same node, // and isn't an avatar that the viewing node has ignored // or that has ignored the viewing node - if ((destinationNode->isIgnoringNodeWithID(avatarNode->getUUID()) && !PALIsOpen) - || (avatarNode->isIgnoringNodeWithID(destinationNode->getUUID()) && !getsAnyIgnored)) { + if (!avatarNode->getLinkedData() + || avatarNode->getUUID() == node->getUUID() + || (node->isIgnoringNodeWithID(avatarNode->getUUID()) && !PALIsOpen) + || (avatarNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) { shouldIgnore = true; } else { // Check to see if the space bubble is enabled // Don't bother with these checks if the other avatar has their bubble enabled and we're gettingAnyIgnored - if (destinationNode->isIgnoreRadiusEnabled() || (avatarNode->isIgnoreRadiusEnabled() && !getsAnyIgnored)) { + if (node->isIgnoreRadiusEnabled() || (avatarNode->isIgnoreRadiusEnabled() && !getsAnyIgnored)) { + float sensorToWorldScale = avatarNodeData->getAvatarSharedPointer()->getSensorToWorldScale(); + // Define the scale of the box for the current other node + glm::vec3 otherNodeBoxScale = (avatarNodeData->getPosition() - avatarNodeData->getGlobalBoundingBoxCorner()) * 2.0f * sensorToWorldScale; + // Set up the bounding box for the current other node + AABox otherNodeBox(avatarNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); + // Clamp the size of the bounding box to a minimum scale + if (glm::any(glm::lessThan(otherNodeBoxScale, minBubbleSize))) { + otherNodeBox.setScaleStayCentered(minBubbleSize); + } + // Change the scale of both bounding boxes + // (This is an arbitrary number determined empirically) + otherNodeBox.embiggen(2.4f); + // Perform the collision check between the two bounding boxes - const float OTHER_AVATAR_BUBBLE_EXPANSION_FACTOR = 2.4f; // magic number determined empirically - AABox otherNodeBox = computeBubbleBox(avatarClientNodeData->getAvatar(), OTHER_AVATAR_BUBBLE_EXPANSION_FACTOR); if (nodeBox.touches(otherNodeBox)) { - nodeData->ignoreOther(destinationNode, avatarNode); + nodeData->ignoreOther(node, avatarNode); shouldIgnore = !getsAnyIgnored; } } // Not close enough to ignore if (!shouldIgnore) { - nodeData->removeFromRadiusIgnoringSet(avatarNode->getUUID()); + nodeData->removeFromRadiusIgnoringSet(node, avatarNode->getUUID()); } } if (!shouldIgnore) { AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(avatarNode->getUUID()); - AvatarDataSequenceNumber lastSeqFromSender = avatarClientNodeData->getLastReceivedSequenceNumber(); + AvatarDataSequenceNumber lastSeqFromSender = avatarNodeData->getLastReceivedSequenceNumber(); // FIXME - This code does appear to be working. But it seems brittle. // It supports determining if the frame of data for this "other" @@ -387,10 +417,12 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) if (!shouldIgnore) { // sort this one for later - const AvatarData* avatarNodeData = avatarClientNodeData->getConstAvatarData(); - auto lastEncodeTime = nodeData->getLastOtherAvatarEncodeTime(avatarNodeData->getSessionUUID()); - - sortedAvatars.push(SortableAvatar(avatarNodeData, avatarNode, lastEncodeTime)); + uint64_t lastEncodeTime = 0; + std::unordered_map::const_iterator itr = avatarEncodeTimes.find(avatar->getSessionUUID()); + if (itr != avatarEncodeTimes.end()) { + lastEncodeTime = itr->second; + } + sortedAvatars.push(SortableAvatar(avatar, lastEncodeTime)); } } @@ -398,31 +430,19 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) int remainingAvatars = (int)sortedAvatars.size(); auto traitsPacketList = NLPacketList::create(PacketType::BulkAvatarTraits, QByteArray(), true, true); - const auto& sortedAvatarVector = sortedAvatars.getSortedVector(); for (const auto& sortedAvatar : sortedAvatarVector) { - const Node* otherNode = sortedAvatar.getNode(); - auto lastEncodeForOther = sortedAvatar.getTimestamp(); + const auto& avatarData = sortedAvatar.getAvatar(); + remainingAvatars--; + auto otherNode = avatarDataToNodes[avatarData]; assert(otherNode); // we can't have gotten here without the avatarData being a valid key in the map - AvatarData::AvatarDataDetail detail = AvatarData::NoData; - - // NOTE: Here's where we determine if we are over budget and drop remaining avatars, - // or send minimal avatar data in uncommon case of PALIsOpen. + // NOTE: Here's where we determine if we are over budget and drop to bare minimum data int minimRemainingAvatarBytes = minimumBytesPerAvatar * remainingAvatars; bool overBudget = (identityBytesSent + numAvatarDataBytes + minimRemainingAvatarBytes) > maxAvatarBytesPerFrame; - if (overBudget) { - if (PALIsOpen) { - _stats.overBudgetAvatars++; - detail = AvatarData::PALMinimum; - } else { - _stats.overBudgetAvatars += remainingAvatars; - break; - } - } - auto startAvatarDataPacking = chrono::high_resolution_clock::now(); + quint64 startAvatarDataPacking = usecTimestampNow(); ++numOtherAvatars; @@ -439,18 +459,32 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) nodeData->setLastBroadcastTime(otherNode->getUUID(), usecTimestampNow()); } - // Typically all out-of-view avatars but such avatars' priorities will rise with time: - bool isLowerPriority = sortedAvatar.getPriority() <= OUT_OF_VIEW_THRESHOLD; + // determine if avatar is in view which determines how much data to send + glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition(); + glm::vec3 otherNodeBoxScale = (otherPosition - otherNodeData->getGlobalBoundingBoxCorner()) * 2.0f * otherAvatar->getSensorToWorldScale(); + AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); + bool isInView = nodeData->otherAvatarInView(otherNodeBox); - if (isLowerPriority) { + // start a new segment in the PacketList for this avatar + avatarPacketList->startSegment(); + + AvatarData::AvatarDataDetail detail; + + if (overBudget) { + overBudgetAvatars++; + _stats.overBudgetAvatars++; + detail = PALIsOpen ? AvatarData::PALMinimum : AvatarData::NoData; + } else if (!isInView) { detail = PALIsOpen ? AvatarData::PALMinimum : AvatarData::MinimumData; nodeData->incrementAvatarOutOfView(); - } else if (!overBudget) { - detail = distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO ? AvatarData::SendAllData : AvatarData::CullSmallData; + } else { + detail = distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO + ? AvatarData::SendAllData : AvatarData::CullSmallData; nodeData->incrementAvatarInView(); } bool includeThisAvatar = true; + auto lastEncodeForOther = nodeData->getLastOtherAvatarEncodeTime(otherNode->getUUID()); QVector& lastSentJointsForOther = nodeData->getLastOtherAvatarSentJoints(otherNode->getUUID()); lastSentJointsForOther.resize(otherAvatar->getJointCount()); @@ -460,14 +494,14 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) AvatarDataPacket::HasFlags hasFlagsOut; // the result of the toByteArray bool dropFaceTracking = false; - auto startSerialize = chrono::high_resolution_clock::now(); + quint64 start = usecTimestampNow(); QByteArray bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther, hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther); - auto endSerialize = chrono::high_resolution_clock::now(); - _stats.toByteArrayElapsedTime += - (quint64) chrono::duration_cast(endSerialize - startSerialize).count(); + quint64 end = usecTimestampNow(); + _stats.toByteArrayElapsedTime += (end - start); + static auto maxAvatarDataBytes = avatarPacketList->getMaxSegmentSize() - NUM_BYTES_RFC4122_UUID; if (bytes.size() > maxAvatarDataBytes) { qCWarning(avatars) << "otherAvatar.toByteArray() for" << otherNode->getUUID() << "resulted in very large buffer of" << bytes.size() << "bytes - dropping facial data"; @@ -493,11 +527,8 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) } if (includeThisAvatar) { - // start a new segment in the PacketList for this avatar - avatarPacketList->startSegment(); numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122()); numAvatarDataBytes += avatarPacketList->write(bytes); - avatarPacketList->endSegment(); if (detail != AvatarData::NoData) { _stats.numOthersIncluded++; @@ -515,13 +546,15 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // It would be nice if we could tweak its future sort priority to put it at the back of the list. } - auto endAvatarDataPacking = chrono::high_resolution_clock::now(); - _stats.avatarDataPackingElapsedTime += - (quint64) chrono::duration_cast(endAvatarDataPacking - startAvatarDataPacking).count(); + avatarPacketList->endSegment(); + + quint64 endAvatarDataPacking = usecTimestampNow(); + _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); // use helper to add any changed traits to our packet list traitBytesSent += addChangedTraitsToBulkPacket(nodeData, otherNodeData, *traitsPacketList); - remainingAvatars--; + + traitsPacketList->getDataSize(); } quint64 startPacketSending = usecTimestampNow(); @@ -533,7 +566,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) _stats.numBytesSent += numAvatarDataBytes; // send the avatar data PacketList - nodeList->sendPacketList(std::move(avatarPacketList), *destinationNode); + nodeList->sendPacketList(std::move(avatarPacketList), *node); // record the bytes sent for other avatar data in the AvatarMixerClientData nodeData->recordSentAvatarData(numAvatarDataBytes); @@ -543,7 +576,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) if (traitsPacketList->getNumPackets() >= 1) { // send the traits packet list - nodeList->sendPacketList(std::move(traitsPacketList), *destinationNode); + nodeList->sendPacketList(std::move(traitsPacketList), *node); } // record the number of avatars held back this frame diff --git a/interface/resources/meshes/redirect/oopsDialog_timeout.fbx b/interface/resources/meshes/redirect/oopsDialog_timeout.fbx deleted file mode 100644 index 846d38bbee3f38ddb874005669bb38561f293584..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31904 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 diff --git a/interface/resources/meshes/redirect/oopsDialog_vague.fbx b/interface/resources/meshes/redirect/oopsDialog_vague.fbx index 707f09e51a8872b44615aafbe7d5dab5c7306f51..324d90578b833552f01a1199fbded078b49900ba 100644 GIT binary patch delta 1201 zcmZ{kT}+#G6vof_%L?t>TG%M*SVv2lV+LpoZO4cikc|vnD=-+em@Ok(qz3KVzHMm> zj3s6_%7yYwFQ&3_vzRO&22-(0TOT5AP!G#}jADP5tR?XW2W=ni8pPW3; zFXue}H=W@-8Jo5NIV&sjCp9Ea^D*meUxEv_lLsc#Y&+i^=0>I z=5d62_Mpvd!(8KTz6oHc+VRYjS}*G zvKW^XZ`~8#D=`sx6l;vh>1AvN@bSw?kDl; zAICczg*{{w7ov~x%|)|(N1`TiBbMc}#kTn5*cRXQ*d`zU4Jl?79m8O`Lre~50dFES zoCOpGaeN~m5=Y|L9&rdcj^>D4oJqnOe@3PIb?JVF@nXD8v?Q}(tqDxWTg5vG^cOd3 zB-g}OP9pj)0b`{BoXwv_7SsX^XmT% zK21c&0s{k4IX72<=23G|uF*>VMooINa;}d=?#;kAvvTeY61g|A=M-6+dvRN`^2gv( zq(_Zx#_DL55a)1ruO8)NMLY)Qn4M?w%2*x$jQ7Vb=y%>uufFu@$od;)_bQ}~B@m(d G9R3AsWTBP- delta 1251 zcmZ`&UrbwN6hGft|BN~;AnSsotX(_CoY3B~G9V_5F$H5giXd%C=GK_jb+X>udv8k% z?O=)73pkM@50Xt-Voaus5$k-I$?TK|eK0uVpTq~3r?&dJF+ zzw`Uf`JG#9thmNr&occs0GOu*w!^6F;Zx$n7P`P3%t}S(FjN9S1=1yEp>^;U=fOjWxNztXC(+`Tn*D%A;7%UKh()i7iNb7)%c@8Cqq`l z&)q7LiMQfPf?f$Z@poQAMZ9)l*i8LYf;hO>t3?0Hx1a%JklXmtD z`IENY6%Q$R0R3hFh{cDaTpO@~Xhu$n(S87c2tAwR*g3k9bmH|S^*&;uwWJM~MYSs@ zlC`0S7MwaJ({tisd`qOoh6m`yS}U%L!waKfR zxb$HhOqY1Ed0I}p@R#%^t!C;Q=|o|qL<=t~?m_%&gigr^usTBvvJcm@bUssWIPus^ ziYl&UcHy^Kwbyr%Vmb?HkktJ(G4PD7L`(jLMsM8EIZCKXT zj{wjc@ckE~0sscYNJ0`KVl*ME{mOG%;g*g18pYCluDnV+1%OSULo}J&U8%|%z*qW< zM3Xstm3FHDzS2ZqR>EfOGB?AtShf>&XH;dc()->setAudioPaused(_interstitialMode); @@ -6348,7 +6346,6 @@ void Application::updateWindowTitle() const { auto nodeList = DependencyManager::get(); auto accountManager = DependencyManager::get(); - auto isInErrorState = nodeList->getDomainHandler().isInErrorState(); QString buildVersion = " - " + (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? QString("Version") : QString("Build")) @@ -6356,19 +6353,14 @@ void Application::updateWindowTitle() const { QString loginStatus = accountManager->isLoggedIn() ? "" : " (NOT LOGGED IN)"; - QString connectionStatus = isInErrorState ? " (ERROR CONNECTING)" : - nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED)"; + QString connectionStatus = nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED)"; QString username = accountManager->getAccountInfo().getUsername(); setCrashAnnotation("username", username.toStdString()); QString currentPlaceName; if (isServerlessMode()) { - if (isInErrorState) { - currentPlaceName = "serverless: " + nodeList->getDomainHandler().getErrorDomainURL().toString(); - } else { - currentPlaceName = "serverless: " + DependencyManager::get()->getDomainURL().toString(); - } + currentPlaceName = "serverless: " + DependencyManager::get()->getDomainURL().toString(); } else { currentPlaceName = DependencyManager::get()->getDomainURL().host(); if (currentPlaceName.isEmpty()) { diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 1faf17ea9a..443d19e473 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -176,29 +176,29 @@ float AvatarManager::getAvatarSimulationRate(const QUuid& sessionID, const QStri } void AvatarManager::updateOtherAvatars(float deltaTime) { - { - // lock the hash for read to check the size - QReadLocker lock(&_hashLock); - if (_avatarHash.size() < 2 && _avatarsToFade.isEmpty()) { - return; - } + // lock the hash for read to check the size + QReadLocker lock(&_hashLock); + if (_avatarHash.size() < 2 && _avatarsToFade.isEmpty()) { + return; } + lock.unlock(); PerformanceTimer perfTimer("otherAvatars"); class SortableAvatar: public PrioritySortUtil::Sortable { public: SortableAvatar() = delete; - SortableAvatar(const std::shared_ptr& avatar) : _avatar(avatar) {} + SortableAvatar(const AvatarSharedPointer& avatar) : _avatar(avatar) {} glm::vec3 getPosition() const override { return _avatar->getWorldPosition(); } - float getRadius() const override { return _avatar->getBoundingRadius(); } - uint64_t getTimestamp() const override { return _avatar->getLastRenderUpdateTime(); } - std::shared_ptr getAvatar() const { return _avatar; } + float getRadius() const override { return std::static_pointer_cast(_avatar)->getBoundingRadius(); } + uint64_t getTimestamp() const override { return std::static_pointer_cast(_avatar)->getLastRenderUpdateTime(); } + AvatarSharedPointer getAvatar() const { return _avatar; } private: - std::shared_ptr _avatar; + AvatarSharedPointer _avatar; }; auto avatarMap = getHashCopy(); + AvatarHash::iterator itr = avatarMap.begin(); const auto& views = qApp->getConicalViews(); PrioritySortUtil::PriorityQueue sortedAvatars(views, @@ -207,24 +207,22 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { AvatarData::_avatarSortCoefficientAge); sortedAvatars.reserve(avatarMap.size() - 1); // don't include MyAvatar - // Build vector and compute priorities - auto nodeList = DependencyManager::get(); - AvatarHash::iterator itr = avatarMap.begin(); + // sort while (itr != avatarMap.end()) { const auto& avatar = std::static_pointer_cast(*itr); // DO NOT update _myAvatar! Its update has already been done earlier in the main loop. // DO NOT update or fade out uninitialized Avatars - if (avatar != _myAvatar && avatar->isInitialized() && !nodeList->isPersonalMutingNode(avatar->getID())) { + if (avatar != _myAvatar && avatar->isInitialized()) { sortedAvatars.push(SortableAvatar(avatar)); } ++itr; } - // Sort const auto& sortedAvatarVector = sortedAvatars.getSortedVector(); // process in sorted order uint64_t startTime = usecTimestampNow(); - uint64_t updateExpiry = startTime + MAX_UPDATE_AVATARS_TIME_BUDGET; + const uint64_t UPDATE_BUDGET = 2000; // usec + uint64_t updateExpiry = startTime + UPDATE_BUDGET; int numAvatarsUpdated = 0; int numAVatarsNotUpdated = 0; @@ -243,12 +241,18 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { avatar->updateOrbPosition(); } + bool ignoring = DependencyManager::get()->isPersonalMutingNode(avatar->getID()); + if (ignoring) { + continue; + } + // for ALL avatars... if (_shouldRender) { avatar->ensureInScene(avatar, qApp->getMain3DScene()); } avatar->animateScaleChanges(deltaTime); + const float OUT_OF_VIEW_THRESHOLD = 0.5f * AvatarData::OUT_OF_VIEW_PENALTY; uint64_t now = usecTimestampNow(); if (now < updateExpiry) { // we're within budget @@ -269,7 +273,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { // no time to simulate, but we take the time to count how many were tragically missed while (it != sortedAvatarVector.end()) { const SortableAvatar& newSortData = *it; - const auto& newAvatar = newSortData.getAvatar(); + const auto newAvatar = std::static_pointer_cast(newSortData.getAvatar()); bool inView = newSortData.getPriority() > OUT_OF_VIEW_THRESHOLD; // Once we reach an avatar that's not in view, all avatars after it will also be out of view if (!inView) { diff --git a/interface/src/octree/SafeLanding.cpp b/interface/src/octree/SafeLanding.cpp index 5d4ebe9853..9785e0ba95 100644 --- a/interface/src/octree/SafeLanding.cpp +++ b/interface/src/octree/SafeLanding.cpp @@ -66,18 +66,13 @@ void SafeLanding::addTrackedEntity(const EntityItemID& entityID) { Locker lock(_lock); EntityItemPointer entity = _entityTree->findEntityByID(entityID); - if (entity) { + _trackedEntities.emplace(entityID, entity); + int trackedEntityCount = (int)_trackedEntities.size(); - _trackedEntities.emplace(entityID, entity); - int trackedEntityCount = (int)_trackedEntities.size(); - - if (trackedEntityCount > _maxTrackedEntityCount) { - _maxTrackedEntityCount = trackedEntityCount; - } - qCDebug(interfaceapp) << "Safe Landing: Tracking entity " << entity->getItemName(); + if (trackedEntityCount > _maxTrackedEntityCount) { + _maxTrackedEntityCount = trackedEntityCount; } - } else { - qCDebug(interfaceapp) << "Safe Landing: Null Entity: " << entityID; + qCDebug(interfaceapp) << "Safe Landing: Tracking entity " << entity->getItemName(); } } @@ -151,7 +146,7 @@ bool isEntityPhysicsReady(const EntityItemPointer& entity) { bool hasAABox; entity->getAABox(hasAABox); if (hasAABox && downloadedCollisionTypes.count(modelEntity->getShapeType()) != 0) { - return (!entity->shouldBePhysical() || entity->isReadyToComputeShape()); + return entity->isReadyToComputeShape(); } } } @@ -161,23 +156,12 @@ bool isEntityPhysicsReady(const EntityItemPointer& entity) { bool SafeLanding::isEntityLoadingComplete() { Locker lock(_lock); - - auto entityTree = qApp->getEntities(); auto entityMapIter = _trackedEntities.begin(); while (entityMapIter != _trackedEntities.end()) { auto entity = entityMapIter->second; - - bool isVisuallyReady = true; - - Settings settings; - bool enableInterstitial = settings.value("enableIntersitialMode", false).toBool(); - - if (enableInterstitial) { - isVisuallyReady = (entity->isVisuallyReady() || !entityTree->renderableForEntityId(entityMapIter->first)); - } - + bool isVisuallyReady = (entity->isVisuallyReady() || !entityTree->renderableForEntityId(entityMapIter->first)); if (isEntityPhysicsReady(entity) && isVisuallyReady) { entityMapIter = _trackedEntities.erase(entityMapIter); } else { diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 834754e228..ea6dbd7074 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -363,13 +363,13 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent memcpy(destinationBuffer, &packetStateFlags, sizeof(packetStateFlags)); destinationBuffer += sizeof(packetStateFlags); -#define AVATAR_MEMCPY(src) \ - memcpy(destinationBuffer, &(src), sizeof(src)); \ - destinationBuffer += sizeof(src); - if (hasAvatarGlobalPosition) { auto startSection = destinationBuffer; - AVATAR_MEMCPY(_globalPosition); + auto data = reinterpret_cast(destinationBuffer); + data->globalPosition[0] = _globalPosition.x; + data->globalPosition[1] = _globalPosition.y; + data->globalPosition[2] = _globalPosition.z; + destinationBuffer += sizeof(AvatarDataPacket::AvatarGlobalPosition); int numBytes = destinationBuffer - startSection; @@ -380,8 +380,17 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent if (hasAvatarBoundingBox) { auto startSection = destinationBuffer; - AVATAR_MEMCPY(_globalBoundingBoxDimensions); - AVATAR_MEMCPY(_globalBoundingBoxOffset); + auto data = reinterpret_cast(destinationBuffer); + + data->avatarDimensions[0] = _globalBoundingBoxDimensions.x; + data->avatarDimensions[1] = _globalBoundingBoxDimensions.y; + data->avatarDimensions[2] = _globalBoundingBoxDimensions.z; + + data->boundOriginOffset[0] = _globalBoundingBoxOffset.x; + data->boundOriginOffset[1] = _globalBoundingBoxOffset.y; + data->boundOriginOffset[2] = _globalBoundingBoxOffset.z; + + destinationBuffer += sizeof(AvatarDataPacket::AvatarBoundingBox); int numBytes = destinationBuffer - startSection; if (outboundDataRateOut) { @@ -415,7 +424,13 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent if (hasLookAtPosition) { auto startSection = destinationBuffer; - AVATAR_MEMCPY(_headData->getLookAtPosition()); + auto data = reinterpret_cast(destinationBuffer); + auto lookAt = _headData->getLookAtPosition(); + data->lookAtPosition[0] = lookAt.x; + data->lookAtPosition[1] = lookAt.y; + data->lookAtPosition[2] = lookAt.z; + destinationBuffer += sizeof(AvatarDataPacket::LookAtPosition); + int numBytes = destinationBuffer - startSection; if (outboundDataRateOut) { outboundDataRateOut->lookAtPositionRate.increment(numBytes); @@ -516,8 +531,12 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent if (hasAvatarLocalPosition) { auto startSection = destinationBuffer; - const auto localPosition = getLocalPosition(); - AVATAR_MEMCPY(localPosition); + auto data = reinterpret_cast(destinationBuffer); + auto localPosition = getLocalPosition(); + data->localPosition[0] = localPosition.x; + data->localPosition[1] = localPosition.y; + data->localPosition[2] = localPosition.z; + destinationBuffer += sizeof(AvatarDataPacket::AvatarLocalPosition); int numBytes = destinationBuffer - startSection; if (outboundDataRateOut) { @@ -548,24 +567,19 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent } } - QVector jointData; - if (hasJointData || hasJointDefaultPoseFlags) { - QReadLocker readLock(&_jointDataLock); - jointData = _jointData; - } - // If it is connected, pack up the data if (hasJointData) { auto startSection = destinationBuffer; + QReadLocker readLock(&_jointDataLock); // joint rotation data - int numJoints = jointData.size(); + int numJoints = _jointData.size(); *destinationBuffer++ = (uint8_t)numJoints; unsigned char* validityPosition = destinationBuffer; unsigned char validity = 0; int validityBit = 0; - int numValidityBytes = calcBitVectorSize(numJoints); + int numValidityBytes = (int)std::ceil(numJoints / (float)BITS_IN_BYTE); #ifdef WANT_DEBUG int rotationSentCount = 0; @@ -575,37 +589,43 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent destinationBuffer += numValidityBytes; // Move pointer past the validity bytes // sentJointDataOut and lastSentJointData might be the same vector + // build sentJointDataOut locally and then swap it at the end. + QVector localSentJointDataOut; if (sentJointDataOut) { - sentJointDataOut->resize(numJoints); // Make sure the destination is resized before using it + localSentJointDataOut.resize(numJoints); // Make sure the destination is resized before using it } - float minRotationDOT = (distanceAdjust && cullSmallChanges) ? getDistanceBasedMinRotationDOT(viewerPosition) : AVATAR_MIN_ROTATION_DOT; + float minRotationDOT = !distanceAdjust ? AVATAR_MIN_ROTATION_DOT : getDistanceBasedMinRotationDOT(viewerPosition); - for (int i = 0; i < jointData.size(); i++) { - const JointData& data = jointData[i]; + for (int i = 0; i < _jointData.size(); i++) { + const JointData& data = _jointData[i]; const JointData& last = lastSentJointData[i]; if (!data.rotationIsDefaultPose) { - // The dot product for larger rotations is a lower number. - // So if the dot() is less than the value, then the rotation is a larger angle of rotation - if (sendAll || last.rotationIsDefaultPose || (!cullSmallChanges && last.rotation != data.rotation) - || (cullSmallChanges && glm::dot(last.rotation, data.rotation) < minRotationDOT) ) { - validity |= (1 << validityBit); -#ifdef WANT_DEBUG - rotationSentCount++; -#endif - destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, data.rotation); + bool mustSend = sendAll || last.rotationIsDefaultPose; + if (mustSend || last.rotation != data.rotation) { - if (sentJointDataOut) { - (*sentJointDataOut)[i].rotation = data.rotation; + bool largeEnoughRotation = true; + if (cullSmallChanges) { + // The dot product for smaller rotations is a smaller number. + // So if the dot() is less than the value, then the rotation is a larger angle of rotation + largeEnoughRotation = fabsf(glm::dot(last.rotation, data.rotation)) < minRotationDOT; + } + + if (mustSend || !cullSmallChanges || largeEnoughRotation) { + validity |= (1 << validityBit); +#ifdef WANT_DEBUG + rotationSentCount++; +#endif + destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, data.rotation); + + if (sentJointDataOut) { + localSentJointDataOut[i].rotation = data.rotation; + localSentJointDataOut[i].rotationIsDefaultPose = false; + } } } } - - if (sentJointDataOut) { - (*sentJointDataOut)[i].rotationIsDefaultPose = data.rotationIsDefaultPose; - } - if (++validityBit == BITS_IN_BYTE) { *validityPosition++ = validity; validityBit = validity = 0; @@ -627,38 +647,35 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent destinationBuffer += numValidityBytes; // Move pointer past the validity bytes - float minTranslation = (distanceAdjust && cullSmallChanges) ? getDistanceBasedMinTranslationDistance(viewerPosition) : AVATAR_MIN_TRANSLATION; + float minTranslation = !distanceAdjust ? AVATAR_MIN_TRANSLATION : getDistanceBasedMinTranslationDistance(viewerPosition); float maxTranslationDimension = 0.0; - for (int i = 0; i < jointData.size(); i++) { - const JointData& data = jointData[i]; + for (int i = 0; i < _jointData.size(); i++) { + const JointData& data = _jointData[i]; const JointData& last = lastSentJointData[i]; if (!data.translationIsDefaultPose) { - if (sendAll || last.translationIsDefaultPose || (!cullSmallChanges && last.translation != data.translation) - || (cullSmallChanges && glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation)) { - - validity |= (1 << validityBit); + bool mustSend = sendAll || last.translationIsDefaultPose; + if (mustSend || last.translation != data.translation) { + if (mustSend || !cullSmallChanges || glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation) { + validity |= (1 << validityBit); #ifdef WANT_DEBUG - translationSentCount++; + translationSentCount++; #endif - maxTranslationDimension = glm::max(fabsf(data.translation.x), maxTranslationDimension); - maxTranslationDimension = glm::max(fabsf(data.translation.y), maxTranslationDimension); - maxTranslationDimension = glm::max(fabsf(data.translation.z), maxTranslationDimension); + maxTranslationDimension = glm::max(fabsf(data.translation.x), maxTranslationDimension); + maxTranslationDimension = glm::max(fabsf(data.translation.y), maxTranslationDimension); + maxTranslationDimension = glm::max(fabsf(data.translation.z), maxTranslationDimension); - destinationBuffer += - packFloatVec3ToSignedTwoByteFixed(destinationBuffer, data.translation, TRANSLATION_COMPRESSION_RADIX); + destinationBuffer += + packFloatVec3ToSignedTwoByteFixed(destinationBuffer, data.translation, TRANSLATION_COMPRESSION_RADIX); - if (sentJointDataOut) { - (*sentJointDataOut)[i].translation = data.translation; + if (sentJointDataOut) { + localSentJointDataOut[i].translation = data.translation; + localSentJointDataOut[i].translationIsDefaultPose = false; + } } } } - - if (sentJointDataOut) { - (*sentJointDataOut)[i].translationIsDefaultPose = data.translationIsDefaultPose; - } - if (++validityBit == BITS_IN_BYTE) { *validityPosition++ = validity; validityBit = validity = 0; @@ -674,7 +691,6 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, controllerLeftHandTransform.getRotation()); destinationBuffer += packFloatVec3ToSignedTwoByteFixed(destinationBuffer, controllerLeftHandTransform.getTranslation(), TRANSLATION_COMPRESSION_RADIX); - Transform controllerRightHandTransform = Transform(getControllerRightHandMatrix()); destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, controllerRightHandTransform.getRotation()); destinationBuffer += packFloatVec3ToSignedTwoByteFixed(destinationBuffer, controllerRightHandTransform.getTranslation(), @@ -691,27 +707,34 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent glm::vec3 mouseFarGrabPosition = extractTranslation(mouseFarGrabMatrix); glm::quat mouseFarGrabRotation = extractRotation(mouseFarGrabMatrix); - AVATAR_MEMCPY(leftFarGrabPosition); - // Can't do block copy as struct order is x, y, z, w. + data->leftFarGrabPosition[0] = leftFarGrabPosition.x; + data->leftFarGrabPosition[1] = leftFarGrabPosition.y; + data->leftFarGrabPosition[2] = leftFarGrabPosition.z; + data->leftFarGrabRotation[0] = leftFarGrabRotation.w; data->leftFarGrabRotation[1] = leftFarGrabRotation.x; data->leftFarGrabRotation[2] = leftFarGrabRotation.y; data->leftFarGrabRotation[3] = leftFarGrabRotation.z; - destinationBuffer += sizeof(data->leftFarGrabPosition); - AVATAR_MEMCPY(rightFarGrabPosition); + data->rightFarGrabPosition[0] = rightFarGrabPosition.x; + data->rightFarGrabPosition[1] = rightFarGrabPosition.y; + data->rightFarGrabPosition[2] = rightFarGrabPosition.z; + data->rightFarGrabRotation[0] = rightFarGrabRotation.w; data->rightFarGrabRotation[1] = rightFarGrabRotation.x; data->rightFarGrabRotation[2] = rightFarGrabRotation.y; data->rightFarGrabRotation[3] = rightFarGrabRotation.z; - destinationBuffer += sizeof(data->rightFarGrabRotation); - AVATAR_MEMCPY(mouseFarGrabPosition); + data->mouseFarGrabPosition[0] = mouseFarGrabPosition.x; + data->mouseFarGrabPosition[1] = mouseFarGrabPosition.y; + data->mouseFarGrabPosition[2] = mouseFarGrabPosition.z; + data->mouseFarGrabRotation[0] = mouseFarGrabRotation.w; data->mouseFarGrabRotation[1] = mouseFarGrabRotation.x; data->mouseFarGrabRotation[2] = mouseFarGrabRotation.y; data->mouseFarGrabRotation[3] = mouseFarGrabRotation.z; - destinationBuffer += sizeof(data->mouseFarGrabRotation); + + destinationBuffer += sizeof(AvatarDataPacket::FarGrabJoints); int numBytes = destinationBuffer - startSection; @@ -738,23 +761,41 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent outboundDataRateOut->jointDataRate.increment(numBytes); } + if (sentJointDataOut) { + + // Mark default poses in lastSentJointData, so when they become non-default we send them. + for (int i = 0; i < _jointData.size(); i++) { + const JointData& data = _jointData[i]; + JointData& local = localSentJointDataOut[i]; + if (data.rotationIsDefaultPose) { + local.rotationIsDefaultPose = true; + } + if (data.translationIsDefaultPose) { + local.translationIsDefaultPose = true; + } + } + + // Push new sent joint data to sentJointDataOut + sentJointDataOut->swap(localSentJointDataOut); + } } if (hasJointDefaultPoseFlags) { auto startSection = destinationBuffer; + QReadLocker readLock(&_jointDataLock); // write numJoints - int numJoints = jointData.size(); + int numJoints = _jointData.size(); *destinationBuffer++ = (uint8_t)numJoints; // write rotationIsDefaultPose bits destinationBuffer += writeBitVector(destinationBuffer, numJoints, [&](int i) { - return jointData[i].rotationIsDefaultPose; + return _jointData[i].rotationIsDefaultPose; }); // write translationIsDefaultPose bits destinationBuffer += writeBitVector(destinationBuffer, numJoints, [&](int i) { - return jointData[i].translationIsDefaultPose; + return _jointData[i].translationIsDefaultPose; }); if (outboundDataRateOut) { @@ -839,6 +880,7 @@ const unsigned char* unpackFauxJoint(const unsigned char* sourceBuffer, ThreadSa // read data in packet starting at byte offset and return number of bytes parsed int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { + // lazily allocate memory for HeadData in case we're not an Avatar instance lazyInitHeadData(); @@ -890,7 +932,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { auto newValue = glm::vec3(data->globalPosition[0], data->globalPosition[1], data->globalPosition[2]) + offset; if (_globalPosition != newValue) { _globalPosition = newValue; - _globalPositionChanged = now; + _globalPositionChanged = usecTimestampNow(); } sourceBuffer += sizeof(AvatarDataPacket::AvatarGlobalPosition); int numBytesRead = sourceBuffer - startSection; @@ -914,11 +956,11 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { if (_globalBoundingBoxDimensions != newDimensions) { _globalBoundingBoxDimensions = newDimensions; - _avatarBoundingBoxChanged = now; + _avatarBoundingBoxChanged = usecTimestampNow(); } if (_globalBoundingBoxOffset != newOffset) { _globalBoundingBoxOffset = newOffset; - _avatarBoundingBoxChanged = now; + _avatarBoundingBoxChanged = usecTimestampNow(); } sourceBuffer += sizeof(AvatarDataPacket::AvatarBoundingBox); @@ -1019,7 +1061,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { glm::mat4 sensorToWorldMatrix = createMatFromScaleQuatAndPos(glm::vec3(sensorToWorldScale), sensorToWorldQuat, sensorToWorldTrans); if (_sensorToWorldMatrixCache.get() != sensorToWorldMatrix) { _sensorToWorldMatrixCache.set(sensorToWorldMatrix); - _sensorToWorldMatrixChanged = now; + _sensorToWorldMatrixChanged = usecTimestampNow(); } sourceBuffer += sizeof(AvatarDataPacket::SensorToWorldMatrix); int numBytesRead = sourceBuffer - startSection; @@ -1076,7 +1118,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { sourceBuffer += sizeof(AvatarDataPacket::AdditionalFlags); if (somethingChanged) { - _additionalFlagsChanged = now; + _additionalFlagsChanged = usecTimestampNow(); } int numBytesRead = sourceBuffer - startSection; _additionalFlagsRate.increment(numBytesRead); @@ -1096,7 +1138,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { if ((getParentID() != newParentID) || (getParentJointIndex() != parentInfo->parentJointIndex)) { SpatiallyNestable::setParentID(newParentID); SpatiallyNestable::setParentJointIndex(parentInfo->parentJointIndex); - _parentChanged = now; + _parentChanged = usecTimestampNow(); } int numBytesRead = sourceBuffer - startSection; @@ -1145,6 +1187,8 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { int numBytesRead = sourceBuffer - startSection; _faceTrackerRate.increment(numBytesRead); _faceTrackerUpdateRate.increment(); + } else { + _headData->_blendshapeCoefficients.fill(0, _headData->_blendshapeCoefficients.size()); } if (hasJointData) { @@ -2829,8 +2873,10 @@ void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, Ra value.extraInfo = object.property("extraInfo").toVariant().toMap(); } -float AvatarData::_avatarSortCoefficientSize { 8.0f }; -float AvatarData::_avatarSortCoefficientCenter { 4.0f }; +const float AvatarData::OUT_OF_VIEW_PENALTY = -10.0f; + +float AvatarData::_avatarSortCoefficientSize { 1.0f }; +float AvatarData::_avatarSortCoefficientCenter { 0.25 }; float AvatarData::_avatarSortCoefficientAge { 1.0f }; QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEntityMap& value) { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 056e745370..db2b82b0b7 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -1097,7 +1097,7 @@ public: void fromJson(const QJsonObject& json, bool useFrameSkeleton = true); glm::vec3 getClientGlobalPosition() const { return _globalPosition; } - AABox getGlobalBoundingBox() const { return AABox(_globalPosition + _globalBoundingBoxOffset - _globalBoundingBoxDimensions, _globalBoundingBoxDimensions); } + glm::vec3 getGlobalBoundingBoxCorner() const { return _globalPosition + _globalBoundingBoxOffset - _globalBoundingBoxDimensions; } /**jsdoc * @function MyAvatar.getAvatarEntityData @@ -1169,6 +1169,8 @@ public: // A method intended to be overriden by MyAvatar for polling orientation for network transmission. virtual glm::quat getOrientationOutbound() const; + static const float OUT_OF_VIEW_PENALTY; + // TODO: remove this HACK once we settle on optimal sort coefficients // These coefficients exposed for fine tuning the sort priority for transfering new _jointData to the render pipeline. static float _avatarSortCoefficientSize; diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index d99c0020da..5b3196a2bf 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -538,6 +538,7 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas QNetworkReply* requestReply = networkAccessManager.post(request, postData); connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestAccessTokenFinished); + connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError))); } void AccountManager::requestAccessTokenWithSteam(QByteArray authSessionTicket) { @@ -632,6 +633,12 @@ void AccountManager::requestAccessTokenFinished() { } } +void AccountManager::requestAccessTokenError(QNetworkReply::NetworkError error) { + // TODO: error handling + qCDebug(networking) << "AccountManager: failed to fetch access token - " << error; + emit loginFailed(); +} + void AccountManager::refreshAccessTokenFinished() { QNetworkReply* requestReply = reinterpret_cast(sender()); diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index f3b81cf1c9..b122115dd0 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -106,6 +106,7 @@ public slots: void requestAccessTokenFinished(); void refreshAccessTokenFinished(); void requestProfileFinished(); + void requestAccessTokenError(QNetworkReply::NetworkError error); void refreshAccessTokenError(QNetworkReply::NetworkError error); void requestProfileError(QNetworkReply::NetworkError error); void logout(); diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 8085039b02..9e5cbcaa7b 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -816,10 +816,8 @@ bool AddressManager::setDomainInfo(const QUrl& domainURL, LookupTrigger trigger) const QString hostname = domainURL.host(); quint16 port = domainURL.port(); bool emitHostChanged { false }; - // Check if domain handler is in error state. always emit host changed if true. - bool isInErrorState = DependencyManager::get()->getDomainHandler().isInErrorState(); - if (domainURL != _domainURL || isInErrorState) { + if (domainURL != _domainURL) { addCurrentAddressToHistory(trigger); emitHostChanged = true; } diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 4c9231b7c9..59e3de922f 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -55,9 +55,6 @@ DomainHandler::DomainHandler(QObject* parent) : // stop the refresh timer if we connect to a domain connect(this, &DomainHandler::connectedToDomain, &_apiRefreshTimer, &QTimer::stop); - - // stop the refresh timer if redirected to the error domain - connect(this, &DomainHandler::redirectToErrorDomainURL, &_apiRefreshTimer, &QTimer::stop); } void DomainHandler::disconnect() { @@ -110,15 +107,13 @@ void DomainHandler::softReset() { QMetaObject::invokeMethod(&_settingsTimer, "stop"); // restart the API refresh timer in case we fail to connect and need to refresh information - if (!_isInErrorState) { - QMetaObject::invokeMethod(&_apiRefreshTimer, "start"); + QMetaObject::invokeMethod(&_apiRefreshTimer, "start"); } void DomainHandler::hardReset() { emit resetting(); softReset(); - _isInErrorState = false; qCDebug(networking) << "Hard reset in NodeList DomainHandler."; _pendingDomainID = QUuid(); diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 90da86a929..ab20936f43 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -172,11 +172,6 @@ public slots: void processICEResponsePacket(QSharedPointer icePacket); void processDomainServerConnectionDeniedPacket(QSharedPointer message); - // sets domain handler in error state. - void setRedirectErrorState(QUrl errorUrl, int reasonCode); - - bool isInErrorState() { return _isInErrorState; } - private slots: void completedHostnameLookup(const QHostInfo& hostInfo); void completedIceServerHostnameLookup(); diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp index 44220df8f8..c378987cd0 100644 --- a/libraries/networking/src/udt/Socket.cpp +++ b/libraries/networking/src/udt/Socket.cpp @@ -37,7 +37,6 @@ Socket::Socket(QObject* parent, bool shouldChangeSocketOptions) : _shouldChangeSocketOptions(shouldChangeSocketOptions) { connect(&_udpSocket, &QUdpSocket::readyRead, this, &Socket::readPendingDatagrams); - connect(this, &Socket::pendingDatagrams, this, &Socket::processPendingDatagrams, Qt::QueuedConnection); // make sure we hear about errors and state changes from the underlying socket connect(&_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)), @@ -316,85 +315,55 @@ void Socket::checkForReadyReadBackup() { } void Socket::readPendingDatagrams() { - int packetsRead = 0; - int packetSizeWithHeader = -1; - // Max datagrams to read before processing: - static const int MAX_DATAGRAMS_CONSECUTIVELY = 10000; - while (_udpSocket.hasPendingDatagrams() - && (packetSizeWithHeader = _udpSocket.pendingDatagramSize()) != -1 - && packetsRead <= MAX_DATAGRAMS_CONSECUTIVELY) { - // grab a time point we can mark as the receive time of this packet - auto receiveTime = p_high_resolution_clock::now(); - - // setup a buffer to read the packet into - auto buffer = std::unique_ptr(new char[packetSizeWithHeader]); - - QHostAddress senderAddress; - quint16 senderPort; - - // pull the datagram - auto sizeRead = _udpSocket.readDatagram(buffer.get(), packetSizeWithHeader, - &senderAddress, &senderPort); - - // we either didn't pull anything for this packet or there was an error reading (this seems to trigger - // on windows even if there's not a packet available) - if (sizeRead < 0) { - continue; - } - - _incomingDatagrams.push_back({ senderAddress, senderPort, packetSizeWithHeader, - std::move(buffer), receiveTime }); - ++packetsRead; - - } - - if (packetsRead > _maxDatagramsRead) { - _maxDatagramsRead = packetsRead; - qCDebug(networking) << "readPendingDatagrams: Datagrams read:" << packetsRead; - } - emit pendingDatagrams(packetsRead); -} - -void Socket::processPendingDatagrams(int) { - // setup a HifiSockAddr to read into - HifiSockAddr senderSockAddr; - - while (!_incomingDatagrams.empty()) { - auto& datagram = _incomingDatagrams.front(); - senderSockAddr.setAddress(datagram._senderAddress); - senderSockAddr.setPort(datagram._senderPort); - int datagramSize = datagram._datagramLength; - auto receiveTime = datagram._receiveTime; + while (_udpSocket.hasPendingDatagrams() && (packetSizeWithHeader = _udpSocket.pendingDatagramSize()) != -1) { // we're reading a packet so re-start the readyRead backup timer _readyReadBackupTimer->start(); + // grab a time point we can mark as the receive time of this packet + auto receiveTime = p_high_resolution_clock::now(); + + // setup a HifiSockAddr to read into + HifiSockAddr senderSockAddr; + + // setup a buffer to read the packet into + auto buffer = std::unique_ptr(new char[packetSizeWithHeader]); + + // pull the datagram + auto sizeRead = _udpSocket.readDatagram(buffer.get(), packetSizeWithHeader, + senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); + // save information for this packet, in case it is the one that sticks readyRead - _lastPacketSizeRead = datagramSize; + _lastPacketSizeRead = sizeRead; _lastPacketSockAddr = senderSockAddr; - // Process unfiltered packets first. + if (sizeRead <= 0) { + // we either didn't pull anything for this packet or there was an error reading (this seems to trigger + // on windows even if there's not a packet available) + continue; + } + auto it = _unfilteredHandlers.find(senderSockAddr); + if (it != _unfilteredHandlers.end()) { - // we have a registered unfiltered handler for this HifiSockAddr (eg. STUN packet) - call that and return + // we have a registered unfiltered handler for this HifiSockAddr - call that and return if (it->second) { - auto basePacket = BasePacket::fromReceivedPacket(std::move(datagram._datagram), - datagramSize, senderSockAddr); + auto basePacket = BasePacket::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, senderSockAddr); basePacket->setReceiveTime(receiveTime); it->second(std::move(basePacket)); } - _incomingDatagrams.pop_front(); + continue; } // check if this was a control packet or a data packet - bool isControlPacket = *reinterpret_cast(datagram._datagram.get()) & CONTROL_BIT_MASK; + bool isControlPacket = *reinterpret_cast(buffer.get()) & CONTROL_BIT_MASK; if (isControlPacket) { // setup a control packet from the data we just read - auto controlPacket = ControlPacket::fromReceivedPacket(std::move(datagram._datagram), datagramSize, senderSockAddr); + auto controlPacket = ControlPacket::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, senderSockAddr); controlPacket->setReceiveTime(receiveTime); // move this control packet to the matching connection, if there is one @@ -406,13 +375,13 @@ void Socket::processPendingDatagrams(int) { } else { // setup a Packet from the data we just read - auto packet = Packet::fromReceivedPacket(std::move(datagram._datagram), datagramSize, senderSockAddr); + auto packet = Packet::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, senderSockAddr); packet->setReceiveTime(receiveTime); // save the sequence number in case this is the packet that sticks readyRead _lastReceivedSequenceNumber = packet->getSequenceNumber(); - // call our hash verification operator to see if this packet is verified + // call our verification operator to see if this packet is verified if (!_packetFilterOperator || _packetFilterOperator(*packet)) { if (packet->isReliable()) { // if this was a reliable packet then signal the matching connection with the sequence number @@ -426,7 +395,6 @@ void Socket::processPendingDatagrams(int) { qCDebug(networking) << "Can't process packet: version" << (unsigned int)NLPacket::versionInHeader(*packet) << ", type" << NLPacket::typeInHeader(*packet); #endif - _incomingDatagrams.pop_front(); continue; } } @@ -442,8 +410,6 @@ void Socket::processPendingDatagrams(int) { } } } - - _incomingDatagrams.pop_front(); } } diff --git a/libraries/networking/src/udt/Socket.h b/libraries/networking/src/udt/Socket.h index ef4a457116..1f28592c83 100644 --- a/libraries/networking/src/udt/Socket.h +++ b/libraries/networking/src/udt/Socket.h @@ -17,7 +17,6 @@ #include #include #include -#include #include #include @@ -95,7 +94,6 @@ public: signals: void clientHandshakeRequestComplete(const HifiSockAddr& sockAddr); - void pendingDatagrams(int datagramCount); public slots: void cleanupConnection(HifiSockAddr sockAddr); @@ -103,7 +101,6 @@ public slots: private slots: void readPendingDatagrams(); - void processPendingDatagrams(int datagramCount); void checkForReadyReadBackup(); void handleSocketError(QAbstractSocket::SocketError socketError); @@ -147,17 +144,6 @@ private: int _lastPacketSizeRead { 0 }; SequenceNumber _lastReceivedSequenceNumber; HifiSockAddr _lastPacketSockAddr; - - struct Datagram { - QHostAddress _senderAddress; - int _senderPort; - int _datagramLength; - std::unique_ptr _datagram; - p_high_resolution_clock::time_point _receiveTime; - }; - - std::list _incomingDatagrams; - int _maxDatagramsRead { 0 }; friend UDTTest; }; diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 871958f883..58c197d6f4 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -286,7 +286,6 @@ void PhysicsEngine::reinsertObject(ObjectMotionState* object) { void PhysicsEngine::processTransaction(PhysicsEngine::Transaction& transaction) { // removes for (auto object : transaction.objectsToRemove) { - bumpAndPruneContacts(object); btRigidBody* body = object->getRigidBody(); if (body) { removeDynamicsForBody(body); diff --git a/libraries/shared/src/PrioritySortUtil.h b/libraries/shared/src/PrioritySortUtil.h index 27f6b193ba..8ded047212 100644 --- a/libraries/shared/src/PrioritySortUtil.h +++ b/libraries/shared/src/PrioritySortUtil.h @@ -16,16 +16,55 @@ #include "NumericalConstants.h" #include "shared/ConicalViewFrustum.h" -// PrioritySortUtil is a helper for sorting 3D things relative to a ViewFrustum. +/* PrioritySortUtil is a helper for sorting 3D things relative to a ViewFrustum. To use: -const float OUT_OF_VIEW_PENALTY = -10.0f; -const float OUT_OF_VIEW_THRESHOLD = 0.5f * OUT_OF_VIEW_PENALTY; +(1) Derive a class from pure-virtual PrioritySortUtil::Sortable that wraps a copy of + the Thing you want to prioritize and sort: + + class SortableWrapper: public PrioritySortUtil::Sortable { + public: + SortableWrapper(const Thing& thing) : _thing(thing) { } + glm::vec3 getPosition() const override { return _thing->getPosition(); } + float getRadius() const override { return 0.5f * _thing->getBoundingRadius(); } + uint64_t getTimestamp() const override { return _thing->getLastTime(); } + Thing getThing() const { return _thing; } + private: + Thing _thing; + }; + +(2) Make a PrioritySortUtil::PriorityQueue and add them to the queue: + + PrioritySortUtil::PriorityQueue sortedThings(viewFrustum); + std::priority_queue< PrioritySortUtil::Sortable > sortedThings; + for (thing in things) { + sortedThings.push(SortableWrapper(thing)); + } + +(3) Loop over your priority queue and do timeboxed work: + + NOTE: Be careful using references to members of instances of T from std::priority_queue. + Under the hood std::priority_queue may re-use instances of T. + For example, after a pop() or a push() the top T may have the same memory address + as the top T before the pop() or push() (but point to a swapped instance of T). + This causes a reference to member variable of T to point to a different value + when operations taken on std::priority_queue shuffle around the instances of T. + + uint64_t cutoffTime = usecTimestampNow() + TIME_BUDGET; + while (!sortedThings.empty()) { + const Thing& thing = sortedThings.top(); + // ...do work on thing... + sortedThings.pop(); + if (usecTimestampNow() > cutoffTime) { + break; + } + } +*/ namespace PrioritySortUtil { constexpr float DEFAULT_ANGULAR_COEF { 1.0f }; constexpr float DEFAULT_CENTER_COEF { 0.5f }; - constexpr float DEFAULT_AGE_COEF { 0.25f }; + constexpr float DEFAULT_AGE_COEF { 0.25f / (float)(USECS_PER_SECOND) }; class Sortable { public: @@ -45,9 +84,8 @@ namespace PrioritySortUtil { PriorityQueue() = delete; PriorityQueue(const ConicalViewFrustums& views) : _views(views) { } PriorityQueue(const ConicalViewFrustums& views, float angularWeight, float centerWeight, float ageWeight) - : _views(views), _angularWeight(angularWeight), _centerWeight(centerWeight), _ageWeight(ageWeight) - , _usecCurrentTime(usecTimestampNow()) { - } + : _views(views), _angularWeight(angularWeight), _centerWeight(centerWeight), _ageWeight(ageWeight) + { } void setViews(const ConicalViewFrustums& views) { _views = views; } @@ -55,7 +93,6 @@ namespace PrioritySortUtil { _angularWeight = angularWeight; _centerWeight = centerWeight; _ageWeight = ageWeight; - _usecCurrentTime = usecTimestampNow(); } size_t size() const { return _vector.size(); } @@ -94,18 +131,23 @@ namespace PrioritySortUtil { glm::vec3 offset = position - view.getPosition(); float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero const float MIN_RADIUS = 0.1f; // WORKAROUND for zero size objects (we still want them to sort by distance) - float radius = glm::max(thing.getRadius(), MIN_RADIUS); - // Other item's angle from view centre: - float cosineAngle = glm::dot(offset, view.getDirection()) / distance; - float age = float((_usecCurrentTime - thing.getTimestamp()) / USECS_PER_SECOND); + float radius = glm::min(thing.getRadius(), MIN_RADIUS); + float cosineAngle = (glm::dot(offset, view.getDirection()) / distance); + float age = (float)(usecTimestampNow() - thing.getTimestamp()); - // the "age" term accumulates at the sum of all weights - float angularSize = radius / distance; - float priority = (_angularWeight * angularSize + _centerWeight * cosineAngle) * (age + 1.0f) + _ageWeight * age; + // we modulatate "age" drift rate by the cosineAngle term to make periphrial objects sort forward + // at a reduced rate but we don't want the "age" term to go zero or negative so we clamp it + const float MIN_COSINE_ANGLE_FACTOR = 0.1f; + float cosineAngleFactor = glm::max(cosineAngle, MIN_COSINE_ANGLE_FACTOR); + + float priority = _angularWeight * glm::max(radius, MIN_RADIUS) / distance + + _centerWeight * cosineAngle + + _ageWeight * cosineAngleFactor * age; // decrement priority of things outside keyhole if (distance - radius > view.getRadius()) { if (!view.intersects(offset, distance, radius)) { + constexpr float OUT_OF_VIEW_PENALTY = -10.0f; priority += OUT_OF_VIEW_PENALTY; } } @@ -117,13 +159,12 @@ namespace PrioritySortUtil { float _angularWeight { DEFAULT_ANGULAR_COEF }; float _centerWeight { DEFAULT_CENTER_COEF }; float _ageWeight { DEFAULT_AGE_COEF }; - quint64 _usecCurrentTime { 0 }; }; } // namespace PrioritySortUtil - // for now we're keeping hard-coded sorted time budgets in one spot +// for now we're keeping hard-coded sorted time budgets in one spot const uint64_t MAX_UPDATE_RENDERABLES_TIME_BUDGET = 2000; // usec const uint64_t MIN_SORTED_UPDATE_RENDERABLES_TIME_BUDGET = 1000; // usec -const uint64_t MAX_UPDATE_AVATARS_TIME_BUDGET = 2000; // usec #endif // hifi_PrioritySortUtil_h + diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index aaf5ca7260..31510831c8 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -35,13 +35,10 @@ var DEFAULT_SCRIPTS_COMBINED = [ ]; var DEFAULT_SCRIPTS_SEPARATE = [ "system/controllers/controllerScripts.js", + "system/interstitialPage.js" //"system/chat.js" ]; -if (Settings.getValue("enableInterstitialMode", false)) { - DEFAULT_SCRIPTS_SEPARATE.push("system/interstitialPage.js"); -} - // add a menu item for debugging var MENU_CATEGORY = "Developer"; var MENU_ITEM = "Debug defaultScripts.js"; diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 88ae2852be..ed2a179613 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -860,7 +860,7 @@ var toolBar = (function () { propertiesTool.setVisible(false); selectionManager.clearSelections(); cameraManager.disable(); - selectionDisplay.disableTriggerMapping(); + selectionDisplay.triggerMapping.disable(); tablet.landscape = false; Controller.disableMapping(CONTROLLER_MAPPING_NAME); } else { @@ -876,7 +876,7 @@ var toolBar = (function () { gridTool.setVisible(true); grid.setEnabled(true); propertiesTool.setVisible(true); - selectionDisplay.enableTriggerMapping(); + selectionDisplay.triggerMapping.enable(); print("starting tablet in landscape mode"); tablet.landscape = true; Controller.enableMapping(CONTROLLER_MAPPING_NAME); diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 5bca58b1ac..05f5e41950 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -14,7 +14,7 @@ // /* global SelectionManager, SelectionDisplay, grid, rayPlaneIntersection, rayPlaneIntersection2, pushCommandForSelections, - getMainTabletIDs, getControllerWorldLocation, TRIGGER_ON_VALUE */ + getMainTabletIDs, getControllerWorldLocation */ var SPACE_LOCAL = "local"; var SPACE_WORLD = "world"; @@ -22,7 +22,6 @@ var HIGHLIGHT_LIST_NAME = "editHandleHighlightList"; Script.include([ "./controllers.js", - "./controllerDispatcherUtils.js", "./utils.js" ]); @@ -449,8 +448,6 @@ SelectionDisplay = (function() { var CTRL_KEY_CODE = 16777249; var RAIL_AXIS_LENGTH = 10000; - - var NO_HAND = -1; var TRANSLATE_DIRECTION = { X: 0, @@ -481,6 +478,8 @@ SelectionDisplay = (function() { YAW: 1, ROLL: 2 }; + + var NO_TRIGGER_HAND = -1; var spaceMode = SPACE_LOCAL; var overlayNames = []; @@ -803,21 +802,11 @@ SelectionDisplay = (function() { // We get mouseMoveEvents from the handControllers, via handControllerPointer. // But we dont' get mousePressEvents. - that.triggerClickMapping = Controller.newMapping(Script.resolvePath('') + '-click'); - that.triggerPressMapping = Controller.newMapping(Script.resolvePath('') + '-press'); - that.triggeredHand = NO_HAND; - that.pressedHand = NO_HAND; + that.triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click'); + Script.scriptEnding.connect(that.triggerMapping.disable); + that.triggeredHand = NO_TRIGGER_HAND; that.triggered = function() { - return that.triggeredHand !== NO_HAND; - } - function pointingAtDesktopWindowOrTablet(hand) { - var pointingAtDesktopWindow = (hand === Controller.Standard.RightHand && - SelectionManager.pointingAtDesktopWindowRight) || - (hand === Controller.Standard.LeftHand && - SelectionManager.pointingAtDesktopWindowLeft); - var pointingAtTablet = (hand === Controller.Standard.RightHand && SelectionManager.pointingAtTabletRight) || - (hand === Controller.Standard.LeftHand && SelectionManager.pointingAtTabletLeft); - return pointingAtDesktopWindow || pointingAtTablet; + return that.triggeredHand !== NO_TRIGGER_HAND; } function makeClickHandler(hand) { return function (clicked) { @@ -825,39 +814,26 @@ SelectionDisplay = (function() { if (that.triggered() && hand !== that.triggeredHand) { return; } - if (!that.triggered() && clicked && !pointingAtDesktopWindowOrTablet(hand)) { + if (!that.triggered() && clicked) { + var pointingAtDesktopWindow = (hand === Controller.Standard.RightHand && + SelectionManager.pointingAtDesktopWindowRight) || + (hand === Controller.Standard.LeftHand && + SelectionManager.pointingAtDesktopWindowLeft); + var pointingAtTablet = (hand === Controller.Standard.RightHand && SelectionManager.pointingAtTabletRight) || + (hand === Controller.Standard.LeftHand && SelectionManager.pointingAtTabletLeft); + if (pointingAtDesktopWindow || pointingAtTablet) { + return; + } that.triggeredHand = hand; that.mousePressEvent({}); } else if (that.triggered() && !clicked) { - that.triggeredHand = NO_HAND; + that.triggeredHand = NO_TRIGGER_HAND; that.mouseReleaseEvent({}); } }; } - function makePressHandler(hand) { - return function (value) { - if (value >= TRIGGER_ON_VALUE && !that.triggered() && !pointingAtDesktopWindowOrTablet(hand)) { - that.pressedHand = hand; - that.updateHighlight({}); - } else { - that.pressedHand = NO_HAND; - that.resetPreviousHandleColor(); - } - } - } - that.triggerClickMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Controller.Standard.RightHand)); - that.triggerClickMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Controller.Standard.LeftHand)); - that.triggerPressMapping.from(Controller.Standard.RT).peek().to(makePressHandler(Controller.Standard.RightHand)); - that.triggerPressMapping.from(Controller.Standard.LT).peek().to(makePressHandler(Controller.Standard.LeftHand)); - that.enableTriggerMapping = function() { - that.triggerClickMapping.enable(); - that.triggerPressMapping.enable(); - }; - that.disableTriggerMapping = function() { - that.triggerClickMapping.disable(); - that.triggerPressMapping.disable(); - } - Script.scriptEnding.connect(that.disableTriggerMapping); + that.triggerMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Controller.Standard.RightHand)); + that.triggerMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Controller.Standard.LeftHand)); // FUNCTION DEF(s): Intersection Check Helpers function testRayIntersect(queryRay, overlayIncludes, overlayExcludes) { @@ -984,10 +960,35 @@ SelectionDisplay = (function() { } return Uuid.NULL; }; - - that.updateHighlight = function(event) { + + // FUNCTION: MOUSE MOVE EVENT + var lastMouseEvent = null; + that.mouseMoveEvent = function(event) { + var wantDebug = false; + if (wantDebug) { + print("=============== eST::MouseMoveEvent BEG ======================="); + } + lastMouseEvent = event; + if (activeTool) { + if (wantDebug) { + print(" Trigger ActiveTool(" + activeTool.mode + ")'s onMove"); + } + activeTool.onMove(event); + + if (wantDebug) { + print(" Trigger SelectionManager::update"); + } + SelectionManager._update(); + + if (wantDebug) { + print("=============== eST::MouseMoveEvent END ======================="); + } + // EARLY EXIT--(Move handled via active tool) + return true; + } + // if no tool is active, then just look for handles to highlight... - var pickRay = generalComputePickRay(event.x, event.y); + var pickRay = generalComputePickRay(event.x, event.y); var result = Overlays.findRayIntersection(pickRay); var pickedColor; var highlightNeeded = false; @@ -1038,36 +1039,7 @@ SelectionDisplay = (function() { } else { that.resetPreviousHandleColor(); } - }; - // FUNCTION: MOUSE MOVE EVENT - var lastMouseEvent = null; - that.mouseMoveEvent = function(event) { - var wantDebug = false; - if (wantDebug) { - print("=============== eST::MouseMoveEvent BEG ======================="); - } - lastMouseEvent = event; - if (activeTool) { - if (wantDebug) { - print(" Trigger ActiveTool(" + activeTool.mode + ")'s onMove"); - } - activeTool.onMove(event); - - if (wantDebug) { - print(" Trigger SelectionManager::update"); - } - SelectionManager._update(); - - if (wantDebug) { - print("=============== eST::MouseMoveEvent END ======================="); - } - // EARLY EXIT--(Move handled via active tool) - return true; - } - - that.updateHighlight(event); - if (wantDebug) { print("=============== eST::MouseMoveEvent END ======================="); } @@ -1163,10 +1135,9 @@ SelectionDisplay = (function() { } }; - function controllerComputePickRay(hand) { - var hand = that.triggered() ? that.triggeredHand : that.pressedHand; - var controllerPose = getControllerWorldLocation(hand, true); - if (controllerPose.valid) { + function controllerComputePickRay() { + var controllerPose = getControllerWorldLocation(that.triggeredHand, true); + if (controllerPose.valid && that.triggered()) { var controllerPosition = controllerPose.translation; // This gets point direction right, but if you want general quaternion it would be more complicated: var controllerDirection = Quat.getUp(controllerPose.rotation); @@ -1177,12 +1148,6 @@ SelectionDisplay = (function() { function generalComputePickRay(x, y) { return controllerComputePickRay() || Camera.computePickRay(x, y); } - - function getControllerAvatarFramePositionFromPickRay(pickRay) { - var controllerPosition = Vec3.subtract(pickRay.origin, MyAvatar.position); - controllerPosition = Vec3.multiplyQbyV(Quat.inverse(MyAvatar.orientation), controllerPosition); - return controllerPosition; - } function getDistanceToCamera(position) { var cameraPosition = Camera.getPosition(); @@ -2118,7 +2083,6 @@ SelectionDisplay = (function() { var rotation = null; var previousPickRay = null; var beginMouseEvent = null; - var beginControllerPosition = null; var onBegin = function(event, pickRay, pickResult) { var proportional = directionEnum === STRETCH_DIRECTION.ALL; @@ -2254,9 +2218,6 @@ SelectionDisplay = (function() { previousPickRay = pickRay; beginMouseEvent = event; - if (that.triggered()) { - beginControllerPosition = getControllerAvatarFramePositionFromPickRay(pickRay); - } }; var onEnd = function(event, reason) { @@ -2295,15 +2256,13 @@ SelectionDisplay = (function() { if (usePreviousPickRay(pickRay.direction, previousPickRay.direction, planeNormal)) { pickRay = previousPickRay; } - - var controllerPose = getControllerWorldLocation(that.triggeredHand, true); - var controllerTrigger = HMD.isHMDAvailable() && HMD.isHandControllerAvailable() && - controllerPose.valid && that.triggered(); // Are we using handControllers or Mouse - only relevant for 3D tools + var controllerPose = getControllerWorldLocation(that.triggeredHand, true); var vector = null; var newPick = null; - if (controllerTrigger && directionFor3DStretch) { + if (HMD.isHMDAvailable() && HMD.isHandControllerAvailable() && + controllerPose.valid && that.triggered() && directionFor3DStretch) { localDeltaPivot = deltaPivot3D; newPick = pickRay.origin; vector = Vec3.subtract(newPick, lastPick3D); @@ -2327,23 +2286,12 @@ SelectionDisplay = (function() { var newDimensions; if (proportional) { var viewportDimensions = Controller.getViewportDimensions(); + var mouseXDifference = (event.x - beginMouseEvent.x) / viewportDimensions.x; + var mouseYDifference = (beginMouseEvent.y - event.y) / viewportDimensions.y; + var mouseDifference = mouseXDifference + mouseYDifference; var toCameraDistance = getDistanceToCamera(position); var dimensionsMultiple = toCameraDistance * STRETCH_DIRECTION_ALL_CAMERA_DISTANCE_MULTIPLE; - - var dimensionChange; - if (controllerTrigger) { - var controllerPosition = getControllerAvatarFramePositionFromPickRay(pickRay); - var vecControllerDifference = Vec3.subtract(controllerPosition, beginControllerPosition); - var controllerDifference = vecControllerDifference.x + vecControllerDifference.y + - vecControllerDifference.z; - dimensionChange = controllerDifference * dimensionsMultiple; - } else { - var mouseXDifference = (event.x - beginMouseEvent.x) / viewportDimensions.x; - var mouseYDifference = (beginMouseEvent.y - event.y) / viewportDimensions.y; - var mouseDifference = mouseXDifference + mouseYDifference; - dimensionChange = mouseDifference * dimensionsMultiple; - } - + var dimensionChange = mouseDifference * dimensionsMultiple; var averageInitialDimension = (initialDimensions.x + initialDimensions.y + initialDimensions.z) / 3; percentChange = dimensionChange / averageInitialDimension; percentChange += 1.0; From 40451a1fc1f22fa9e6f169b36f0b3802777cd095 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Thu, 13 Sep 2018 17:21:07 -0700 Subject: [PATCH 5/8] Revert "Revert "Merge branch 'master' into fixGoToButtonInterstitial"" This reverts commit c5468fd8e9169cf97a814e3db2db6d66ac1549ed. --- .../src/avatars/AvatarMixerClientData.cpp | 22 +- .../src/avatars/AvatarMixerClientData.h | 13 +- .../src/avatars/AvatarMixerSlave.cpp | 211 ++++++++---------- .../meshes/redirect/oopsDialog_timeout.fbx | Bin 0 -> 31904 bytes .../meshes/redirect/oopsDialog_timeout.png | Bin 0 -> 4555 bytes .../meshes/redirect/oopsDialog_vague.fbx | Bin 32480 -> 32448 bytes .../qml/LoginDialog/LinkAccountBody.qml | 25 ++- interface/src/Application.cpp | 14 +- interface/src/avatar/AvatarManager.cpp | 40 ++-- interface/src/octree/SafeLanding.cpp | 30 ++- libraries/avatars/src/AvatarData.cpp | 196 +++++++--------- libraries/avatars/src/AvatarData.h | 4 +- libraries/networking/src/AccountManager.cpp | 7 - libraries/networking/src/AccountManager.h | 1 - libraries/networking/src/AddressManager.cpp | 4 +- libraries/networking/src/DomainHandler.cpp | 7 +- libraries/networking/src/DomainHandler.h | 5 + libraries/networking/src/udt/Socket.cpp | 82 +++++-- libraries/networking/src/udt/Socket.h | 14 ++ libraries/physics/src/PhysicsEngine.cpp | 1 + libraries/shared/src/PrioritySortUtil.h | 77 ++----- scripts/defaultScripts.js | 5 +- scripts/system/edit.js | 4 +- .../system/libraries/entitySelectionTool.js | 168 +++++++++----- 24 files changed, 479 insertions(+), 451 deletions(-) create mode 100644 interface/resources/meshes/redirect/oopsDialog_timeout.fbx create mode 100644 interface/resources/meshes/redirect/oopsDialog_timeout.png diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index f524c071ec..6c01e6e02b 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -11,6 +11,7 @@ #include "AvatarMixerClientData.h" +#include #include #include @@ -218,6 +219,10 @@ uint16_t AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& node } void AvatarMixerClientData::ignoreOther(SharedNodePointer self, SharedNodePointer other) { + ignoreOther(self.data(), other.data()); +} + +void AvatarMixerClientData::ignoreOther(const Node* self, const Node* other) { if (!isRadiusIgnoring(other->getUUID())) { addToRadiusIgnoringSet(other->getUUID()); auto killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason), true); @@ -235,9 +240,20 @@ void AvatarMixerClientData::ignoreOther(SharedNodePointer self, SharedNodePointe } } -void AvatarMixerClientData::removeFromRadiusIgnoringSet(SharedNodePointer self, const QUuid& other) { - if (isRadiusIgnoring(other)) { - _radiusIgnoredOthers.erase(other); +bool AvatarMixerClientData::isRadiusIgnoring(const QUuid& other) const { + return std::find(_radiusIgnoredOthers.cbegin(), _radiusIgnoredOthers.cend(), other) != _radiusIgnoredOthers.cend(); +} + +void AvatarMixerClientData::addToRadiusIgnoringSet(const QUuid& other) { + if (!isRadiusIgnoring(other)) { + _radiusIgnoredOthers.push_back(other); + } +} + +void AvatarMixerClientData::removeFromRadiusIgnoringSet(const QUuid& other) { + auto ignoredOtherIter = std::find(_radiusIgnoredOthers.cbegin(), _radiusIgnoredOthers.cend(), other); + if (ignoredOtherIter != _radiusIgnoredOthers.cend()) { + _radiusIgnoredOthers.erase(ignoredOtherIter); } } diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index a892455fe3..d38a90ef1f 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include @@ -45,6 +45,7 @@ public: int parseData(ReceivedMessage& message) override; AvatarData& getAvatar() { return *_avatar; } + const AvatarData& getAvatar() const { return *_avatar; } const AvatarData* getConstAvatarData() const { return _avatar.get(); } AvatarSharedPointer getAvatarSharedPointer() const { return _avatar; } @@ -90,11 +91,11 @@ public: void loadJSONStats(QJsonObject& jsonObject) const; glm::vec3 getPosition() const { return _avatar ? _avatar->getWorldPosition() : glm::vec3(0); } - glm::vec3 getGlobalBoundingBoxCorner() const { return _avatar ? _avatar->getGlobalBoundingBoxCorner() : glm::vec3(0); } - bool isRadiusIgnoring(const QUuid& other) const { return _radiusIgnoredOthers.find(other) != _radiusIgnoredOthers.end(); } - void addToRadiusIgnoringSet(const QUuid& other) { _radiusIgnoredOthers.insert(other); } - void removeFromRadiusIgnoringSet(SharedNodePointer self, const QUuid& other); + bool isRadiusIgnoring(const QUuid& other) const; + void addToRadiusIgnoringSet(const QUuid& other); + void removeFromRadiusIgnoringSet(const QUuid& other); void ignoreOther(SharedNodePointer self, SharedNodePointer other); + void ignoreOther(const Node* self, const Node* other); void readViewFrustumPacket(const QByteArray& message); @@ -166,7 +167,7 @@ private: int _numOutOfOrderSends = 0; SimpleMovingAverage _avgOtherAvatarDataRate; - std::unordered_set _radiusIgnoredOthers; + std::vector _radiusIgnoredOthers; ConicalViewFrustums _currentViewFrustums; int _recentOtherAvatarsInView { 0 }; diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index c434d82116..7368db0c31 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -33,6 +34,8 @@ #include "AvatarMixer.h" #include "AvatarMixerClientData.h" +namespace chrono = std::chrono; + void AvatarMixerSlave::configure(ConstIter begin, ConstIter end) { _begin = begin; _end = end; @@ -209,7 +212,18 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { _stats.jobElapsedTime += (end - start); } +AABox computeBubbleBox(const AvatarData& avatar, float bubbleExpansionFactor) { + AABox box = avatar.getGlobalBoundingBox(); + glm::vec3 scale = box.getScale(); + scale *= bubbleExpansionFactor; + const glm::vec3 MIN_BUBBLE_SCALE(0.3f, 1.3f, 0.3); + scale = glm::max(scale, MIN_BUBBLE_SCALE); + box.setScaleStayCentered(glm::max(scale, MIN_BUBBLE_SCALE)); + return box; +} + void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) { + const Node* destinationNode = node.data(); auto nodeList = DependencyManager::get(); @@ -220,7 +234,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) _stats.nodesBroadcastedTo++; - AvatarMixerClientData* nodeData = reinterpret_cast(node->getLinkedData()); + AvatarMixerClientData* nodeData = reinterpret_cast(destinationNode->getLinkedData()); nodeData->resetInViewStats(); @@ -242,12 +256,8 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) int traitBytesSent = 0; // max number of avatarBytes per frame - auto maxAvatarBytesPerFrame = (_maxKbpsPerNode * BYTES_PER_KILOBIT) / AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND; + int maxAvatarBytesPerFrame = int(_maxKbpsPerNode * BYTES_PER_KILOBIT / AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND); - // FIXME - find a way to not send the sessionID for every avatar - int minimumBytesPerAvatar = AvatarDataPacket::AVATAR_HAS_FLAGS_SIZE + NUM_BYTES_RFC4122_UUID; - - int overBudgetAvatars = 0; // keep track of the number of other avatars held back in this frame int numAvatarsHeldBack = 0; @@ -260,66 +270,38 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) bool PALIsOpen = nodeData->getRequestsDomainListData(); // When this is true, the AvatarMixer will send Avatar data to a client about avatars that have ignored them - bool getsAnyIgnored = PALIsOpen && node->getCanKick(); + bool getsAnyIgnored = PALIsOpen && destinationNode->getCanKick(); - if (PALIsOpen) { - // Increase minimumBytesPerAvatar if the PAL is open - minimumBytesPerAvatar += sizeof(AvatarDataPacket::AvatarGlobalPosition) + - sizeof(AvatarDataPacket::AudioLoudness); - } + // Bandwidth allowance for data that must be sent. + int minimumBytesPerAvatar = PALIsOpen ? AvatarDataPacket::AVATAR_HAS_FLAGS_SIZE + NUM_BYTES_RFC4122_UUID + + sizeof(AvatarDataPacket::AvatarGlobalPosition) + sizeof(AvatarDataPacket::AudioLoudness) : 0; // setup a PacketList for the avatarPackets auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData); + static auto maxAvatarDataBytes = avatarPacketList->getMaxSegmentSize() - NUM_BYTES_RFC4122_UUID; - // Define the minimum bubble size - static const glm::vec3 minBubbleSize = avatar.getSensorToWorldScale() * glm::vec3(0.3f, 1.3f, 0.3f); - // Define the scale of the box for the current node - glm::vec3 nodeBoxScale = (nodeData->getPosition() - nodeData->getGlobalBoundingBoxCorner()) * 2.0f * avatar.getSensorToWorldScale(); - // Set up the bounding box for the current node - AABox nodeBox(nodeData->getGlobalBoundingBoxCorner(), nodeBoxScale); - // Clamp the size of the bounding box to a minimum scale - if (glm::any(glm::lessThan(nodeBoxScale, minBubbleSize))) { - nodeBox.setScaleStayCentered(minBubbleSize); - } - // Quadruple the scale of both bounding boxes - nodeBox.embiggen(4.0f); - - - // setup list of AvatarData as well as maps to map betweeen the AvatarData and the original nodes - std::vector avatarsToSort; - std::unordered_map avatarDataToNodes; - std::unordered_map avatarEncodeTimes; - std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) { - // make sure this is an agent that we have avatar data for before considering it for inclusion - if (otherNode->getType() == NodeType::Agent - && otherNode->getLinkedData()) { - const AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); - - AvatarSharedPointer otherAvatar = otherNodeData->getAvatarSharedPointer(); - avatarsToSort.push_back(otherAvatar); - avatarDataToNodes[otherAvatar] = otherNode; - QUuid id = otherAvatar->getSessionUUID(); - avatarEncodeTimes[id] = nodeData->getLastOtherAvatarEncodeTime(id); - } - }); + // compute node bounding box + const float MY_AVATAR_BUBBLE_EXPANSION_FACTOR = 4.0f; // magic number determined emperically + AABox nodeBox = computeBubbleBox(avatar, MY_AVATAR_BUBBLE_EXPANSION_FACTOR); class SortableAvatar: public PrioritySortUtil::Sortable { public: SortableAvatar() = delete; - SortableAvatar(const AvatarSharedPointer& avatar, uint64_t lastEncodeTime) - : _avatar(avatar), _lastEncodeTime(lastEncodeTime) {} - glm::vec3 getPosition() const override { return _avatar->getWorldPosition(); } + SortableAvatar(const AvatarData* avatar, const Node* avatarNode, uint64_t lastEncodeTime) + : _avatar(avatar), _node(avatarNode), _lastEncodeTime(lastEncodeTime) {} + glm::vec3 getPosition() const override { return _avatar->getClientGlobalPosition(); } float getRadius() const override { - glm::vec3 nodeBoxHalfScale = (_avatar->getWorldPosition() - _avatar->getGlobalBoundingBoxCorner() * _avatar->getSensorToWorldScale()); - return glm::max(nodeBoxHalfScale.x, glm::max(nodeBoxHalfScale.y, nodeBoxHalfScale.z)); + glm::vec3 nodeBoxScale = _avatar->getGlobalBoundingBox().getScale(); + return 0.5f * glm::max(nodeBoxScale.x, glm::max(nodeBoxScale.y, nodeBoxScale.z)); } uint64_t getTimestamp() const override { return _lastEncodeTime; } - AvatarSharedPointer getAvatar() const { return _avatar; } + const Node* getNode() const { return _node; } private: - AvatarSharedPointer _avatar; + const AvatarData* _avatar; + const Node* _node; uint64_t _lastEncodeTime; }; @@ -329,16 +311,18 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) AvatarData::_avatarSortCoefficientSize, AvatarData::_avatarSortCoefficientCenter, AvatarData::_avatarSortCoefficientAge); - sortedAvatars.reserve(avatarsToSort.size()); + sortedAvatars.reserve(_end - _begin); - // ignore or sort - const AvatarSharedPointer& thisAvatar = nodeData->getAvatarSharedPointer(); - for (const auto& avatar : avatarsToSort) { - if (avatar == thisAvatar) { - // don't echo updates to self + for (auto listedNode = _begin; listedNode != _end; ++listedNode) { + Node* otherNodeRaw = (*listedNode).data(); + if (otherNodeRaw->getType() != NodeType::Agent + || !otherNodeRaw->getLinkedData() + || otherNodeRaw == destinationNode) { continue; } + auto avatarNode = otherNodeRaw; + bool shouldIgnore = false; // We ignore other nodes for a couple of reasons: // 1) ignore bubbles and ignore specific node @@ -346,53 +330,39 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // happen if for example the avatar is connected on a desktop and sending // updates at ~30hz. So every 3 frames we skip a frame. - auto avatarNode = avatarDataToNodes[avatar]; assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map - const AvatarMixerClientData* avatarNodeData = reinterpret_cast(avatarNode->getLinkedData()); - assert(avatarNodeData); // we can't have gotten here without avatarNode having valid data + const AvatarMixerClientData* avatarClientNodeData = reinterpret_cast(avatarNode->getLinkedData()); + assert(avatarClientNodeData); // we can't have gotten here without avatarNode having valid data quint64 startIgnoreCalculation = usecTimestampNow(); // make sure we have data for this avatar, that it isn't the same node, // and isn't an avatar that the viewing node has ignored // or that has ignored the viewing node - if (!avatarNode->getLinkedData() - || avatarNode->getUUID() == node->getUUID() - || (node->isIgnoringNodeWithID(avatarNode->getUUID()) && !PALIsOpen) - || (avatarNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) { + if ((destinationNode->isIgnoringNodeWithID(avatarNode->getUUID()) && !PALIsOpen) + || (avatarNode->isIgnoringNodeWithID(destinationNode->getUUID()) && !getsAnyIgnored)) { shouldIgnore = true; } else { // Check to see if the space bubble is enabled // Don't bother with these checks if the other avatar has their bubble enabled and we're gettingAnyIgnored - if (node->isIgnoreRadiusEnabled() || (avatarNode->isIgnoreRadiusEnabled() && !getsAnyIgnored)) { - float sensorToWorldScale = avatarNodeData->getAvatarSharedPointer()->getSensorToWorldScale(); - // Define the scale of the box for the current other node - glm::vec3 otherNodeBoxScale = (avatarNodeData->getPosition() - avatarNodeData->getGlobalBoundingBoxCorner()) * 2.0f * sensorToWorldScale; - // Set up the bounding box for the current other node - AABox otherNodeBox(avatarNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); - // Clamp the size of the bounding box to a minimum scale - if (glm::any(glm::lessThan(otherNodeBoxScale, minBubbleSize))) { - otherNodeBox.setScaleStayCentered(minBubbleSize); - } - // Change the scale of both bounding boxes - // (This is an arbitrary number determined empirically) - otherNodeBox.embiggen(2.4f); - + if (destinationNode->isIgnoreRadiusEnabled() || (avatarNode->isIgnoreRadiusEnabled() && !getsAnyIgnored)) { // Perform the collision check between the two bounding boxes + const float OTHER_AVATAR_BUBBLE_EXPANSION_FACTOR = 2.4f; // magic number determined empirically + AABox otherNodeBox = computeBubbleBox(avatarClientNodeData->getAvatar(), OTHER_AVATAR_BUBBLE_EXPANSION_FACTOR); if (nodeBox.touches(otherNodeBox)) { - nodeData->ignoreOther(node, avatarNode); + nodeData->ignoreOther(destinationNode, avatarNode); shouldIgnore = !getsAnyIgnored; } } // Not close enough to ignore if (!shouldIgnore) { - nodeData->removeFromRadiusIgnoringSet(node, avatarNode->getUUID()); + nodeData->removeFromRadiusIgnoringSet(avatarNode->getUUID()); } } if (!shouldIgnore) { AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(avatarNode->getUUID()); - AvatarDataSequenceNumber lastSeqFromSender = avatarNodeData->getLastReceivedSequenceNumber(); + AvatarDataSequenceNumber lastSeqFromSender = avatarClientNodeData->getLastReceivedSequenceNumber(); // FIXME - This code does appear to be working. But it seems brittle. // It supports determining if the frame of data for this "other" @@ -417,12 +387,10 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) if (!shouldIgnore) { // sort this one for later - uint64_t lastEncodeTime = 0; - std::unordered_map::const_iterator itr = avatarEncodeTimes.find(avatar->getSessionUUID()); - if (itr != avatarEncodeTimes.end()) { - lastEncodeTime = itr->second; - } - sortedAvatars.push(SortableAvatar(avatar, lastEncodeTime)); + const AvatarData* avatarNodeData = avatarClientNodeData->getConstAvatarData(); + auto lastEncodeTime = nodeData->getLastOtherAvatarEncodeTime(avatarNodeData->getSessionUUID()); + + sortedAvatars.push(SortableAvatar(avatarNodeData, avatarNode, lastEncodeTime)); } } @@ -430,19 +398,31 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) int remainingAvatars = (int)sortedAvatars.size(); auto traitsPacketList = NLPacketList::create(PacketType::BulkAvatarTraits, QByteArray(), true, true); + const auto& sortedAvatarVector = sortedAvatars.getSortedVector(); for (const auto& sortedAvatar : sortedAvatarVector) { - const auto& avatarData = sortedAvatar.getAvatar(); - remainingAvatars--; + const Node* otherNode = sortedAvatar.getNode(); + auto lastEncodeForOther = sortedAvatar.getTimestamp(); - auto otherNode = avatarDataToNodes[avatarData]; assert(otherNode); // we can't have gotten here without the avatarData being a valid key in the map - // NOTE: Here's where we determine if we are over budget and drop to bare minimum data + AvatarData::AvatarDataDetail detail = AvatarData::NoData; + + // NOTE: Here's where we determine if we are over budget and drop remaining avatars, + // or send minimal avatar data in uncommon case of PALIsOpen. int minimRemainingAvatarBytes = minimumBytesPerAvatar * remainingAvatars; bool overBudget = (identityBytesSent + numAvatarDataBytes + minimRemainingAvatarBytes) > maxAvatarBytesPerFrame; + if (overBudget) { + if (PALIsOpen) { + _stats.overBudgetAvatars++; + detail = AvatarData::PALMinimum; + } else { + _stats.overBudgetAvatars += remainingAvatars; + break; + } + } - quint64 startAvatarDataPacking = usecTimestampNow(); + auto startAvatarDataPacking = chrono::high_resolution_clock::now(); ++numOtherAvatars; @@ -459,32 +439,18 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) nodeData->setLastBroadcastTime(otherNode->getUUID(), usecTimestampNow()); } - // determine if avatar is in view which determines how much data to send - glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition(); - glm::vec3 otherNodeBoxScale = (otherPosition - otherNodeData->getGlobalBoundingBoxCorner()) * 2.0f * otherAvatar->getSensorToWorldScale(); - AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); - bool isInView = nodeData->otherAvatarInView(otherNodeBox); + // Typically all out-of-view avatars but such avatars' priorities will rise with time: + bool isLowerPriority = sortedAvatar.getPriority() <= OUT_OF_VIEW_THRESHOLD; - // start a new segment in the PacketList for this avatar - avatarPacketList->startSegment(); - - AvatarData::AvatarDataDetail detail; - - if (overBudget) { - overBudgetAvatars++; - _stats.overBudgetAvatars++; - detail = PALIsOpen ? AvatarData::PALMinimum : AvatarData::NoData; - } else if (!isInView) { + if (isLowerPriority) { detail = PALIsOpen ? AvatarData::PALMinimum : AvatarData::MinimumData; nodeData->incrementAvatarOutOfView(); - } else { - detail = distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO - ? AvatarData::SendAllData : AvatarData::CullSmallData; + } else if (!overBudget) { + detail = distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO ? AvatarData::SendAllData : AvatarData::CullSmallData; nodeData->incrementAvatarInView(); } bool includeThisAvatar = true; - auto lastEncodeForOther = nodeData->getLastOtherAvatarEncodeTime(otherNode->getUUID()); QVector& lastSentJointsForOther = nodeData->getLastOtherAvatarSentJoints(otherNode->getUUID()); lastSentJointsForOther.resize(otherAvatar->getJointCount()); @@ -494,14 +460,14 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) AvatarDataPacket::HasFlags hasFlagsOut; // the result of the toByteArray bool dropFaceTracking = false; - quint64 start = usecTimestampNow(); + auto startSerialize = chrono::high_resolution_clock::now(); QByteArray bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther, hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther); - quint64 end = usecTimestampNow(); - _stats.toByteArrayElapsedTime += (end - start); + auto endSerialize = chrono::high_resolution_clock::now(); + _stats.toByteArrayElapsedTime += + (quint64) chrono::duration_cast(endSerialize - startSerialize).count(); - static auto maxAvatarDataBytes = avatarPacketList->getMaxSegmentSize() - NUM_BYTES_RFC4122_UUID; if (bytes.size() > maxAvatarDataBytes) { qCWarning(avatars) << "otherAvatar.toByteArray() for" << otherNode->getUUID() << "resulted in very large buffer of" << bytes.size() << "bytes - dropping facial data"; @@ -527,8 +493,11 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) } if (includeThisAvatar) { + // start a new segment in the PacketList for this avatar + avatarPacketList->startSegment(); numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122()); numAvatarDataBytes += avatarPacketList->write(bytes); + avatarPacketList->endSegment(); if (detail != AvatarData::NoData) { _stats.numOthersIncluded++; @@ -546,15 +515,13 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // It would be nice if we could tweak its future sort priority to put it at the back of the list. } - avatarPacketList->endSegment(); - - quint64 endAvatarDataPacking = usecTimestampNow(); - _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); + auto endAvatarDataPacking = chrono::high_resolution_clock::now(); + _stats.avatarDataPackingElapsedTime += + (quint64) chrono::duration_cast(endAvatarDataPacking - startAvatarDataPacking).count(); // use helper to add any changed traits to our packet list traitBytesSent += addChangedTraitsToBulkPacket(nodeData, otherNodeData, *traitsPacketList); - - traitsPacketList->getDataSize(); + remainingAvatars--; } quint64 startPacketSending = usecTimestampNow(); @@ -566,7 +533,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) _stats.numBytesSent += numAvatarDataBytes; // send the avatar data PacketList - nodeList->sendPacketList(std::move(avatarPacketList), *node); + nodeList->sendPacketList(std::move(avatarPacketList), *destinationNode); // record the bytes sent for other avatar data in the AvatarMixerClientData nodeData->recordSentAvatarData(numAvatarDataBytes); @@ -576,7 +543,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) if (traitsPacketList->getNumPackets() >= 1) { // send the traits packet list - nodeList->sendPacketList(std::move(traitsPacketList), *node); + nodeList->sendPacketList(std::move(traitsPacketList), *destinationNode); } // record the number of avatars held back this frame diff --git a/interface/resources/meshes/redirect/oopsDialog_timeout.fbx b/interface/resources/meshes/redirect/oopsDialog_timeout.fbx new file mode 100644 index 0000000000000000000000000000000000000000..846d38bbee3f38ddb874005669bb38561f293584 GIT binary patch literal 31904 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 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/redirect/oopsDialog_vague.fbx b/interface/resources/meshes/redirect/oopsDialog_vague.fbx index 324d90578b833552f01a1199fbded078b49900ba..707f09e51a8872b44615aafbe7d5dab5c7306f51 100644 GIT binary patch delta 1251 zcmZ`&UrbwN6hGft|BN~;AnSsotX(_CoY3B~G9V_5F$H5giXd%C=GK_jb+X>udv8k% z?O=)73pkM@50Xt-Voaus5$k-I$?TK|eK0uVpTq~3r?&dJF+ zzw`Uf`JG#9thmNr&occs0GOu*w!^6F;Zx$n7P`P3%t}S(FjN9S1=1yEp>^;U=fOjWxNztXC(+`Tn*D%A;7%UKh()i7iNb7)%c@8Cqq`l z&)q7LiMQfPf?f$Z@poQAMZ9)l*i8LYf;hO>t3?0Hx1a%JklXmtD z`IENY6%Q$R0R3hFh{cDaTpO@~Xhu$n(S87c2tAwR*g3k9bmH|S^*&;uwWJM~MYSs@ zlC`0S7MwaJ({tisd`qOoh6m`yS}U%L!waKfR zxb$HhOqY1Ed0I}p@R#%^t!C;Q=|o|qL<=t~?m_%&gigr^usTBvvJcm@bUssWIPus^ ziYl&UcHy^Kwbyr%Vmb?HkktJ(G4PD7L`(jLMsM8EIZCKXT zj{wjc@ckE~0sscYNJ0`KVl*ME{mOG%;g*g18pYCluDnV+1%OSULo}J&U8%|%z*qW< zM3Xstm3FHDzS2ZqR>EfOGB?AtShf>&XH;dcTG%M*SVv2lV+LpoZO4cikc|vnD=-+em@Ok(qz3KVzHMm> zj3s6_%7yYwFQ&3_vzRO&22-(0TOT5AP!G#}jADP5tR?XW2W=ni8pPW3; zFXue}H=W@-8Jo5NIV&sjCp9Ea^D*meUxEv_lLsc#Y&+i^=0>I z=5d62_Mpvd!(8KTz6oHc+VRYjS}*G zvKW^XZ`~8#D=`sx6l;vh>1AvN@bSw?kDl; zAICczg*{{w7ov~x%|)|(N1`TiBbMc}#kTn5*cRXQ*d`zU4Jl?79m8O`Lre~50dFES zoCOpGaeN~m5=Y|L9&rdcj^>D4oJqnOe@3PIb?JVF@nXD8v?Q}(tqDxWTg5vG^cOd3 zB-g}OP9pj)0b`{BoXwv_7SsX^XmT% zK21c&0s{k4IX72<=23G|uF*>VMooINa;}d=?#;kAvvTeY61g|A=M-6+dvRN`^2gv( zq(_Zx#_DL55a)1ruO8)NMLY)Qn4M?w%2*x$jQ7Vb=y%>uufFu@$od;)_bQ}~B@m(d G9R3AsWTBP- diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 4196b7f168..57293cb5e3 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -346,7 +346,15 @@ Item { target: loginDialog onHandleLoginCompleted: { console.log("Login Succeeded, linking steam account") - + var poppedUp = Settings.getValue("loginDialogPoppedUp", false); + if (poppedUp) { + console.log("[ENCOURAGELOGINDIALOG]: logging in") + var data = { + "action": "user logged in" + }; + UserActivityLogger.logAction("encourageLoginDialog", data); + Settings.setValue("loginDialogPoppedUp", false); + } if (loginDialog.isSteamRunning()) { loginDialog.linkSteam() } else { @@ -354,23 +362,20 @@ Item { bodyLoader.item.width = root.pane.width bodyLoader.item.height = root.pane.height } - if (Settings.getValue("loginDialogPoppedUp", false)) { - var data = { - "action": "user logged in" - }; - UserActivityLogger.logAction("encourageLoginDialog", data); - } } onHandleLoginFailed: { console.log("Login Failed") - mainTextContainer.visible = true - toggleLoading(false) - if (Settings.getValue("loginDialogPoppedUp", false)) { + var poppedUp = Settings.getValue("loginDialogPoppedUp", false); + if (poppedUp) { + console.log("[ENCOURAGELOGINDIALOG]: failed logging in") var data = { "action": "user failed logging in" }; UserActivityLogger.logAction("encourageLoginDialog", data); + Settings.setValue("loginDialogPoppedUp", false); } + mainTextContainer.visible = true + toggleLoading(false) } onHandleLinkCompleted: { console.log("Link Succeeded") diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 77e2cb3211..826f6a87a9 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3498,7 +3498,9 @@ bool Application::isServerlessMode() const { } void Application::setIsInterstitialMode(bool interstitialMode) { - if (_interstitialMode != interstitialMode) { + Settings settings; + bool enableInterstitial = settings.value("enableIntersitialMode", false).toBool(); + if (_interstitialMode != interstitialMode && enableInterstitial) { _interstitialMode = interstitialMode; DependencyManager::get()->setAudioPaused(_interstitialMode); @@ -6346,6 +6348,7 @@ void Application::updateWindowTitle() const { auto nodeList = DependencyManager::get(); auto accountManager = DependencyManager::get(); + auto isInErrorState = nodeList->getDomainHandler().isInErrorState(); QString buildVersion = " - " + (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? QString("Version") : QString("Build")) @@ -6353,14 +6356,19 @@ void Application::updateWindowTitle() const { QString loginStatus = accountManager->isLoggedIn() ? "" : " (NOT LOGGED IN)"; - QString connectionStatus = nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED)"; + QString connectionStatus = isInErrorState ? " (ERROR CONNECTING)" : + nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED)"; QString username = accountManager->getAccountInfo().getUsername(); setCrashAnnotation("username", username.toStdString()); QString currentPlaceName; if (isServerlessMode()) { - currentPlaceName = "serverless: " + DependencyManager::get()->getDomainURL().toString(); + if (isInErrorState) { + currentPlaceName = "serverless: " + nodeList->getDomainHandler().getErrorDomainURL().toString(); + } else { + currentPlaceName = "serverless: " + DependencyManager::get()->getDomainURL().toString(); + } } else { currentPlaceName = DependencyManager::get()->getDomainURL().host(); if (currentPlaceName.isEmpty()) { diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 443d19e473..1faf17ea9a 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -176,29 +176,29 @@ float AvatarManager::getAvatarSimulationRate(const QUuid& sessionID, const QStri } void AvatarManager::updateOtherAvatars(float deltaTime) { - // lock the hash for read to check the size - QReadLocker lock(&_hashLock); - if (_avatarHash.size() < 2 && _avatarsToFade.isEmpty()) { - return; + { + // lock the hash for read to check the size + QReadLocker lock(&_hashLock); + if (_avatarHash.size() < 2 && _avatarsToFade.isEmpty()) { + return; + } } - lock.unlock(); PerformanceTimer perfTimer("otherAvatars"); class SortableAvatar: public PrioritySortUtil::Sortable { public: SortableAvatar() = delete; - SortableAvatar(const AvatarSharedPointer& avatar) : _avatar(avatar) {} + SortableAvatar(const std::shared_ptr& avatar) : _avatar(avatar) {} glm::vec3 getPosition() const override { return _avatar->getWorldPosition(); } - float getRadius() const override { return std::static_pointer_cast(_avatar)->getBoundingRadius(); } - uint64_t getTimestamp() const override { return std::static_pointer_cast(_avatar)->getLastRenderUpdateTime(); } - AvatarSharedPointer getAvatar() const { return _avatar; } + float getRadius() const override { return _avatar->getBoundingRadius(); } + uint64_t getTimestamp() const override { return _avatar->getLastRenderUpdateTime(); } + std::shared_ptr getAvatar() const { return _avatar; } private: - AvatarSharedPointer _avatar; + std::shared_ptr _avatar; }; auto avatarMap = getHashCopy(); - AvatarHash::iterator itr = avatarMap.begin(); const auto& views = qApp->getConicalViews(); PrioritySortUtil::PriorityQueue sortedAvatars(views, @@ -207,22 +207,24 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { AvatarData::_avatarSortCoefficientAge); sortedAvatars.reserve(avatarMap.size() - 1); // don't include MyAvatar - // sort + // Build vector and compute priorities + auto nodeList = DependencyManager::get(); + AvatarHash::iterator itr = avatarMap.begin(); while (itr != avatarMap.end()) { const auto& avatar = std::static_pointer_cast(*itr); // DO NOT update _myAvatar! Its update has already been done earlier in the main loop. // DO NOT update or fade out uninitialized Avatars - if (avatar != _myAvatar && avatar->isInitialized()) { + if (avatar != _myAvatar && avatar->isInitialized() && !nodeList->isPersonalMutingNode(avatar->getID())) { sortedAvatars.push(SortableAvatar(avatar)); } ++itr; } + // Sort const auto& sortedAvatarVector = sortedAvatars.getSortedVector(); // process in sorted order uint64_t startTime = usecTimestampNow(); - const uint64_t UPDATE_BUDGET = 2000; // usec - uint64_t updateExpiry = startTime + UPDATE_BUDGET; + uint64_t updateExpiry = startTime + MAX_UPDATE_AVATARS_TIME_BUDGET; int numAvatarsUpdated = 0; int numAVatarsNotUpdated = 0; @@ -241,18 +243,12 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { avatar->updateOrbPosition(); } - bool ignoring = DependencyManager::get()->isPersonalMutingNode(avatar->getID()); - if (ignoring) { - continue; - } - // for ALL avatars... if (_shouldRender) { avatar->ensureInScene(avatar, qApp->getMain3DScene()); } avatar->animateScaleChanges(deltaTime); - const float OUT_OF_VIEW_THRESHOLD = 0.5f * AvatarData::OUT_OF_VIEW_PENALTY; uint64_t now = usecTimestampNow(); if (now < updateExpiry) { // we're within budget @@ -273,7 +269,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { // no time to simulate, but we take the time to count how many were tragically missed while (it != sortedAvatarVector.end()) { const SortableAvatar& newSortData = *it; - const auto newAvatar = std::static_pointer_cast(newSortData.getAvatar()); + const auto& newAvatar = newSortData.getAvatar(); bool inView = newSortData.getPriority() > OUT_OF_VIEW_THRESHOLD; // Once we reach an avatar that's not in view, all avatars after it will also be out of view if (!inView) { diff --git a/interface/src/octree/SafeLanding.cpp b/interface/src/octree/SafeLanding.cpp index 9785e0ba95..5d4ebe9853 100644 --- a/interface/src/octree/SafeLanding.cpp +++ b/interface/src/octree/SafeLanding.cpp @@ -66,13 +66,18 @@ void SafeLanding::addTrackedEntity(const EntityItemID& entityID) { Locker lock(_lock); EntityItemPointer entity = _entityTree->findEntityByID(entityID); - _trackedEntities.emplace(entityID, entity); - int trackedEntityCount = (int)_trackedEntities.size(); + if (entity) { - if (trackedEntityCount > _maxTrackedEntityCount) { - _maxTrackedEntityCount = trackedEntityCount; + _trackedEntities.emplace(entityID, entity); + int trackedEntityCount = (int)_trackedEntities.size(); + + if (trackedEntityCount > _maxTrackedEntityCount) { + _maxTrackedEntityCount = trackedEntityCount; + } + qCDebug(interfaceapp) << "Safe Landing: Tracking entity " << entity->getItemName(); } - qCDebug(interfaceapp) << "Safe Landing: Tracking entity " << entity->getItemName(); + } else { + qCDebug(interfaceapp) << "Safe Landing: Null Entity: " << entityID; } } @@ -146,7 +151,7 @@ bool isEntityPhysicsReady(const EntityItemPointer& entity) { bool hasAABox; entity->getAABox(hasAABox); if (hasAABox && downloadedCollisionTypes.count(modelEntity->getShapeType()) != 0) { - return entity->isReadyToComputeShape(); + return (!entity->shouldBePhysical() || entity->isReadyToComputeShape()); } } } @@ -156,12 +161,23 @@ bool isEntityPhysicsReady(const EntityItemPointer& entity) { bool SafeLanding::isEntityLoadingComplete() { Locker lock(_lock); + + auto entityTree = qApp->getEntities(); auto entityMapIter = _trackedEntities.begin(); while (entityMapIter != _trackedEntities.end()) { auto entity = entityMapIter->second; - bool isVisuallyReady = (entity->isVisuallyReady() || !entityTree->renderableForEntityId(entityMapIter->first)); + + bool isVisuallyReady = true; + + Settings settings; + bool enableInterstitial = settings.value("enableIntersitialMode", false).toBool(); + + if (enableInterstitial) { + isVisuallyReady = (entity->isVisuallyReady() || !entityTree->renderableForEntityId(entityMapIter->first)); + } + if (isEntityPhysicsReady(entity) && isVisuallyReady) { entityMapIter = _trackedEntities.erase(entityMapIter); } else { diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index ea6dbd7074..834754e228 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -363,13 +363,13 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent memcpy(destinationBuffer, &packetStateFlags, sizeof(packetStateFlags)); destinationBuffer += sizeof(packetStateFlags); +#define AVATAR_MEMCPY(src) \ + memcpy(destinationBuffer, &(src), sizeof(src)); \ + destinationBuffer += sizeof(src); + if (hasAvatarGlobalPosition) { auto startSection = destinationBuffer; - auto data = reinterpret_cast(destinationBuffer); - data->globalPosition[0] = _globalPosition.x; - data->globalPosition[1] = _globalPosition.y; - data->globalPosition[2] = _globalPosition.z; - destinationBuffer += sizeof(AvatarDataPacket::AvatarGlobalPosition); + AVATAR_MEMCPY(_globalPosition); int numBytes = destinationBuffer - startSection; @@ -380,17 +380,8 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent if (hasAvatarBoundingBox) { auto startSection = destinationBuffer; - auto data = reinterpret_cast(destinationBuffer); - - data->avatarDimensions[0] = _globalBoundingBoxDimensions.x; - data->avatarDimensions[1] = _globalBoundingBoxDimensions.y; - data->avatarDimensions[2] = _globalBoundingBoxDimensions.z; - - data->boundOriginOffset[0] = _globalBoundingBoxOffset.x; - data->boundOriginOffset[1] = _globalBoundingBoxOffset.y; - data->boundOriginOffset[2] = _globalBoundingBoxOffset.z; - - destinationBuffer += sizeof(AvatarDataPacket::AvatarBoundingBox); + AVATAR_MEMCPY(_globalBoundingBoxDimensions); + AVATAR_MEMCPY(_globalBoundingBoxOffset); int numBytes = destinationBuffer - startSection; if (outboundDataRateOut) { @@ -424,13 +415,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent if (hasLookAtPosition) { auto startSection = destinationBuffer; - auto data = reinterpret_cast(destinationBuffer); - auto lookAt = _headData->getLookAtPosition(); - data->lookAtPosition[0] = lookAt.x; - data->lookAtPosition[1] = lookAt.y; - data->lookAtPosition[2] = lookAt.z; - destinationBuffer += sizeof(AvatarDataPacket::LookAtPosition); - + AVATAR_MEMCPY(_headData->getLookAtPosition()); int numBytes = destinationBuffer - startSection; if (outboundDataRateOut) { outboundDataRateOut->lookAtPositionRate.increment(numBytes); @@ -531,12 +516,8 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent if (hasAvatarLocalPosition) { auto startSection = destinationBuffer; - auto data = reinterpret_cast(destinationBuffer); - auto localPosition = getLocalPosition(); - data->localPosition[0] = localPosition.x; - data->localPosition[1] = localPosition.y; - data->localPosition[2] = localPosition.z; - destinationBuffer += sizeof(AvatarDataPacket::AvatarLocalPosition); + const auto localPosition = getLocalPosition(); + AVATAR_MEMCPY(localPosition); int numBytes = destinationBuffer - startSection; if (outboundDataRateOut) { @@ -567,19 +548,24 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent } } + QVector jointData; + if (hasJointData || hasJointDefaultPoseFlags) { + QReadLocker readLock(&_jointDataLock); + jointData = _jointData; + } + // If it is connected, pack up the data if (hasJointData) { auto startSection = destinationBuffer; - QReadLocker readLock(&_jointDataLock); // joint rotation data - int numJoints = _jointData.size(); + int numJoints = jointData.size(); *destinationBuffer++ = (uint8_t)numJoints; unsigned char* validityPosition = destinationBuffer; unsigned char validity = 0; int validityBit = 0; - int numValidityBytes = (int)std::ceil(numJoints / (float)BITS_IN_BYTE); + int numValidityBytes = calcBitVectorSize(numJoints); #ifdef WANT_DEBUG int rotationSentCount = 0; @@ -589,43 +575,37 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent destinationBuffer += numValidityBytes; // Move pointer past the validity bytes // sentJointDataOut and lastSentJointData might be the same vector - // build sentJointDataOut locally and then swap it at the end. - QVector localSentJointDataOut; if (sentJointDataOut) { - localSentJointDataOut.resize(numJoints); // Make sure the destination is resized before using it + sentJointDataOut->resize(numJoints); // Make sure the destination is resized before using it } - float minRotationDOT = !distanceAdjust ? AVATAR_MIN_ROTATION_DOT : getDistanceBasedMinRotationDOT(viewerPosition); + float minRotationDOT = (distanceAdjust && cullSmallChanges) ? getDistanceBasedMinRotationDOT(viewerPosition) : AVATAR_MIN_ROTATION_DOT; - for (int i = 0; i < _jointData.size(); i++) { - const JointData& data = _jointData[i]; + for (int i = 0; i < jointData.size(); i++) { + const JointData& data = jointData[i]; const JointData& last = lastSentJointData[i]; if (!data.rotationIsDefaultPose) { - bool mustSend = sendAll || last.rotationIsDefaultPose; - if (mustSend || last.rotation != data.rotation) { - - bool largeEnoughRotation = true; - if (cullSmallChanges) { - // The dot product for smaller rotations is a smaller number. - // So if the dot() is less than the value, then the rotation is a larger angle of rotation - largeEnoughRotation = fabsf(glm::dot(last.rotation, data.rotation)) < minRotationDOT; - } - - if (mustSend || !cullSmallChanges || largeEnoughRotation) { - validity |= (1 << validityBit); + // The dot product for larger rotations is a lower number. + // So if the dot() is less than the value, then the rotation is a larger angle of rotation + if (sendAll || last.rotationIsDefaultPose || (!cullSmallChanges && last.rotation != data.rotation) + || (cullSmallChanges && glm::dot(last.rotation, data.rotation) < minRotationDOT) ) { + validity |= (1 << validityBit); #ifdef WANT_DEBUG - rotationSentCount++; + rotationSentCount++; #endif - destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, data.rotation); + destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, data.rotation); - if (sentJointDataOut) { - localSentJointDataOut[i].rotation = data.rotation; - localSentJointDataOut[i].rotationIsDefaultPose = false; - } + if (sentJointDataOut) { + (*sentJointDataOut)[i].rotation = data.rotation; } } } + + if (sentJointDataOut) { + (*sentJointDataOut)[i].rotationIsDefaultPose = data.rotationIsDefaultPose; + } + if (++validityBit == BITS_IN_BYTE) { *validityPosition++ = validity; validityBit = validity = 0; @@ -647,35 +627,38 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent destinationBuffer += numValidityBytes; // Move pointer past the validity bytes - float minTranslation = !distanceAdjust ? AVATAR_MIN_TRANSLATION : getDistanceBasedMinTranslationDistance(viewerPosition); + float minTranslation = (distanceAdjust && cullSmallChanges) ? getDistanceBasedMinTranslationDistance(viewerPosition) : AVATAR_MIN_TRANSLATION; float maxTranslationDimension = 0.0; - for (int i = 0; i < _jointData.size(); i++) { - const JointData& data = _jointData[i]; + for (int i = 0; i < jointData.size(); i++) { + const JointData& data = jointData[i]; const JointData& last = lastSentJointData[i]; if (!data.translationIsDefaultPose) { - bool mustSend = sendAll || last.translationIsDefaultPose; - if (mustSend || last.translation != data.translation) { - if (mustSend || !cullSmallChanges || glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation) { - validity |= (1 << validityBit); + if (sendAll || last.translationIsDefaultPose || (!cullSmallChanges && last.translation != data.translation) + || (cullSmallChanges && glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation)) { + + validity |= (1 << validityBit); #ifdef WANT_DEBUG - translationSentCount++; + translationSentCount++; #endif - maxTranslationDimension = glm::max(fabsf(data.translation.x), maxTranslationDimension); - maxTranslationDimension = glm::max(fabsf(data.translation.y), maxTranslationDimension); - maxTranslationDimension = glm::max(fabsf(data.translation.z), maxTranslationDimension); + maxTranslationDimension = glm::max(fabsf(data.translation.x), maxTranslationDimension); + maxTranslationDimension = glm::max(fabsf(data.translation.y), maxTranslationDimension); + maxTranslationDimension = glm::max(fabsf(data.translation.z), maxTranslationDimension); - destinationBuffer += - packFloatVec3ToSignedTwoByteFixed(destinationBuffer, data.translation, TRANSLATION_COMPRESSION_RADIX); + destinationBuffer += + packFloatVec3ToSignedTwoByteFixed(destinationBuffer, data.translation, TRANSLATION_COMPRESSION_RADIX); - if (sentJointDataOut) { - localSentJointDataOut[i].translation = data.translation; - localSentJointDataOut[i].translationIsDefaultPose = false; - } + if (sentJointDataOut) { + (*sentJointDataOut)[i].translation = data.translation; } } } + + if (sentJointDataOut) { + (*sentJointDataOut)[i].translationIsDefaultPose = data.translationIsDefaultPose; + } + if (++validityBit == BITS_IN_BYTE) { *validityPosition++ = validity; validityBit = validity = 0; @@ -691,6 +674,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, controllerLeftHandTransform.getRotation()); destinationBuffer += packFloatVec3ToSignedTwoByteFixed(destinationBuffer, controllerLeftHandTransform.getTranslation(), TRANSLATION_COMPRESSION_RADIX); + Transform controllerRightHandTransform = Transform(getControllerRightHandMatrix()); destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, controllerRightHandTransform.getRotation()); destinationBuffer += packFloatVec3ToSignedTwoByteFixed(destinationBuffer, controllerRightHandTransform.getTranslation(), @@ -707,34 +691,27 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent glm::vec3 mouseFarGrabPosition = extractTranslation(mouseFarGrabMatrix); glm::quat mouseFarGrabRotation = extractRotation(mouseFarGrabMatrix); - data->leftFarGrabPosition[0] = leftFarGrabPosition.x; - data->leftFarGrabPosition[1] = leftFarGrabPosition.y; - data->leftFarGrabPosition[2] = leftFarGrabPosition.z; - + AVATAR_MEMCPY(leftFarGrabPosition); + // Can't do block copy as struct order is x, y, z, w. data->leftFarGrabRotation[0] = leftFarGrabRotation.w; data->leftFarGrabRotation[1] = leftFarGrabRotation.x; data->leftFarGrabRotation[2] = leftFarGrabRotation.y; data->leftFarGrabRotation[3] = leftFarGrabRotation.z; + destinationBuffer += sizeof(data->leftFarGrabPosition); - data->rightFarGrabPosition[0] = rightFarGrabPosition.x; - data->rightFarGrabPosition[1] = rightFarGrabPosition.y; - data->rightFarGrabPosition[2] = rightFarGrabPosition.z; - + AVATAR_MEMCPY(rightFarGrabPosition); data->rightFarGrabRotation[0] = rightFarGrabRotation.w; data->rightFarGrabRotation[1] = rightFarGrabRotation.x; data->rightFarGrabRotation[2] = rightFarGrabRotation.y; data->rightFarGrabRotation[3] = rightFarGrabRotation.z; + destinationBuffer += sizeof(data->rightFarGrabRotation); - data->mouseFarGrabPosition[0] = mouseFarGrabPosition.x; - data->mouseFarGrabPosition[1] = mouseFarGrabPosition.y; - data->mouseFarGrabPosition[2] = mouseFarGrabPosition.z; - + AVATAR_MEMCPY(mouseFarGrabPosition); data->mouseFarGrabRotation[0] = mouseFarGrabRotation.w; data->mouseFarGrabRotation[1] = mouseFarGrabRotation.x; data->mouseFarGrabRotation[2] = mouseFarGrabRotation.y; data->mouseFarGrabRotation[3] = mouseFarGrabRotation.z; - - destinationBuffer += sizeof(AvatarDataPacket::FarGrabJoints); + destinationBuffer += sizeof(data->mouseFarGrabRotation); int numBytes = destinationBuffer - startSection; @@ -761,41 +738,23 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent outboundDataRateOut->jointDataRate.increment(numBytes); } - if (sentJointDataOut) { - - // Mark default poses in lastSentJointData, so when they become non-default we send them. - for (int i = 0; i < _jointData.size(); i++) { - const JointData& data = _jointData[i]; - JointData& local = localSentJointDataOut[i]; - if (data.rotationIsDefaultPose) { - local.rotationIsDefaultPose = true; - } - if (data.translationIsDefaultPose) { - local.translationIsDefaultPose = true; - } - } - - // Push new sent joint data to sentJointDataOut - sentJointDataOut->swap(localSentJointDataOut); - } } if (hasJointDefaultPoseFlags) { auto startSection = destinationBuffer; - QReadLocker readLock(&_jointDataLock); // write numJoints - int numJoints = _jointData.size(); + int numJoints = jointData.size(); *destinationBuffer++ = (uint8_t)numJoints; // write rotationIsDefaultPose bits destinationBuffer += writeBitVector(destinationBuffer, numJoints, [&](int i) { - return _jointData[i].rotationIsDefaultPose; + return jointData[i].rotationIsDefaultPose; }); // write translationIsDefaultPose bits destinationBuffer += writeBitVector(destinationBuffer, numJoints, [&](int i) { - return _jointData[i].translationIsDefaultPose; + return jointData[i].translationIsDefaultPose; }); if (outboundDataRateOut) { @@ -880,7 +839,6 @@ const unsigned char* unpackFauxJoint(const unsigned char* sourceBuffer, ThreadSa // read data in packet starting at byte offset and return number of bytes parsed int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { - // lazily allocate memory for HeadData in case we're not an Avatar instance lazyInitHeadData(); @@ -932,7 +890,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { auto newValue = glm::vec3(data->globalPosition[0], data->globalPosition[1], data->globalPosition[2]) + offset; if (_globalPosition != newValue) { _globalPosition = newValue; - _globalPositionChanged = usecTimestampNow(); + _globalPositionChanged = now; } sourceBuffer += sizeof(AvatarDataPacket::AvatarGlobalPosition); int numBytesRead = sourceBuffer - startSection; @@ -956,11 +914,11 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { if (_globalBoundingBoxDimensions != newDimensions) { _globalBoundingBoxDimensions = newDimensions; - _avatarBoundingBoxChanged = usecTimestampNow(); + _avatarBoundingBoxChanged = now; } if (_globalBoundingBoxOffset != newOffset) { _globalBoundingBoxOffset = newOffset; - _avatarBoundingBoxChanged = usecTimestampNow(); + _avatarBoundingBoxChanged = now; } sourceBuffer += sizeof(AvatarDataPacket::AvatarBoundingBox); @@ -1061,7 +1019,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { glm::mat4 sensorToWorldMatrix = createMatFromScaleQuatAndPos(glm::vec3(sensorToWorldScale), sensorToWorldQuat, sensorToWorldTrans); if (_sensorToWorldMatrixCache.get() != sensorToWorldMatrix) { _sensorToWorldMatrixCache.set(sensorToWorldMatrix); - _sensorToWorldMatrixChanged = usecTimestampNow(); + _sensorToWorldMatrixChanged = now; } sourceBuffer += sizeof(AvatarDataPacket::SensorToWorldMatrix); int numBytesRead = sourceBuffer - startSection; @@ -1118,7 +1076,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { sourceBuffer += sizeof(AvatarDataPacket::AdditionalFlags); if (somethingChanged) { - _additionalFlagsChanged = usecTimestampNow(); + _additionalFlagsChanged = now; } int numBytesRead = sourceBuffer - startSection; _additionalFlagsRate.increment(numBytesRead); @@ -1138,7 +1096,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { if ((getParentID() != newParentID) || (getParentJointIndex() != parentInfo->parentJointIndex)) { SpatiallyNestable::setParentID(newParentID); SpatiallyNestable::setParentJointIndex(parentInfo->parentJointIndex); - _parentChanged = usecTimestampNow(); + _parentChanged = now; } int numBytesRead = sourceBuffer - startSection; @@ -1187,8 +1145,6 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { int numBytesRead = sourceBuffer - startSection; _faceTrackerRate.increment(numBytesRead); _faceTrackerUpdateRate.increment(); - } else { - _headData->_blendshapeCoefficients.fill(0, _headData->_blendshapeCoefficients.size()); } if (hasJointData) { @@ -2873,10 +2829,8 @@ void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, Ra value.extraInfo = object.property("extraInfo").toVariant().toMap(); } -const float AvatarData::OUT_OF_VIEW_PENALTY = -10.0f; - -float AvatarData::_avatarSortCoefficientSize { 1.0f }; -float AvatarData::_avatarSortCoefficientCenter { 0.25 }; +float AvatarData::_avatarSortCoefficientSize { 8.0f }; +float AvatarData::_avatarSortCoefficientCenter { 4.0f }; float AvatarData::_avatarSortCoefficientAge { 1.0f }; QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEntityMap& value) { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index db2b82b0b7..056e745370 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -1097,7 +1097,7 @@ public: void fromJson(const QJsonObject& json, bool useFrameSkeleton = true); glm::vec3 getClientGlobalPosition() const { return _globalPosition; } - glm::vec3 getGlobalBoundingBoxCorner() const { return _globalPosition + _globalBoundingBoxOffset - _globalBoundingBoxDimensions; } + AABox getGlobalBoundingBox() const { return AABox(_globalPosition + _globalBoundingBoxOffset - _globalBoundingBoxDimensions, _globalBoundingBoxDimensions); } /**jsdoc * @function MyAvatar.getAvatarEntityData @@ -1169,8 +1169,6 @@ public: // A method intended to be overriden by MyAvatar for polling orientation for network transmission. virtual glm::quat getOrientationOutbound() const; - static const float OUT_OF_VIEW_PENALTY; - // TODO: remove this HACK once we settle on optimal sort coefficients // These coefficients exposed for fine tuning the sort priority for transfering new _jointData to the render pipeline. static float _avatarSortCoefficientSize; diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 5b3196a2bf..d99c0020da 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -538,7 +538,6 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas QNetworkReply* requestReply = networkAccessManager.post(request, postData); connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestAccessTokenFinished); - connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError))); } void AccountManager::requestAccessTokenWithSteam(QByteArray authSessionTicket) { @@ -633,12 +632,6 @@ void AccountManager::requestAccessTokenFinished() { } } -void AccountManager::requestAccessTokenError(QNetworkReply::NetworkError error) { - // TODO: error handling - qCDebug(networking) << "AccountManager: failed to fetch access token - " << error; - emit loginFailed(); -} - void AccountManager::refreshAccessTokenFinished() { QNetworkReply* requestReply = reinterpret_cast(sender()); diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index b122115dd0..f3b81cf1c9 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -106,7 +106,6 @@ public slots: void requestAccessTokenFinished(); void refreshAccessTokenFinished(); void requestProfileFinished(); - void requestAccessTokenError(QNetworkReply::NetworkError error); void refreshAccessTokenError(QNetworkReply::NetworkError error); void requestProfileError(QNetworkReply::NetworkError error); void logout(); diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 9e5cbcaa7b..8085039b02 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -816,8 +816,10 @@ bool AddressManager::setDomainInfo(const QUrl& domainURL, LookupTrigger trigger) const QString hostname = domainURL.host(); quint16 port = domainURL.port(); bool emitHostChanged { false }; + // Check if domain handler is in error state. always emit host changed if true. + bool isInErrorState = DependencyManager::get()->getDomainHandler().isInErrorState(); - if (domainURL != _domainURL) { + if (domainURL != _domainURL || isInErrorState) { addCurrentAddressToHistory(trigger); emitHostChanged = true; } diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 59e3de922f..4c9231b7c9 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -55,6 +55,9 @@ DomainHandler::DomainHandler(QObject* parent) : // stop the refresh timer if we connect to a domain connect(this, &DomainHandler::connectedToDomain, &_apiRefreshTimer, &QTimer::stop); + + // stop the refresh timer if redirected to the error domain + connect(this, &DomainHandler::redirectToErrorDomainURL, &_apiRefreshTimer, &QTimer::stop); } void DomainHandler::disconnect() { @@ -107,13 +110,15 @@ void DomainHandler::softReset() { QMetaObject::invokeMethod(&_settingsTimer, "stop"); // restart the API refresh timer in case we fail to connect and need to refresh information - QMetaObject::invokeMethod(&_apiRefreshTimer, "start"); + if (!_isInErrorState) { + QMetaObject::invokeMethod(&_apiRefreshTimer, "start"); } void DomainHandler::hardReset() { emit resetting(); softReset(); + _isInErrorState = false; qCDebug(networking) << "Hard reset in NodeList DomainHandler."; _pendingDomainID = QUuid(); diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index ab20936f43..90da86a929 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -172,6 +172,11 @@ public slots: void processICEResponsePacket(QSharedPointer icePacket); void processDomainServerConnectionDeniedPacket(QSharedPointer message); + // sets domain handler in error state. + void setRedirectErrorState(QUrl errorUrl, int reasonCode); + + bool isInErrorState() { return _isInErrorState; } + private slots: void completedHostnameLookup(const QHostInfo& hostInfo); void completedIceServerHostnameLookup(); diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp index c378987cd0..44220df8f8 100644 --- a/libraries/networking/src/udt/Socket.cpp +++ b/libraries/networking/src/udt/Socket.cpp @@ -37,6 +37,7 @@ Socket::Socket(QObject* parent, bool shouldChangeSocketOptions) : _shouldChangeSocketOptions(shouldChangeSocketOptions) { connect(&_udpSocket, &QUdpSocket::readyRead, this, &Socket::readPendingDatagrams); + connect(this, &Socket::pendingDatagrams, this, &Socket::processPendingDatagrams, Qt::QueuedConnection); // make sure we hear about errors and state changes from the underlying socket connect(&_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)), @@ -315,55 +316,85 @@ void Socket::checkForReadyReadBackup() { } void Socket::readPendingDatagrams() { + int packetsRead = 0; + int packetSizeWithHeader = -1; - - while (_udpSocket.hasPendingDatagrams() && (packetSizeWithHeader = _udpSocket.pendingDatagramSize()) != -1) { - - // we're reading a packet so re-start the readyRead backup timer - _readyReadBackupTimer->start(); - + // Max datagrams to read before processing: + static const int MAX_DATAGRAMS_CONSECUTIVELY = 10000; + while (_udpSocket.hasPendingDatagrams() + && (packetSizeWithHeader = _udpSocket.pendingDatagramSize()) != -1 + && packetsRead <= MAX_DATAGRAMS_CONSECUTIVELY) { // grab a time point we can mark as the receive time of this packet auto receiveTime = p_high_resolution_clock::now(); - // setup a HifiSockAddr to read into - HifiSockAddr senderSockAddr; // setup a buffer to read the packet into auto buffer = std::unique_ptr(new char[packetSizeWithHeader]); + QHostAddress senderAddress; + quint16 senderPort; + // pull the datagram auto sizeRead = _udpSocket.readDatagram(buffer.get(), packetSizeWithHeader, - senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); + &senderAddress, &senderPort); - // save information for this packet, in case it is the one that sticks readyRead - _lastPacketSizeRead = sizeRead; - _lastPacketSockAddr = senderSockAddr; - - if (sizeRead <= 0) { - // we either didn't pull anything for this packet or there was an error reading (this seems to trigger - // on windows even if there's not a packet available) + // we either didn't pull anything for this packet or there was an error reading (this seems to trigger + // on windows even if there's not a packet available) + if (sizeRead < 0) { continue; } - auto it = _unfilteredHandlers.find(senderSockAddr); + _incomingDatagrams.push_back({ senderAddress, senderPort, packetSizeWithHeader, + std::move(buffer), receiveTime }); + ++packetsRead; + } + + if (packetsRead > _maxDatagramsRead) { + _maxDatagramsRead = packetsRead; + qCDebug(networking) << "readPendingDatagrams: Datagrams read:" << packetsRead; + } + emit pendingDatagrams(packetsRead); +} + +void Socket::processPendingDatagrams(int) { + // setup a HifiSockAddr to read into + HifiSockAddr senderSockAddr; + + while (!_incomingDatagrams.empty()) { + auto& datagram = _incomingDatagrams.front(); + senderSockAddr.setAddress(datagram._senderAddress); + senderSockAddr.setPort(datagram._senderPort); + int datagramSize = datagram._datagramLength; + auto receiveTime = datagram._receiveTime; + + // we're reading a packet so re-start the readyRead backup timer + _readyReadBackupTimer->start(); + + // save information for this packet, in case it is the one that sticks readyRead + _lastPacketSizeRead = datagramSize; + _lastPacketSockAddr = senderSockAddr; + + // Process unfiltered packets first. + auto it = _unfilteredHandlers.find(senderSockAddr); if (it != _unfilteredHandlers.end()) { - // we have a registered unfiltered handler for this HifiSockAddr - call that and return + // we have a registered unfiltered handler for this HifiSockAddr (eg. STUN packet) - call that and return if (it->second) { - auto basePacket = BasePacket::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, senderSockAddr); + auto basePacket = BasePacket::fromReceivedPacket(std::move(datagram._datagram), + datagramSize, senderSockAddr); basePacket->setReceiveTime(receiveTime); it->second(std::move(basePacket)); } - + _incomingDatagrams.pop_front(); continue; } // check if this was a control packet or a data packet - bool isControlPacket = *reinterpret_cast(buffer.get()) & CONTROL_BIT_MASK; + bool isControlPacket = *reinterpret_cast(datagram._datagram.get()) & CONTROL_BIT_MASK; if (isControlPacket) { // setup a control packet from the data we just read - auto controlPacket = ControlPacket::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, senderSockAddr); + auto controlPacket = ControlPacket::fromReceivedPacket(std::move(datagram._datagram), datagramSize, senderSockAddr); controlPacket->setReceiveTime(receiveTime); // move this control packet to the matching connection, if there is one @@ -375,13 +406,13 @@ void Socket::readPendingDatagrams() { } else { // setup a Packet from the data we just read - auto packet = Packet::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, senderSockAddr); + auto packet = Packet::fromReceivedPacket(std::move(datagram._datagram), datagramSize, senderSockAddr); packet->setReceiveTime(receiveTime); // save the sequence number in case this is the packet that sticks readyRead _lastReceivedSequenceNumber = packet->getSequenceNumber(); - // call our verification operator to see if this packet is verified + // call our hash verification operator to see if this packet is verified if (!_packetFilterOperator || _packetFilterOperator(*packet)) { if (packet->isReliable()) { // if this was a reliable packet then signal the matching connection with the sequence number @@ -395,6 +426,7 @@ void Socket::readPendingDatagrams() { qCDebug(networking) << "Can't process packet: version" << (unsigned int)NLPacket::versionInHeader(*packet) << ", type" << NLPacket::typeInHeader(*packet); #endif + _incomingDatagrams.pop_front(); continue; } } @@ -410,6 +442,8 @@ void Socket::readPendingDatagrams() { } } } + + _incomingDatagrams.pop_front(); } } diff --git a/libraries/networking/src/udt/Socket.h b/libraries/networking/src/udt/Socket.h index 1f28592c83..ef4a457116 100644 --- a/libraries/networking/src/udt/Socket.h +++ b/libraries/networking/src/udt/Socket.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -94,6 +95,7 @@ public: signals: void clientHandshakeRequestComplete(const HifiSockAddr& sockAddr); + void pendingDatagrams(int datagramCount); public slots: void cleanupConnection(HifiSockAddr sockAddr); @@ -101,6 +103,7 @@ public slots: private slots: void readPendingDatagrams(); + void processPendingDatagrams(int datagramCount); void checkForReadyReadBackup(); void handleSocketError(QAbstractSocket::SocketError socketError); @@ -144,6 +147,17 @@ private: int _lastPacketSizeRead { 0 }; SequenceNumber _lastReceivedSequenceNumber; HifiSockAddr _lastPacketSockAddr; + + struct Datagram { + QHostAddress _senderAddress; + int _senderPort; + int _datagramLength; + std::unique_ptr _datagram; + p_high_resolution_clock::time_point _receiveTime; + }; + + std::list _incomingDatagrams; + int _maxDatagramsRead { 0 }; friend UDTTest; }; diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 58c197d6f4..871958f883 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -286,6 +286,7 @@ void PhysicsEngine::reinsertObject(ObjectMotionState* object) { void PhysicsEngine::processTransaction(PhysicsEngine::Transaction& transaction) { // removes for (auto object : transaction.objectsToRemove) { + bumpAndPruneContacts(object); btRigidBody* body = object->getRigidBody(); if (body) { removeDynamicsForBody(body); diff --git a/libraries/shared/src/PrioritySortUtil.h b/libraries/shared/src/PrioritySortUtil.h index 8ded047212..27f6b193ba 100644 --- a/libraries/shared/src/PrioritySortUtil.h +++ b/libraries/shared/src/PrioritySortUtil.h @@ -16,55 +16,16 @@ #include "NumericalConstants.h" #include "shared/ConicalViewFrustum.h" -/* PrioritySortUtil is a helper for sorting 3D things relative to a ViewFrustum. To use: +// PrioritySortUtil is a helper for sorting 3D things relative to a ViewFrustum. -(1) Derive a class from pure-virtual PrioritySortUtil::Sortable that wraps a copy of - the Thing you want to prioritize and sort: - - class SortableWrapper: public PrioritySortUtil::Sortable { - public: - SortableWrapper(const Thing& thing) : _thing(thing) { } - glm::vec3 getPosition() const override { return _thing->getPosition(); } - float getRadius() const override { return 0.5f * _thing->getBoundingRadius(); } - uint64_t getTimestamp() const override { return _thing->getLastTime(); } - Thing getThing() const { return _thing; } - private: - Thing _thing; - }; - -(2) Make a PrioritySortUtil::PriorityQueue and add them to the queue: - - PrioritySortUtil::PriorityQueue sortedThings(viewFrustum); - std::priority_queue< PrioritySortUtil::Sortable > sortedThings; - for (thing in things) { - sortedThings.push(SortableWrapper(thing)); - } - -(3) Loop over your priority queue and do timeboxed work: - - NOTE: Be careful using references to members of instances of T from std::priority_queue. - Under the hood std::priority_queue may re-use instances of T. - For example, after a pop() or a push() the top T may have the same memory address - as the top T before the pop() or push() (but point to a swapped instance of T). - This causes a reference to member variable of T to point to a different value - when operations taken on std::priority_queue shuffle around the instances of T. - - uint64_t cutoffTime = usecTimestampNow() + TIME_BUDGET; - while (!sortedThings.empty()) { - const Thing& thing = sortedThings.top(); - // ...do work on thing... - sortedThings.pop(); - if (usecTimestampNow() > cutoffTime) { - break; - } - } -*/ +const float OUT_OF_VIEW_PENALTY = -10.0f; +const float OUT_OF_VIEW_THRESHOLD = 0.5f * OUT_OF_VIEW_PENALTY; namespace PrioritySortUtil { constexpr float DEFAULT_ANGULAR_COEF { 1.0f }; constexpr float DEFAULT_CENTER_COEF { 0.5f }; - constexpr float DEFAULT_AGE_COEF { 0.25f / (float)(USECS_PER_SECOND) }; + constexpr float DEFAULT_AGE_COEF { 0.25f }; class Sortable { public: @@ -84,8 +45,9 @@ namespace PrioritySortUtil { PriorityQueue() = delete; PriorityQueue(const ConicalViewFrustums& views) : _views(views) { } PriorityQueue(const ConicalViewFrustums& views, float angularWeight, float centerWeight, float ageWeight) - : _views(views), _angularWeight(angularWeight), _centerWeight(centerWeight), _ageWeight(ageWeight) - { } + : _views(views), _angularWeight(angularWeight), _centerWeight(centerWeight), _ageWeight(ageWeight) + , _usecCurrentTime(usecTimestampNow()) { + } void setViews(const ConicalViewFrustums& views) { _views = views; } @@ -93,6 +55,7 @@ namespace PrioritySortUtil { _angularWeight = angularWeight; _centerWeight = centerWeight; _ageWeight = ageWeight; + _usecCurrentTime = usecTimestampNow(); } size_t size() const { return _vector.size(); } @@ -131,23 +94,18 @@ namespace PrioritySortUtil { glm::vec3 offset = position - view.getPosition(); float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero const float MIN_RADIUS = 0.1f; // WORKAROUND for zero size objects (we still want them to sort by distance) - float radius = glm::min(thing.getRadius(), MIN_RADIUS); - float cosineAngle = (glm::dot(offset, view.getDirection()) / distance); - float age = (float)(usecTimestampNow() - thing.getTimestamp()); + float radius = glm::max(thing.getRadius(), MIN_RADIUS); + // Other item's angle from view centre: + float cosineAngle = glm::dot(offset, view.getDirection()) / distance; + float age = float((_usecCurrentTime - thing.getTimestamp()) / USECS_PER_SECOND); - // we modulatate "age" drift rate by the cosineAngle term to make periphrial objects sort forward - // at a reduced rate but we don't want the "age" term to go zero or negative so we clamp it - const float MIN_COSINE_ANGLE_FACTOR = 0.1f; - float cosineAngleFactor = glm::max(cosineAngle, MIN_COSINE_ANGLE_FACTOR); - - float priority = _angularWeight * glm::max(radius, MIN_RADIUS) / distance - + _centerWeight * cosineAngle - + _ageWeight * cosineAngleFactor * age; + // the "age" term accumulates at the sum of all weights + float angularSize = radius / distance; + float priority = (_angularWeight * angularSize + _centerWeight * cosineAngle) * (age + 1.0f) + _ageWeight * age; // decrement priority of things outside keyhole if (distance - radius > view.getRadius()) { if (!view.intersects(offset, distance, radius)) { - constexpr float OUT_OF_VIEW_PENALTY = -10.0f; priority += OUT_OF_VIEW_PENALTY; } } @@ -159,12 +117,13 @@ namespace PrioritySortUtil { float _angularWeight { DEFAULT_ANGULAR_COEF }; float _centerWeight { DEFAULT_CENTER_COEF }; float _ageWeight { DEFAULT_AGE_COEF }; + quint64 _usecCurrentTime { 0 }; }; } // namespace PrioritySortUtil -// for now we're keeping hard-coded sorted time budgets in one spot + // for now we're keeping hard-coded sorted time budgets in one spot const uint64_t MAX_UPDATE_RENDERABLES_TIME_BUDGET = 2000; // usec const uint64_t MIN_SORTED_UPDATE_RENDERABLES_TIME_BUDGET = 1000; // usec +const uint64_t MAX_UPDATE_AVATARS_TIME_BUDGET = 2000; // usec #endif // hifi_PrioritySortUtil_h - diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 31510831c8..aaf5ca7260 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -35,10 +35,13 @@ var DEFAULT_SCRIPTS_COMBINED = [ ]; var DEFAULT_SCRIPTS_SEPARATE = [ "system/controllers/controllerScripts.js", - "system/interstitialPage.js" //"system/chat.js" ]; +if (Settings.getValue("enableInterstitialMode", false)) { + DEFAULT_SCRIPTS_SEPARATE.push("system/interstitialPage.js"); +} + // add a menu item for debugging var MENU_CATEGORY = "Developer"; var MENU_ITEM = "Debug defaultScripts.js"; diff --git a/scripts/system/edit.js b/scripts/system/edit.js index ed2a179613..88ae2852be 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -860,7 +860,7 @@ var toolBar = (function () { propertiesTool.setVisible(false); selectionManager.clearSelections(); cameraManager.disable(); - selectionDisplay.triggerMapping.disable(); + selectionDisplay.disableTriggerMapping(); tablet.landscape = false; Controller.disableMapping(CONTROLLER_MAPPING_NAME); } else { @@ -876,7 +876,7 @@ var toolBar = (function () { gridTool.setVisible(true); grid.setEnabled(true); propertiesTool.setVisible(true); - selectionDisplay.triggerMapping.enable(); + selectionDisplay.enableTriggerMapping(); print("starting tablet in landscape mode"); tablet.landscape = true; Controller.enableMapping(CONTROLLER_MAPPING_NAME); diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 05f5e41950..5bca58b1ac 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -14,7 +14,7 @@ // /* global SelectionManager, SelectionDisplay, grid, rayPlaneIntersection, rayPlaneIntersection2, pushCommandForSelections, - getMainTabletIDs, getControllerWorldLocation */ + getMainTabletIDs, getControllerWorldLocation, TRIGGER_ON_VALUE */ var SPACE_LOCAL = "local"; var SPACE_WORLD = "world"; @@ -22,6 +22,7 @@ var HIGHLIGHT_LIST_NAME = "editHandleHighlightList"; Script.include([ "./controllers.js", + "./controllerDispatcherUtils.js", "./utils.js" ]); @@ -448,6 +449,8 @@ SelectionDisplay = (function() { var CTRL_KEY_CODE = 16777249; var RAIL_AXIS_LENGTH = 10000; + + var NO_HAND = -1; var TRANSLATE_DIRECTION = { X: 0, @@ -478,8 +481,6 @@ SelectionDisplay = (function() { YAW: 1, ROLL: 2 }; - - var NO_TRIGGER_HAND = -1; var spaceMode = SPACE_LOCAL; var overlayNames = []; @@ -802,11 +803,21 @@ SelectionDisplay = (function() { // We get mouseMoveEvents from the handControllers, via handControllerPointer. // But we dont' get mousePressEvents. - that.triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click'); - Script.scriptEnding.connect(that.triggerMapping.disable); - that.triggeredHand = NO_TRIGGER_HAND; + that.triggerClickMapping = Controller.newMapping(Script.resolvePath('') + '-click'); + that.triggerPressMapping = Controller.newMapping(Script.resolvePath('') + '-press'); + that.triggeredHand = NO_HAND; + that.pressedHand = NO_HAND; that.triggered = function() { - return that.triggeredHand !== NO_TRIGGER_HAND; + return that.triggeredHand !== NO_HAND; + } + function pointingAtDesktopWindowOrTablet(hand) { + var pointingAtDesktopWindow = (hand === Controller.Standard.RightHand && + SelectionManager.pointingAtDesktopWindowRight) || + (hand === Controller.Standard.LeftHand && + SelectionManager.pointingAtDesktopWindowLeft); + var pointingAtTablet = (hand === Controller.Standard.RightHand && SelectionManager.pointingAtTabletRight) || + (hand === Controller.Standard.LeftHand && SelectionManager.pointingAtTabletLeft); + return pointingAtDesktopWindow || pointingAtTablet; } function makeClickHandler(hand) { return function (clicked) { @@ -814,26 +825,39 @@ SelectionDisplay = (function() { if (that.triggered() && hand !== that.triggeredHand) { return; } - if (!that.triggered() && clicked) { - var pointingAtDesktopWindow = (hand === Controller.Standard.RightHand && - SelectionManager.pointingAtDesktopWindowRight) || - (hand === Controller.Standard.LeftHand && - SelectionManager.pointingAtDesktopWindowLeft); - var pointingAtTablet = (hand === Controller.Standard.RightHand && SelectionManager.pointingAtTabletRight) || - (hand === Controller.Standard.LeftHand && SelectionManager.pointingAtTabletLeft); - if (pointingAtDesktopWindow || pointingAtTablet) { - return; - } + if (!that.triggered() && clicked && !pointingAtDesktopWindowOrTablet(hand)) { that.triggeredHand = hand; that.mousePressEvent({}); } else if (that.triggered() && !clicked) { - that.triggeredHand = NO_TRIGGER_HAND; + that.triggeredHand = NO_HAND; that.mouseReleaseEvent({}); } }; } - that.triggerMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Controller.Standard.RightHand)); - that.triggerMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Controller.Standard.LeftHand)); + function makePressHandler(hand) { + return function (value) { + if (value >= TRIGGER_ON_VALUE && !that.triggered() && !pointingAtDesktopWindowOrTablet(hand)) { + that.pressedHand = hand; + that.updateHighlight({}); + } else { + that.pressedHand = NO_HAND; + that.resetPreviousHandleColor(); + } + } + } + that.triggerClickMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Controller.Standard.RightHand)); + that.triggerClickMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Controller.Standard.LeftHand)); + that.triggerPressMapping.from(Controller.Standard.RT).peek().to(makePressHandler(Controller.Standard.RightHand)); + that.triggerPressMapping.from(Controller.Standard.LT).peek().to(makePressHandler(Controller.Standard.LeftHand)); + that.enableTriggerMapping = function() { + that.triggerClickMapping.enable(); + that.triggerPressMapping.enable(); + }; + that.disableTriggerMapping = function() { + that.triggerClickMapping.disable(); + that.triggerPressMapping.disable(); + } + Script.scriptEnding.connect(that.disableTriggerMapping); // FUNCTION DEF(s): Intersection Check Helpers function testRayIntersect(queryRay, overlayIncludes, overlayExcludes) { @@ -960,35 +984,10 @@ SelectionDisplay = (function() { } return Uuid.NULL; }; - - // FUNCTION: MOUSE MOVE EVENT - var lastMouseEvent = null; - that.mouseMoveEvent = function(event) { - var wantDebug = false; - if (wantDebug) { - print("=============== eST::MouseMoveEvent BEG ======================="); - } - lastMouseEvent = event; - if (activeTool) { - if (wantDebug) { - print(" Trigger ActiveTool(" + activeTool.mode + ")'s onMove"); - } - activeTool.onMove(event); - - if (wantDebug) { - print(" Trigger SelectionManager::update"); - } - SelectionManager._update(); - - if (wantDebug) { - print("=============== eST::MouseMoveEvent END ======================="); - } - // EARLY EXIT--(Move handled via active tool) - return true; - } - + + that.updateHighlight = function(event) { // if no tool is active, then just look for handles to highlight... - var pickRay = generalComputePickRay(event.x, event.y); + var pickRay = generalComputePickRay(event.x, event.y); var result = Overlays.findRayIntersection(pickRay); var pickedColor; var highlightNeeded = false; @@ -1039,7 +1038,36 @@ SelectionDisplay = (function() { } else { that.resetPreviousHandleColor(); } + }; + // FUNCTION: MOUSE MOVE EVENT + var lastMouseEvent = null; + that.mouseMoveEvent = function(event) { + var wantDebug = false; + if (wantDebug) { + print("=============== eST::MouseMoveEvent BEG ======================="); + } + lastMouseEvent = event; + if (activeTool) { + if (wantDebug) { + print(" Trigger ActiveTool(" + activeTool.mode + ")'s onMove"); + } + activeTool.onMove(event); + + if (wantDebug) { + print(" Trigger SelectionManager::update"); + } + SelectionManager._update(); + + if (wantDebug) { + print("=============== eST::MouseMoveEvent END ======================="); + } + // EARLY EXIT--(Move handled via active tool) + return true; + } + + that.updateHighlight(event); + if (wantDebug) { print("=============== eST::MouseMoveEvent END ======================="); } @@ -1135,9 +1163,10 @@ SelectionDisplay = (function() { } }; - function controllerComputePickRay() { - var controllerPose = getControllerWorldLocation(that.triggeredHand, true); - if (controllerPose.valid && that.triggered()) { + function controllerComputePickRay(hand) { + var hand = that.triggered() ? that.triggeredHand : that.pressedHand; + var controllerPose = getControllerWorldLocation(hand, true); + if (controllerPose.valid) { var controllerPosition = controllerPose.translation; // This gets point direction right, but if you want general quaternion it would be more complicated: var controllerDirection = Quat.getUp(controllerPose.rotation); @@ -1148,6 +1177,12 @@ SelectionDisplay = (function() { function generalComputePickRay(x, y) { return controllerComputePickRay() || Camera.computePickRay(x, y); } + + function getControllerAvatarFramePositionFromPickRay(pickRay) { + var controllerPosition = Vec3.subtract(pickRay.origin, MyAvatar.position); + controllerPosition = Vec3.multiplyQbyV(Quat.inverse(MyAvatar.orientation), controllerPosition); + return controllerPosition; + } function getDistanceToCamera(position) { var cameraPosition = Camera.getPosition(); @@ -2083,6 +2118,7 @@ SelectionDisplay = (function() { var rotation = null; var previousPickRay = null; var beginMouseEvent = null; + var beginControllerPosition = null; var onBegin = function(event, pickRay, pickResult) { var proportional = directionEnum === STRETCH_DIRECTION.ALL; @@ -2218,6 +2254,9 @@ SelectionDisplay = (function() { previousPickRay = pickRay; beginMouseEvent = event; + if (that.triggered()) { + beginControllerPosition = getControllerAvatarFramePositionFromPickRay(pickRay); + } }; var onEnd = function(event, reason) { @@ -2256,13 +2295,15 @@ SelectionDisplay = (function() { if (usePreviousPickRay(pickRay.direction, previousPickRay.direction, planeNormal)) { pickRay = previousPickRay; } + + var controllerPose = getControllerWorldLocation(that.triggeredHand, true); + var controllerTrigger = HMD.isHMDAvailable() && HMD.isHandControllerAvailable() && + controllerPose.valid && that.triggered(); // Are we using handControllers or Mouse - only relevant for 3D tools - var controllerPose = getControllerWorldLocation(that.triggeredHand, true); var vector = null; var newPick = null; - if (HMD.isHMDAvailable() && HMD.isHandControllerAvailable() && - controllerPose.valid && that.triggered() && directionFor3DStretch) { + if (controllerTrigger && directionFor3DStretch) { localDeltaPivot = deltaPivot3D; newPick = pickRay.origin; vector = Vec3.subtract(newPick, lastPick3D); @@ -2286,12 +2327,23 @@ SelectionDisplay = (function() { var newDimensions; if (proportional) { var viewportDimensions = Controller.getViewportDimensions(); - var mouseXDifference = (event.x - beginMouseEvent.x) / viewportDimensions.x; - var mouseYDifference = (beginMouseEvent.y - event.y) / viewportDimensions.y; - var mouseDifference = mouseXDifference + mouseYDifference; var toCameraDistance = getDistanceToCamera(position); var dimensionsMultiple = toCameraDistance * STRETCH_DIRECTION_ALL_CAMERA_DISTANCE_MULTIPLE; - var dimensionChange = mouseDifference * dimensionsMultiple; + + var dimensionChange; + if (controllerTrigger) { + var controllerPosition = getControllerAvatarFramePositionFromPickRay(pickRay); + var vecControllerDifference = Vec3.subtract(controllerPosition, beginControllerPosition); + var controllerDifference = vecControllerDifference.x + vecControllerDifference.y + + vecControllerDifference.z; + dimensionChange = controllerDifference * dimensionsMultiple; + } else { + var mouseXDifference = (event.x - beginMouseEvent.x) / viewportDimensions.x; + var mouseYDifference = (beginMouseEvent.y - event.y) / viewportDimensions.y; + var mouseDifference = mouseXDifference + mouseYDifference; + dimensionChange = mouseDifference * dimensionsMultiple; + } + var averageInitialDimension = (initialDimensions.x + initialDimensions.y + initialDimensions.z) / 3; percentChange = dimensionChange / averageInitialDimension; percentChange += 1.0; From 2b8aebdb6b0461880e7005425472e6d62d986ab8 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Thu, 13 Sep 2018 17:21:29 -0700 Subject: [PATCH 6/8] Revert "Merge branch 'master' into fixGoToButtonInterstitial" This reverts commit b4b0c519f39ab543f77dc6e88ba5cb954a2b45e6, reversing changes made to aef3ed3e0104fbe470b0105b4f2c03ef331141e2. --- .../images/interstitialPage/goTo_button.png | Bin 9189 -> 0 bytes libraries/networking/src/DomainHandler.cpp | 6 +++--- libraries/networking/src/DomainHandler.h | 2 -- scripts/system/interstitialPage.js | 10 +++++----- 4 files changed, 8 insertions(+), 10 deletions(-) delete mode 100644 interface/resources/images/interstitialPage/goTo_button.png diff --git a/interface/resources/images/interstitialPage/goTo_button.png b/interface/resources/images/interstitialPage/goTo_button.png deleted file mode 100644 index 7c1b0d85003f4757522ee820d6bef0cff0561f6c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9189 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 P{!7$S*Lzf^W)ty$o<}NJ diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 4c9231b7c9..f34a93de96 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -102,7 +102,6 @@ void DomainHandler::softReset() { clearSettings(); - _isInErrorState = false; _connectionDenialsSinceKeypairRegen = 0; _checkInPacketsSinceLastReply = 0; @@ -112,6 +111,7 @@ void DomainHandler::softReset() { // restart the API refresh timer in case we fail to connect and need to refresh information if (!_isInErrorState) { QMetaObject::invokeMethod(&_apiRefreshTimer, "start"); + } } void DomainHandler::hardReset() { @@ -343,6 +343,7 @@ void DomainHandler::loadedErrorDomain(std::map namedPaths) { void DomainHandler::setRedirectErrorState(QUrl errorUrl, int reasonCode) { _errorDomainURL = errorUrl; _lastDomainConnectionError = reasonCode; + _isInErrorState = true; emit redirectToErrorDomainURL(_errorDomainURL); } @@ -485,9 +486,8 @@ void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer namedPaths); void loadedErrorDomain(std::map namedPaths); - // sets domain handler in error state. - void setRedirectErrorState(QUrl errorUrl, int reasonCode); QString getViewPointFromNamedPath(QString namedPath); diff --git a/scripts/system/interstitialPage.js b/scripts/system/interstitialPage.js index 57726f397b..dcce721cd9 100644 --- a/scripts/system/interstitialPage.js +++ b/scripts/system/interstitialPage.js @@ -137,10 +137,10 @@ var loadingToTheSpotID = Overlays.addOverlay("image3d", { name: "Loading-Destination-Card-Text", - localPosition: { x: 0.0 , y: -1.5, z: -0.3 }, - url: Script.resourcesPath() + "images/interstitialPage/goTo_button.png", + localPosition: { x: 0.0 , y: -1.8, z: 0.0 }, + url: "http://hifi-content.s3.amazonaws.com/alexia/LoadingScreens/goTo_button.png", alpha: 1, - dimensions: { x: 1.5, y: 1.0 }, + dimensions: { x: 1.2, y: 0.6}, visible: isVisible, emissive: true, ignoreRayIntersection: false, @@ -415,13 +415,13 @@ Overlays.mouseReleaseOnOverlay.connect(clickedOnOverlay); Overlays.hoverEnterOverlay.connect(function(overlayID, event) { if (overlayID === loadingToTheSpotID) { - Overlays.editOverlay(loadingToTheSpotID, { color: greyColor }); + Overlays.editOverlay(loadingToTheSpotID, { color: greyColor}); } }); Overlays.hoverLeaveOverlay.connect(function(overlayID, event) { if (overlayID === loadingToTheSpotID) { - Overlays.editOverlay(loadingToTheSpotID, { color: whiteColor }); + Overlays.editOverlay(loadingToTheSpotID, { color: whiteColor}); } }); From bf7dd94db786a3f8fdeb5be189b1823b1168bd72 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Thu, 13 Sep 2018 17:25:33 -0700 Subject: [PATCH 7/8] button was clipped; adding higher res img --- .../images/interstitialPage/goTo_button.png | Bin 0 -> 9189 bytes scripts/system/interstitialPage.js | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 interface/resources/images/interstitialPage/goTo_button.png diff --git a/interface/resources/images/interstitialPage/goTo_button.png b/interface/resources/images/interstitialPage/goTo_button.png new file mode 100644 index 0000000000000000000000000000000000000000..7c1b0d85003f4757522ee820d6bef0cff0561f6c GIT binary patch literal 9189 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 P{!7$S*Lzf^W)ty$o<}NJ literal 0 HcmV?d00001 diff --git a/scripts/system/interstitialPage.js b/scripts/system/interstitialPage.js index dcce721cd9..885e903684 100644 --- a/scripts/system/interstitialPage.js +++ b/scripts/system/interstitialPage.js @@ -415,13 +415,13 @@ Overlays.mouseReleaseOnOverlay.connect(clickedOnOverlay); Overlays.hoverEnterOverlay.connect(function(overlayID, event) { if (overlayID === loadingToTheSpotID) { - Overlays.editOverlay(loadingToTheSpotID, { color: greyColor}); + Overlays.editOverlay(loadingToTheSpotID, { color: greyColor }); } }); Overlays.hoverLeaveOverlay.connect(function(overlayID, event) { if (overlayID === loadingToTheSpotID) { - Overlays.editOverlay(loadingToTheSpotID, { color: whiteColor}); + Overlays.editOverlay(loadingToTheSpotID, { color: whiteColor }); } }); From 53582bddcd0e2e007aa0aa175c3bb6e31aa95ee5 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Thu, 13 Sep 2018 17:26:08 -0700 Subject: [PATCH 8/8] pushing more changes --- scripts/system/interstitialPage.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/system/interstitialPage.js b/scripts/system/interstitialPage.js index 885e903684..57726f397b 100644 --- a/scripts/system/interstitialPage.js +++ b/scripts/system/interstitialPage.js @@ -137,10 +137,10 @@ var loadingToTheSpotID = Overlays.addOverlay("image3d", { name: "Loading-Destination-Card-Text", - localPosition: { x: 0.0 , y: -1.8, z: 0.0 }, - url: "http://hifi-content.s3.amazonaws.com/alexia/LoadingScreens/goTo_button.png", + localPosition: { x: 0.0 , y: -1.5, z: -0.3 }, + url: Script.resourcesPath() + "images/interstitialPage/goTo_button.png", alpha: 1, - dimensions: { x: 1.2, y: 0.6}, + dimensions: { x: 1.5, y: 1.0 }, visible: isVisible, emissive: true, ignoreRayIntersection: false,