Added support for onDone and onLoop triggers.

This commit is contained in:
Anthony J. Thibault 2015-08-31 12:13:05 -07:00
parent 77b857031b
commit 9786954585
12 changed files with 50 additions and 37 deletions

View file

@ -176,7 +176,12 @@ void MyAvatar::update(float deltaTime) {
}
t += deltaTime;
_animNode->evaluate(_animVars, deltaTime);
AnimNode::Triggers triggers;
_animNode->evaluate(_animVars, deltaTime, triggers);
_animVars.clearTriggers();
for (auto& trigger : triggers) {
_animVars.setTrigger(trigger);
}
}
if (_referential) {

View file

@ -22,7 +22,7 @@ AnimBlendLinear::~AnimBlendLinear() {
}
const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, float dt) {
const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) {
_alpha = animVars.lookup(_alphaVar, _alpha);
@ -31,7 +31,7 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, flo
pose = AnimPose::identity;
}
} else if (_children.size() == 1) {
_poses = _children[0]->evaluate(animVars, dt);
_poses = _children[0]->evaluate(animVars, dt, triggersOut);
} else {
float clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1));
size_t prevPoseIndex = glm::floor(clampedAlpha);
@ -39,11 +39,11 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, flo
float alpha = glm::fract(clampedAlpha);
if (prevPoseIndex == nextPoseIndex) {
// this can happen if alpha is on an integer boundary
_poses = _children[prevPoseIndex]->evaluate(animVars, dt);
_poses = _children[prevPoseIndex]->evaluate(animVars, dt, triggersOut);
} else {
// need to eval and blend between two children.
auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, dt);
auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, dt);
auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, dt, triggersOut);
auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, dt, triggersOut);
if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) {
_poses.resize(prevPoses.size());

View file

@ -29,7 +29,7 @@ public:
AnimBlendLinear(const std::string& id, float alpha);
virtual ~AnimBlendLinear() override;
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt) override;
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override;
void setAlphaVar(const std::string& alphaVar) { _alphaVar = alphaVar; }

View file

@ -27,14 +27,14 @@ AnimClip::~AnimClip() {
}
const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, float dt) {
const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) {
// lookup parameters from animVars, using current instance variables as defaults.
_startFrame = animVars.lookup(_startFrameVar, _startFrame);
_endFrame = animVars.lookup(_endFrameVar, _endFrame);
_timeScale = animVars.lookup(_timeScaleVar, _timeScale);
_loopFlag = animVars.lookup(_loopFlagVar, _loopFlag);
_frame = accumulateTime(animVars.lookup(_frameVar, _frame), dt);
_frame = accumulateTime(animVars.lookup(_frameVar, _frame), dt, triggersOut);
// poll network anim to see if it's finished loading yet.
if (_networkAnim && _networkAnim->isLoaded() && _skeleton) {
@ -74,11 +74,13 @@ void AnimClip::loadURL(const std::string& url) {
}
void AnimClip::setCurrentFrameInternal(float frame) {
// because dt is 0, we should not encounter any triggers
const float dt = 0.0f;
_frame = accumulateTime(frame * _timeScale, dt);
Triggers triggers;
_frame = accumulateTime(frame * _timeScale, dt, triggers);
}
float AnimClip::accumulateTime(float frame, float dt) const {
float AnimClip::accumulateTime(float frame, float dt, Triggers& triggersOut) const {
const float startFrame = std::min(_startFrame, _endFrame);
if (startFrame == _endFrame) {
// when startFrame >= endFrame
@ -92,12 +94,12 @@ float AnimClip::accumulateTime(float frame, float dt) const {
if (framesRemaining >= framesTillEnd) {
if (_loopFlag) {
// anim loop
// TODO: trigger onLoop event
triggersOut.push_back(_id + "OnLoop");
framesRemaining -= framesTillEnd;
frame = startFrame;
} else {
// anim end
// TODO: trigger onDone event
triggersOut.push_back(_id + "OnDone");
frame = _endFrame;
framesRemaining = 0.0f;
}

View file

@ -27,7 +27,7 @@ public:
AnimClip(const std::string& id, const std::string& url, float startFrame, float endFrame, float timeScale, bool loopFlag);
virtual ~AnimClip() override;
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt) override;
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override;
void setStartFrameVar(const std::string& startFrameVar) { _startFrameVar = startFrameVar; }
void setEndFrameVar(const std::string& endFrameVar) { _endFrameVar = endFrameVar; }
@ -40,7 +40,7 @@ protected:
virtual void setCurrentFrameInternal(float frame) override;
float accumulateTime(float frame, float dt) const;
float accumulateTime(float frame, float dt, Triggers& triggersOut) const;
void copyFromNetworkAnim();
// for AnimDebugDraw rendering

View file

@ -30,6 +30,7 @@ class QJsonObject;
// * hierarchy accessors, for adding, removing and iterating over child AnimNodes
// * skeleton accessors, the skeleton is from the model whose bones we are going to manipulate
// * evaluate method, perform actual joint manipulations here and return result by reference.
// Also, append any triggers that are detected during evaluation.
class AnimNode {
public:
@ -41,6 +42,7 @@ public:
NumTypes
};
using Pointer = std::shared_ptr<AnimNode>;
using Triggers = std::vector<std::string>;
friend class AnimDebugDraw;
friend void buildChildMap(std::map<std::string, Pointer>& map, Pointer node);
@ -76,9 +78,9 @@ public:
AnimSkeleton::ConstPointer getSkeleton() const { return _skeleton; }
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt) = 0;
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, float dt, const AnimPoseVec& underPoses) {
return evaluate(animVars, dt);
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) = 0;
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) {
return evaluate(animVars, dt, triggersOut);
}
protected:

View file

@ -36,7 +36,7 @@ void AnimOverlay::buildBoneSet(BoneSet boneSet) {
}
}
const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, float dt) {
const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) {
// lookup parameters from animVars, using current instance variables as defaults.
// NOTE: switching bonesets can be an expensive operation, let's try to avoid it.
@ -48,8 +48,8 @@ const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, float d
_alpha = animVars.lookup(_alphaVar, _alpha);
if (_children.size() >= 2) {
auto underPoses = _children[1]->evaluate(animVars, dt);
auto overPoses = _children[0]->overlay(animVars, dt, underPoses);
auto underPoses = _children[1]->evaluate(animVars, dt, triggersOut);
auto overPoses = _children[0]->overlay(animVars, dt, triggersOut, underPoses);
if (underPoses.size() > 0 && underPoses.size() == overPoses.size()) {
_poses.resize(underPoses.size());

View file

@ -40,7 +40,7 @@ public:
AnimOverlay(const std::string& id, BoneSet boneSet, float alpha);
virtual ~AnimOverlay() override;
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt) override;
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override;
void setBoneSetVar(const std::string& boneSetVar) { _boneSetVar = boneSetVar; }
void setAlphaVar(const std::string& alphaVar) { _alphaVar = alphaVar; }

View file

@ -20,7 +20,7 @@ AnimStateMachine::~AnimStateMachine() {
}
const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, float dt) {
const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) {
std::string desiredStateID = animVars.lookup(_currentStateVar, _currentState->getID());
if (_currentState->getID() != desiredStateID) {
@ -60,7 +60,7 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, fl
}
}
if (!_duringInterp) {
_poses = currentStateNode->evaluate(animVars, dt);
_poses = currentStateNode->evaluate(animVars, dt, triggersOut);
}
return _poses;
}
@ -88,7 +88,11 @@ void AnimStateMachine::switchState(const AnimVariantMap& animVars, State::Pointe
_alphaVel = FRAMES_PER_SECOND / duration;
_prevPoses = _poses;
nextStateNode->setCurrentFrame(desiredState->_interpTarget);
_nextPoses = nextStateNode->evaluate(animVars, 0.0f);
// because dt is 0, we should not encounter any triggers
const float dt = 0.0f;
Triggers triggers;
_nextPoses = nextStateNode->evaluate(animVars, dt, triggers);
_currentState = desiredState;
}

View file

@ -77,7 +77,7 @@ public:
AnimStateMachine(const std::string& id);
virtual ~AnimStateMachine() override;
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt) override;
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override;
void setCurrentStateVar(std::string& currentStateVar) { _currentStateVar = currentStateVar; }

