Support for EvaluteBoth interpType and easing

This commit is contained in:
Anthony J. Thibault 2019-07-31 15:00:38 -07:00
parent ee4bb233b9
commit ec08139a46
7 changed files with 230 additions and 11 deletions

View file

@ -29,6 +29,7 @@
#include "AnimTwoBoneIK.h"
#include "AnimSplineIK.h"
#include "AnimPoleVectorConstraint.h"
#include "AnimUtil.h"
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);
@ -91,6 +92,8 @@ static AnimStateMachine::InterpType stringToInterpType(const QString& str) {
return AnimStateMachine::InterpType::SnapshotBoth;
} else if (str == "snapshotPrev") {
return AnimStateMachine::InterpType::SnapshotPrev;
} else if (str == "evaluateBoth") {
return AnimStateMachine::InterpType::EvaluateBoth;
} else {
return AnimStateMachine::InterpType::NumTypes;
}
@ -101,11 +104,61 @@ static AnimRandomSwitch::InterpType stringToRandomInterpType(const QString& str)
return AnimRandomSwitch::InterpType::SnapshotBoth;
} else if (str == "snapshotPrev") {
return AnimRandomSwitch::InterpType::SnapshotPrev;
} else if (str == "evaluateBoth") {
return AnimRandomSwitch::InterpType::EvaluateBoth;
} else {
return AnimRandomSwitch::InterpType::NumTypes;
}
}
static EasingType stringToEasingType(const QString& str) {
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) {
switch (type) {
case AnimManipulator::JointVar::Type::Absolute: return "absolute";
@ -723,6 +776,7 @@ bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj,
READ_FLOAT(interpTarget, stateObj, nodeId, jsonUrl, false);
READ_FLOAT(interpDuration, stateObj, nodeId, jsonUrl, false);
READ_OPTIONAL_STRING(interpType, stateObj);
READ_OPTIONAL_STRING(easingType, stateObj);
READ_OPTIONAL_STRING(interpTargetVar, stateObj);
READ_OPTIONAL_STRING(interpDurationVar, stateObj);
@ -743,7 +797,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);
if (!interpTargetVar.isEmpty()) {
@ -845,6 +908,7 @@ bool processRandomSwitchStateMachineNode(AnimNode::Pointer node, const QJsonObje
READ_FLOAT(interpTarget, stateObj, nodeId, jsonUrl, false);
READ_FLOAT(interpDuration, stateObj, nodeId, jsonUrl, false);
READ_OPTIONAL_STRING(interpType, stateObj);
READ_OPTIONAL_STRING(easingType, stateObj);
READ_FLOAT(priority, stateObj, nodeId, jsonUrl, false);
READ_BOOL(resume, stateObj, nodeId, jsonUrl, false);
@ -867,7 +931,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) {
smNode->addToPrioritySum(priority);
}

View file

@ -89,6 +89,7 @@ const AnimPoseVec& AnimRandomSwitch::evaluate(const AnimVariantMap& animVars, co
assert(_currentState);
auto currentStateNode = _children[_currentState->getChildIndex()];
auto previousStateNode = _children[_previousState->getChildIndex()];
assert(currentStateNode);
if (_duringInterp) {
@ -97,6 +98,7 @@ const AnimPoseVec& AnimRandomSwitch::evaluate(const AnimVariantMap& animVars, co
AnimPoseVec* nextPoses = nullptr;
AnimPoseVec* prevPoses = nullptr;
AnimPoseVec localNextPoses;
AnimPoseVec localPrevPoses;
if (_interpType == InterpType::SnapshotBoth) {
// interp between both snapshots
prevPoses = &_prevPoses;
@ -107,13 +109,18 @@ const AnimPoseVec& AnimRandomSwitch::evaluate(const AnimVariantMap& animVars, co
localNextPoses = currentStateNode->evaluate(animVars, context, dt, triggersOut);
prevPoses = &_prevPoses;
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 {
assert(false);
}
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 {
_duringInterp = false;
_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));
_alphaVel = FRAMES_PER_SECOND / duration;
_interpType = (InterpType)animVars.lookup(desiredState->_interpTypeVar, (int)desiredState->_interpType);
_easingType = desiredState->_easingType;
// because dt is 0, we should not encounter any triggers
const float dt = 0.0f;

View file

@ -14,6 +14,7 @@
#include <string>
#include <vector>
#include "AnimNode.h"
#include "AnimUtil.h"
// Random Switch State Machine for random transitioning between children AnimNodes
//
@ -51,6 +52,7 @@ public:
enum class InterpType {
SnapshotBoth = 0,
SnapshotPrev,
EvaluateBoth,
NumTypes
};
@ -73,12 +75,13 @@ protected:
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),
_childIndex(childIndex),
_interpTarget(interpTarget),
_interpDuration(interpDuration),
_interpType(interpType),
_easingType(easingType),
_priority(priority),
_resume(resume){
}
@ -106,6 +109,7 @@ protected:
float _interpTarget; // frames
float _interpDuration; // frames
InterpType _interpType;
EasingType _easingType;
float _priority {0.0f};
bool _resume {false};
@ -154,7 +158,8 @@ protected:
int _randomSwitchEvaluationCount { 0 };
// interpolation state
bool _duringInterp = false;
InterpType _interpType{ InterpType::SnapshotPrev };
InterpType _interpType { InterpType::SnapshotPrev };
EasingType _easingType { EasingType_Linear };
float _alphaVel = 0.0f;
float _alpha = 0.0f;
AnimPoseVec _prevPoses;

View file

@ -48,6 +48,7 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co
assert(_currentState);
auto currentStateNode = _children[_currentState->getChildIndex()];
auto previousStateNode = _children[_previousState->getChildIndex()];
assert(currentStateNode);
if (_duringInterp) {
@ -56,6 +57,8 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co
AnimPoseVec* nextPoses = nullptr;
AnimPoseVec* prevPoses = nullptr;
AnimPoseVec localNextPoses;
AnimPoseVec localPrevPoses;
if (_interpType == InterpType::SnapshotBoth) {
// interp between both snapshots
prevPoses = &_prevPoses;
@ -66,13 +69,18 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co
localNextPoses = currentStateNode->evaluate(animVars, context, dt, triggersOut);
prevPoses = &_prevPoses;
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 {
assert(false);
}
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 {
_duringInterp = false;
_prevPoses.clear();
@ -125,6 +133,7 @@ void AnimStateMachine::switchState(const AnimVariantMap& animVars, const AnimCon
float duration = std::max(0.001f, animVars.lookup(desiredState->_interpDurationVar, desiredState->_interpDuration));
_alphaVel = FRAMES_PER_SECOND / duration;
_interpType = (InterpType)animVars.lookup(desiredState->_interpTypeVar, (int)desiredState->_interpType);
_easingType = desiredState->_easingType;
// because dt is 0, we should not encounter any triggers
const float dt = 0.0f;

View file

@ -14,6 +14,7 @@
#include <string>
#include <vector>
#include "AnimNode.h"
#include "AnimUtil.h"
// State Machine for transitioning between children AnimNodes
//
@ -47,6 +48,7 @@ public:
enum class InterpType {
SnapshotBoth = 0,
SnapshotPrev,
EvaluateBoth,
NumTypes
};
@ -69,12 +71,13 @@ protected:
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),
_childIndex(childIndex),
_interpTarget(interpTarget),
_interpDuration(interpDuration),
_interpType(interpType) {}
_interpType(interpType),
_easingType(easingType) {}
void setInterpTargetVar(const QString& interpTargetVar) { _interpTargetVar = interpTargetVar; }
void setInterpDurationVar(const QString& interpDurationVar) { _interpDurationVar = interpDurationVar; }
@ -95,6 +98,7 @@ protected:
float _interpTarget; // frames
float _interpDuration; // frames
InterpType _interpType;
EasingType _easingType;
QString _interpTargetVar;
QString _interpDurationVar;
@ -135,6 +139,7 @@ protected:
// interpolation state
bool _duringInterp = false;
InterpType _interpType { InterpType::SnapshotPrev };
EasingType _easingType { EasingType_Linear };
float _alphaVel = 0.0f;
float _alpha = 0.0f;
AnimPoseVec _prevPoses;

View file

@ -211,3 +211,95 @@ bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose
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 - cos(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;
}
break;
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;
}
break;
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;
}
break;
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;
}
break;
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.5) {
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;
}
break;
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);
}
break;
default:
return alpha;
}
}

View file

@ -128,10 +128,37 @@ protected:
bool _snapshotValid { false };
};
// 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
// 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);
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