From 11f2d29bf80ce439ac3c697d7cdaffceea03f7f5 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 20 Oct 2015 10:36:37 -0700 Subject: [PATCH] AnimBlendLinear: bugfixes for sync flag added timeScale --- .../defaultAvatar_full/avatar-animation.json | 37 +++++++++-- libraries/animation/src/AnimBlendLinear.cpp | 61 +++++++++++-------- libraries/animation/src/AnimBlendLinear.h | 17 +++--- libraries/animation/src/AnimClip.cpp | 2 +- libraries/animation/src/AnimClip.h | 3 + libraries/animation/src/AnimNodeLoader.cpp | 8 ++- 6 files changed, 87 insertions(+), 41 deletions(-) diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index be1abbc820..ea4ef63d16 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -191,6 +191,7 @@ "data": { "alpha": 0.0, "sync": false, + "timeScale": 1.0, "alphaVar": "rightHandGrabBlend" }, "children": [ @@ -341,6 +342,7 @@ "data": { "alpha": 0.0, "sync": false, + "timeScale": 1.0, "alphaVar": "leftHandGrabBlend" }, "children": [ @@ -525,16 +527,39 @@ }, { "id": "walkFwd", - "type": "clip", + "type": "blendLinear", "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx", - "startFrame": 0.0, - "endFrame": 35.0, + "alpha": 0.0, + "sync": true, "timeScale": 1.0, - "loopFlag": true, "timeScaleVar": "walkTimeScale" }, - "children": [] + "children": [ + { + "id": "walkFwdShort", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_short_fwd.fbx", + "startFrame": 0.0, + "endFrame": 39.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "walkFwdNormal", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx", + "startFrame": 0.0, + "endFrame": 35.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + } + ] }, { "id": "walkBwd", diff --git a/libraries/animation/src/AnimBlendLinear.cpp b/libraries/animation/src/AnimBlendLinear.cpp index 1e65ba2b36..1f9026de9d 100644 --- a/libraries/animation/src/AnimBlendLinear.cpp +++ b/libraries/animation/src/AnimBlendLinear.cpp @@ -14,10 +14,11 @@ #include "AnimUtil.h" #include "AnimClip.h" -AnimBlendLinear::AnimBlendLinear(const QString& id, float alpha, bool sync) : +AnimBlendLinear::AnimBlendLinear(const QString& id, float alpha, bool sync, float timeScale) : AnimNode(AnimNode::Type::BlendLinear, id), _alpha(alpha), - _sync(sync) { + _sync(sync), + _timeScale(timeScale) { } @@ -28,6 +29,7 @@ AnimBlendLinear::~AnimBlendLinear() { const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { _alpha = animVars.lookup(_alphaVar, _alpha); + _timeScale = animVars.lookup(_timeScaleVar, _timeScale); if (_children.size() == 0) { for (auto&& pose : _poses) { @@ -42,13 +44,11 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, flo size_t nextPoseIndex = glm::ceil(clampedAlpha); float alpha = glm::fract(clampedAlpha); - float prevPoseDeltaTime = dt; - float nextPoseDeltaTime = dt; if (_sync) { - setSyncFrameAndComputeDeltaTime(dt, prevPoseIndex, nextPoseIndex, &prevPoseDeltaTime, &nextPoseDeltaTime, triggersOut); + setSyncAndAccumulateTime(dt, prevPoseIndex, nextPoseIndex, triggersOut); } - evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevPoseDeltaTime, nextPoseDeltaTime); + evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, dt); } return _poses; } @@ -59,15 +59,14 @@ const AnimPoseVec& AnimBlendLinear::getPosesInternal() const { } void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha, - size_t prevPoseIndex, size_t nextPoseIndex, - float prevPoseDeltaTime, float nextPoseDeltaTime) { + size_t prevPoseIndex, size_t nextPoseIndex, float dt) { if (prevPoseIndex == nextPoseIndex) { // this can happen if alpha is on an integer boundary - _poses = _children[prevPoseIndex]->evaluate(animVars, prevPoseDeltaTime, triggersOut); + _poses = _children[prevPoseIndex]->evaluate(animVars, dt, triggersOut); } else { // need to eval and blend between two children. - auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, prevPoseDeltaTime, triggersOut); - auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, nextPoseDeltaTime, triggersOut); + 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()); @@ -77,9 +76,7 @@ void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, T } } -void AnimBlendLinear::setSyncFrameAndComputeDeltaTime(float dt, size_t prevPoseIndex, size_t nextPoseIndex, - float* prevPoseDeltaTime, float* nextPoseDeltaTime, - Triggers& triggersOut) { +void AnimBlendLinear::setSyncAndAccumulateTime(float dt, size_t prevPoseIndex, size_t nextPoseIndex, Triggers& triggersOut) { std::vector offsets(_children.size(), 0.0f); std::vector timeScales(_children.size(), 1.0f); @@ -88,33 +85,45 @@ void AnimBlendLinear::setSyncFrameAndComputeDeltaTime(float dt, size_t prevPoseI // abort if we find a child that is NOT a clipNode. if (_children[i]->getType() != AnimNode::Type::Clip) { // TODO: FIXME: make sync this work for other node types. - *prevPoseDeltaTime = dt; - *nextPoseDeltaTime = dt; return; } auto clipNode = std::dynamic_pointer_cast(_children[i]); assert(clipNode); if (clipNode) { - lengthSum += clipNode->getEndFrame() - clipNode->getStartFrame(); + lengthSum += (clipNode->getEndFrame() - clipNode->getStartFrame()) + 1.0f; } } - float averageLength = lengthSum / (float)_children.size(); + _averageLength = lengthSum / (float)_children.size(); + + float progress = (_syncFrame / _averageLength); auto prevClipNode = std::dynamic_pointer_cast(_children[prevPoseIndex]); - float prevTimeScale = (prevClipNode->getEndFrame() - prevClipNode->getStartFrame()) / averageLength; + float prevLength = (prevClipNode->getEndFrame() - prevClipNode->getStartFrame()) + 1.0f; float prevOffset = prevClipNode->getStartFrame(); - prevClipNode->setCurrentFrame(prevOffset + prevTimeScale / _syncFrame); + float prevFrame = prevOffset + (progress * prevLength); + float prevTimeScale = _timeScale * (_averageLength / prevLength); + prevClipNode->setTimeScale(prevTimeScale); + prevClipNode->setCurrentFrame(prevFrame); auto nextClipNode = std::dynamic_pointer_cast(_children[nextPoseIndex]); - float nextTimeScale = (nextClipNode->getEndFrame() - nextClipNode->getStartFrame()) / averageLength; + float nextLength = (nextClipNode->getEndFrame() - nextClipNode->getStartFrame()) + 1.0f; float nextOffset = nextClipNode->getStartFrame(); - nextClipNode->setCurrentFrame(nextOffset + nextTimeScale / _syncFrame); + float nextFrame = nextOffset + (progress * nextLength); + float nextTimeScale = _timeScale * (_averageLength / nextLength); + nextClipNode->setTimeScale(nextTimeScale); + nextClipNode->setCurrentFrame(nextFrame); + const float START_FRAME = 0.0f; const bool LOOP_FLAG = true; - _syncFrame = ::accumulateTime(0.0f, averageLength, _timeScale, _syncFrame, dt, LOOP_FLAG, _id, triggersOut); - - *prevPoseDeltaTime = prevTimeScale; - *nextPoseDeltaTime = nextTimeScale; + _syncFrame = ::accumulateTime(START_FRAME, _averageLength, _timeScale, _syncFrame, dt, LOOP_FLAG, _id, triggersOut); } +void AnimBlendLinear::setCurrentFrameInternal(float frame) { + // because dt is 0, we should not encounter any triggers + const float dt = 0.0f; + Triggers triggers; + const float START_FRAME = 0.0f; + const bool LOOP_FLAG = true; + _syncFrame = ::accumulateTime(START_FRAME, _averageLength, _timeScale, frame, dt, LOOP_FLAG, _id, triggers); +} diff --git a/libraries/animation/src/AnimBlendLinear.h b/libraries/animation/src/AnimBlendLinear.h index d8b4f52fb6..7def6be91d 100644 --- a/libraries/animation/src/AnimBlendLinear.h +++ b/libraries/animation/src/AnimBlendLinear.h @@ -30,32 +30,35 @@ class AnimBlendLinear : public AnimNode { public: friend class AnimTests; - AnimBlendLinear(const QString& id, float alpha, bool sync); + AnimBlendLinear(const QString& id, float alpha, bool sync, float timeScale); virtual ~AnimBlendLinear() override; virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; } + void setTimeScaleVar(const QString& timeScaleVar) { _timeScaleVar = timeScaleVar; } protected: // for AnimDebugDraw rendering virtual const AnimPoseVec& getPosesInternal() const override; void evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha, - size_t prevPoseIndex, size_t nextPoseIndex, - float prevPoseDeltaTime, float nextPoseDeltaTime); - void setSyncFrameAndComputeDeltaTime(float dt, size_t prevPoseIndex, size_t nextPoseIndex, - float* prevPoseDeltaTime, float* nextPoseDeltaTime, - Triggers& triggersOut); + size_t prevPoseIndex, size_t nextPoseIndex, float dt); + void setSyncAndAccumulateTime(float dt, size_t prevPoseIndex, size_t nextPoseIndex, Triggers& triggersOut); + + virtual void setCurrentFrameInternal(float frame) override; AnimPoseVec _poses; float _alpha; bool _sync; + float _timeScale; + float _syncFrame = 0.0f; - float _timeScale = 1.0f; // TODO: HOOK THIS UP TO AN ANIMVAR. + float _averageLength = 0.0f; // average length of child animations in frames. QString _alphaVar; + QString _timeScaleVar; // no copies AnimBlendLinear(const AnimBlendLinear&) = delete; diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 613194164f..8f50874ed3 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -81,7 +81,7 @@ void AnimClip::setCurrentFrameInternal(float frame) { // because dt is 0, we should not encounter any triggers const float dt = 0.0f; Triggers triggers; - _frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame * _timeScale, dt, _loopFlag, _id, triggers); + _frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame, dt, _loopFlag, _id, triggers); } void AnimClip::copyFromNetworkAnim() { diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index b7cd2861fa..36867e622b 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -39,6 +39,9 @@ public: float getStartFrame() const { return _startFrame; } float getEndFrame() const { return _endFrame; } + void setTimeScale(float timeScale) { _timeScale = timeScale; } + float getTimeScale() const { return _timeScale; } + protected: void loadURL(const QString& url); diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 4222364629..f9ebf2a630 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -225,15 +225,21 @@ static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const Q READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr); READ_BOOL(sync, jsonObj, id, jsonUrl, nullptr); + READ_FLOAT(timeScale, jsonObj, id, jsonUrl, nullptr); READ_OPTIONAL_STRING(alphaVar, jsonObj); + READ_OPTIONAL_STRING(timeScaleVar, jsonObj); - auto node = std::make_shared(id, alpha, sync); + auto node = std::make_shared(id, alpha, sync, timeScale); if (!alphaVar.isEmpty()) { node->setAlphaVar(alphaVar); } + if (!timeScaleVar.isEmpty()) { + node->setTimeScaleVar(timeScaleVar); + } + return node; }