From 18b0ed9e8fd2070fc01bbf70da8866964e85ac93 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 30 Jul 2016 14:16:08 +1200 Subject: [PATCH 01/22] Close file browser dialog immediately after selecting asset to upload --- interface/resources/qml/AssetServer.qml | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index 8d971e48d3..1ad2d1a1e4 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -314,6 +314,14 @@ ScrollingWindow { }); } + Timer { + id: doUploadTimer + property var url + property bool isConnected: false + interval: 5 + repeat: false + running: false + } property var uploadOpen: false; Timer { @@ -366,6 +374,10 @@ ScrollingWindow { }, dropping); } + function initiateUpload(url) { + doUpload(doUploadTimer.url, false); + } + if (fileUrl) { doUpload(fileUrl, true); } else { @@ -373,12 +385,21 @@ ScrollingWindow { selectDirectory: false, dir: currentDirectory }); + browser.canceled.connect(function() { uploadOpen = false; }); + browser.selectedFile.connect(function(url) { currentDirectory = browser.dir; - doUpload(url, false); + + // Initiate upload from a timer so that file browser dialog can close beforehand. + doUploadTimer.url = url; + if (!doUploadTimer.isConnected) { + doUploadTimer.triggered.connect(function() { initiateUpload(); }); + doUploadTimer.isConnected = true; + } + doUploadTimer.start(); }); } } From b953e6f0ff78c9e0fdf9cbd9a77f5d8ef19843fe Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 1 Aug 2016 18:12:30 -0700 Subject: [PATCH 02/22] when an avatar URL fails, switch to the default --- interface/src/avatar/Avatar.cpp | 10 ++++++++++ interface/src/avatar/Avatar.h | 4 +++- .../src/model-networking/ModelCache.cpp | 1 + .../src/model-networking/ModelCache.h | 3 +++ libraries/render-utils/src/Model.cpp | 11 +++++++++++ libraries/render-utils/src/Model.h | 8 ++++++++ 6 files changed, 36 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 4d9481f002..f09eb5bdec 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -98,6 +98,7 @@ Avatar::Avatar(RigPointer rig) : _headData = static_cast(new Head(this)); _skeletonModel = std::make_shared(this, nullptr, rig); + connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished); } Avatar::~Avatar() { @@ -916,6 +917,15 @@ void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { } } +void Avatar::setModelURLFinished(bool success) { + if (!success && _skeletonModelURL != AvatarData::defaultFullAvatarModelUrl()) { + qDebug() << "Using default after failing to load Avatar model: " << _skeletonModelURL; + QMetaObject::invokeMethod(this, "setSkeletonModelURL", + Qt::QueuedConnection, Q_ARG(QUrl, AvatarData::defaultFullAvatarModelUrl())); + } +} + + // create new model, can return an instance of a SoftAttachmentModel rather then Model static std::shared_ptr allocateAttachmentModel(bool isSoft, RigPointer rigOverride) { if (isSoft) { diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index b9f44613c7..f646fe57f4 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -115,7 +115,7 @@ public: virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override { return false; } virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) override { return false; } - virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override; + Q_INVOKABLE virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override; virtual void setAttachmentData(const QVector& attachmentData) override; void setShowDisplayName(bool showDisplayName); @@ -184,6 +184,8 @@ public slots: glm::vec3 getRightPalmPosition() const; glm::quat getRightPalmRotation() const; + void setModelURLFinished(bool success); + protected: friend class AvatarManager; diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 26798070a6..3b7092ce8d 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -404,6 +404,7 @@ void GeometryResourceWatcher::resourceFinished(bool success) { if (success) { _geometryRef = std::make_shared(*_resource); } + emit finished(success); } void GeometryResourceWatcher::resourceRefreshed() { diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index 4a0a921a04..962a919d6c 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -111,6 +111,9 @@ public: QUrl getURL() const { return (bool)_resource ? _resource->getURL() : QUrl(); } +signals: + void finished(bool success); + private: void startWatching(); void stopWatching(); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index b04a1d8023..de0d69930b 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -109,6 +109,9 @@ Model::Model(RigPointer rig, QObject* parent) : } setSnapModelToRegistrationPoint(true, glm::vec3(0.5f)); + + connect(&_renderWatcher, &GeometryResourceWatcher::finished, this, &Model::loadURLFinished); + connect(&_collisionWatcher, &GeometryResourceWatcher::finished, this, &Model::loadURLFinished); } Model::~Model() { @@ -825,6 +828,10 @@ void Model::setURL(const QUrl& url) { onInvalidate(); } +void Model::loadURLFinished(bool success) { + emit setURLFinished(success); +} + void Model::setCollisionModelURL(const QUrl& url) { if (_collisionUrl == url && _collisionWatcher.getURL() == url) { return; @@ -833,6 +840,10 @@ void Model::setCollisionModelURL(const QUrl& url) { _collisionWatcher.setResource(DependencyManager::get()->getGeometryResource(url)); } +void Model::loadCollisionModelURLFinished(bool success) { + emit setCollisionModelURLFinished(success); +} + bool Model::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const { return _rig->getJointPositionInWorldFrame(jointIndex, position, _translation, _rotation); } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 98e50c66f4..076913a883 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -236,6 +236,14 @@ public: // returns 'true' if needs fullUpdate after geometry change bool updateGeometry(); +public slots: + void loadURLFinished(bool success); + void loadCollisionModelURLFinished(bool success); + +signals: + void setURLFinished(bool success); + void setCollisionModelURLFinished(bool success); + protected: void setPupilDilation(float dilation) { _pupilDilation = dilation; } From ed4b0b3589d700b532eb546cca007900826cd7a8 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 2 Aug 2016 09:57:17 -0700 Subject: [PATCH 03/22] call _skeletonModel.setURL, but leave our copy of _skeletonModelURL alone. This is so that we don't redo this every time we receive an identity packet from the avatar with the bad url. --- interface/src/avatar/Avatar.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index f09eb5bdec..001dfea10b 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -920,7 +920,9 @@ void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { void Avatar::setModelURLFinished(bool success) { if (!success && _skeletonModelURL != AvatarData::defaultFullAvatarModelUrl()) { qDebug() << "Using default after failing to load Avatar model: " << _skeletonModelURL; - QMetaObject::invokeMethod(this, "setSkeletonModelURL", + // call _skeletonModel.setURL, but leave our copy of _skeletonModelURL alone. This is so that + // we don't redo this every time we receive an identity packet from the avatar with the bad url. + QMetaObject::invokeMethod(_skeletonModel.get(), "setURL", Qt::QueuedConnection, Q_ARG(QUrl, AvatarData::defaultFullAvatarModelUrl())); } } From 229b8d3b5ebd795087729bd336a5cdaa4d25a778 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 2 Aug 2016 10:46:11 -0700 Subject: [PATCH 04/22] testing a more explicit fix of the audio failure --- interface/src/avatar/Avatar.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 001dfea10b..ab9c4b2f79 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -299,7 +299,9 @@ void Avatar::simulate(float deltaTime) { { PerformanceTimer perfTimer("head"); glm::vec3 headPosition = getPosition(); - _skeletonModel->getHeadPosition(headPosition); + if (!_skeletonModel->getHeadPosition(headPosition)) { + headPosition = getPosition(); + } Head* head = getHead(); head->setPosition(headPosition); head->setScale(getUniformScale()); @@ -922,8 +924,8 @@ void Avatar::setModelURLFinished(bool success) { qDebug() << "Using default after failing to load Avatar model: " << _skeletonModelURL; // call _skeletonModel.setURL, but leave our copy of _skeletonModelURL alone. This is so that // we don't redo this every time we receive an identity packet from the avatar with the bad url. - QMetaObject::invokeMethod(_skeletonModel.get(), "setURL", - Qt::QueuedConnection, Q_ARG(QUrl, AvatarData::defaultFullAvatarModelUrl())); + // QMetaObject::invokeMethod(_skeletonModel.get(), "setURL", + // Qt::QueuedConnection, Q_ARG(QUrl, AvatarData::defaultFullAvatarModelUrl())); } } From 56af36ae64f27eeddf390b1d642341e401228dca Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 2 Aug 2016 11:29:43 -0700 Subject: [PATCH 05/22] set head position of other avatars even if they can't be drawn --- interface/src/avatar/Avatar.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index ab9c4b2f79..45cdba9ace 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -309,6 +309,7 @@ void Avatar::simulate(float deltaTime) { } } else { // a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated. + getHead()->setPosition(getPosition()); _skeletonModel->simulate(deltaTime, false); } From 75f9626c2d046773c9e5ff527569db55749fe86f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 2 Aug 2016 11:50:53 -0700 Subject: [PATCH 06/22] if MyAvatar simulation is cut short (due to no skeleton), still update the head position --- interface/src/avatar/MyAvatar.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index eef176338f..de5455fc14 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -430,6 +430,7 @@ void MyAvatar::simulate(float deltaTime) { if (!_skeletonModel->hasSkeleton()) { // All the simulation that can be done has been done + getHead()->setPosition(getPosition()); // so audio-position isn't 0,0,0 return; } From f91df4997d7a2f8179cc394c3e673ddb010b1304 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 2 Aug 2016 11:55:11 -0700 Subject: [PATCH 07/22] re-enable falling-back to default avatar if an avatar's url is bad --- interface/src/avatar/Avatar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 45cdba9ace..b1b30a1acd 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -925,8 +925,8 @@ void Avatar::setModelURLFinished(bool success) { qDebug() << "Using default after failing to load Avatar model: " << _skeletonModelURL; // call _skeletonModel.setURL, but leave our copy of _skeletonModelURL alone. This is so that // we don't redo this every time we receive an identity packet from the avatar with the bad url. - // QMetaObject::invokeMethod(_skeletonModel.get(), "setURL", - // Qt::QueuedConnection, Q_ARG(QUrl, AvatarData::defaultFullAvatarModelUrl())); + QMetaObject::invokeMethod(_skeletonModel.get(), "setURL", + Qt::QueuedConnection, Q_ARG(QUrl, AvatarData::defaultFullAvatarModelUrl())); } } From 79121133dfc6e7528d8de0c91fe42ed20ccc4991 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 2 Aug 2016 12:00:45 -0700 Subject: [PATCH 08/22] remove unneeded change --- interface/src/avatar/Avatar.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index f646fe57f4..910f2cc1e6 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -115,7 +115,7 @@ public: virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override { return false; } virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) override { return false; } - Q_INVOKABLE virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override; + virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override; virtual void setAttachmentData(const QVector& attachmentData) override; void setShowDisplayName(bool showDisplayName); From 363915b836e3a327231ffed1ae859fa8a5fa6234 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 2 Aug 2016 19:37:38 -0700 Subject: [PATCH 09/22] switch audio script to use a valid but invisible avatar url --- .../audioExamples/acAudioSearching/ACAudioSearchAndInject.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script-archive/audioExamples/acAudioSearching/ACAudioSearchAndInject.js b/script-archive/audioExamples/acAudioSearching/ACAudioSearchAndInject.js index 30567b4fc7..daef1d5db3 100644 --- a/script-archive/audioExamples/acAudioSearching/ACAudioSearchAndInject.js +++ b/script-archive/audioExamples/acAudioSearching/ACAudioSearchAndInject.js @@ -40,7 +40,7 @@ var DEFAULT_SOUND_DATA = { Script.include("../../libraries/utils.js"); Agent.isAvatar = true; // This puts a robot at 0,0,0, but is currently necessary in order to use AvatarList. -Avatar.skeletonModelURL = "http://invalid-url"; +Avatar.skeletonModelURL = "http://hifi-content.s3.amazonaws.com/ozan/dev/avatars/invisible_avatar/invisible_avatar.fst"; function ignore() {} function debug() { // Display the arguments not just [Object object]. //print.apply(null, [].map.call(arguments, JSON.stringify)); From c743cf379ee23367924adaabf01f2e5c93b9965d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 3 Aug 2016 11:31:51 -0700 Subject: [PATCH 10/22] use loadCollisionModelURLFinished when collisions model is finished --- libraries/render-utils/src/Model.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index bedd158f2a..d755dc3aca 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -112,7 +112,7 @@ Model::Model(RigPointer rig, QObject* parent) : setSnapModelToRegistrationPoint(true, glm::vec3(0.5f)); connect(&_renderWatcher, &GeometryResourceWatcher::finished, this, &Model::loadURLFinished); - connect(&_collisionWatcher, &GeometryResourceWatcher::finished, this, &Model::loadURLFinished); + connect(&_collisionWatcher, &GeometryResourceWatcher::finished, this, &Model::loadCollisionModelURLFinished); } Model::~Model() { From f9b6db12e3f1ebc5211f607e7483d8ded75e2df0 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Wed, 3 Aug 2016 13:48:27 -0700 Subject: [PATCH 11/22] Fix parsing of embedded entity scripts Now correctly identifies when scripts are not urls as well as javascript: urls. --- libraries/script-engine/src/ScriptCache.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libraries/script-engine/src/ScriptCache.cpp b/libraries/script-engine/src/ScriptCache.cpp index 2114289095..d80baf9a7b 100644 --- a/libraries/script-engine/src/ScriptCache.cpp +++ b/libraries/script-engine/src/ScriptCache.cpp @@ -110,11 +110,18 @@ void ScriptCache::getScriptContents(const QString& scriptOrURL, contentAvailable QUrl url = ResourceManager::normalizeURL(unnormalizedURL); // attempt to determine if this is a URL to a script, or if this is actually a script itself (which is valid in the entityScript use case) - if (url.scheme().isEmpty() && scriptOrURL.simplified().replace(" ", "").contains("(function(){")) { + if (unnormalizedURL.scheme().isEmpty() && scriptOrURL.simplified().replace(" ", "").contains("(function(){")) { contentAvailable(scriptOrURL, scriptOrURL, false, true); return; } + // give a similar treatment to javacript: urls + if (unnormalizedURL.scheme() == "javascript") { + QString contents{ scriptOrURL }; + contents.replace(QRegExp("^javascript:"), ""); + contentAvailable(scriptOrURL, contents, false, true); + } + Lock lock(_containerLock); if (_scriptCache.contains(url) && !forceDownload) { auto scriptContent = _scriptCache[url]; From 78e59106b40b63c833c7f533ff4fe7bb078d729e Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 3 Aug 2016 15:21:49 -0700 Subject: [PATCH 12/22] adjust threshold for how far above the ground will cause the avatar to auto-fly --- libraries/physics/src/CharacterController.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 30fb3536ca..df166ceb59 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -540,7 +540,8 @@ void CharacterController::preSimulation() { btScalar rayLength = _radius + MAX_FALL_HEIGHT; btVector3 rayEnd = rayStart - rayLength * _currentUp; - const btScalar JUMP_PROXIMITY_THRESHOLD = 0.1f * _radius; + const btScalar FLY_TO_GROUND_THRESHOLD = 0.1f * _radius; + const btScalar GROUND_TO_AUTOFLY_THRESHOLD = 0.5f * _radius; const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 250 * MSECS_PER_SECOND; const btScalar MIN_HOVER_HEIGHT = 2.5f; const quint64 JUMP_TO_HOVER_PERIOD = 1100 * MSECS_PER_SECOND; @@ -581,7 +582,7 @@ void CharacterController::preSimulation() { _takeoffJumpButtonID = _jumpButtonDownCount; _takeoffToInAirStartTime = now; SET_STATE(State::Takeoff, "jump pressed"); - } else if (rayHasHit && !_hasSupport && _floorDistance > JUMP_PROXIMITY_THRESHOLD) { + } else if (rayHasHit && !_hasSupport && _floorDistance > GROUND_TO_AUTOFLY_THRESHOLD) { SET_STATE(State::InAir, "falling"); } break; @@ -595,7 +596,7 @@ void CharacterController::preSimulation() { } break; case State::InAir: { - if ((velocity.dot(_currentUp) <= (JUMP_SPEED / 2.0f)) && ((_floorDistance < JUMP_PROXIMITY_THRESHOLD) || _hasSupport)) { + if ((velocity.dot(_currentUp) <= (JUMP_SPEED / 2.0f)) && ((_floorDistance < FLY_TO_GROUND_THRESHOLD) || _hasSupport)) { SET_STATE(State::Ground, "hit ground"); } else { btVector3 desiredVelocity = _targetVelocity; @@ -614,7 +615,7 @@ void CharacterController::preSimulation() { case State::Hover: if ((_floorDistance < MIN_HOVER_HEIGHT) && !jumpButtonHeld && !flyingFast) { SET_STATE(State::InAir, "near ground"); - } else if (((_floorDistance < JUMP_PROXIMITY_THRESHOLD) || _hasSupport) && !flyingFast) { + } else if (((_floorDistance < FLY_TO_GROUND_THRESHOLD) || _hasSupport) && !flyingFast) { SET_STATE(State::Ground, "touching ground"); } break; From a826f4eca1c689ec641b0f43393adab0fa73fe46 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Wed, 3 Aug 2016 15:55:51 -0700 Subject: [PATCH 13/22] Fix console errors for javascript: urls --- libraries/script-engine/src/ScriptCache.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/libraries/script-engine/src/ScriptCache.cpp b/libraries/script-engine/src/ScriptCache.cpp index d80baf9a7b..40234e8134 100644 --- a/libraries/script-engine/src/ScriptCache.cpp +++ b/libraries/script-engine/src/ScriptCache.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -109,17 +110,20 @@ void ScriptCache::getScriptContents(const QString& scriptOrURL, contentAvailable QUrl unnormalizedURL(scriptOrURL); QUrl url = ResourceManager::normalizeURL(unnormalizedURL); - // attempt to determine if this is a URL to a script, or if this is actually a script itself (which is valid in the entityScript use case) - if (unnormalizedURL.scheme().isEmpty() && scriptOrURL.simplified().replace(" ", "").contains("(function(){")) { + // attempt to determine if this is a URL to a script, or if this is actually a script itself (which is valid in the + // entityScript use case) + if (unnormalizedURL.scheme().isEmpty() && + scriptOrURL.simplified().replace(" ", "").contains(QRegularExpression(R"(\(function\([a-z]?[\w,]*\){)"))) { contentAvailable(scriptOrURL, scriptOrURL, false, true); return; } // give a similar treatment to javacript: urls if (unnormalizedURL.scheme() == "javascript") { - QString contents{ scriptOrURL }; - contents.replace(QRegExp("^javascript:"), ""); + QString contents { scriptOrURL }; + contents.replace(QRegularExpression("^javascript:"), ""); contentAvailable(scriptOrURL, contents, false, true); + return; } Lock lock(_containerLock); @@ -140,6 +144,7 @@ void ScriptCache::getScriptContents(const QString& scriptOrURL, contentAvailable qCDebug(scriptengine) << "about to call: ResourceManager::createResourceRequest(this, url); on thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]"; #endif auto request = ResourceManager::createResourceRequest(nullptr, url); + Q_ASSERT(request); request->setCacheEnabled(!forceDownload); connect(request, &ResourceRequest::finished, this, &ScriptCache::scriptContentAvailable); request->send(); From ba49fd2c6133ab6c649df51fc8181e39861d117f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 4 Aug 2016 09:19:01 -0700 Subject: [PATCH 14/22] don't heartbeat immediately unless metaverse domain --- domain-server/src/DomainServer.cpp | 85 ++++++++++++++++++++---------- domain-server/src/DomainServer.h | 8 +++ 2 files changed, 65 insertions(+), 28 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 23e37efaf1..d352cb375f 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -117,9 +117,18 @@ DomainServer::DomainServer(int argc, char* argv[]) : _settingsManager.apiRefreshGroupInformation(); setupNodeListAndAssignments(); + + if (_type == MetaverseDomain) { + // if we have a metaverse domain, we'll need an access token to heartbeat handle auto-networking + resetAccountManagerAccessToken(); + } + setupAutomaticNetworking(); - if (!getID().isNull()) { + + if (!getID().isNull() && _type != NonMetaverse) { + // setup periodic heartbeats to metaverse API setupHeartbeatToMetaverse(); + // send the first heartbeat immediately sendHeartbeatToMetaverse(); } @@ -301,16 +310,22 @@ void DomainServer::handleTempDomainSuccess(QNetworkReply& requestReply) { // store the new ID and auto networking setting on disk _settingsManager.persistToFile(); - // change our domain ID immediately - DependencyManager::get()->setSessionUUID(QUuid { id }); - // store the new token to the account info auto accountManager = DependencyManager::get(); accountManager->setTemporaryDomain(id, key); + // change our domain ID immediately + DependencyManager::get()->setSessionUUID(QUuid { id }); + + // change our type to reflect that we are a temporary domain now + _type = MetaverseTemporaryDomain; + // update our heartbeats to use the correct id setupICEHeartbeatForFullNetworking(); setupHeartbeatToMetaverse(); + + // if we have a current ICE server address, update it in the API for the new temporary domain + sendICEServerAddressToMetaverseAPI(); } else { qWarning() << "There were problems parsing the API response containing a temporary domain name. Please try again" << "via domain-server relaunch or from the domain-server settings."; @@ -394,6 +409,16 @@ void DomainServer::setupNodeListAndAssignments() { const QVariant* idValueVariant = valueForKeyPath(settingsMap, METAVERSE_DOMAIN_ID_KEY_PATH); if (idValueVariant) { nodeList->setSessionUUID(idValueVariant->toString()); + + // if we have an ID, we'll assume we're a metaverse domain + // now see if we think we're a temp domain (we have an API key) or a full domain + const auto& temporaryDomainKey = DependencyManager::get()->getTemporaryDomainKey(getID()); + if (temporaryDomainKey.isEmpty()) { + _type = MetaverseDomain; + } else { + _type = MetaverseTemporaryDomain; + } + } else { nodeList->setSessionUUID(QUuid::createUuid()); // Use random UUID } @@ -477,42 +502,46 @@ bool DomainServer::resetAccountManagerAccessToken() { } void DomainServer::setupAutomaticNetworking() { - qDebug() << "Updating automatic networking setting in domain-server to" << _automaticNetworkingSetting; - - resetAccountManagerAccessToken(); _automaticNetworkingSetting = _settingsManager.valueOrDefaultValueForKeyPath(METAVERSE_AUTOMATIC_NETWORKING_KEY_PATH).toString(); - auto nodeList = DependencyManager::get(); - const QUuid& domainID = getID(); + qDebug() << "Configuring automatic networking in domain-server as" << _automaticNetworkingSetting; - if (_automaticNetworkingSetting == FULL_AUTOMATIC_NETWORKING_VALUE) { - setupICEHeartbeatForFullNetworking(); - } + if (_automaticNetworkingSetting != DISABLED_AUTOMATIC_NETWORKING_VALUE) { + const QUuid& domainID = getID(); - if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE || - _automaticNetworkingSetting == FULL_AUTOMATIC_NETWORKING_VALUE) { + if (_automaticNetworkingSetting == FULL_AUTOMATIC_NETWORKING_VALUE) { + setupICEHeartbeatForFullNetworking(); + } - if (!domainID.isNull()) { - qDebug() << "domain-server" << _automaticNetworkingSetting << "automatic networking enabled for ID" - << uuidStringWithoutCurlyBraces(domainID) << "via" << _oauthProviderURL.toString(); + if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE || + _automaticNetworkingSetting == FULL_AUTOMATIC_NETWORKING_VALUE) { - if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE) { - // send any public socket changes to the data server so nodes can find us at our new IP - connect(nodeList.data(), &LimitedNodeList::publicSockAddrChanged, - this, &DomainServer::performIPAddressUpdate); + if (!domainID.isNull()) { + qDebug() << "domain-server" << _automaticNetworkingSetting << "automatic networking enabled for ID" + << uuidStringWithoutCurlyBraces(domainID) << "via" << _oauthProviderURL.toString(); - // have the LNL enable public socket updating via STUN - nodeList->startSTUNPublicSocketUpdate(); + if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE) { + + auto nodeList = DependencyManager::get(); + + // send any public socket changes to the data server so nodes can find us at our new IP + connect(nodeList.data(), &LimitedNodeList::publicSockAddrChanged, + this, &DomainServer::performIPAddressUpdate); + + // have the LNL enable public socket updating via STUN + nodeList->startSTUNPublicSocketUpdate(); + } + } else { + qDebug() << "Cannot enable domain-server automatic networking without a domain ID." + << "Please add an ID to your config file or via the web interface."; + + return; } - } else { - qDebug() << "Cannot enable domain-server automatic networking without a domain ID." - << "Please add an ID to your config file or via the web interface."; - - return; } } + } void DomainServer::setupHeartbeatToMetaverse() { diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 4004333789..06b3ed2c0a 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -42,6 +42,12 @@ public: DomainServer(int argc, char* argv[]); ~DomainServer(); + enum DomainType { + NonMetaverse, + MetaverseDomain, + MetaverseTemporaryDomain + }; + static int const EXIT_CODE_REBOOT; bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false); @@ -195,6 +201,8 @@ private: int _numHeartbeatDenials { 0 }; bool _connectedToICEServer { false }; + DomainType _type { DomainType::NonMetaverse }; + friend class DomainGatekeeper; friend class DomainMetadata; }; From f3e30221f0553fe33ed772d06c0a95e0274edf30 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 4 Aug 2016 09:32:01 -0700 Subject: [PATCH 15/22] only force a new temp name if already a temp domain-server --- domain-server/src/DomainServer.cpp | 63 ++++++++++++++++-------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index d352cb375f..6c4b12d4c0 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1168,42 +1168,45 @@ void DomainServer::handleMetaverseHeartbeatError(QNetworkReply& requestReply) { return; } - // check if we need to force a new temporary domain name - switch (requestReply.error()) { - // if we have a temporary domain with a bad token, we get a 401 - case QNetworkReply::NetworkError::AuthenticationRequiredError: { - static const QString DATA_KEY = "data"; - static const QString TOKEN_KEY = "api_key"; + // only attempt to grab a new temporary name if we're already a temporary domain server + if (_type == MetaverseTemporaryDomain) { + // check if we need to force a new temporary domain name + switch (requestReply.error()) { + // if we have a temporary domain with a bad token, we get a 401 + case QNetworkReply::NetworkError::AuthenticationRequiredError: { + static const QString DATA_KEY = "data"; + static const QString TOKEN_KEY = "api_key"; - QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); - auto tokenFailure = jsonObject[DATA_KEY].toObject()[TOKEN_KEY]; + QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); + auto tokenFailure = jsonObject[DATA_KEY].toObject()[TOKEN_KEY]; - if (!tokenFailure.isNull()) { - qWarning() << "Temporary domain name lacks a valid API key, and is being reset."; + if (!tokenFailure.isNull()) { + qWarning() << "Temporary domain name lacks a valid API key, and is being reset."; + } + break; } - break; + // if the domain does not (or no longer) exists, we get a 404 + case QNetworkReply::NetworkError::ContentNotFoundError: + qWarning() << "Domain not found, getting a new temporary domain."; + break; + // otherwise, we erred on something else, and should not force a temporary domain + default: + return; } - // if the domain does not (or no longer) exists, we get a 404 - case QNetworkReply::NetworkError::ContentNotFoundError: - qWarning() << "Domain not found, getting a new temporary domain."; - break; - // otherwise, we erred on something else, and should not force a temporary domain - default: - return; - } - // halt heartbeats until we have a token - _metaverseHeartbeatTimer->deleteLater(); - _metaverseHeartbeatTimer = nullptr; + // halt heartbeats until we have a token + _metaverseHeartbeatTimer->deleteLater(); + _metaverseHeartbeatTimer = nullptr; - // give up eventually to avoid flooding traffic - static const int MAX_ATTEMPTS = 5; - static int attempt = 0; - if (++attempt < MAX_ATTEMPTS) { - // get a new temporary name and token - getTemporaryName(true); - } else { - qWarning() << "Already attempted too many temporary domain requests. Please set a domain ID manually or restart."; + // give up eventually to avoid flooding traffic + static const int MAX_ATTEMPTS = 5; + static int attempt = 0; + if (++attempt < MAX_ATTEMPTS) { + // get a new temporary name and token + getTemporaryName(true); + } else { + qWarning() << "Already attempted too many temporary domain requests. Please set a domain ID manually or restart."; + } } } From e1c7ced652720642ed032a966fa38400828245cb Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 4 Aug 2016 09:54:26 -0700 Subject: [PATCH 16/22] increase GROUND_TO_AUTOFLY_THRESHOLD --- libraries/physics/src/CharacterController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index df166ceb59..1c6836769b 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -541,7 +541,7 @@ void CharacterController::preSimulation() { btVector3 rayEnd = rayStart - rayLength * _currentUp; const btScalar FLY_TO_GROUND_THRESHOLD = 0.1f * _radius; - const btScalar GROUND_TO_AUTOFLY_THRESHOLD = 0.5f * _radius; + const btScalar GROUND_TO_AUTOFLY_THRESHOLD = 1.2f * _radius; const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 250 * MSECS_PER_SECOND; const btScalar MIN_HOVER_HEIGHT = 2.5f; const quint64 JUMP_TO_HOVER_PERIOD = 1100 * MSECS_PER_SECOND; From 7629e34586702d3172d643f9cea8b01ff7562f55 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 4 Aug 2016 11:05:34 -0700 Subject: [PATCH 17/22] optimized distance-attnuation calculation --- assignment-client/src/audio/AudioMixer.cpp | 59 +++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 8f752e70d0..eabb4955d9 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -100,6 +100,63 @@ AudioMixer::AudioMixer(ReceivedMessage& message) : const float ATTENUATION_BEGINS_AT_DISTANCE = 1.0f; +const int IEEE754_MANT_BITS = 23; +const int IEEE754_EXPN_BIAS = 127; + +// +// for x > 0.0f, returns log2(x) +// for x <= 0.0f, returns large negative value +// +// abs |error| < 8e-3, smooth (exact for x=2^N) for NPOLY=3 +// abs |error| < 2e-4, smooth (exact for x=2^N) for NPOLY=5 +// rel |error| < 0.4 from precision loss very close to 1.0f +// +static inline float fastlog2(float x) { + + union { float f; int32_t i; } mant, bits = { x }; + + // split into mantissa and exponent + mant.i = (bits.i & ((1 << IEEE754_MANT_BITS) - 1)) | (IEEE754_EXPN_BIAS << IEEE754_MANT_BITS); + int32_t expn = (bits.i >> IEEE754_MANT_BITS) - IEEE754_EXPN_BIAS; + + mant.f -= 1.0f; + + // polynomial for log2(1+x) over x=[0,1] + //x = (-0.346555386f * mant.f + 1.346555386f) * mant.f; + x = (((-0.0821307180f * mant.f + 0.321188984f) * mant.f - 0.677784014f) * mant.f + 1.43872575f) * mant.f; + + return x + expn; +} + +// +// for -126 <= x < 128, returns exp2(x) +// +// rel |error| < 3e-3, smooth (exact for x=N) for NPOLY=3 +// rel |error| < 9e-6, smooth (exact for x=N) for NPOLY=5 +// +static inline float fastexp2(float x) { + + union { float f; int32_t i; } xi; + + // bias such that x > 0 + x += IEEE754_EXPN_BIAS; + //x = MAX(x, 1.0f); + //x = MIN(x, 254.9999f); + + // split into integer and fraction + xi.i = (int32_t)x; + x -= xi.i; + + // construct exp2(xi) as a float + xi.i <<= IEEE754_MANT_BITS; + + // polynomial for exp2(x) over x=[0,1] + //x = (0.339766028f * x + 0.660233972f) * x + 1.0f; + x = (((0.0135557472f * x + 0.0520323690f) * x + 0.241379763f) * x + 0.693032121f) * x + 1.0f; + + return x * xi.f; +} + float AudioMixer::gainForSource(const PositionalAudioStream& streamToAdd, const AvatarAudioStream& listeningNodeStream, const glm::vec3& relativePosition, bool isEcho) { float gain = 1.0f; @@ -148,7 +205,7 @@ float AudioMixer::gainForSource(const PositionalAudioStream& streamToAdd, g = (g > 1.0f) ? 1.0f : g; // calculate the distance coefficient using the distance to this node - float distanceCoefficient = exp2f(log2f(g) * log2f(distanceBetween/ATTENUATION_BEGINS_AT_DISTANCE)); + float distanceCoefficient = fastexp2(fastlog2(g) * fastlog2(distanceBetween/ATTENUATION_BEGINS_AT_DISTANCE)); // multiply the current attenuation coefficient by the distance coefficient gain *= distanceCoefficient; From aeadbda1e171cfe67f19af7dcf687c02812b5171 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 4 Aug 2016 11:34:42 -0700 Subject: [PATCH 18/22] adjust ground-to-fly logic --- libraries/physics/src/CharacterController.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 1c6836769b..b5ef03be0f 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -534,14 +534,14 @@ void CharacterController::preSimulation() { // scan for distant floor // rayStart is at center of bottom sphere - btVector3 rayStart = _characterBodyTransform.getOrigin() - _halfHeight * _currentUp; + btVector3 rayStart = _characterBodyTransform.getOrigin(); // rayEnd is straight down MAX_FALL_HEIGHT btScalar rayLength = _radius + MAX_FALL_HEIGHT; btVector3 rayEnd = rayStart - rayLength * _currentUp; const btScalar FLY_TO_GROUND_THRESHOLD = 0.1f * _radius; - const btScalar GROUND_TO_AUTOFLY_THRESHOLD = 1.2f * _radius; + const btScalar GROUND_TO_FLY_THRESHOLD = 0.8f * _radius + _halfHeight; const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 250 * MSECS_PER_SECOND; const btScalar MIN_HOVER_HEIGHT = 2.5f; const quint64 JUMP_TO_HOVER_PERIOD = 1100 * MSECS_PER_SECOND; @@ -554,7 +554,7 @@ void CharacterController::preSimulation() { bool rayHasHit = rayCallback.hasHit(); if (rayHasHit) { _rayHitStartTime = now; - _floorDistance = rayLength * rayCallback.m_closestHitFraction - _radius; + _floorDistance = rayLength * rayCallback.m_closestHitFraction - (_radius + _halfHeight); } else if ((now - _rayHitStartTime) < RAY_HIT_START_PERIOD) { rayHasHit = true; } else { @@ -582,7 +582,7 @@ void CharacterController::preSimulation() { _takeoffJumpButtonID = _jumpButtonDownCount; _takeoffToInAirStartTime = now; SET_STATE(State::Takeoff, "jump pressed"); - } else if (rayHasHit && !_hasSupport && _floorDistance > GROUND_TO_AUTOFLY_THRESHOLD) { + } else if (rayHasHit && !_hasSupport && _floorDistance > GROUND_TO_FLY_THRESHOLD) { SET_STATE(State::InAir, "falling"); } break; From dc22b579f749827f6b278af15a38cd29247dbe7b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 5 Aug 2016 15:28:37 -0700 Subject: [PATCH 19/22] adjust code that auto-unequips items when they are pulled too far from the equipping hand --- .../system/controllers/handControllerGrab.js | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 9e11f839b3..b17a2354a7 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -107,7 +107,10 @@ var NEAR_GRAB_PICK_RADIUS = 0.25; // radius used for search ray vs object for ne var PICK_BACKOFF_DISTANCE = 0.2; // helps when hand is intersecting the grabble object var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-grabbed -var CHECK_TOO_FAR_UNEQUIP_TIME = 1.0; // seconds + +// if an equipped item is "adjusted" to be too far from the hand it's in, it will be unequipped. +var CHECK_TOO_FAR_UNEQUIP_TIME = 0.3; // seconds, duration between checks +var AUTO_UNEQUIP_DISTANCE_FACTOR = 1.2; // multiplied by maximum dimention of held item, > this means drop // // other constants @@ -220,6 +223,17 @@ CONTROLLER_STATE_MACHINE[STATE_FAR_TRIGGER] = { updateMethod: "farTrigger" }; +function getMaxDimensions(props) { + var maxDimension = props.dimensions.x; + if (props.dimensions.y > maxDimension) { + maxDimension = props.dimensions.y; + } + if (props.dimensions.z > maxDimension) { + maxDimension = props.dimensions.z; + } + return maxDimension; +} + function stateToName(state) { return CONTROLLER_STATE_MACHINE[state] ? CONTROLLER_STATE_MACHINE[state].name : "???"; } @@ -1818,7 +1832,8 @@ function MyController(hand) { this.heartBeat(this.grabbedEntity); - var props = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "parentID", "position", "rotation"]); + var props = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "parentID", + "position", "rotation", "dimensions"]); if (!props.position) { // server may have reset, taking our equipped entity with it. move back to "off" stte this.callEntityMethodOnGrabbed("releaseGrab"); @@ -1830,10 +1845,11 @@ function MyController(hand) { if (now - this.lastUnequipCheckTime > MSECS_PER_SEC * CHECK_TOO_FAR_UNEQUIP_TIME) { this.lastUnequipCheckTime = now; + var maxDimension = getMaxDimensions(props); if (props.parentID == MyAvatar.sessionUUID && - Vec3.length(props.localPosition) > NEAR_GRAB_MAX_DISTANCE) { + Vec3.length(props.localPosition) > maxDimension * AUTO_UNEQUIP_DISTANCE_FACTOR) { var handPosition = this.getHandPosition(); - // the center of the equipped object being far from the hand isn't enough to autoequip -- we also + // the center of the equipped object being far from the hand isn't enough to auto-unequip -- we also // need to fail the findEntities test. var nearPickedCandidateEntities = Entities.findEntities(handPosition, NEAR_GRAB_RADIUS); if (nearPickedCandidateEntities.indexOf(this.grabbedEntity) == -1) { @@ -2416,4 +2432,4 @@ function cleanup() { } Script.scriptEnding.connect(cleanup); -Script.update.connect(update); \ No newline at end of file +Script.update.connect(update); From e59c010642a4e376c293cbe7d677d50f1cbf6d73 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 5 Aug 2016 15:39:20 -0700 Subject: [PATCH 20/22] speling --- scripts/system/controllers/handControllerGrab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index b17a2354a7..ea95422c7f 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -110,7 +110,7 @@ var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-g // if an equipped item is "adjusted" to be too far from the hand it's in, it will be unequipped. var CHECK_TOO_FAR_UNEQUIP_TIME = 0.3; // seconds, duration between checks -var AUTO_UNEQUIP_DISTANCE_FACTOR = 1.2; // multiplied by maximum dimention of held item, > this means drop +var AUTO_UNEQUIP_DISTANCE_FACTOR = 1.2; // multiplied by maximum dimension of held item, > this means drop // // other constants From 090a0a6e9a7f9fc85184f80bb76410ec80172b51 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 5 Aug 2016 18:01:48 -0700 Subject: [PATCH 21/22] only use hand-sphere vs equipped-item bounding box when deciding to auto-unequip --- scripts/system/controllers/handControllerGrab.js | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index ea95422c7f..f29fdfb00a 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -223,17 +223,6 @@ CONTROLLER_STATE_MACHINE[STATE_FAR_TRIGGER] = { updateMethod: "farTrigger" }; -function getMaxDimensions(props) { - var maxDimension = props.dimensions.x; - if (props.dimensions.y > maxDimension) { - maxDimension = props.dimensions.y; - } - if (props.dimensions.z > maxDimension) { - maxDimension = props.dimensions.z; - } - return maxDimension; -} - function stateToName(state) { return CONTROLLER_STATE_MACHINE[state] ? CONTROLLER_STATE_MACHINE[state].name : "???"; } @@ -1845,9 +1834,7 @@ function MyController(hand) { if (now - this.lastUnequipCheckTime > MSECS_PER_SEC * CHECK_TOO_FAR_UNEQUIP_TIME) { this.lastUnequipCheckTime = now; - var maxDimension = getMaxDimensions(props); - if (props.parentID == MyAvatar.sessionUUID && - Vec3.length(props.localPosition) > maxDimension * AUTO_UNEQUIP_DISTANCE_FACTOR) { + if (props.parentID == MyAvatar.sessionUUID) { var handPosition = this.getHandPosition(); // the center of the equipped object being far from the hand isn't enough to auto-unequip -- we also // need to fail the findEntities test. From f348b56106247f067467fd1dd705d55ce9da561d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 5 Aug 2016 18:16:48 -0700 Subject: [PATCH 22/22] remove hidden attribute from advanced settings button --- domain-server/resources/web/settings/index.shtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/resources/web/settings/index.shtml b/domain-server/resources/web/settings/index.shtml index 4c937d6139..802038d806 100644 --- a/domain-server/resources/web/settings/index.shtml +++ b/domain-server/resources/web/settings/index.shtml @@ -25,7 +25,7 @@ - +