From 837b19ed1b7d9cea9afb0f84c11d36a110f20ab2 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 19 Jul 2016 14:42:24 -0700 Subject: [PATCH 1/7] fix for pushing avatar into floor when exiting away mode. * Removed MyAvatar.reset() access from JavaScript * Added HMD.centerUI() to JavaScript, which can be used to reset the 3D UI sphere around the current HMD orientation. * Added MyAvatar.clearIKJOintLimitHistory() which can be used to reset any remembered IK joint limit history. * Added MyAvatar.centerBody() which can be used to instantly re-orient the avatar's so that the hips and toes are facing the same direction as the current HMD orientation. away.js now uses the above new API's instead of MyAvatar.reset() --- interface/src/Application.cpp | 4 ++ interface/src/Application.h | 1 + interface/src/avatar/MyAvatar.cpp | 39 +++++++++++++++++-- interface/src/avatar/MyAvatar.h | 6 ++- .../src/scripting/HMDScriptingInterface.cpp | 4 ++ .../src/scripting/HMDScriptingInterface.h | 3 ++ .../animation/src/AnimInverseKinematics.cpp | 6 +++ .../animation/src/AnimInverseKinematics.h | 2 + libraries/animation/src/Rig.cpp | 13 +++++++ libraries/animation/src/Rig.h | 2 + libraries/animation/src/RotationConstraint.h | 3 ++ .../animation/src/SwingTwistConstraint.h | 3 +- scripts/system/away.js | 11 +++++- 13 files changed, 90 insertions(+), 7 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index fb48472b14..48e82081d4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3404,6 +3404,10 @@ void Application::setOverlaysVisible(bool visible) { menu->setIsOptionChecked(MenuOption::Overlays, true); } +void Application::centerUI() { + _overlayConductor.centerUI(); +} + void Application::cycleCamera() { auto menu = Menu::getInstance(); if (menu->isOptionChecked(MenuOption::FullscreenMirror)) { diff --git a/interface/src/Application.h b/interface/src/Application.h index a1e3e2f930..a254072561 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -298,6 +298,7 @@ public slots: void cameraMenuChanged(); void toggleOverlays(); void setOverlaysVisible(bool visible); + Q_INVOKABLE void centerUI(); void resetPhysicsReadyInformation(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6a69ee9a9a..50294d9f53 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -231,13 +231,46 @@ QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll) { return AvatarData::toByteArray(cullSmallChanges, sendAll); } -void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) { - +void MyAvatar::centerBody() { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "reset", Q_ARG(bool, andRecenter), Q_ARG(bool, andReload), Q_ARG(bool, andHead)); + QMetaObject::invokeMethod(this, "centerBody"); return; } + // derive the desired body orientation from the current hmd orientation, before the sensor reset. + auto newBodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation.. + + // transform this body into world space + auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix; + auto worldBodyPos = extractTranslation(worldBodyMatrix); + auto worldBodyRot = glm::normalize(glm::quat_cast(worldBodyMatrix)); + + // this will become our new position. + setPosition(worldBodyPos); + setOrientation(worldBodyRot); + + // reset the body in sensor space + _bodySensorMatrix = newBodySensorMatrix; + + // rebuild the sensor to world matrix + updateSensorToWorldMatrix(); +} + +void MyAvatar::clearIKJointLimitHistory() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "clearIKJointLimitHistory"); + return; + } + + if (_rig) { + _rig->clearIKJointLimitHistory(); + } +} + +void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) { + + assert(QThread::currentThread() == thread()); + // Reset dynamic state. _wasPushing = _isPushing = _isBraking = false; _follow.deactivate(); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index a21922f1b1..965418aee9 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -96,7 +96,11 @@ public: AudioListenerMode getAudioListenerModeCamera() const { return FROM_CAMERA; } AudioListenerMode getAudioListenerModeCustom() const { return CUSTOM; } - Q_INVOKABLE void reset(bool andRecenter = false, bool andReload = true, bool andHead = true); + void reset(bool andRecenter = false, bool andReload = true, bool andHead = true); + + Q_INVOKABLE void centerBody(); // thread-safe + Q_INVOKABLE void clearIKJointLimitHistory(); // thread-safe + void update(float deltaTime); virtual void postUpdate(float deltaTime) override; void preDisplaySide(RenderArgs* renderArgs); diff --git a/interface/src/scripting/HMDScriptingInterface.cpp b/interface/src/scripting/HMDScriptingInterface.cpp index 36cde378f8..b031e05789 100644 --- a/interface/src/scripting/HMDScriptingInterface.cpp +++ b/interface/src/scripting/HMDScriptingInterface.cpp @@ -135,3 +135,7 @@ void HMDScriptingInterface::unsuppressKeyboard() { bool HMDScriptingInterface::isKeyboardVisible() { return qApp->getActiveDisplayPlugin()->isKeyboardVisible(); } + +void HMDScriptingInterface::centerUI() { + QMetaObject::invokeMethod(qApp, "centerUI", Qt::QueuedConnection); +} diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h index 2739522adf..2fbdb76198 100644 --- a/interface/src/scripting/HMDScriptingInterface.h +++ b/interface/src/scripting/HMDScriptingInterface.h @@ -55,6 +55,9 @@ public: /// Query the display plugin to determine the current VR keyboard visibility Q_INVOKABLE bool isKeyboardVisible(); + // rotate the overlay UI sphere so that it is centered about the the current HMD position and orientation + Q_INVOKABLE void centerUI(); + public: HMDScriptingInterface(); static QScriptValue getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine); diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 9ae992b86e..971694bfa8 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -488,6 +488,12 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars return _relativePoses; } +void AnimInverseKinematics::clearIKJointLimitHistory() { + for (auto& pair : _constraints) { + pair.second->clearHistory(); + } +} + RotationConstraint* AnimInverseKinematics::getConstraint(int index) { RotationConstraint* constraint = nullptr; std::map::iterator constraintItr = _constraints.find(index); diff --git a/libraries/animation/src/AnimInverseKinematics.h b/libraries/animation/src/AnimInverseKinematics.h index 182e7b3492..c9560c7383 100644 --- a/libraries/animation/src/AnimInverseKinematics.h +++ b/libraries/animation/src/AnimInverseKinematics.h @@ -37,6 +37,8 @@ public: virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, AnimNode::Triggers& triggersOut) override; virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override; + void clearIKJointLimitHistory(); + protected: void computeTargets(const AnimVariantMap& animVars, std::vector& targets, const AnimPoseVec& underPoses); void solveWithCyclicCoordinateDescent(const std::vector& targets); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 8aee2245c0..d6ec0487f0 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -295,6 +295,19 @@ void Rig::clearJointAnimationPriority(int index) { } } +void Rig::clearIKJointLimitHistory() { + if (_animNode) { + _animNode->traverse([&](AnimNode::Pointer node) { + // only report clip nodes as valid roles. + auto ikNode = std::dynamic_pointer_cast(node); + if (ikNode) { + ikNode->clearIKJointLimitHistory(); + } + return true; + }); + } +} + void Rig::setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority) { if (isIndexValid(index)) { if (valid) { diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index e2193e8479..86b8dadd85 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -103,6 +103,8 @@ public: void clearJointStates(); void clearJointAnimationPriority(int index); + void clearIKJointLimitHistory(); + // geometry space void setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority); diff --git a/libraries/animation/src/RotationConstraint.h b/libraries/animation/src/RotationConstraint.h index 9e34537cab..277e5293c6 100644 --- a/libraries/animation/src/RotationConstraint.h +++ b/libraries/animation/src/RotationConstraint.h @@ -35,6 +35,9 @@ public: /// \brief clear previous adjustment and adjust constraint limits to allow rotation virtual void dynamicallyAdjustLimits(const glm::quat& rotation) {} + /// \brief reset any remembered joint limit history + virtual void clearHistory() {}; + protected: glm::quat _referenceRotation = glm::quat(); }; diff --git a/libraries/animation/src/SwingTwistConstraint.h b/libraries/animation/src/SwingTwistConstraint.h index 93d7449d8f..06afd64232 100644 --- a/libraries/animation/src/SwingTwistConstraint.h +++ b/libraries/animation/src/SwingTwistConstraint.h @@ -97,8 +97,7 @@ public: /// \return reference to SwingLimitFunction instance for unit-testing const SwingLimitFunction& getSwingLimitFunction() const { return _swingLimitFunction; } - /// \brief exposed for unit testing - void clearHistory(); + void clearHistory() override; private: float handleTwistBoundaryConditions(float twistAngle) const; diff --git a/scripts/system/away.js b/scripts/system/away.js index 25c330738f..290cda0d64 100644 --- a/scripts/system/away.js +++ b/scripts/system/away.js @@ -204,7 +204,16 @@ function goActive() { } MyAvatar.setEnableMeshVisible(true); // IWBNI we respected Developer->Avatar->Draw Mesh setting. stopAwayAnimation(); - MyAvatar.reset(true); + + // update the UI sphere to be centered about the current HMD orientation. + HMD.centerUI(); + + // forget about any IK joint limits + MyAvatar.clearIKJointLimitHistory(); + + // update the avatar hips to point in the same direction as the HMD orientation. + MyAvatar.centerBody(); + hideOverlay(); // restore overlays state to what it was when we went "away" From 90beb30e8a0d0bcb1953c8d88d5b4a5cf998713b Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 19 Jul 2016 18:12:20 -0700 Subject: [PATCH 2/7] Filter out bad openvr HMD poses that are below the floor When interface is launched and steam vr is NOT running, openvr can return bad HMD poses for a few frames. To workaround this, filter out any hmd poses that are obviously bad, i.e. beneath the floor. --- plugins/openvr/src/OpenVrDisplayPlugin.cpp | 29 ++++++++++++++++++++++ plugins/openvr/src/OpenVrDisplayPlugin.h | 2 ++ 2 files changed, 31 insertions(+) diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index cfb374e3bd..4e84c6d0fa 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -49,6 +49,19 @@ bool OpenVrDisplayPlugin::isSupported() const { void OpenVrDisplayPlugin::init() { Plugin::init(); + _lastGoodHMDPose.m[0][0] = 1.0f; + _lastGoodHMDPose.m[0][1] = 0.0f; + _lastGoodHMDPose.m[0][2] = 0.0f; + _lastGoodHMDPose.m[0][3] = 0.0f; + _lastGoodHMDPose.m[1][0] = 0.0f; + _lastGoodHMDPose.m[1][1] = 1.0f; + _lastGoodHMDPose.m[1][2] = 0.0f; + _lastGoodHMDPose.m[1][3] = 1.8f; + _lastGoodHMDPose.m[2][0] = 0.0f; + _lastGoodHMDPose.m[2][1] = 0.0f; + _lastGoodHMDPose.m[2][2] = 1.0f; + _lastGoodHMDPose.m[2][3] = 0.0f; + emit deviceConnected(getName()); } @@ -139,6 +152,12 @@ void OpenVrDisplayPlugin::resetSensors() { }); } +static bool isBadPose(vr::HmdMatrix34_t* mat) { + if (mat->m[1][3] < -0.2f) { + return true; + } + return false; +} bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) { handleOpenVrEvents(); @@ -158,6 +177,16 @@ bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) { _system->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseStanding, _currentRenderFrameInfo.predictedDisplayTime, _trackedDevicePose, vr::k_unMaxTrackedDeviceCount); + // HACK: when interface is launched and steam vr is NOT running, openvr will return bad HMD poses for a few frames + // To workaround this, filter out any hmd poses that are obviously bad, i.e. beneath the floor. + if (isBadPose(&_trackedDevicePose[vr::k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking)) { + qDebug() << "WARNING: ignoring bad hmd pose from openvr"; + + // use the last known good HMD pose + _trackedDevicePose[vr::k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking = _lastGoodHMDPose; + } else { + _lastGoodHMDPose = _trackedDevicePose[vr::k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking; + } vr::TrackedDeviceIndex_t handIndices[2] { vr::k_unTrackedDeviceIndexInvalid, vr::k_unTrackedDeviceIndexInvalid }; { diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.h b/plugins/openvr/src/OpenVrDisplayPlugin.h index fca4dab9e9..4c0c0aebbd 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.h +++ b/plugins/openvr/src/OpenVrDisplayPlugin.h @@ -51,4 +51,6 @@ private: std::atomic _hmdActivityLevel { vr::k_EDeviceActivityLevel_Unknown }; std::atomic _keyboardSupressionCount{ 0 }; static const QString NAME; + + vr::HmdMatrix34_t _lastGoodHMDPose; }; From 3fdfc0db141b6d465f6693088be841e632055a72 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 20 Jul 2016 12:55:13 -0700 Subject: [PATCH 3/7] fix bug breaking simple-compound shape type --- .../src/RenderableModelEntityItem.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index bef790299c..4e8ecf3054 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -347,7 +347,6 @@ void RenderableModelEntityItem::updateModelBounds() { return; } - bool movingOrAnimating = isMovingRelativeToParent() || isAnimatingSomething(); glm::vec3 dimensions = getDimensions(); bool success; @@ -701,11 +700,12 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { } info.setParams(type, dimensions, _compoundShapeURL); } else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) { - updateModelBounds(); - // should never fall in here when model not fully loaded assert(_model && _model->isLoaded()); + updateModelBounds(); + _model->updateGeometry(); + // compute meshPart local transforms QVector localTransforms; const FBXGeometry& fbxGeometry = _model->getFBXGeometry(); @@ -741,6 +741,9 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { pointCollection.resize(1); } + ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices(); + triangleIndices.clear(); + Extents extents; int32_t meshCount = 0; int32_t pointListIndex = 0; @@ -775,8 +778,6 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { if (type == SHAPE_TYPE_STATIC_MESH) { // copy into triangleIndices - ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices(); - triangleIndices.clear(); triangleIndices.reserve((int32_t)((gpu::Size)(triangleIndices.size()) + indices.getNumElements())); gpu::BufferView::Iterator partItr = parts.cbegin(); while (partItr != parts.cend()) { @@ -827,8 +828,6 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { } } else if (type == SHAPE_TYPE_SIMPLE_COMPOUND) { // for each mesh copy unique part indices, separated by special bogus (flag) index values - ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices(); - triangleIndices.clear(); gpu::BufferView::Iterator partItr = parts.cbegin(); while (partItr != parts.cend()) { // collect unique list of indices for this part From 20eebb90f88847718693c1bca9c242563893b4d5 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 20 Jul 2016 14:08:25 -0700 Subject: [PATCH 4/7] fix build -- remove some accidental changes --- .../entities-renderer/src/RenderableModelEntityItem.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 4e8ecf3054..5b1b5b01d4 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -700,12 +700,11 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { } info.setParams(type, dimensions, _compoundShapeURL); } else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) { + updateModelBounds(); + // should never fall in here when model not fully loaded assert(_model && _model->isLoaded()); - updateModelBounds(); - _model->updateGeometry(); - // compute meshPart local transforms QVector localTransforms; const FBXGeometry& fbxGeometry = _model->getFBXGeometry(); From 51bc3c51ca0b19a553d9346c5d574faad45caac6 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 19 Jul 2016 08:32:22 -0700 Subject: [PATCH 5/7] less logging, change constant name. --- domain-server/src/DomainServer.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 88f4b94883..664db98acb 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1086,8 +1086,8 @@ void DomainServer::sendHeartbeatToMetaverse(const QString& networkAddress) { // add the versions static const QString VERSION_KEY = "version"; domainObject[VERSION_KEY] = BuildInfo::VERSION; - static const QString PROTOCOL_KEY = "protocol"; - domainObject[PROTOCOL_KEY] = protocolVersionsSignatureBase64(); + static const QString PROTOCOL_VERSION_KEY = "protocol"; + domainObject[PROTOCOL_VERSION_KEY] = protocolVersionsSignatureBase64(); // add networking if (!networkAddress.isEmpty()) { @@ -1121,12 +1121,7 @@ void DomainServer::sendHeartbeatToMetaverse(const QString& networkAddress) { QString domainUpdateJSON = QString("{\"domain\":%1}").arg(QString(QJsonDocument(domainObject).toJson(QJsonDocument::Compact))); static const QString DOMAIN_UPDATE = "/api/v1/domains/%1"; - QString path = DOMAIN_UPDATE.arg(uuidStringWithoutCurlyBraces(getID())); -#if DEV_BUILD || PR_BUILD - qDebug() << "Domain metadata sent to" << path; - qDebug() << "Domain metadata update:" << domainUpdateJSON; -#endif - DependencyManager::get()->sendRequest(path, + DependencyManager::get()->sendRequest(DOMAIN_UPDATE.arg(uuidStringWithoutCurlyBraces(getID())), AccountManagerAuth::Optional, QNetworkAccessManager::PutOperation, JSONCallbackParameters(nullptr, QString(), this, "handleMetaverseHeartbeatError"), From bb8224be8cbdbb03a27a0813b1a2a672755f2eca Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 21 Jul 2016 13:57:11 -0700 Subject: [PATCH 6/7] fix silence on no codec after previous codec has been set --- libraries/audio/src/InboundAudioStream.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/audio/src/InboundAudioStream.cpp b/libraries/audio/src/InboundAudioStream.cpp index a1a5915a8c..de05abaa88 100644 --- a/libraries/audio/src/InboundAudioStream.cpp +++ b/libraries/audio/src/InboundAudioStream.cpp @@ -542,4 +542,5 @@ void InboundAudioStream::cleanupCodec() { _decoder = nullptr; } } + _selectedCodecName = ""; } From b303c6df8e21942a4af4f4749177aada47a14c80 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 21 Jul 2016 16:04:12 -0700 Subject: [PATCH 7/7] strong type checking my arse --- libraries/networking/src/UserActivityLogger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index 75e15db2a4..4b71423883 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -179,7 +179,7 @@ void UserActivityLogger::wentTo(AddressManager::LookupTrigger lookupTrigger, QSt trigger = "StartupFromSettings"; break; case AddressManager::Suggestions: - trigger = "Suggesions"; + trigger = "Suggestions"; break; default: return;