View file

@ -150,7 +150,7 @@ public:
void set(const std::string& key, const std::string& value) { _map[key] = AnimVariant(value); }
void setTrigger(const std::string& key) { _triggers.insert(key); }
void clearTirggers() { _triggers.clear(); }
void clearTriggers() { _triggers.clear(); }
protected:
std::map<std::string, AnimVariant> _map;

View file

@ -11,11 +11,11 @@
"interpTarget": 6,
"interpDuration": 6,
"transitions": [
{ "var": "isMoving", "state": "walk_fwd" }
{ "var": "isMoving", "state": "walkFwd" }
]
},
{
"id": "walk_fwd",
"id": "walkFwd",
"interpTarget": 6,
"interpDuration": 6,
"transitions": [
@ -34,7 +34,7 @@
},
"children": [
{
"id": "normal_idle",
"id": "normalIdle",
"type": "clip",
"data": {
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/idle.fbx",
@ -46,7 +46,7 @@
"children": []
},
{
"id": "other_idle",
"id": "otherIdle",
"type": "clip",
"data": {
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/idle.fbx",
@ -60,7 +60,7 @@
]
},
{
"id": "walk_fwd",
"id": "walkFwd",
"type": "clip",
"data": {
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx",
@ -72,7 +72,7 @@
"children": []
},
{
"id": "walk_bwd",
"id": "walkBwd",
"type": "clip",
"data": {
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_bwd.fbx",
@ -84,7 +84,7 @@
"children": []
},
{
"id": "turn_left",
"id": "turnLeft",
"type": "clip",
"data": {
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/turn_left.fbx",
@ -96,7 +96,7 @@
"children": []
},
{
"id": "turn_right",
"id": "turnRight",
"type": "clip",
"data": {
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/turn_right.fbx",
@ -108,7 +108,7 @@
"children": []
},
{
"id": "strafe_left",
"id": "strafeLeft",
"type": "clip",
"data": {
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/strafe_left.fbx",
@ -120,7 +120,7 @@
"children": []
},
{
"id": "strafe_right",
"id": "strafeRight",
"type": "clip",
"data": {
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/strafe_right.fbx",