mirror of
https://github.com/overte-org/overte.git
synced 2025-07-10 21:18:54 +02:00
AnimStateMachine: added new State parameter interpType
interpType defines how the interpolation between two states is performed. * SnapshotBoth: Stores two snapshots, the previous animation before interpolation begins and the target state at the interTarget frame. Then during the interpolation period the two snapshots are interpolated to produce smooth motion between them. * SnapshotPrev: Stores a snapshot of the previous animation before interpolation begins. However the target state is evaluated dynamically. During the interpolation period the previous snapshot is interpolated with the target pose to produce smooth motion between them. This mode is useful for interping into a blended animation where the actual blend factor is not known at the start of the interp or is might change dramatically during the interp.
This commit is contained in:
parent
63df34541d
commit
a8e092272c
3 changed files with 85 additions and 9 deletions
|
@ -67,6 +67,16 @@ static AnimNode::Type stringToAnimNodeType(const QString& str) {
|
||||||
return AnimNode::Type::NumTypes;
|
return AnimNode::Type::NumTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static AnimStateMachine::InterpType stringToInterpType(const QString& str) {
|
||||||
|
if (str == "snapshotBoth") {
|
||||||
|
return AnimStateMachine::InterpType::SnapshotBoth;
|
||||||
|
} else if (str == "snapshotPrev") {
|
||||||
|
return AnimStateMachine::InterpType::SnapshotPrev;
|
||||||
|
} else {
|
||||||
|
return AnimStateMachine::InterpType::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::AbsoluteRotation: return "absoluteRotation";
|
case AnimManipulator::JointVar::Type::AbsoluteRotation: return "absoluteRotation";
|
||||||
|
@ -465,9 +475,11 @@ bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj,
|
||||||
READ_STRING(id, stateObj, nodeId, jsonUrl, false);
|
READ_STRING(id, stateObj, nodeId, jsonUrl, false);
|
||||||
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(interpTargetVar, stateObj);
|
READ_OPTIONAL_STRING(interpTargetVar, stateObj);
|
||||||
READ_OPTIONAL_STRING(interpDurationVar, stateObj);
|
READ_OPTIONAL_STRING(interpDurationVar, stateObj);
|
||||||
|
READ_OPTIONAL_STRING(interpTypeVar, stateObj);
|
||||||
|
|
||||||
auto iter = childMap.find(id);
|
auto iter = childMap.find(id);
|
||||||
if (iter == childMap.end()) {
|
if (iter == childMap.end()) {
|
||||||
|
@ -475,7 +487,16 @@ bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto statePtr = std::make_shared<AnimStateMachine::State>(id, iter->second, interpTarget, interpDuration);
|
AnimStateMachine::InterpType interpTypeEnum = AnimStateMachine::InterpType::SnapshotBoth; // default value
|
||||||
|
if (!interpType.isEmpty()) {
|
||||||
|
interpTypeEnum = stringToInterpType(interpType);
|
||||||
|
if (interpTypeEnum == AnimStateMachine::InterpType::NumTypes) {
|
||||||
|
qCCritical(animation) << "AnimNodeLoader, bad interpType on stateMachine state, nodeId = " << nodeId << "stateId =" << id << "url = " << jsonUrl.toDisplayString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto statePtr = std::make_shared<AnimStateMachine::State>(id, iter->second, interpTarget, interpDuration, interpTypeEnum);
|
||||||
assert(statePtr);
|
assert(statePtr);
|
||||||
|
|
||||||
if (!interpTargetVar.isEmpty()) {
|
if (!interpTargetVar.isEmpty()) {
|
||||||
|
@ -484,6 +505,9 @@ bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj,
|
||||||
if (!interpDurationVar.isEmpty()) {
|
if (!interpDurationVar.isEmpty()) {
|
||||||
statePtr->setInterpDurationVar(interpDurationVar);
|
statePtr->setInterpDurationVar(interpDurationVar);
|
||||||
}
|
}
|
||||||
|
if (!interpTypeVar.isEmpty()) {
|
||||||
|
statePtr->setInterpTypeVar(interpTypeVar);
|
||||||
|
}
|
||||||
|
|
||||||
smNode->addState(statePtr);
|
smNode->addState(statePtr);
|
||||||
stateMap.insert(StateMap::value_type(statePtr->getID(), statePtr));
|
stateMap.insert(StateMap::value_type(statePtr->getID(), statePtr));
|
||||||
|
|
|
@ -52,8 +52,25 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, fl
|
||||||
if (_duringInterp) {
|
if (_duringInterp) {
|
||||||
_alpha += _alphaVel * dt;
|
_alpha += _alphaVel * dt;
|
||||||
if (_alpha < 1.0f) {
|
if (_alpha < 1.0f) {
|
||||||
if (_poses.size() > 0 && _nextPoses.size() > 0 && _prevPoses.size() > 0) {
|
AnimPoseVec* nextPoses = nullptr;
|
||||||
::blend(_poses.size(), &_prevPoses[0], &_nextPoses[0], _alpha, &_poses[0]);
|
AnimPoseVec* prevPoses = nullptr;
|
||||||
|
AnimPoseVec localNextPoses;
|
||||||
|
if (_interpType == InterpType::SnapshotBoth) {
|
||||||
|
// interp between both snapshots
|
||||||
|
prevPoses = &_prevPoses;
|
||||||
|
nextPoses = &_nextPoses;
|
||||||
|
} else if (_interpType == InterpType::SnapshotPrev) {
|
||||||
|
// interp between the prev snapshot and evaluated next target.
|
||||||
|
// this is useful for interping into a blend
|
||||||
|
localNextPoses = currentStateNode->evaluate(animVars, dt, triggersOut);
|
||||||
|
prevPoses = &_prevPoses;
|
||||||
|
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]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_duringInterp = false;
|
_duringInterp = false;
|
||||||
|
@ -86,16 +103,32 @@ void AnimStateMachine::switchState(const AnimVariantMap& animVars, State::Pointe
|
||||||
_alpha = 0.0f;
|
_alpha = 0.0f;
|
||||||
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;
|
||||||
_prevPoses = _poses;
|
_interpType = (InterpType)animVars.lookup(desiredState->_interpTypeVar, (int)desiredState->_interpType);
|
||||||
nextStateNode->setCurrentFrame(desiredState->_interpTarget);
|
|
||||||
|
|
||||||
// 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;
|
||||||
Triggers triggers;
|
Triggers triggers;
|
||||||
|
|
||||||
|
if (_interpType == InterpType::SnapshotBoth) {
|
||||||
|
// snapshot previous pose.
|
||||||
|
_prevPoses = _poses;
|
||||||
|
// snapshot next pose at the target frame.
|
||||||
|
nextStateNode->setCurrentFrame(desiredState->_interpTarget);
|
||||||
_nextPoses = nextStateNode->evaluate(animVars, dt, triggers);
|
_nextPoses = nextStateNode->evaluate(animVars, dt, triggers);
|
||||||
|
} else if (_interpType == InterpType::SnapshotPrev) {
|
||||||
|
// snapshot previoius pose
|
||||||
|
_prevPoses = _poses;
|
||||||
|
// no need to evaluate _nextPoses we will do it dynamically during the interp,
|
||||||
|
// however we need to set the current frame.
|
||||||
|
nextStateNode->setCurrentFrame(desiredState->_interpTarget - duration);
|
||||||
|
} else {
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
#if WANT_DEBUG
|
#if WANT_DEBUG
|
||||||
qCDebug(animation) << "AnimStateMachine::switchState:" << _currentState->getID() << "->" << desiredState->getID() << "duration =" << duration << "targetFrame =" << desiredState->_interpTarget;
|
qCDebug(animation) << "AnimStateMachine::switchState:" << _currentState->getID() << "->" << desiredState->getID() << "duration =" << duration << "targetFrame =" << desiredState->_interpTarget << "interpType = " << (int)_interpType;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
_currentState = desiredState;
|
_currentState = desiredState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,13 +31,27 @@
|
||||||
// visible after interpolation is complete.
|
// visible after interpolation is complete.
|
||||||
// * interpDuration - (frames) The total length of time it will take to interp between the current pose and the
|
// * interpDuration - (frames) The total length of time it will take to interp between the current pose and the
|
||||||
// interpTarget frame.
|
// interpTarget frame.
|
||||||
|
// * interpType - How the interpolation is performed.
|
||||||
|
// * SnapshotBoth: Stores two snapshots, the previous animation before interpolation begins and the target state at the
|
||||||
|
// interTarget frame. Then during the interpolation period the two snapshots are interpolated to produce smooth motion between them.
|
||||||
|
// * SnapshotPrev: Stores a snapshot of the previous animation before interpolation begins. However the target state is
|
||||||
|
// evaluated dynamically. During the interpolation period the previous snapshot is interpolated with the target pose
|
||||||
|
// to produce smooth motion between them. This mode is useful for interping into a blended animation where the actual
|
||||||
|
// blend factor is not known at the start of the interp or is might change dramatically during the interp.
|
||||||
|
|
||||||
class AnimStateMachine : public AnimNode {
|
class AnimStateMachine : public AnimNode {
|
||||||
public:
|
public:
|
||||||
friend class AnimNodeLoader;
|
friend class AnimNodeLoader;
|
||||||
friend bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& nodeId, const QUrl& jsonUrl);
|
friend bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& nodeId, const QUrl& jsonUrl);
|
||||||
|
|
||||||
|
enum class InterpType {
|
||||||
|
SnapshotBoth = 0,
|
||||||
|
SnapshotPrev,
|
||||||
|
NumTypes
|
||||||
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
class State {
|
class State {
|
||||||
public:
|
public:
|
||||||
friend AnimStateMachine;
|
friend AnimStateMachine;
|
||||||
|
@ -55,14 +69,16 @@ protected:
|
||||||
State::Pointer _state;
|
State::Pointer _state;
|
||||||
};
|
};
|
||||||
|
|
||||||
State(const QString& id, int childIndex, float interpTarget, float interpDuration) :
|
State(const QString& id, int childIndex, float interpTarget, float interpDuration, InterpType interpType) :
|
||||||
_id(id),
|
_id(id),
|
||||||
_childIndex(childIndex),
|
_childIndex(childIndex),
|
||||||
_interpTarget(interpTarget),
|
_interpTarget(interpTarget),
|
||||||
_interpDuration(interpDuration) {}
|
_interpDuration(interpDuration),
|
||||||
|
_interpType(interpType) {}
|
||||||
|
|
||||||
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; }
|
||||||
|
void setInterpTypeVar(const QString& interpTypeVar) { _interpTypeVar = interpTypeVar; }
|
||||||
|
|
||||||
int getChildIndex() const { return _childIndex; }
|
int getChildIndex() const { return _childIndex; }
|
||||||
const QString& getID() const { return _id; }
|
const QString& getID() const { return _id; }
|
||||||
|
@ -78,9 +94,11 @@ protected:
|
||||||
int _childIndex;
|
int _childIndex;
|
||||||
float _interpTarget; // frames
|
float _interpTarget; // frames
|
||||||
float _interpDuration; // frames
|
float _interpDuration; // frames
|
||||||
|
InterpType _interpType;
|
||||||
|
|
||||||
QString _interpTargetVar;
|
QString _interpTargetVar;
|
||||||
QString _interpDurationVar;
|
QString _interpDurationVar;
|
||||||
|
QString _interpTypeVar;
|
||||||
|
|
||||||
std::vector<Transition> _transitions;
|
std::vector<Transition> _transitions;
|
||||||
|
|
||||||
|
@ -115,6 +133,7 @@ protected:
|
||||||
|
|
||||||
// interpolation state
|
// interpolation state
|
||||||
bool _duringInterp = false;
|
bool _duringInterp = false;
|
||||||
|
InterpType _interpType { InterpType::SnapshotBoth };
|
||||||
float _alphaVel = 0.0f;
|
float _alphaVel = 0.0f;
|
||||||
float _alpha = 0.0f;
|
float _alpha = 0.0f;
|
||||||
AnimPoseVec _prevPoses;
|
AnimPoseVec _prevPoses;
|
||||||
|
|
Loading…
Reference in a new issue