From 2b5d8622637f1b27d6b6d3b4718d07f08476ecd9 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 22 Aug 2016 17:06:43 -0700 Subject: [PATCH 1/8] request shareable name in AddressManager once connected --- libraries/networking/src/AddressManager.cpp | 56 +++++++++++++++++++++ libraries/networking/src/AddressManager.h | 6 +++ libraries/networking/src/NodeList.cpp | 4 ++ 3 files changed, 66 insertions(+) diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 6760d44244..c436858c68 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -651,6 +651,9 @@ bool AddressManager::setHost(const QString& host, LookupTrigger trigger, quint16 _port = port; + // any host change should clear the shareable place name + _shareablePlaceName.clear(); + if (host != _host) { _host = host; emit hostChanged(_host); @@ -708,6 +711,59 @@ void AddressManager::copyPath() { QApplication::clipboard()->setText(currentPath()); } +void AddressManager::handleShareableNameAPIResponse(QNetworkReply& requestReply) { + // make sure that this response is for the domain we're currently connected to + auto domainID = DependencyManager::get()->getDomainHandler().getUUID(); + + if (requestReply.url().toString().contains(uuidStringWithoutCurlyBraces(domainID))) { + // check for a name or default name in the API response + + QJsonObject responseObject = QJsonDocument::fromJson(requestReply.readAll()).object(); + QJsonObject domainObject = responseObject["domain"].toObject(); + + const QString DOMAIN_NAME_KEY = "name"; + const QString DOMAIN_DEFAULT_PLACE_NAME_KEY = "default_place_name"; + + bool shareableNameChanged { false }; + + if (domainObject[DOMAIN_NAME_KEY].isString()) { + _shareablePlaceName = domainObject[DOMAIN_NAME_KEY].toString(); + shareableNameChanged = true; + } else if (domainObject[DOMAIN_DEFAULT_PLACE_NAME_KEY].isString()) { + _shareablePlaceName = domainObject[DOMAIN_DEFAULT_PLACE_NAME_KEY].toString(); + shareableNameChanged = true; + } + + if (shareableNameChanged) { + qDebug() << "AddressManager shareable name changed to" << _shareablePlaceName; + } + } +} + +void AddressManager::lookupShareableNameForDomainID(const QUuid& domainID) { + + // if we get to a domain via IP/hostname, often the address is only reachable by this client + // and not by other clients on the LAN or Internet + + // to work around this we use the ID to lookup the default place name, and if it exists we + // then use that for Steam join/invite or copiable address + + // it only makes sense to lookup a shareable default name if we don't have a place name + if (_placeName.isEmpty()) { + JSONCallbackParameters callbackParams; + + // no error callback handling + // in the case of an error we simply assume there is no default place name + callbackParams.jsonCallbackReceiver = this; + callbackParams.jsonCallbackMethod = "handleShareableNameAPIResponse"; + + DependencyManager::get()->sendRequest(GET_DOMAIN_ID.arg(uuidStringWithoutCurlyBraces(domainID)), + AccountManagerAuth::None, + QNetworkAccessManager::GetOperation, + callbackParams); + } +} + void AddressManager::addCurrentAddressToHistory(LookupTrigger trigger) { // if we're cold starting and this is called for the first address (from settings) we don't do anything diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index 8ccddc5975..74cc431448 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -101,6 +101,8 @@ public slots: void copyAddress(); void copyPath(); + void lookupShareableNameForDomainID(const QUuid& domainID); + signals: void lookupResultsFinished(); void lookupResultIsOffline(); @@ -124,6 +126,8 @@ private slots: void handleAPIResponse(QNetworkReply& requestReply); void handleAPIError(QNetworkReply& errorReply); + void handleShareableNameAPIResponse(QNetworkReply& requestReply); + private: void goToAddressFromObject(const QVariantMap& addressMap, const QNetworkReply& reply); @@ -154,6 +158,8 @@ private: PositionGetter _positionGetter; OrientationGetter _orientationGetter; + QString _shareablePlaceName; + QStack _backStack; QStack _forwardStack; quint64 _lastBackPush = 0; diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 781cc00c1c..3a07ea8b54 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -539,6 +539,10 @@ void NodeList::processDomainServerList(QSharedPointer message) if (!_domainHandler.isConnected()) { _domainHandler.setUUID(domainUUID); _domainHandler.setIsConnected(true); + + // in case we didn't use a place name to get to this domain, + // give the address manager a chance to lookup a default one now + DependencyManager::get()->lookupShareableNameForDomainID(domainUUID); } else if (_domainHandler.getUUID() != domainUUID) { // Recieved packet from different domain. qWarning() << "IGNORING DomainList packet from" << domainUUID << "while connected to" << _domainHandler.getUUID(); From edd4f5853e81fb25cdc94303559545102012cddd Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 22 Aug 2016 17:17:13 -0700 Subject: [PATCH 2/8] use temporary domain name as place name on ID lookup --- libraries/networking/src/AddressManager.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index c436858c68..b2f1fc39b5 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -360,6 +360,7 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const LookupTrigger trigger = (LookupTrigger) reply.property(LOOKUP_TRIGGER_KEY).toInt(); + // set our current root place id to the ID that came back const QString PLACE_ID_KEY = "id"; _rootPlaceID = rootMap[PLACE_ID_KEY].toUuid(); @@ -368,6 +369,15 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const const QString PLACE_NAME_KEY = "name"; QString placeName = rootMap[PLACE_NAME_KEY].toString(); + if (placeName.isEmpty()) { + // we didn't get a set place name, check if there is a default or temporary domain name to use + const QString TEMPORARY_DOMAIN_NAME_KEY = "name"; + + if (domainObject.contains(TEMPORARY_DOMAIN_NAME_KEY)) { + placeName = domainObject[TEMPORARY_DOMAIN_NAME_KEY].toString(); + } + } + if (!placeName.isEmpty()) { if (setHost(placeName, trigger)) { trigger = LookupTrigger::Internal; From fbe784155eaf07a73da222584bee9371d19a7c78 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 23 Aug 2016 11:18:42 -0700 Subject: [PATCH 3/8] add a shareable address return to address manager --- interface/src/DiscoverabilityManager.cpp | 2 +- libraries/networking/src/AddressManager.cpp | 32 +++++++++++++++++---- libraries/networking/src/AddressManager.h | 2 ++ 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/interface/src/DiscoverabilityManager.cpp b/interface/src/DiscoverabilityManager.cpp index dd80dadca7..9b61770a3e 100644 --- a/interface/src/DiscoverabilityManager.cpp +++ b/interface/src/DiscoverabilityManager.cpp @@ -111,7 +111,7 @@ void DiscoverabilityManager::updateLocation() { } // Update Steam - SteamClient::updateLocation(domainHandler.getHostname(), addressManager->currentFacingAddress()); + SteamClient::updateLocation(domainHandler.getHostname(), addressManager->currentShareableAddress()); } void DiscoverabilityManager::handleHeartbeatResponse(QNetworkReply& requestReply) { diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index b2f1fc39b5..c9c9d73394 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -63,15 +63,31 @@ QUrl AddressManager::currentAddress() const { } QUrl AddressManager::currentFacingAddress() const { - QUrl hifiURL; + auto hifiURL = currentAddress(); + hifiURL.setPath(currentFacingPath()); - hifiURL.setScheme(HIFI_URL_SCHEME); - hifiURL.setHost(_host); + return hifiURL; +} - if (_port != 0 && _port != DEFAULT_DOMAIN_SERVER_PORT) { - hifiURL.setPort(_port); + +QUrl AddressManager::currentShareableAddress() const { + if (!_shareablePlaceName.isEmpty()) { + // if we have a shareable place name use that instead of whatever the current host is + QUrl hifiURL; + + hifiURL.setScheme(HIFI_URL_SCHEME); + hifiURL.setHost(_shareablePlaceName); + + hifiURL.setPath(currentPath()); + + return hifiURL; + } else { + return currentAddress(); } +} +QUrl AddressManager::currentFacingShareableAddress() const { + auto hifiURL = currentShareableAddress(); hifiURL.setPath(currentFacingPath()); return hifiURL; @@ -372,9 +388,12 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const if (placeName.isEmpty()) { // we didn't get a set place name, check if there is a default or temporary domain name to use const QString TEMPORARY_DOMAIN_NAME_KEY = "name"; + const QString DEFAULT_DOMAIN_NAME_KEY = "default_place_name"; if (domainObject.contains(TEMPORARY_DOMAIN_NAME_KEY)) { placeName = domainObject[TEMPORARY_DOMAIN_NAME_KEY].toString(); + } else if (domainObject.contains(DEFAULT_DOMAIN_NAME_KEY)) { + placeName = domainObject[DEFAULT_DOMAIN_NAME_KEY].toString(); } } @@ -714,7 +733,8 @@ void AddressManager::refreshPreviousLookup() { } void AddressManager::copyAddress() { - QApplication::clipboard()->setText(currentAddress().toString()); + // assume that the address is being copied because the user wants a shareable address + QApplication::clipboard()->setText(currentShareableAddress().toString()); } void AddressManager::copyPath() { diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index 74cc431448..296abbb36c 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -59,6 +59,8 @@ public: QUrl currentAddress() const; QUrl currentFacingAddress() const; + QUrl currentShareableAddress() const; + QUrl currentFacingShareableAddress() const; QString currentPath(bool withOrientation = true) const; QString currentFacingPath() const; From 43b8e01d46636651a9984b2dcca35bf4eb7954a4 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 23 Aug 2016 11:21:15 -0700 Subject: [PATCH 4/8] don't force auto networking on non metaverse domains --- domain-server/src/DomainServer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index cbf533bf64..b61fef8525 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -121,9 +121,9 @@ DomainServer::DomainServer(int argc, char* argv[]) : if (_type != NonMetaverse) { // if we have a metaverse domain, we'll use an access token for API calls resetAccountManagerAccessToken(); - } - setupAutomaticNetworking(); + setupAutomaticNetworking(); + } if (!getID().isNull() && _type != NonMetaverse) { // setup periodic heartbeats to metaverse API From 9461ca0cfca81d4abd088e6b72544ba86300a44b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 23 Aug 2016 11:56:18 -0700 Subject: [PATCH 5/8] use the facing shareable address for steam location --- interface/src/DiscoverabilityManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/DiscoverabilityManager.cpp b/interface/src/DiscoverabilityManager.cpp index 9b61770a3e..eb9a7c7f6d 100644 --- a/interface/src/DiscoverabilityManager.cpp +++ b/interface/src/DiscoverabilityManager.cpp @@ -111,7 +111,7 @@ void DiscoverabilityManager::updateLocation() { } // Update Steam - SteamClient::updateLocation(domainHandler.getHostname(), addressManager->currentShareableAddress()); + SteamClient::updateLocation(domainHandler.getHostname(), addressManager->currentFacingShareableAddress()); } void DiscoverabilityManager::handleHeartbeatResponse(QNetworkReply& requestReply) { From 3ed11d2e5fa50c0f9574a8075dbad81e4f973cd6 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 29 Aug 2016 13:22:57 -0700 Subject: [PATCH 6/8] When trigger is pulled, snap search beam to correct length instead of animating. This improves interaction on web browser tablets. Instead of the beam shooting through the tablet and animating back to the surface, it starts at the correct length. --- scripts/system/controllers/handControllerGrab.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 32e0b047de..1658a661be 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1319,6 +1319,11 @@ function MyController(hand) { this.searchEnter = function() { mostRecentSearchingHand = this.hand; + var rayPickInfo = this.calcRayPickInfo(this.hand); + if (rayPickInfo.entityID || rayPickInfo.overlayID) { + this.intersectionDistance = rayPickInfo.distance; + this.searchSphereDistance = this.intersectionDistance; + } }; this.search = function(deltaTime, timestamp) { From e34aecde49135d6f9f03880962980fff27bd4909 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 29 Aug 2016 16:21:39 -0700 Subject: [PATCH 7/8] Bug fix for click events received by entity scripts PointerEvent.isLeftButton should be true even on left button release events. in a previous PR this behavior was changed. isLeftButton was used as a flag indicating the button state, which would be false on left button release events. Because we have scripts that rely on the old behavior, I've changed it back to the original and introduced isPrimaryHeld properties instead. --- libraries/shared/src/PointerEvent.cpp | 33 ++++++++++++++----- .../system/controllers/handControllerGrab.js | 25 ++++---------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/libraries/shared/src/PointerEvent.cpp b/libraries/shared/src/PointerEvent.cpp index 66289a35dd..ed9acb9ada 100644 --- a/libraries/shared/src/PointerEvent.cpp +++ b/libraries/shared/src/PointerEvent.cpp @@ -81,28 +81,43 @@ QScriptValue PointerEvent::toScriptValue(QScriptEngine* engine, const PointerEve direction.setProperty("z", event._direction.z); obj.setProperty("pos3D", direction); + bool isPrimaryButton = false; + bool isSecondaryButton = false; + bool isTertiaryButton = false; switch (event._button) { case NoButtons: obj.setProperty("button", "None"); break; case PrimaryButton: obj.setProperty("button", "Primary"); + isPrimaryButton = true; break; case SecondaryButton: obj.setProperty("button", "Secondary"); + isSecondaryButton = true; break; case TertiaryButton: obj.setProperty("button", "Tertiary"); + isTertiaryButton = true; break; } - obj.setProperty("isLeftButton", areFlagsSet(event._buttons, PrimaryButton)); - obj.setProperty("isRightButton", areFlagsSet(event._buttons, SecondaryButton)); - obj.setProperty("isMiddleButton", areFlagsSet(event._buttons, TertiaryButton)); + if (isPrimaryButton) { + obj.setProperty("isPrimaryButton", isPrimaryButton); + obj.setProperty("isLeftButton", isPrimaryButton); + } + if (isSecondaryButton) { + obj.setProperty("isSecondaryButton", isSecondaryButton); + obj.setProperty("isRightButton", isSecondaryButton); + } + if (isTertiaryButton) { + obj.setProperty("isTertiaryButton", isTertiaryButton); + obj.setProperty("isMiddleButton", isTertiaryButton); + } - obj.setProperty("isPrimaryButton", areFlagsSet(event._buttons, PrimaryButton)); - obj.setProperty("isSecondaryButton", areFlagsSet(event._buttons, SecondaryButton)); - obj.setProperty("isTertiaryButton", areFlagsSet(event._buttons, TertiaryButton)); + obj.setProperty("isPrimaryHeld", areFlagsSet(event._buttons, PrimaryButton)); + obj.setProperty("isSecondaryHeld", areFlagsSet(event._buttons, SecondaryButton)); + obj.setProperty("isTertiaryHeld", areFlagsSet(event._buttons, TertiaryButton)); return obj; } @@ -146,9 +161,9 @@ void PointerEvent::fromScriptValue(const QScriptValue& object, PointerEvent& eve event._button = NoButtons; } - bool primary = object.property("isPrimary").toBool() || object.property("isLeftButton").toBool(); - bool secondary = object.property("isSecondary").toBool() || object.property("isRightButton").toBool(); - bool tertiary = object.property("isTertiary").toBool() || object.property("isMiddleButton").toBool(); + bool primary = object.property("isPrimaryHeld").toBool(); + bool secondary = object.property("isSecondaryHeld").toBool(); + bool tertiary = object.property("isTertiaryHeld").toBool(); event._buttons = 0; if (primary) { event._buttons |= PrimaryButton; diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 32e0b047de..ac52f1b004 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1426,10 +1426,7 @@ function MyController(hand) { pos3D: rayPickInfo.intersection, normal: rayPickInfo.normal, direction: rayPickInfo.searchRay.direction, - button: "None", - isPrimaryButton: false, - isSecondaryButton: false, - isTertiaryButton: false + button: "None" }; this.hoverEntity = entity; @@ -1449,10 +1446,7 @@ function MyController(hand) { pos3D: rayPickInfo.intersection, normal: rayPickInfo.normal, direction: rayPickInfo.searchRay.direction, - button: "None", - isPrimaryButton: false, - isSecondaryButton: false, - isTertiaryButton: false + button: "None" }; Entities.sendMouseMoveOnEntity(entity, pointerEvent); @@ -2124,9 +2118,7 @@ function MyController(hand) { normal: intersectInfo.normal, direction: intersectInfo.searchRay.direction, button: "Primary", - isPrimaryButton: true, - isSecondaryButton: false, - isTertiaryButton: false + isPrimaryHeld: true }; Entities.sendMousePressOnEntity(this.grabbedEntity, pointerEvent); @@ -2152,15 +2144,12 @@ function MyController(hand) { pos3D: intersectInfo.point, normal: intersectInfo.normal, direction: intersectInfo.searchRay.direction, - button: "Primary", - isPrimaryButton: false, - isSecondaryButton: false, - isTertiaryButton: false + button: "Primary" }; } else { pointerEvent = this.touchingEnterPointerEvent; pointerEvent.button = "Primary"; - pointerEvent.isPrimaryButton = false; + pointerEvent.isPrimaryHeld = false; } Entities.sendMouseReleaseOnEntity(this.grabbedEntity, pointerEvent); @@ -2197,9 +2186,7 @@ function MyController(hand) { normal: intersectInfo.normal, direction: intersectInfo.searchRay.direction, button: "NoButtons", - isPrimaryButton: true, - isSecondaryButton: false, - isTertiaryButton: false + isPrimaryHeld: true }; var POINTER_PRESS_TO_MOVE_DELAY = 0.15; // seconds From 7397b884e7e33db0b4aaf7ae01844e5d4d3684a7 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 30 Aug 2016 09:07:39 -0700 Subject: [PATCH 8/8] support for fbx2016 format --- libraries/fbx/src/FBXReader_Node.cpp | 55 +++++++++++++++++++++------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/libraries/fbx/src/FBXReader_Node.cpp b/libraries/fbx/src/FBXReader_Node.cpp index 68e9d6abac..7bacdbc607 100644 --- a/libraries/fbx/src/FBXReader_Node.cpp +++ b/libraries/fbx/src/FBXReader_Node.cpp @@ -140,17 +140,35 @@ QVariant parseBinaryFBXProperty(QDataStream& in, int& position) { } } -FBXNode parseBinaryFBXNode(QDataStream& in, int& position) { - qint32 endOffset; - quint32 propertyCount; - quint32 propertyListLength; +FBXNode parseBinaryFBXNode(QDataStream& in, int& position, bool has64BitPositions = false) { + qint64 endOffset; + quint64 propertyCount; + quint64 propertyListLength; quint8 nameLength; - in >> endOffset; - in >> propertyCount; - in >> propertyListLength; + // FBX 2016 and beyond uses 64bit positions in the node headers, pre-2016 used 32bit values + // our code generally doesn't care about the size that much, so we will use 64bit values + // from here on out, but if the file is an older format we read the stream into temp 32bit + // values and then assign to our actual 64bit values. + if (has64BitPositions) { + in >> endOffset; + in >> propertyCount; + in >> propertyListLength; + position += sizeof(quint64) * 3; + } else { + qint32 tempEndOffset; + quint32 tempPropertyCount; + quint32 tempPropertyListLength; + in >> tempEndOffset; + in >> tempPropertyCount; + in >> tempPropertyListLength; + position += sizeof(quint32) * 3; + endOffset = tempEndOffset; + propertyCount = tempPropertyCount; + propertyListLength = tempPropertyListLength; + } in >> nameLength; - position += sizeof(quint32) * 3 + sizeof(quint8); + position += sizeof(quint8); FBXNode node; const int MIN_VALID_OFFSET = 40; @@ -166,7 +184,7 @@ FBXNode parseBinaryFBXNode(QDataStream& in, int& position) { } while (endOffset > position) { - FBXNode child = parseBinaryFBXNode(in, position); + FBXNode child = parseBinaryFBXNode(in, position, has64BitPositions); if (child.name.isNull()) { return node; @@ -327,15 +345,24 @@ FBXNode FBXReader::parseFBX(QIODevice* device) { // see http://code.blender.org/index.php/2013/08/fbx-binary-file-format-specification/ for an explanation // of the FBX binary format - // skip the rest of the header - const int HEADER_SIZE = 27; - in.skipRawData(HEADER_SIZE); - int position = HEADER_SIZE; + // The first 27 bytes contain the header. + // Bytes 0 - 20: Kaydara FBX Binary \x00(file - magic, with 2 spaces at the end, then a NULL terminator). + // Bytes 21 - 22: [0x1A, 0x00](unknown but all observed files show these bytes). + // Bytes 23 - 26 : unsigned int, the version number. 7300 for version 7.3 for example. + const int HEADER_BEFORE_VERSION = 23; + const quint32 VERSION_FBX2016 = 7500; + in.skipRawData(HEADER_BEFORE_VERSION); + int position = HEADER_BEFORE_VERSION; + quint32 fileVersion; + in >> fileVersion; + position += sizeof(fileVersion); + qDebug() << "fileVersion:" << fileVersion; + bool has64BitPositions = (fileVersion >= VERSION_FBX2016); // parse the top-level node FBXNode top; while (device->bytesAvailable()) { - FBXNode next = parseBinaryFBXNode(in, position); + FBXNode next = parseBinaryFBXNode(in, position, has64BitPositions); if (next.name.isNull()) { return top;