From 837b19ed1b7d9cea9afb0f84c11d36a110f20ab2 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 19 Jul 2016 14:42:24 -0700 Subject: [PATCH] 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"