diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index acb199bb97..7bb34253c1 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -125,6 +125,18 @@ QString userRecenterModelToString(MyAvatar::SitStandModelType model) { } } +static const QStringList REACTION_NAMES = { + QString("positive"), + QString("negative"), + QString("raiseHand"), + QString("applaud"), + QString("point") +}; + +static int reactionNameToIndex(const QString& reactionName) { + return REACTION_NAMES.indexOf(reactionName); +} + MyAvatar::MyAvatar(QThread* thread) : Avatar(thread), _yawSpeed(YAW_SPEED_DEFAULT), @@ -5808,6 +5820,53 @@ void MyAvatar::setModelScale(float scale) { } } +QStringList MyAvatar::getReactions() const { + return REACTION_NAMES; +} + +bool MyAvatar::triggerReaction(QString reactionName) { + int reactionIndex = reactionNameToIndex(reactionName); + if (reactionIndex >= 0 && reactionIndex < (int)NUM_AVATAR_REACTIONS) { + std::lock_guard guard(_reactionLock); + _reactionTriggers[reactionIndex] = true; + return true; + } + return false; +} + +bool MyAvatar::beginReaction(QString reactionName) { + int reactionIndex = reactionNameToIndex(reactionName); + if (reactionIndex >= 0 && reactionIndex < (int)NUM_AVATAR_REACTIONS) { + std::lock_guard guard(_reactionLock); + _reactionEnabledRefCounts[reactionIndex]++; + return true; + } + return false; +} + +bool MyAvatar::endReaction(QString reactionName) { + int reactionIndex = reactionNameToIndex(reactionName); + if (reactionIndex >= 0 && reactionIndex < (int)NUM_AVATAR_REACTIONS) { + std::lock_guard guard(_reactionLock); + _reactionEnabledRefCounts[reactionIndex]--; + return true; + } + return false; +} + +void MyAvatar::updateRigControllerParameters(Rig::ControllerParameters& params) { + std::lock_guard guard(_reactionLock); + for (int i = 0; i < NUM_AVATAR_REACTIONS; i++) { + + // copy current state into params. + params.reactionEnabledFlags[i] = _reactionEnabledRefCounts[i] > 0; + params.reactionTriggers[i] = _reactionTriggers[i]; + + // clear reaction triggers here as well + _reactionTriggers[i] = false; + } +} + SpatialParentTree* MyAvatar::getParentTree() const { auto entityTreeRenderer = qApp->getEntities(); EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index d092122863..45687bd82d 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1839,6 +1839,10 @@ public: bool getFlowActive() const; bool getNetworkGraphActive() const; + // sets the reaction enabled and triggered parameters of the passed in params + // also clears internal reaction triggers + void updateRigControllerParameters(Rig::ControllerParameters& params); + public slots: /**jsdoc @@ -2192,6 +2196,33 @@ public slots: */ virtual void setModelScale(float scale) override; + /**jsdoc + * MyAvatar.getReactions + * @returns {string[]} Array of reaction names. + */ + QStringList getReactions() const; + + /**jsdoc + * MyAvatar.triggerReaction + * @param {string} reactionName - reaction name + * @returns {bool} false if the given reaction is not supported. + */ + bool triggerReaction(QString reactionName); + + /**jsdoc + * MyAvatar.beginReaction + * @param {string} reactionName - reaction name + * @returns {bool} false if the given reaction is not supported. + */ + bool beginReaction(QString reactionName); + + /**jsdoc + * MyAvatar.endReaction + * @param {string} reactionName - reaction name + * @returns {bool} false if the given reaction is not supported. + */ + bool endReaction(QString reactionName); + signals: /**jsdoc @@ -2823,6 +2854,10 @@ private: mutable std::mutex _scriptEngineLock; 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 }; + mutable std::mutex _reactionLock; }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index df46b428e7..9c555aa35b 100755 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -294,8 +294,6 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _prevIsEstimatingHips = false; } - params.isTalking = head->getTimeWithoutTalking() <= 1.5f; - // pass detailed torso k-dops to rig. int hipsJoint = _rig.indexOfJoint("Hips"); if (hipsJoint >= 0) { @@ -314,6 +312,10 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.spine2ShapeInfo = hfmModel.joints[spine2Joint].shapeInfo; } + params.isTalking = head->getTimeWithoutTalking() <= 1.5f; + + myAvatar->updateRigControllerParameters(params); + _rig.updateFromControllerParameters(params, deltaTime); Rig::CharacterControllerState ccState = convertCharacterControllerState(myAvatar->getCharacterController()->getState()); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 0f0c67b846..0f637bf082 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1873,6 +1873,61 @@ 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 + if (params.reactionTriggers[AVATAR_REACTION_POSITIVE]) { + _animVars.set("reactionPositiveTrigger", true); + } else { + _animVars.set("reactionPositiveTrigger", false); + } + + if (params.reactionTriggers[AVATAR_REACTION_NEGATIVE]) { + _animVars.set("reactionNegativeTrigger", true); + } else { + _animVars.set("reactionNegativeTrigger", false); + } + + if (params.reactionTriggers[AVATAR_REACTION_RAISE_HAND]) { + _animVars.set("reactionRaiseHandTrigger", true); + } else { + _animVars.set("reactionRaiseHandTrigger", false); + } + + if (params.reactionTriggers[AVATAR_REACTION_APPLAUD]) { + _animVars.set("reactionApplaudTrigger", true); + } else { + _animVars.set("reactionApplaudTrigger", false); + } + + if (params.reactionTriggers[AVATAR_REACTION_POINT]) { + _animVars.set("reactionPointTrigger", true); + } else { + _animVars.set("reactionPointTrigger", false); + } +} + void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::vec3& lookAtSpot, const glm::vec3& saccade) { // TODO: does not properly handle avatar scale. @@ -2152,6 +2207,8 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo } } + updateReactions(params); + _previousControllerParameters = params; } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 9baf4644f2..d3fa61e5e7 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -88,6 +88,8 @@ public: AnimPose secondaryControllerPoses[NumSecondaryControllerTypes]; // rig space uint8_t secondaryControllerFlags[NumSecondaryControllerTypes]; bool isTalking; + bool reactionEnabledFlags[NUM_AVATAR_REACTIONS]; + bool reactionTriggers[NUM_AVATAR_REACTIONS]; HFMJointShapeInfo hipsShapeInfo; HFMJointShapeInfo spineShapeInfo; HFMJointShapeInfo spine1ShapeInfo; @@ -268,6 +270,7 @@ protected: void updateFeet(bool leftFootEnabled, bool rightFootEnabled, bool headEnabled, const AnimPose& leftFootPose, const AnimPose& rightFootPose, const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix); + void updateReactions(const ControllerParameters& params); void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::vec3& lookAt, const glm::vec3& saccade); void calcAnimAlpha(float speed, const std::vector& referenceSpeeds, float* alphaOut) const; diff --git a/libraries/shared/src/AvatarConstants.h b/libraries/shared/src/AvatarConstants.h index fcf84a49cb..c4a8b91ed5 100644 --- a/libraries/shared/src/AvatarConstants.h +++ b/libraries/shared/src/AvatarConstants.h @@ -106,4 +106,13 @@ 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 { + AVATAR_REACTION_POSITIVE = 0, + AVATAR_REACTION_NEGATIVE, + AVATAR_REACTION_RAISE_HAND, + AVATAR_REACTION_APPLAUD, + AVATAR_REACTION_POINT, + NUM_AVATAR_REACTIONS +}; + #endif // hifi_AvatarConstants_h