mirror of
https://github.com/overte-org/overte.git
synced 2025-08-10 02:31:13 +02:00
Merge pull request #15992 from hyperlogic/feature/reaction-fixes
Refined reaction and sitting animations
This commit is contained in:
commit
9d3d5b7701
37 changed files with 6224 additions and 3069 deletions
Binary file not shown.
BIN
interface/resources/avatar/animations/emote_agree_headnod.fbx
Normal file
BIN
interface/resources/avatar/animations/emote_agree_headnod.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/emote_agree_headnodyes.fbx
Normal file
BIN
interface/resources/avatar/animations/emote_agree_headnodyes.fbx
Normal file
Binary file not shown.
Binary file not shown.
BIN
interface/resources/avatar/animations/emote_agree_thisorthat.fbx
Normal file
BIN
interface/resources/avatar/animations/emote_agree_thisorthat.fbx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
interface/resources/avatar/animations/emote_clap02_all.fbx
Normal file
BIN
interface/resources/avatar/animations/emote_clap02_all.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/emote_clap03_all.fbx
Normal file
BIN
interface/resources/avatar/animations/emote_clap03_all.fbx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
interface/resources/avatar/animations/emote_disagree_no.fbx
Normal file
BIN
interface/resources/avatar/animations/emote_disagree_no.fbx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
interface/resources/avatar/animations/emote_raisehand03_all.fbx
Normal file
BIN
interface/resources/avatar/animations/emote_raisehand03_all.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/emote_raisehand04_all.fbx
Normal file
BIN
interface/resources/avatar/animations/emote_raisehand04_all.fbx
Normal file
Binary file not shown.
Binary file not shown.
BIN
interface/resources/avatar/animations/sitting_idle03.fbx
Normal file
BIN
interface/resources/avatar/animations/sitting_idle03.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/sitting_talk02.fbx
Normal file
BIN
interface/resources/avatar/animations/sitting_talk02.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/sitting_talk03.fbx
Normal file
BIN
interface/resources/avatar/animations/sitting_talk03.fbx
Normal file
Binary file not shown.
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -125,16 +125,25 @@ QString userRecenterModelToString(MyAvatar::SitStandModelType model) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const QStringList REACTION_NAMES = {
|
static const QStringList TRIGGER_REACTION_NAMES = {
|
||||||
QString("positive"),
|
QString("positive"),
|
||||||
QString("negative"),
|
QString("negative")
|
||||||
|
};
|
||||||
|
|
||||||
|
static const QStringList BEGIN_END_REACTION_NAMES = {
|
||||||
QString("raiseHand"),
|
QString("raiseHand"),
|
||||||
QString("applaud"),
|
QString("applaud"),
|
||||||
QString("point")
|
QString("point")
|
||||||
};
|
};
|
||||||
|
|
||||||
static int reactionNameToIndex(const QString& reactionName) {
|
static int triggerReactionNameToIndex(const QString& reactionName) {
|
||||||
return REACTION_NAMES.indexOf(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) :
|
MyAvatar::MyAvatar(QThread* thread) :
|
||||||
|
@ -5824,13 +5833,17 @@ void MyAvatar::setModelScale(float scale) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList MyAvatar::getReactions() const {
|
QStringList MyAvatar::getBeginEndReactions() const {
|
||||||
return REACTION_NAMES;
|
return BEGIN_END_REACTION_NAMES;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList MyAvatar::getTriggerReactions() const {
|
||||||
|
return TRIGGER_REACTION_NAMES;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MyAvatar::triggerReaction(QString reactionName) {
|
bool MyAvatar::triggerReaction(QString reactionName) {
|
||||||
int reactionIndex = reactionNameToIndex(reactionName);
|
int reactionIndex = triggerReactionNameToIndex(reactionName);
|
||||||
if (reactionIndex >= 0 && reactionIndex < (int)NUM_AVATAR_REACTIONS) {
|
if (reactionIndex >= 0 && reactionIndex < (int)NUM_AVATAR_TRIGGER_REACTIONS) {
|
||||||
std::lock_guard<std::mutex> guard(_reactionLock);
|
std::lock_guard<std::mutex> guard(_reactionLock);
|
||||||
_reactionTriggers[reactionIndex] = true;
|
_reactionTriggers[reactionIndex] = true;
|
||||||
return true;
|
return true;
|
||||||
|
@ -5839,8 +5852,8 @@ bool MyAvatar::triggerReaction(QString reactionName) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MyAvatar::beginReaction(QString reactionName) {
|
bool MyAvatar::beginReaction(QString reactionName) {
|
||||||
int reactionIndex = reactionNameToIndex(reactionName);
|
int reactionIndex = beginEndReactionNameToIndex(reactionName);
|
||||||
if (reactionIndex >= 0 && reactionIndex < (int)NUM_AVATAR_REACTIONS) {
|
if (reactionIndex >= 0 && reactionIndex < (int)NUM_AVATAR_BEGIN_END_REACTIONS) {
|
||||||
std::lock_guard<std::mutex> guard(_reactionLock);
|
std::lock_guard<std::mutex> guard(_reactionLock);
|
||||||
_reactionEnabledRefCounts[reactionIndex]++;
|
_reactionEnabledRefCounts[reactionIndex]++;
|
||||||
return true;
|
return true;
|
||||||
|
@ -5849,8 +5862,8 @@ bool MyAvatar::beginReaction(QString reactionName) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MyAvatar::endReaction(QString reactionName) {
|
bool MyAvatar::endReaction(QString reactionName) {
|
||||||
int reactionIndex = reactionNameToIndex(reactionName);
|
int reactionIndex = beginEndReactionNameToIndex(reactionName);
|
||||||
if (reactionIndex >= 0 && reactionIndex < (int)NUM_AVATAR_REACTIONS) {
|
if (reactionIndex >= 0 && reactionIndex < (int)NUM_AVATAR_BEGIN_END_REACTIONS) {
|
||||||
std::lock_guard<std::mutex> guard(_reactionLock);
|
std::lock_guard<std::mutex> guard(_reactionLock);
|
||||||
_reactionEnabledRefCounts[reactionIndex]--;
|
_reactionEnabledRefCounts[reactionIndex]--;
|
||||||
return true;
|
return true;
|
||||||
|
@ -5860,12 +5873,17 @@ bool MyAvatar::endReaction(QString reactionName) {
|
||||||
|
|
||||||
void MyAvatar::updateRigControllerParameters(Rig::ControllerParameters& params) {
|
void MyAvatar::updateRigControllerParameters(Rig::ControllerParameters& params) {
|
||||||
std::lock_guard<std::mutex> guard(_reactionLock);
|
std::lock_guard<std::mutex> 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.
|
// copy current state into params.
|
||||||
params.reactionEnabledFlags[i] = _reactionEnabledRefCounts[i] > 0;
|
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
|
// clear reaction triggers here as well
|
||||||
_reactionTriggers[i] = false;
|
_reactionTriggers[i] = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2213,13 +2213,23 @@ public slots:
|
||||||
virtual void setModelScale(float scale) override;
|
virtual void setModelScale(float scale) override;
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* MyAvatar.getReactions
|
* MyAvatar.getTriggerReactions
|
||||||
|
* Returns a list of reactions names that can be triggered using MyAvatar.triggerReaction().
|
||||||
* @returns {string[]} Array of reaction names.
|
* @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
|
/**jsdoc
|
||||||
* MyAvatar.triggerReaction
|
* 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
|
* @param {string} reactionName - reaction name
|
||||||
* @returns {bool} false if the given reaction is not supported.
|
* @returns {bool} false if the given reaction is not supported.
|
||||||
*/
|
*/
|
||||||
|
@ -2227,6 +2237,9 @@ public slots:
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* MyAvatar.beginReaction
|
* 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
|
* @param {string} reactionName - reaction name
|
||||||
* @returns {bool} false if the given reaction is not supported.
|
* @returns {bool} false if the given reaction is not supported.
|
||||||
*/
|
*/
|
||||||
|
@ -2234,6 +2247,7 @@ public slots:
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* MyAvatar.endReaction
|
* MyAvatar.endReaction
|
||||||
|
* Used to stop a given reaction that was started via MyAvatar.beginReaction().
|
||||||
* @param {string} reactionName - reaction name
|
* @param {string} reactionName - reaction name
|
||||||
* @returns {bool} false if the given reaction is not supported.
|
* @returns {bool} false if the given reaction is not supported.
|
||||||
*/
|
*/
|
||||||
|
@ -2872,8 +2886,9 @@ private:
|
||||||
QScriptEngine* _scriptEngine { nullptr };
|
QScriptEngine* _scriptEngine { nullptr };
|
||||||
bool _needToSaveAvatarEntitySettings { false };
|
bool _needToSaveAvatarEntitySettings { false };
|
||||||
|
|
||||||
int _reactionEnabledRefCounts[NUM_AVATAR_REACTIONS] { 0, 0, 0, 0, 0 };
|
bool _reactionTriggers[NUM_AVATAR_TRIGGER_REACTIONS] { false, false };
|
||||||
bool _reactionTriggers[NUM_AVATAR_REACTIONS] { false, false, false, false, false };
|
int _reactionEnabledRefCounts[NUM_AVATAR_BEGIN_END_REACTIONS] { 0, 0, 0 };
|
||||||
|
|
||||||
mutable std::mutex _reactionLock;
|
mutable std::mutex _reactionLock;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include "AnimTwoBoneIK.h"
|
#include "AnimTwoBoneIK.h"
|
||||||
#include "AnimSplineIK.h"
|
#include "AnimSplineIK.h"
|
||||||
#include "AnimPoleVectorConstraint.h"
|
#include "AnimPoleVectorConstraint.h"
|
||||||
|
#include "AnimUtil.h"
|
||||||
|
|
||||||
using NodeLoaderFunc = AnimNode::Pointer (*)(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
using NodeLoaderFunc = AnimNode::Pointer (*)(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
using NodeProcessFunc = bool (*)(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
using NodeProcessFunc = bool (*)(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
|
@ -91,6 +92,8 @@ static AnimStateMachine::InterpType stringToInterpType(const QString& str) {
|
||||||
return AnimStateMachine::InterpType::SnapshotBoth;
|
return AnimStateMachine::InterpType::SnapshotBoth;
|
||||||
} else if (str == "snapshotPrev") {
|
} else if (str == "snapshotPrev") {
|
||||||
return AnimStateMachine::InterpType::SnapshotPrev;
|
return AnimStateMachine::InterpType::SnapshotPrev;
|
||||||
|
} else if (str == "evaluateBoth") {
|
||||||
|
return AnimStateMachine::InterpType::EvaluateBoth;
|
||||||
} else {
|
} else {
|
||||||
return AnimStateMachine::InterpType::NumTypes;
|
return AnimStateMachine::InterpType::NumTypes;
|
||||||
}
|
}
|
||||||
|
@ -101,11 +104,63 @@ static AnimRandomSwitch::InterpType stringToRandomInterpType(const QString& str)
|
||||||
return AnimRandomSwitch::InterpType::SnapshotBoth;
|
return AnimRandomSwitch::InterpType::SnapshotBoth;
|
||||||
} else if (str == "snapshotPrev") {
|
} else if (str == "snapshotPrev") {
|
||||||
return AnimRandomSwitch::InterpType::SnapshotPrev;
|
return AnimRandomSwitch::InterpType::SnapshotPrev;
|
||||||
|
} else if (str == "evaluateBoth") {
|
||||||
|
return AnimRandomSwitch::InterpType::EvaluateBoth;
|
||||||
} else {
|
} else {
|
||||||
return AnimRandomSwitch::InterpType::NumTypes;
|
return AnimRandomSwitch::InterpType::NumTypes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static EasingType stringToEasingType(const QString& str) {
|
||||||
|
if (str == "linear") {
|
||||||
|
return EasingType_Linear;
|
||||||
|
} else if (str == "easeInSine") {
|
||||||
|
return EasingType_EaseInSine;
|
||||||
|
} else if (str == "easeOutSine") {
|
||||||
|
return EasingType_EaseOutSine;
|
||||||
|
} else if (str == "easeInOutSine") {
|
||||||
|
return EasingType_EaseInOutSine;
|
||||||
|
} else if (str == "easeInQuad") {
|
||||||
|
return EasingType_EaseInQuad;
|
||||||
|
} else if (str == "easeOutQuad") {
|
||||||
|
return EasingType_EaseOutQuad;
|
||||||
|
} else if (str == "easeInOutQuad") {
|
||||||
|
return EasingType_EaseInOutQuad;
|
||||||
|
} else if (str == "easeInCubic") {
|
||||||
|
return EasingType_EaseInCubic;
|
||||||
|
} else if (str == "easeOutCubic") {
|
||||||
|
return EasingType_EaseOutCubic;
|
||||||
|
} else if (str == "easeInOutCubic") {
|
||||||
|
return EasingType_EaseInOutCubic;
|
||||||
|
} else if (str == "easeInQuart") {
|
||||||
|
return EasingType_EaseInQuart;
|
||||||
|
} else if (str == "easeOutQuart") {
|
||||||
|
return EasingType_EaseOutQuart;
|
||||||
|
} else if (str == "easeInOutQuart") {
|
||||||
|
return EasingType_EaseInOutQuart;
|
||||||
|
} else if (str == "easeInQuint") {
|
||||||
|
return EasingType_EaseInQuint;
|
||||||
|
} else if (str == "easeOutQuint") {
|
||||||
|
return EasingType_EaseOutQuint;
|
||||||
|
} else if (str == "easeInOutQuint") {
|
||||||
|
return EasingType_EaseInOutQuint;
|
||||||
|
} else if (str == "easeInExpo") {
|
||||||
|
return EasingType_EaseInExpo;
|
||||||
|
} else if (str == "easeOutExpo") {
|
||||||
|
return EasingType_EaseOutExpo;
|
||||||
|
} else if (str == "easeInOutExpo") {
|
||||||
|
return EasingType_EaseInOutExpo;
|
||||||
|
} else if (str == "easeInCirc") {
|
||||||
|
return EasingType_EaseInCirc;
|
||||||
|
} else if (str == "easeOutCirc") {
|
||||||
|
return EasingType_EaseOutCirc;
|
||||||
|
} else if (str == "easeInOutCirc") {
|
||||||
|
return EasingType_EaseInOutCirc;
|
||||||
|
} else {
|
||||||
|
return EasingType_NumTypes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static const char* animManipulatorJointVarTypeToString(AnimManipulator::JointVar::Type type) {
|
static const char* animManipulatorJointVarTypeToString(AnimManipulator::JointVar::Type type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case AnimManipulator::JointVar::Type::Absolute: return "absolute";
|
case AnimManipulator::JointVar::Type::Absolute: return "absolute";
|
||||||
|
@ -723,6 +778,7 @@ bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj,
|
||||||
READ_FLOAT(interpTarget, stateObj, nodeId, jsonUrl, false);
|
READ_FLOAT(interpTarget, stateObj, nodeId, jsonUrl, false);
|
||||||
READ_FLOAT(interpDuration, stateObj, nodeId, jsonUrl, false);
|
READ_FLOAT(interpDuration, stateObj, nodeId, jsonUrl, false);
|
||||||
READ_OPTIONAL_STRING(interpType, stateObj);
|
READ_OPTIONAL_STRING(interpType, stateObj);
|
||||||
|
READ_OPTIONAL_STRING(easingType, stateObj);
|
||||||
|
|
||||||
READ_OPTIONAL_STRING(interpTargetVar, stateObj);
|
READ_OPTIONAL_STRING(interpTargetVar, stateObj);
|
||||||
READ_OPTIONAL_STRING(interpDurationVar, stateObj);
|
READ_OPTIONAL_STRING(interpDurationVar, stateObj);
|
||||||
|
@ -743,7 +799,16 @@ bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto statePtr = std::make_shared<AnimStateMachine::State>(id, iter->second, interpTarget, interpDuration, interpTypeEnum);
|
EasingType easingTypeEnum = EasingType_Linear; // default value
|
||||||
|
if (!easingType.isEmpty()) {
|
||||||
|
easingTypeEnum = stringToEasingType(easingType);
|
||||||
|
if (easingTypeEnum == EasingType_NumTypes) {
|
||||||
|
qCCritical(animation) << "AnimNodeLoader, bad easingType on stateMachine state, nodeId = " << nodeId << "stateId =" << id;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto statePtr = std::make_shared<AnimStateMachine::State>(id, iter->second, interpTarget, interpDuration, interpTypeEnum, easingTypeEnum);
|
||||||
assert(statePtr);
|
assert(statePtr);
|
||||||
|
|
||||||
if (!interpTargetVar.isEmpty()) {
|
if (!interpTargetVar.isEmpty()) {
|
||||||
|
@ -845,6 +910,7 @@ bool processRandomSwitchStateMachineNode(AnimNode::Pointer node, const QJsonObje
|
||||||
READ_FLOAT(interpTarget, stateObj, nodeId, jsonUrl, false);
|
READ_FLOAT(interpTarget, stateObj, nodeId, jsonUrl, false);
|
||||||
READ_FLOAT(interpDuration, stateObj, nodeId, jsonUrl, false);
|
READ_FLOAT(interpDuration, stateObj, nodeId, jsonUrl, false);
|
||||||
READ_OPTIONAL_STRING(interpType, stateObj);
|
READ_OPTIONAL_STRING(interpType, stateObj);
|
||||||
|
READ_OPTIONAL_STRING(easingType, stateObj);
|
||||||
READ_FLOAT(priority, stateObj, nodeId, jsonUrl, false);
|
READ_FLOAT(priority, stateObj, nodeId, jsonUrl, false);
|
||||||
READ_BOOL(resume, stateObj, nodeId, jsonUrl, false);
|
READ_BOOL(resume, stateObj, nodeId, jsonUrl, false);
|
||||||
|
|
||||||
|
@ -867,7 +933,16 @@ bool processRandomSwitchStateMachineNode(AnimNode::Pointer node, const QJsonObje
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto randomStatePtr = std::make_shared<AnimRandomSwitch::RandomSwitchState>(id, iter->second, interpTarget, interpDuration, interpTypeEnum, priority, resume);
|
EasingType easingTypeEnum = EasingType_Linear; // default value
|
||||||
|
if (!easingType.isEmpty()) {
|
||||||
|
easingTypeEnum = stringToEasingType(easingType);
|
||||||
|
if (easingTypeEnum == EasingType_NumTypes) {
|
||||||
|
qCCritical(animation) << "AnimNodeLoader, bad easingType on randomSwitch state, nodeId = " << nodeId << "stateId =" << id;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto randomStatePtr = std::make_shared<AnimRandomSwitch::RandomSwitchState>(id, iter->second, interpTarget, interpDuration, interpTypeEnum, easingTypeEnum, priority, resume);
|
||||||
if (priority > 0.0f) {
|
if (priority > 0.0f) {
|
||||||
smNode->addToPrioritySum(priority);
|
smNode->addToPrioritySum(priority);
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,15 +54,20 @@ const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, const A
|
||||||
|
|
||||||
if (_children.size() >= 2) {
|
if (_children.size() >= 2) {
|
||||||
auto& underPoses = _children[1]->evaluate(animVars, context, dt, triggersOut);
|
auto& underPoses = _children[1]->evaluate(animVars, context, dt, triggersOut);
|
||||||
auto& overPoses = _children[0]->overlay(animVars, context, dt, triggersOut, underPoses);
|
|
||||||
|
|
||||||
if (underPoses.size() > 0 && underPoses.size() == overPoses.size()) {
|
if (_alpha == 0.0f) {
|
||||||
_poses.resize(underPoses.size());
|
_poses = underPoses;
|
||||||
assert(_boneSetVec.size() == _poses.size());
|
} else {
|
||||||
|
auto& overPoses = _children[0]->overlay(animVars, context, dt, triggersOut, underPoses);
|
||||||
|
|
||||||
for (size_t i = 0; i < _poses.size(); i++) {
|
if (underPoses.size() > 0 && underPoses.size() == overPoses.size()) {
|
||||||
float alpha = _boneSetVec[i] * _alpha;
|
_poses.resize(underPoses.size());
|
||||||
::blend(1, &underPoses[i], &overPoses[i], alpha, &_poses[i]);
|
assert(_boneSetVec.size() == _poses.size());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < _poses.size(); i++) {
|
||||||
|
float alpha = _boneSetVec[i] * _alpha;
|
||||||
|
::blend(1, &underPoses[i], &overPoses[i], alpha, &_poses[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,6 +89,7 @@ const AnimPoseVec& AnimRandomSwitch::evaluate(const AnimVariantMap& animVars, co
|
||||||
|
|
||||||
assert(_currentState);
|
assert(_currentState);
|
||||||
auto currentStateNode = _children[_currentState->getChildIndex()];
|
auto currentStateNode = _children[_currentState->getChildIndex()];
|
||||||
|
auto previousStateNode = _children[_previousState->getChildIndex()];
|
||||||
assert(currentStateNode);
|
assert(currentStateNode);
|
||||||
|
|
||||||
if (_duringInterp) {
|
if (_duringInterp) {
|
||||||
|
@ -97,6 +98,7 @@ const AnimPoseVec& AnimRandomSwitch::evaluate(const AnimVariantMap& animVars, co
|
||||||
AnimPoseVec* nextPoses = nullptr;
|
AnimPoseVec* nextPoses = nullptr;
|
||||||
AnimPoseVec* prevPoses = nullptr;
|
AnimPoseVec* prevPoses = nullptr;
|
||||||
AnimPoseVec localNextPoses;
|
AnimPoseVec localNextPoses;
|
||||||
|
AnimPoseVec localPrevPoses;
|
||||||
if (_interpType == InterpType::SnapshotBoth) {
|
if (_interpType == InterpType::SnapshotBoth) {
|
||||||
// interp between both snapshots
|
// interp between both snapshots
|
||||||
prevPoses = &_prevPoses;
|
prevPoses = &_prevPoses;
|
||||||
|
@ -107,13 +109,18 @@ const AnimPoseVec& AnimRandomSwitch::evaluate(const AnimVariantMap& animVars, co
|
||||||
localNextPoses = currentStateNode->evaluate(animVars, context, dt, triggersOut);
|
localNextPoses = currentStateNode->evaluate(animVars, context, dt, triggersOut);
|
||||||
prevPoses = &_prevPoses;
|
prevPoses = &_prevPoses;
|
||||||
nextPoses = &localNextPoses;
|
nextPoses = &localNextPoses;
|
||||||
|
} else if (_interpType == InterpType::EvaluateBoth) {
|
||||||
|
localPrevPoses = previousStateNode->evaluate(animVars, context, dt, triggersOut);
|
||||||
|
localNextPoses = currentStateNode->evaluate(animVars, context, dt, triggersOut);
|
||||||
|
prevPoses = &localPrevPoses;
|
||||||
|
nextPoses = &localNextPoses;
|
||||||
} else {
|
} else {
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
if (_poses.size() > 0 && nextPoses && prevPoses && nextPoses->size() > 0 && prevPoses->size() > 0) {
|
if (_poses.size() > 0 && nextPoses && prevPoses && nextPoses->size() > 0 && prevPoses->size() > 0) {
|
||||||
::blend(_poses.size(), &(prevPoses->at(0)), &(nextPoses->at(0)), _alpha, &_poses[0]);
|
::blend(_poses.size(), &(prevPoses->at(0)), &(nextPoses->at(0)), easingFunc(_alpha, _easingType), &_poses[0]);
|
||||||
}
|
}
|
||||||
context.setDebugAlpha(_currentState->getID(), _alpha * parentDebugAlpha, _children[_currentState->getChildIndex()]->getType());
|
context.setDebugAlpha(_currentState->getID(), easingFunc(_alpha, _easingType) * parentDebugAlpha, _children[_currentState->getChildIndex()]->getType());
|
||||||
} else {
|
} else {
|
||||||
_duringInterp = false;
|
_duringInterp = false;
|
||||||
_prevPoses.clear();
|
_prevPoses.clear();
|
||||||
|
@ -160,6 +167,7 @@ void AnimRandomSwitch::switchRandomState(const AnimVariantMap& animVars, const A
|
||||||
float duration = std::max(0.001f, animVars.lookup(desiredState->_interpDurationVar, desiredState->_interpDuration));
|
float duration = std::max(0.001f, animVars.lookup(desiredState->_interpDurationVar, desiredState->_interpDuration));
|
||||||
_alphaVel = FRAMES_PER_SECOND / duration;
|
_alphaVel = FRAMES_PER_SECOND / duration;
|
||||||
_interpType = (InterpType)animVars.lookup(desiredState->_interpTypeVar, (int)desiredState->_interpType);
|
_interpType = (InterpType)animVars.lookup(desiredState->_interpTypeVar, (int)desiredState->_interpType);
|
||||||
|
_easingType = desiredState->_easingType;
|
||||||
|
|
||||||
// because dt is 0, we should not encounter any triggers
|
// because dt is 0, we should not encounter any triggers
|
||||||
const float dt = 0.0f;
|
const float dt = 0.0f;
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "AnimNode.h"
|
#include "AnimNode.h"
|
||||||
|
#include "AnimUtil.h"
|
||||||
|
|
||||||
// Random Switch State Machine for random transitioning between children AnimNodes
|
// Random Switch State Machine for random transitioning between children AnimNodes
|
||||||
//
|
//
|
||||||
|
@ -51,6 +52,7 @@ public:
|
||||||
enum class InterpType {
|
enum class InterpType {
|
||||||
SnapshotBoth = 0,
|
SnapshotBoth = 0,
|
||||||
SnapshotPrev,
|
SnapshotPrev,
|
||||||
|
EvaluateBoth,
|
||||||
NumTypes
|
NumTypes
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -73,12 +75,13 @@ protected:
|
||||||
RandomSwitchState::Pointer _randomSwitchState;
|
RandomSwitchState::Pointer _randomSwitchState;
|
||||||
};
|
};
|
||||||
|
|
||||||
RandomSwitchState(const QString& id, int childIndex, float interpTarget, float interpDuration, InterpType interpType, float priority, bool resume) :
|
RandomSwitchState(const QString& id, int childIndex, float interpTarget, float interpDuration, InterpType interpType, EasingType easingType, float priority, bool resume) :
|
||||||
_id(id),
|
_id(id),
|
||||||
_childIndex(childIndex),
|
_childIndex(childIndex),
|
||||||
_interpTarget(interpTarget),
|
_interpTarget(interpTarget),
|
||||||
_interpDuration(interpDuration),
|
_interpDuration(interpDuration),
|
||||||
_interpType(interpType),
|
_interpType(interpType),
|
||||||
|
_easingType(easingType),
|
||||||
_priority(priority),
|
_priority(priority),
|
||||||
_resume(resume){
|
_resume(resume){
|
||||||
}
|
}
|
||||||
|
@ -106,6 +109,7 @@ protected:
|
||||||
float _interpTarget; // frames
|
float _interpTarget; // frames
|
||||||
float _interpDuration; // frames
|
float _interpDuration; // frames
|
||||||
InterpType _interpType;
|
InterpType _interpType;
|
||||||
|
EasingType _easingType;
|
||||||
float _priority {0.0f};
|
float _priority {0.0f};
|
||||||
bool _resume {false};
|
bool _resume {false};
|
||||||
|
|
||||||
|
@ -154,7 +158,8 @@ protected:
|
||||||
int _randomSwitchEvaluationCount { 0 };
|
int _randomSwitchEvaluationCount { 0 };
|
||||||
// interpolation state
|
// interpolation state
|
||||||
bool _duringInterp = false;
|
bool _duringInterp = false;
|
||||||
InterpType _interpType{ InterpType::SnapshotPrev };
|
InterpType _interpType { InterpType::SnapshotPrev };
|
||||||
|
EasingType _easingType { EasingType_Linear };
|
||||||
float _alphaVel = 0.0f;
|
float _alphaVel = 0.0f;
|
||||||
float _alpha = 0.0f;
|
float _alpha = 0.0f;
|
||||||
AnimPoseVec _prevPoses;
|
AnimPoseVec _prevPoses;
|
||||||
|
|
|
@ -48,6 +48,7 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co
|
||||||
|
|
||||||
assert(_currentState);
|
assert(_currentState);
|
||||||
auto currentStateNode = _children[_currentState->getChildIndex()];
|
auto currentStateNode = _children[_currentState->getChildIndex()];
|
||||||
|
auto previousStateNode = _children[_previousState->getChildIndex()];
|
||||||
assert(currentStateNode);
|
assert(currentStateNode);
|
||||||
|
|
||||||
if (_duringInterp) {
|
if (_duringInterp) {
|
||||||
|
@ -56,6 +57,8 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co
|
||||||
AnimPoseVec* nextPoses = nullptr;
|
AnimPoseVec* nextPoses = nullptr;
|
||||||
AnimPoseVec* prevPoses = nullptr;
|
AnimPoseVec* prevPoses = nullptr;
|
||||||
AnimPoseVec localNextPoses;
|
AnimPoseVec localNextPoses;
|
||||||
|
AnimPoseVec localPrevPoses;
|
||||||
|
|
||||||
if (_interpType == InterpType::SnapshotBoth) {
|
if (_interpType == InterpType::SnapshotBoth) {
|
||||||
// interp between both snapshots
|
// interp between both snapshots
|
||||||
prevPoses = &_prevPoses;
|
prevPoses = &_prevPoses;
|
||||||
|
@ -66,13 +69,18 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co
|
||||||
localNextPoses = currentStateNode->evaluate(animVars, context, dt, triggersOut);
|
localNextPoses = currentStateNode->evaluate(animVars, context, dt, triggersOut);
|
||||||
prevPoses = &_prevPoses;
|
prevPoses = &_prevPoses;
|
||||||
nextPoses = &localNextPoses;
|
nextPoses = &localNextPoses;
|
||||||
|
} else if (_interpType == InterpType::EvaluateBoth) {
|
||||||
|
localPrevPoses = previousStateNode->evaluate(animVars, context, dt, triggersOut);
|
||||||
|
localNextPoses = currentStateNode->evaluate(animVars, context, dt, triggersOut);
|
||||||
|
prevPoses = &localPrevPoses;
|
||||||
|
nextPoses = &localNextPoses;
|
||||||
} else {
|
} else {
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
if (_poses.size() > 0 && nextPoses && prevPoses && nextPoses->size() > 0 && prevPoses->size() > 0) {
|
if (_poses.size() > 0 && nextPoses && prevPoses && nextPoses->size() > 0 && prevPoses->size() > 0) {
|
||||||
::blend(_poses.size(), &(prevPoses->at(0)), &(nextPoses->at(0)), _alpha, &_poses[0]);
|
::blend(_poses.size(), &(prevPoses->at(0)), &(nextPoses->at(0)), easingFunc(_alpha, _easingType), &_poses[0]);
|
||||||
}
|
}
|
||||||
context.setDebugAlpha(_currentState->getID(), _alpha * parentDebugAlpha, _children[_currentState->getChildIndex()]->getType());
|
context.setDebugAlpha(_currentState->getID(), easingFunc(_alpha, _easingType) * parentDebugAlpha, _children[_currentState->getChildIndex()]->getType());
|
||||||
} else {
|
} else {
|
||||||
_duringInterp = false;
|
_duringInterp = false;
|
||||||
_prevPoses.clear();
|
_prevPoses.clear();
|
||||||
|
@ -95,6 +103,15 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co
|
||||||
return _poses;
|
return _poses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QString& AnimStateMachine::getCurrentStateID() const {
|
||||||
|
if (_currentState) {
|
||||||
|
return _currentState->getID();
|
||||||
|
} else {
|
||||||
|
static QString emptyString;
|
||||||
|
return emptyString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AnimStateMachine::setCurrentState(State::Pointer state) {
|
void AnimStateMachine::setCurrentState(State::Pointer state) {
|
||||||
_previousState = _currentState ? _currentState : state;
|
_previousState = _currentState ? _currentState : state;
|
||||||
_currentState = state;
|
_currentState = state;
|
||||||
|
@ -116,6 +133,7 @@ void AnimStateMachine::switchState(const AnimVariantMap& animVars, const AnimCon
|
||||||
float duration = std::max(0.001f, animVars.lookup(desiredState->_interpDurationVar, desiredState->_interpDuration));
|
float duration = std::max(0.001f, animVars.lookup(desiredState->_interpDurationVar, desiredState->_interpDuration));
|
||||||
_alphaVel = FRAMES_PER_SECOND / duration;
|
_alphaVel = FRAMES_PER_SECOND / duration;
|
||||||
_interpType = (InterpType)animVars.lookup(desiredState->_interpTypeVar, (int)desiredState->_interpType);
|
_interpType = (InterpType)animVars.lookup(desiredState->_interpTypeVar, (int)desiredState->_interpType);
|
||||||
|
_easingType = desiredState->_easingType;
|
||||||
|
|
||||||
// because dt is 0, we should not encounter any triggers
|
// because dt is 0, we should not encounter any triggers
|
||||||
const float dt = 0.0f;
|
const float dt = 0.0f;
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "AnimNode.h"
|
#include "AnimNode.h"
|
||||||
|
#include "AnimUtil.h"
|
||||||
|
|
||||||
// State Machine for transitioning between children AnimNodes
|
// State Machine for transitioning between children AnimNodes
|
||||||
//
|
//
|
||||||
|
@ -47,6 +48,7 @@ public:
|
||||||
enum class InterpType {
|
enum class InterpType {
|
||||||
SnapshotBoth = 0,
|
SnapshotBoth = 0,
|
||||||
SnapshotPrev,
|
SnapshotPrev,
|
||||||
|
EvaluateBoth,
|
||||||
NumTypes
|
NumTypes
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -69,12 +71,13 @@ protected:
|
||||||
State::Pointer _state;
|
State::Pointer _state;
|
||||||
};
|
};
|
||||||
|
|
||||||
State(const QString& id, int childIndex, float interpTarget, float interpDuration, InterpType interpType) :
|
State(const QString& id, int childIndex, float interpTarget, float interpDuration, InterpType interpType, EasingType easingType) :
|
||||||
_id(id),
|
_id(id),
|
||||||
_childIndex(childIndex),
|
_childIndex(childIndex),
|
||||||
_interpTarget(interpTarget),
|
_interpTarget(interpTarget),
|
||||||
_interpDuration(interpDuration),
|
_interpDuration(interpDuration),
|
||||||
_interpType(interpType) {}
|
_interpType(interpType),
|
||||||
|
_easingType(easingType) {}
|
||||||
|
|
||||||
void setInterpTargetVar(const QString& interpTargetVar) { _interpTargetVar = interpTargetVar; }
|
void setInterpTargetVar(const QString& interpTargetVar) { _interpTargetVar = interpTargetVar; }
|
||||||
void setInterpDurationVar(const QString& interpDurationVar) { _interpDurationVar = interpDurationVar; }
|
void setInterpDurationVar(const QString& interpDurationVar) { _interpDurationVar = interpDurationVar; }
|
||||||
|
@ -95,6 +98,7 @@ protected:
|
||||||
float _interpTarget; // frames
|
float _interpTarget; // frames
|
||||||
float _interpDuration; // frames
|
float _interpDuration; // frames
|
||||||
InterpType _interpType;
|
InterpType _interpType;
|
||||||
|
EasingType _easingType;
|
||||||
|
|
||||||
QString _interpTargetVar;
|
QString _interpTargetVar;
|
||||||
QString _interpDurationVar;
|
QString _interpDurationVar;
|
||||||
|
@ -116,6 +120,7 @@ public:
|
||||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||||
|
|
||||||
void setCurrentStateVar(QString& currentStateVar) { _currentStateVar = currentStateVar; }
|
void setCurrentStateVar(QString& currentStateVar) { _currentStateVar = currentStateVar; }
|
||||||
|
const QString& getCurrentStateID() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
@ -134,6 +139,7 @@ protected:
|
||||||
// interpolation state
|
// interpolation state
|
||||||
bool _duringInterp = false;
|
bool _duringInterp = false;
|
||||||
InterpType _interpType { InterpType::SnapshotPrev };
|
InterpType _interpType { InterpType::SnapshotPrev };
|
||||||
|
EasingType _easingType { EasingType_Linear };
|
||||||
float _alphaVel = 0.0f;
|
float _alphaVel = 0.0f;
|
||||||
float _alpha = 0.0f;
|
float _alpha = 0.0f;
|
||||||
AnimPoseVec _prevPoses;
|
AnimPoseVec _prevPoses;
|
||||||
|
|
|
@ -211,3 +211,86 @@ bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See https://easings.net/en# for a graphical visualiztion of easing types.
|
||||||
|
float easingFunc(float alpha, EasingType type) {
|
||||||
|
switch (type) {
|
||||||
|
case EasingType_Linear:
|
||||||
|
return alpha;
|
||||||
|
case EasingType_EaseInSine:
|
||||||
|
return sinf((alpha - 1.0f) * PI_OVER_TWO) + 1.0f;
|
||||||
|
case EasingType_EaseOutSine:
|
||||||
|
return sinf(alpha * PI_OVER_TWO);
|
||||||
|
case EasingType_EaseInOutSine:
|
||||||
|
return 0.5f * (1.0f - cosf(alpha * PI));
|
||||||
|
case EasingType_EaseInQuad:
|
||||||
|
return alpha * alpha;
|
||||||
|
case EasingType_EaseOutQuad:
|
||||||
|
return -(alpha * (alpha - 2.0f));
|
||||||
|
case EasingType_EaseInOutQuad:
|
||||||
|
return (alpha < 0.5f) ? (2.0f * alpha * alpha) : ((-2.0f * alpha * alpha) + (4.0f * alpha) - 1.0f);
|
||||||
|
case EasingType_EaseInCubic:
|
||||||
|
return alpha * alpha * alpha;
|
||||||
|
case EasingType_EaseOutCubic: {
|
||||||
|
float temp = alpha - 1.0f;
|
||||||
|
return temp * temp * temp + 1.0f;
|
||||||
|
}
|
||||||
|
case EasingType_EaseInOutCubic:
|
||||||
|
if (alpha < 0.5f) {
|
||||||
|
return 4.0f * alpha * alpha * alpha;
|
||||||
|
} else {
|
||||||
|
float temp = ((2.0f * alpha) - 2.0f);
|
||||||
|
return 0.5f * temp * temp * temp + 1.0f;
|
||||||
|
}
|
||||||
|
case EasingType_EaseInQuart:
|
||||||
|
return alpha * alpha * alpha * alpha;
|
||||||
|
case EasingType_EaseOutQuart: {
|
||||||
|
float temp = alpha - 1.0f;
|
||||||
|
return temp * temp * temp * (1.0f - alpha) + 1.0f;
|
||||||
|
}
|
||||||
|
case EasingType_EaseInOutQuart:
|
||||||
|
if (alpha < 0.5f) {
|
||||||
|
return 8.0f * alpha * alpha * alpha * alpha;
|
||||||
|
} else {
|
||||||
|
float temp = alpha - 1.0f;
|
||||||
|
return -8.0f * temp * temp * temp * temp + 1.0f;
|
||||||
|
}
|
||||||
|
case EasingType_EaseInQuint:
|
||||||
|
return alpha * alpha * alpha * alpha * alpha;
|
||||||
|
case EasingType_EaseOutQuint: {
|
||||||
|
float temp = (alpha - 1.0f);
|
||||||
|
return temp * temp * temp * temp * temp + 1.0f;
|
||||||
|
}
|
||||||
|
case EasingType_EaseInOutQuint:
|
||||||
|
if (alpha < 0.5f) {
|
||||||
|
return 16.0f * alpha * alpha * alpha * alpha * alpha;
|
||||||
|
} else {
|
||||||
|
float temp = ((2.0f * alpha) - 2.0f);
|
||||||
|
return 0.5f * temp * temp * temp * temp * temp + 1.0f;
|
||||||
|
}
|
||||||
|
case EasingType_EaseInExpo:
|
||||||
|
return (alpha == 0.0f) ? alpha : powf(2.0f, 10.0f * (alpha - 1.0f));
|
||||||
|
case EasingType_EaseOutExpo:
|
||||||
|
return (alpha == 1.0f) ? alpha : 1.0f - powf(2.0f, -10.0f * alpha);
|
||||||
|
case EasingType_EaseInOutExpo:
|
||||||
|
if (alpha == 0.0f || alpha == 1.0f)
|
||||||
|
return alpha;
|
||||||
|
else if (alpha < 0.5f) {
|
||||||
|
return 0.5f * powf(2.0f, (20.0f * alpha) - 10.0f);
|
||||||
|
} else {
|
||||||
|
return -0.5f * powf(2.0f, (-20.0f * alpha) + 10.0f) + 1.0f;
|
||||||
|
}
|
||||||
|
case EasingType_EaseInCirc:
|
||||||
|
return 1.0f - sqrtf(1.0f - (alpha * alpha));
|
||||||
|
case EasingType_EaseOutCirc:
|
||||||
|
return sqrtf((2.0f - alpha) * alpha);
|
||||||
|
case EasingType_EaseInOutCirc:
|
||||||
|
if (alpha < 0.5f) {
|
||||||
|
return 0.5f * (1.0f - sqrtf(1.0f - 4.0f * (alpha * alpha)));
|
||||||
|
} else {
|
||||||
|
return 0.5f * (sqrtf(-((2.0f * alpha) - 3.0f) * ((2.0f * alpha) - 1.0f)) + 1.0f);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return alpha;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -128,10 +128,37 @@ protected:
|
||||||
bool _snapshotValid { false };
|
bool _snapshotValid { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// returns true if the given point lies inside of the k-dop, specified by shapeInfo & shapePose.
|
// returns true if the given point lies inside of the k-dop, specified by shapeInfo & shapePose.
|
||||||
// if the given point does lie within the k-dop, it also returns the amount of displacement necessary to push that point outward
|
// if the given point does lie within the k-dop, it also returns the amount of displacement necessary to push that point outward
|
||||||
// such that it lies on the surface of the kdop.
|
// such that it lies on the surface of the kdop.
|
||||||
bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose, const HFMJointShapeInfo& shapeInfo, glm::vec3& displacementOut);
|
bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose, const HFMJointShapeInfo& shapeInfo, glm::vec3& displacementOut);
|
||||||
|
|
||||||
|
enum EasingType {
|
||||||
|
EasingType_Linear,
|
||||||
|
EasingType_EaseInSine,
|
||||||
|
EasingType_EaseOutSine,
|
||||||
|
EasingType_EaseInOutSine,
|
||||||
|
EasingType_EaseInQuad,
|
||||||
|
EasingType_EaseOutQuad,
|
||||||
|
EasingType_EaseInOutQuad,
|
||||||
|
EasingType_EaseInCubic,
|
||||||
|
EasingType_EaseOutCubic,
|
||||||
|
EasingType_EaseInOutCubic,
|
||||||
|
EasingType_EaseInQuart,
|
||||||
|
EasingType_EaseOutQuart,
|
||||||
|
EasingType_EaseInOutQuart,
|
||||||
|
EasingType_EaseInQuint,
|
||||||
|
EasingType_EaseOutQuint,
|
||||||
|
EasingType_EaseInOutQuint,
|
||||||
|
EasingType_EaseInExpo,
|
||||||
|
EasingType_EaseOutExpo,
|
||||||
|
EasingType_EaseInOutExpo,
|
||||||
|
EasingType_EaseInCirc,
|
||||||
|
EasingType_EaseOutCirc,
|
||||||
|
EasingType_EaseInOutCirc,
|
||||||
|
EasingType_NumTypes
|
||||||
|
};
|
||||||
|
|
||||||
|
float easingFunc(float alpha, EasingType type);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include "AnimInverseKinematics.h"
|
#include "AnimInverseKinematics.h"
|
||||||
#include "AnimOverlay.h"
|
#include "AnimOverlay.h"
|
||||||
#include "AnimSkeleton.h"
|
#include "AnimSkeleton.h"
|
||||||
|
#include "AnimStateMachine.h"
|
||||||
#include "AnimUtil.h"
|
#include "AnimUtil.h"
|
||||||
#include "AvatarConstants.h"
|
#include "AvatarConstants.h"
|
||||||
#include "IKTarget.h"
|
#include "IKTarget.h"
|
||||||
|
@ -1906,28 +1907,7 @@ void Rig::updateFeet(bool leftFootEnabled, bool rightFootEnabled, bool headEnabl
|
||||||
|
|
||||||
void Rig::updateReactions(const ControllerParameters& params) {
|
void Rig::updateReactions(const ControllerParameters& params) {
|
||||||
|
|
||||||
// enable/disable animVars
|
// trigger reactions
|
||||||
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]) {
|
if (params.reactionTriggers[AVATAR_REACTION_POSITIVE]) {
|
||||||
_animVars.set("reactionPositiveTrigger", true);
|
_animVars.set("reactionPositiveTrigger", true);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1940,22 +1920,38 @@ void Rig::updateReactions(const ControllerParameters& params) {
|
||||||
_animVars.set("reactionNegativeTrigger", false);
|
_animVars.set("reactionNegativeTrigger", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.reactionTriggers[AVATAR_REACTION_RAISE_HAND]) {
|
// begin end reactions
|
||||||
_animVars.set("reactionRaiseHandTrigger", true);
|
bool enabled = params.reactionEnabledFlags[AVATAR_REACTION_RAISE_HAND];
|
||||||
} else {
|
_animVars.set("reactionRaiseHandEnabled", enabled);
|
||||||
_animVars.set("reactionRaiseHandTrigger", false);
|
_animVars.set("reactionRaiseHandDisabled", !enabled);
|
||||||
}
|
|
||||||
|
|
||||||
if (params.reactionTriggers[AVATAR_REACTION_APPLAUD]) {
|
enabled = params.reactionEnabledFlags[AVATAR_REACTION_APPLAUD];
|
||||||
_animVars.set("reactionApplaudTrigger", true);
|
_animVars.set("reactionApplaudEnabled", enabled);
|
||||||
} else {
|
_animVars.set("reactionApplaudDisabled", !enabled);
|
||||||
_animVars.set("reactionApplaudTrigger", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.reactionTriggers[AVATAR_REACTION_POINT]) {
|
enabled = params.reactionEnabledFlags[AVATAR_REACTION_POINT];
|
||||||
_animVars.set("reactionPointTrigger", true);
|
_animVars.set("reactionPointEnabled", enabled);
|
||||||
} else {
|
_animVars.set("reactionPointDisabled", !enabled);
|
||||||
_animVars.set("reactionPointTrigger", false);
|
|
||||||
|
// determine if we should ramp off IK
|
||||||
|
if (_enableInverseKinematics) {
|
||||||
|
bool reactionPlaying = false;
|
||||||
|
std::shared_ptr<AnimStateMachine> mainStateMachine = std::dynamic_pointer_cast<AnimStateMachine>(_animNode->findByName("mainStateMachine"));
|
||||||
|
std::shared_ptr<AnimStateMachine> idleStateMachine = std::dynamic_pointer_cast<AnimStateMachine>(_animNode->findByName("idle"));
|
||||||
|
if (mainStateMachine && mainStateMachine->getCurrentStateID() == "idle" && idleStateMachine) {
|
||||||
|
reactionPlaying = idleStateMachine->getCurrentStateID().startsWith("reaction");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSeated = _state == RigRole::Seated;
|
||||||
|
bool hipsEnabled = params.primaryControllerFlags[PrimaryControllerType_Hips] & (uint8_t)ControllerFlags::Enabled;
|
||||||
|
bool hipsEstimated = params.primaryControllerFlags[PrimaryControllerType_Hips] & (uint8_t)ControllerFlags::Estimated;
|
||||||
|
bool hmdMode = hipsEnabled && !hipsEstimated;
|
||||||
|
|
||||||
|
if ((reactionPlaying || isSeated) && !hmdMode) {
|
||||||
|
// TODO: make this smooth.
|
||||||
|
// disable head IK while reaction is playing, but only in "desktop" mode.
|
||||||
|
_animVars.set("headType", (int)IKTarget::Type::Unknown);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2115,9 +2111,9 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
|
||||||
_talkIdleInterpTime = 1.0f;
|
_talkIdleInterpTime = 1.0f;
|
||||||
}
|
}
|
||||||
float easeOutInValue = _talkIdleInterpTime < 0.5f ? 4.0f * powf(_talkIdleInterpTime, 3.0f) : 4.0f * powf((_talkIdleInterpTime - 1.0f), 3.0f) + 1.0f;
|
float easeOutInValue = _talkIdleInterpTime < 0.5f ? 4.0f * powf(_talkIdleInterpTime, 3.0f) : 4.0f * powf((_talkIdleInterpTime - 1.0f), 3.0f) + 1.0f;
|
||||||
_animVars.set("idleOverlayAlpha", easeOutInValue);
|
_animVars.set("talkOverlayAlpha", easeOutInValue);
|
||||||
} else {
|
} else {
|
||||||
_animVars.set("idleOverlayAlpha", 1.0f);
|
_animVars.set("talkOverlayAlpha", 1.0f);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (_talkIdleInterpTime < 1.0f) {
|
if (_talkIdleInterpTime < 1.0f) {
|
||||||
|
@ -2127,9 +2123,9 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
|
||||||
}
|
}
|
||||||
float easeOutInValue = _talkIdleInterpTime < 0.5f ? 4.0f * powf(_talkIdleInterpTime, 3.0f) : 4.0f * powf((_talkIdleInterpTime - 1.0f), 3.0f) + 1.0f;
|
float easeOutInValue = _talkIdleInterpTime < 0.5f ? 4.0f * powf(_talkIdleInterpTime, 3.0f) : 4.0f * powf((_talkIdleInterpTime - 1.0f), 3.0f) + 1.0f;
|
||||||
float talkAlpha = 1.0f - easeOutInValue;
|
float talkAlpha = 1.0f - easeOutInValue;
|
||||||
_animVars.set("idleOverlayAlpha", talkAlpha);
|
_animVars.set("talkOverlayAlpha", talkAlpha);
|
||||||
} else {
|
} else {
|
||||||
_animVars.set("idleOverlayAlpha", 0.0f);
|
_animVars.set("talkOverlayAlpha", 0.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,8 +88,8 @@ public:
|
||||||
AnimPose secondaryControllerPoses[NumSecondaryControllerTypes]; // rig space
|
AnimPose secondaryControllerPoses[NumSecondaryControllerTypes]; // rig space
|
||||||
uint8_t secondaryControllerFlags[NumSecondaryControllerTypes];
|
uint8_t secondaryControllerFlags[NumSecondaryControllerTypes];
|
||||||
bool isTalking;
|
bool isTalking;
|
||||||
bool reactionEnabledFlags[NUM_AVATAR_REACTIONS];
|
bool reactionEnabledFlags[NUM_AVATAR_BEGIN_END_REACTIONS];
|
||||||
bool reactionTriggers[NUM_AVATAR_REACTIONS];
|
bool reactionTriggers[NUM_AVATAR_TRIGGER_REACTIONS];
|
||||||
HFMJointShapeInfo hipsShapeInfo;
|
HFMJointShapeInfo hipsShapeInfo;
|
||||||
HFMJointShapeInfo spineShapeInfo;
|
HFMJointShapeInfo spineShapeInfo;
|
||||||
HFMJointShapeInfo spine1ShapeInfo;
|
HFMJointShapeInfo spine1ShapeInfo;
|
||||||
|
|
|
@ -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_DESKTOP_SPRINT_SPEED_SCALAR = 3.0f;
|
||||||
static const float AVATAR_HMD_SPRINT_SPEED_SCALAR = 2.0f;
|
static const float AVATAR_HMD_SPRINT_SPEED_SCALAR = 2.0f;
|
||||||
|
|
||||||
enum AvatarReaction {
|
enum AvatarTriggerReaction {
|
||||||
AVATAR_REACTION_POSITIVE = 0,
|
AVATAR_REACTION_POSITIVE = 0,
|
||||||
AVATAR_REACTION_NEGATIVE,
|
AVATAR_REACTION_NEGATIVE,
|
||||||
|
NUM_AVATAR_TRIGGER_REACTIONS
|
||||||
|
};
|
||||||
|
|
||||||
|
enum AvatarBeginEndReaction {
|
||||||
AVATAR_REACTION_RAISE_HAND,
|
AVATAR_REACTION_RAISE_HAND,
|
||||||
AVATAR_REACTION_APPLAUD,
|
AVATAR_REACTION_APPLAUD,
|
||||||
AVATAR_REACTION_POINT,
|
AVATAR_REACTION_POINT,
|
||||||
NUM_AVATAR_REACTIONS
|
NUM_AVATAR_BEGIN_END_REACTIONS
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_AvatarConstants_h
|
#endif // hifi_AvatarConstants_h
|
||||||
|
|
Loading…
Reference in a new issue