AnimBlendLinear: bugfixes for sync flag added timeScale

This commit is contained in:
Anthony J. Thibault 2015-10-20 10:36:37 -07:00
parent 0c36180e2f
commit 11f2d29bf8
6 changed files with 87 additions and 41 deletions

View file

@ -191,6 +191,7 @@
"data": { "data": {
"alpha": 0.0, "alpha": 0.0,
"sync": false, "sync": false,
"timeScale": 1.0,
"alphaVar": "rightHandGrabBlend" "alphaVar": "rightHandGrabBlend"
}, },
"children": [ "children": [
@ -341,6 +342,7 @@
"data": { "data": {
"alpha": 0.0, "alpha": 0.0,
"sync": false, "sync": false,
"timeScale": 1.0,
"alphaVar": "leftHandGrabBlend" "alphaVar": "leftHandGrabBlend"
}, },
"children": [ "children": [
@ -525,16 +527,39 @@
}, },
{ {
"id": "walkFwd", "id": "walkFwd",
"type": "clip", "type": "blendLinear",
"data": { "data": {
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx", "alpha": 0.0,
"startFrame": 0.0, "sync": true,
"endFrame": 35.0,
"timeScale": 1.0, "timeScale": 1.0,
"loopFlag": true,
"timeScaleVar": "walkTimeScale" "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", "id": "walkBwd",

View file

@ -14,10 +14,11 @@
#include "AnimUtil.h" #include "AnimUtil.h"
#include "AnimClip.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), AnimNode(AnimNode::Type::BlendLinear, id),
_alpha(alpha), _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) { const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) {
_alpha = animVars.lookup(_alphaVar, _alpha); _alpha = animVars.lookup(_alphaVar, _alpha);
_timeScale = animVars.lookup(_timeScaleVar, _timeScale);
if (_children.size() == 0) { if (_children.size() == 0) {
for (auto&& pose : _poses) { for (auto&& pose : _poses) {
@ -42,13 +44,11 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, flo
size_t nextPoseIndex = glm::ceil(clampedAlpha); size_t nextPoseIndex = glm::ceil(clampedAlpha);
float alpha = glm::fract(clampedAlpha); float alpha = glm::fract(clampedAlpha);
float prevPoseDeltaTime = dt;
float nextPoseDeltaTime = dt;
if (_sync) { 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; return _poses;
} }
@ -59,15 +59,14 @@ const AnimPoseVec& AnimBlendLinear::getPosesInternal() const {
} }
void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha, void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha,
size_t prevPoseIndex, size_t nextPoseIndex, size_t prevPoseIndex, size_t nextPoseIndex, float dt) {
float prevPoseDeltaTime, float nextPoseDeltaTime) {
if (prevPoseIndex == nextPoseIndex) { if (prevPoseIndex == nextPoseIndex) {
// this can happen if alpha is on an integer boundary // 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 { } else {
// need to eval and blend between two children. // need to eval and blend between two children.
auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, prevPoseDeltaTime, triggersOut); auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, dt, triggersOut);
auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, nextPoseDeltaTime, triggersOut); auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, dt, triggersOut);
if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) { if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) {
_poses.resize(prevPoses.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, void AnimBlendLinear::setSyncAndAccumulateTime(float dt, size_t prevPoseIndex, size_t nextPoseIndex, Triggers& triggersOut) {
float* prevPoseDeltaTime, float* nextPoseDeltaTime,
Triggers& triggersOut) {
std::vector<float> offsets(_children.size(), 0.0f); std::vector<float> offsets(_children.size(), 0.0f);
std::vector<float> timeScales(_children.size(), 1.0f); std::vector<float> 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. // abort if we find a child that is NOT a clipNode.
if (_children[i]->getType() != AnimNode::Type::Clip) { if (_children[i]->getType() != AnimNode::Type::Clip) {
// TODO: FIXME: make sync this work for other node types. // TODO: FIXME: make sync this work for other node types.
*prevPoseDeltaTime = dt;
*nextPoseDeltaTime = dt;
return; return;
} }
auto clipNode = std::dynamic_pointer_cast<AnimClip>(_children[i]); auto clipNode = std::dynamic_pointer_cast<AnimClip>(_children[i]);
assert(clipNode); assert(clipNode);
if (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<AnimClip>(_children[prevPoseIndex]); auto prevClipNode = std::dynamic_pointer_cast<AnimClip>(_children[prevPoseIndex]);
float prevTimeScale = (prevClipNode->getEndFrame() - prevClipNode->getStartFrame()) / averageLength; float prevLength = (prevClipNode->getEndFrame() - prevClipNode->getStartFrame()) + 1.0f;
float prevOffset = prevClipNode->getStartFrame(); 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<AnimClip>(_children[nextPoseIndex]); auto nextClipNode = std::dynamic_pointer_cast<AnimClip>(_children[nextPoseIndex]);
float nextTimeScale = (nextClipNode->getEndFrame() - nextClipNode->getStartFrame()) / averageLength; float nextLength = (nextClipNode->getEndFrame() - nextClipNode->getStartFrame()) + 1.0f;
float nextOffset = nextClipNode->getStartFrame(); 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; const bool LOOP_FLAG = true;
_syncFrame = ::accumulateTime(0.0f, averageLength, _timeScale, _syncFrame, dt, LOOP_FLAG, _id, triggersOut); _syncFrame = ::accumulateTime(START_FRAME, _averageLength, _timeScale, _syncFrame, dt, LOOP_FLAG, _id, triggersOut);
*prevPoseDeltaTime = prevTimeScale;
*nextPoseDeltaTime = nextTimeScale;
} }
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);
}

View file

@ -30,32 +30,35 @@ class AnimBlendLinear : public AnimNode {
public: public:
friend class AnimTests; 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 ~AnimBlendLinear() override;
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override;
void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; } void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; }
void setTimeScaleVar(const QString& timeScaleVar) { _timeScaleVar = timeScaleVar; }
protected: protected:
// for AnimDebugDraw rendering // for AnimDebugDraw rendering
virtual const AnimPoseVec& getPosesInternal() const override; virtual const AnimPoseVec& getPosesInternal() const override;
void evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha, void evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha,
size_t prevPoseIndex, size_t nextPoseIndex, size_t prevPoseIndex, size_t nextPoseIndex, float dt);
float prevPoseDeltaTime, float nextPoseDeltaTime); void setSyncAndAccumulateTime(float dt, size_t prevPoseIndex, size_t nextPoseIndex, Triggers& triggersOut);
void setSyncFrameAndComputeDeltaTime(float dt, size_t prevPoseIndex, size_t nextPoseIndex,
float* prevPoseDeltaTime, float* nextPoseDeltaTime, virtual void setCurrentFrameInternal(float frame) override;
Triggers& triggersOut);
AnimPoseVec _poses; AnimPoseVec _poses;
float _alpha; float _alpha;
bool _sync; bool _sync;
float _timeScale;
float _syncFrame = 0.0f; 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 _alphaVar;
QString _timeScaleVar;
// no copies // no copies
AnimBlendLinear(const AnimBlendLinear&) = delete; AnimBlendLinear(const AnimBlendLinear&) = delete;

View file

@ -81,7 +81,7 @@ void AnimClip::setCurrentFrameInternal(float frame) {
// 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;
_frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame * _timeScale, dt, _loopFlag, _id, triggers); _frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame, dt, _loopFlag, _id, triggers);
} }
void AnimClip::copyFromNetworkAnim() { void AnimClip::copyFromNetworkAnim() {

View file

@ -39,6 +39,9 @@ public:
float getStartFrame() const { return _startFrame; } float getStartFrame() const { return _startFrame; }
float getEndFrame() const { return _endFrame; } float getEndFrame() const { return _endFrame; }
void setTimeScale(float timeScale) { _timeScale = timeScale; }
float getTimeScale() const { return _timeScale; }
protected: protected:
void loadURL(const QString& url); void loadURL(const QString& url);

View file

@ -225,15 +225,21 @@ static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const Q
READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr); READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr);
READ_BOOL(sync, 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(alphaVar, jsonObj);
READ_OPTIONAL_STRING(timeScaleVar, jsonObj);
auto node = std::make_shared<AnimBlendLinear>(id, alpha, sync); auto node = std::make_shared<AnimBlendLinear>(id, alpha, sync, timeScale);
if (!alphaVar.isEmpty()) { if (!alphaVar.isEmpty()) {
node->setAlphaVar(alphaVar); node->setAlphaVar(alphaVar);
} }
if (!timeScaleVar.isEmpty()) {
node->setTimeScaleVar(timeScaleVar);
}
return node; return node;
} }