From 2f949a4d4d9e7e1722dcdfcd1082bec620e00a9e Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 26 Jul 2019 08:45:52 -0700 Subject: [PATCH] Disable head ik while reacting Also, split getReactions() API into two calls * getTriggerReactions() - lists all reactions that can be triggered with MyAvatar.triggerReaction(). * getBeginReactions() - lists all reactions that can be used with MyAvatar.beginReaction() and MyAvatar.endReaction(). --- interface/src/avatar/MyAvatar.cpp | 46 +++++++++----- interface/src/avatar/MyAvatar.h | 23 +++++-- libraries/animation/src/AnimStateMachine.cpp | 9 +++ libraries/animation/src/AnimStateMachine.h | 1 + libraries/animation/src/Rig.cpp | 63 +++++++++----------- libraries/animation/src/Rig.h | 4 +- libraries/shared/src/AvatarConstants.h | 8 ++- 7 files changed, 96 insertions(+), 58 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index d495077a87..28f6644d7f 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -125,16 +125,25 @@ QString userRecenterModelToString(MyAvatar::SitStandModelType model) { } } -static const QStringList REACTION_NAMES = { +static const QStringList TRIGGER_REACTION_NAMES = { QString("positive"), - QString("negative"), + QString("negative") +}; + +static const QStringList BEGIN_END_REACTION_NAMES = { QString("raiseHand"), QString("applaud"), QString("point") }; -static int reactionNameToIndex(const QString& reactionName) { - return REACTION_NAMES.indexOf(reactionName); +static int triggerReactionNameToIndex(const QString& reactionName) { + assert(NUM_AVATAR_TRIGGER_REACTIONS == TRIGGER_REACTION_NAMES.size()); + return TRIGGER_REACTION_NAMES.indexOf(reactionName); +} + +static int beginEndReactionNameToIndex(const QString& reactionName) { + assert(NUM_AVATAR_BEGIN_END_REACTIONS == TRIGGER_REACTION_NAMES.size()); + return BEGIN_END_REACTION_NAMES.indexOf(reactionName); } MyAvatar::MyAvatar(QThread* thread) : @@ -5824,13 +5833,17 @@ void MyAvatar::setModelScale(float scale) { } } -QStringList MyAvatar::getReactions() const { - return REACTION_NAMES; +QStringList MyAvatar::getBeginEndReactions() const { + return BEGIN_END_REACTION_NAMES; +} + +QStringList MyAvatar::getTriggerReactions() const { + return TRIGGER_REACTION_NAMES; } bool MyAvatar::triggerReaction(QString reactionName) { - int reactionIndex = reactionNameToIndex(reactionName); - if (reactionIndex >= 0 && reactionIndex < (int)NUM_AVATAR_REACTIONS) { + int reactionIndex = triggerReactionNameToIndex(reactionName); + if (reactionIndex >= 0 && reactionIndex < (int)NUM_AVATAR_TRIGGER_REACTIONS) { std::lock_guard guard(_reactionLock); _reactionTriggers[reactionIndex] = true; return true; @@ -5839,8 +5852,8 @@ bool MyAvatar::triggerReaction(QString reactionName) { } bool MyAvatar::beginReaction(QString reactionName) { - int reactionIndex = reactionNameToIndex(reactionName); - if (reactionIndex >= 0 && reactionIndex < (int)NUM_AVATAR_REACTIONS) { + int reactionIndex = beginEndReactionNameToIndex(reactionName); + if (reactionIndex >= 0 && reactionIndex < (int)NUM_AVATAR_BEGIN_END_REACTIONS) { std::lock_guard guard(_reactionLock); _reactionEnabledRefCounts[reactionIndex]++; return true; @@ -5849,8 +5862,8 @@ bool MyAvatar::beginReaction(QString reactionName) { } bool MyAvatar::endReaction(QString reactionName) { - int reactionIndex = reactionNameToIndex(reactionName); - if (reactionIndex >= 0 && reactionIndex < (int)NUM_AVATAR_REACTIONS) { + int reactionIndex = beginEndReactionNameToIndex(reactionName); + if (reactionIndex >= 0 && reactionIndex < (int)NUM_AVATAR_BEGIN_END_REACTIONS) { std::lock_guard guard(_reactionLock); _reactionEnabledRefCounts[reactionIndex]--; return true; @@ -5860,12 +5873,17 @@ bool MyAvatar::endReaction(QString reactionName) { void MyAvatar::updateRigControllerParameters(Rig::ControllerParameters& params) { std::lock_guard guard(_reactionLock); - for (int i = 0; i < NUM_AVATAR_REACTIONS; i++) { + for (int i = 0; i < TRIGGER_REACTION_NAMES.size(); i++) { + params.reactionTriggers[i] = _reactionTriggers[i]; + } + + for (int i = 0; i < BEGIN_END_REACTION_NAMES.size(); i++) { // copy current state into params. params.reactionEnabledFlags[i] = _reactionEnabledRefCounts[i] > 0; - params.reactionTriggers[i] = _reactionTriggers[i]; + } + for (int i = 0; i < TRIGGER_REACTION_NAMES.size(); i++) { // clear reaction triggers here as well _reactionTriggers[i] = false; } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index db373c6402..73b6ad08ea 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -2213,13 +2213,23 @@ public slots: virtual void setModelScale(float scale) override; /**jsdoc - * MyAvatar.getReactions + * MyAvatar.getTriggerReactions + * Returns a list of reactions names that can be triggered using MyAvatar.triggerReaction(). * @returns {string[]} Array of reaction names. */ - QStringList getReactions() const; + QStringList getTriggerReactions() const; + + + /**jsdoc + * MyAvatar.getBeginReactions + * Returns a list of reactions names that can be enabled using MyAvatar.beginReaction() and MyAvatar.endReaction(). + * @returns {string[]} Array of reaction names. + */ + QStringList getBeginEndReactions() const; /**jsdoc * MyAvatar.triggerReaction + * Plays the given reaction on the avatar, once the reaction is complete it will automatically complete. Only reaction names returned from MyAvatar.getTriggerReactions() are available. * @param {string} reactionName - reaction name * @returns {bool} false if the given reaction is not supported. */ @@ -2227,6 +2237,9 @@ public slots: /**jsdoc * MyAvatar.beginReaction + * Plays the given reaction on the avatar. The avatar will continue to play the reaction until stopped via the MyAvatar.endReaction() call or superseeded by another reaction. + * Only reaction names returned from MyAvatar.getBeginEndReactions() are available. + * NOTE: the caller is responsible for calling the corresponding MyAvatar.endReaction(), otherwise the avatar might become stuck in the reaction forever. * @param {string} reactionName - reaction name * @returns {bool} false if the given reaction is not supported. */ @@ -2234,6 +2247,7 @@ public slots: /**jsdoc * MyAvatar.endReaction + * Used to stop a given reaction that was started via MyAvatar.beginReaction(). * @param {string} reactionName - reaction name * @returns {bool} false if the given reaction is not supported. */ @@ -2872,8 +2886,9 @@ private: QScriptEngine* _scriptEngine { nullptr }; bool _needToSaveAvatarEntitySettings { false }; - int _reactionEnabledRefCounts[NUM_AVATAR_REACTIONS] { 0, 0, 0, 0, 0 }; - bool _reactionTriggers[NUM_AVATAR_REACTIONS] { false, false, false, false, false }; + bool _reactionTriggers[NUM_AVATAR_TRIGGER_REACTIONS] { false, false }; + int _reactionEnabledRefCounts[NUM_AVATAR_BEGIN_END_REACTIONS] { 0, 0, 0 }; + mutable std::mutex _reactionLock; }; diff --git a/libraries/animation/src/AnimStateMachine.cpp b/libraries/animation/src/AnimStateMachine.cpp index 2c5d4ad0f3..22a362ee1c 100644 --- a/libraries/animation/src/AnimStateMachine.cpp +++ b/libraries/animation/src/AnimStateMachine.cpp @@ -95,6 +95,15 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co return _poses; } +const QString& AnimStateMachine::getCurrentStateID() const { + if (_currentState) { + return _currentState->getID(); + } else { + static QString emptyString; + return emptyString; + } +} + void AnimStateMachine::setCurrentState(State::Pointer state) { _previousState = _currentState ? _currentState : state; _currentState = state; diff --git a/libraries/animation/src/AnimStateMachine.h b/libraries/animation/src/AnimStateMachine.h index 713c659a1d..aee7ba65e1 100644 --- a/libraries/animation/src/AnimStateMachine.h +++ b/libraries/animation/src/AnimStateMachine.h @@ -116,6 +116,7 @@ public: virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override; void setCurrentStateVar(QString& currentStateVar) { _currentStateVar = currentStateVar; } + const QString& getCurrentStateID() const; protected: diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 5f4c49364e..03014f576f 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -29,6 +29,7 @@ #include "AnimInverseKinematics.h" #include "AnimOverlay.h" #include "AnimSkeleton.h" +#include "AnimStateMachine.h" #include "AnimUtil.h" #include "AvatarConstants.h" #include "IKTarget.h" @@ -1906,28 +1907,7 @@ void Rig::updateFeet(bool leftFootEnabled, bool rightFootEnabled, bool headEnabl void Rig::updateReactions(const ControllerParameters& params) { - // enable/disable animVars - bool enabled = params.reactionEnabledFlags[AVATAR_REACTION_POSITIVE]; - _animVars.set("reactionPositiveEnabled", enabled); - _animVars.set("reactionPositiveDisabled", !enabled); - - enabled = params.reactionEnabledFlags[AVATAR_REACTION_NEGATIVE]; - _animVars.set("reactionNegativeEnabled", enabled); - _animVars.set("reactionNegativeDisabled", !enabled); - - enabled = params.reactionEnabledFlags[AVATAR_REACTION_RAISE_HAND]; - _animVars.set("reactionRaiseHandEnabled", enabled); - _animVars.set("reactionRaiseHandDisabled", !enabled); - - enabled = params.reactionEnabledFlags[AVATAR_REACTION_APPLAUD]; - _animVars.set("reactionApplaudEnabled", enabled); - _animVars.set("reactionApplaudDisabled", !enabled); - - enabled = params.reactionEnabledFlags[AVATAR_REACTION_POINT]; - _animVars.set("reactionPointEnabled", enabled); - _animVars.set("reactionPointDisabled", !enabled); - - // trigger animVars + // trigger reactions if (params.reactionTriggers[AVATAR_REACTION_POSITIVE]) { _animVars.set("reactionPositiveTrigger", true); } else { @@ -1940,22 +1920,33 @@ void Rig::updateReactions(const ControllerParameters& params) { _animVars.set("reactionNegativeTrigger", false); } - if (params.reactionTriggers[AVATAR_REACTION_RAISE_HAND]) { - _animVars.set("reactionRaiseHandTrigger", true); - } else { - _animVars.set("reactionRaiseHandTrigger", false); - } + // begin end reactions + bool enabled = params.reactionEnabledFlags[AVATAR_REACTION_RAISE_HAND]; + _animVars.set("reactionRaiseHandEnabled", enabled); + _animVars.set("reactionRaiseHandDisabled", !enabled); - if (params.reactionTriggers[AVATAR_REACTION_APPLAUD]) { - _animVars.set("reactionApplaudTrigger", true); - } else { - _animVars.set("reactionApplaudTrigger", false); - } + enabled = params.reactionEnabledFlags[AVATAR_REACTION_APPLAUD]; + _animVars.set("reactionApplaudEnabled", enabled); + _animVars.set("reactionApplaudDisabled", !enabled); - if (params.reactionTriggers[AVATAR_REACTION_POINT]) { - _animVars.set("reactionPointTrigger", true); - } else { - _animVars.set("reactionPointTrigger", false); + enabled = params.reactionEnabledFlags[AVATAR_REACTION_POINT]; + _animVars.set("reactionPointEnabled", enabled); + _animVars.set("reactionPointDisabled", !enabled); + + // determine if we should ramp off IK + if (_enableInverseKinematics) { + bool reactionPlaying = false; + std::shared_ptr mainStateMachine = std::dynamic_pointer_cast(_animNode->findByName("mainStateMachine")); + std::shared_ptr idleStateMachine = std::dynamic_pointer_cast(_animNode->findByName("idle")); + if (mainStateMachine && mainStateMachine->getCurrentStateID() == "idle" && idleStateMachine) { + reactionPlaying = idleStateMachine->getCurrentStateID().startsWith("reaction"); + } + + bool hipsEnabled = params.primaryControllerFlags[PrimaryControllerType_Hips] & (uint8_t)ControllerFlags::Enabled; + if (reactionPlaying && !hipsEnabled) { + // disable head IK while reaction is playing, but only in "desktop" mode. + _animVars.set("headType", (int)IKTarget::Type::Unknown); + } } } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 7cf17ca391..99794fd0a7 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -88,8 +88,8 @@ public: AnimPose secondaryControllerPoses[NumSecondaryControllerTypes]; // rig space uint8_t secondaryControllerFlags[NumSecondaryControllerTypes]; bool isTalking; - bool reactionEnabledFlags[NUM_AVATAR_REACTIONS]; - bool reactionTriggers[NUM_AVATAR_REACTIONS]; + bool reactionEnabledFlags[NUM_AVATAR_BEGIN_END_REACTIONS]; + bool reactionTriggers[NUM_AVATAR_TRIGGER_REACTIONS]; HFMJointShapeInfo hipsShapeInfo; HFMJointShapeInfo spineShapeInfo; HFMJointShapeInfo spine1ShapeInfo; diff --git a/libraries/shared/src/AvatarConstants.h b/libraries/shared/src/AvatarConstants.h index c4a8b91ed5..a955e0f0c3 100644 --- a/libraries/shared/src/AvatarConstants.h +++ b/libraries/shared/src/AvatarConstants.h @@ -106,13 +106,17 @@ static const float AVATAR_WALK_SPEED_SCALAR = 1.0f; static const float AVATAR_DESKTOP_SPRINT_SPEED_SCALAR = 3.0f; static const float AVATAR_HMD_SPRINT_SPEED_SCALAR = 2.0f; -enum AvatarReaction { +enum AvatarTriggerReaction { AVATAR_REACTION_POSITIVE = 0, AVATAR_REACTION_NEGATIVE, + NUM_AVATAR_TRIGGER_REACTIONS +}; + +enum AvatarBeginEndReaction { AVATAR_REACTION_RAISE_HAND, AVATAR_REACTION_APPLAUD, AVATAR_REACTION_POINT, - NUM_AVATAR_REACTIONS + NUM_AVATAR_BEGIN_END_REACTIONS }; #endif // hifi_AvatarConstants_h