From 001e4bc952553b61416e374a957543827768ae30 Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Wed, 21 Aug 2019 16:11:10 -0700 Subject: [PATCH 1/7] AnimClip now supports relative animations. Relative animations are animations that are rotation deltas from some base reference pose. They are calculated by multiplying each frame of each joint the inverse of that joints in the base reference pose. It is intended for use with additive blending. Added the following fields to AnimClip node. * isRelative (boolean) * baseURL (string) * baseFrame (int) --- libraries/animation/src/AnimClip.cpp | 265 ++++++++++++--------- libraries/animation/src/AnimClip.h | 11 +- libraries/animation/src/AnimNodeLoader.cpp | 6 +- libraries/animation/src/Rig.cpp | 2 +- 4 files changed, 173 insertions(+), 111 deletions(-) diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index e5edb46f69..3d045341ff 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -16,100 +16,6 @@ #include "AnimationLogging.h" #include "AnimUtil.h" - -AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag) : - AnimNode(AnimNode::Type::Clip, id), - _startFrame(startFrame), - _endFrame(endFrame), - _timeScale(timeScale), - _loopFlag(loopFlag), - _mirrorFlag(mirrorFlag), - _frame(startFrame) -{ - loadURL(url); -} - -AnimClip::~AnimClip() { - -} - -const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& 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); - _mirrorFlag = animVars.lookup(_mirrorFlagVar, _mirrorFlag); - float frame = animVars.lookup(_frameVar, _frame); - - _frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame, dt, _loopFlag, _id, triggersOut); - - // poll network anim to see if it's finished loading yet. - if (_networkAnim && _networkAnim->isLoaded() && _skeleton) { - // loading is complete, copy animation frames from network animation, then throw it away. - copyFromNetworkAnim(); - _networkAnim.reset(); - } - - if (_anim.size()) { - - // lazy creation of mirrored animation frames. - if (_mirrorFlag && _anim.size() != _mirrorAnim.size()) { - buildMirrorAnim(); - } - - int prevIndex = (int)glm::floor(_frame); - int nextIndex; - if (_loopFlag && _frame >= _endFrame) { - nextIndex = (int)glm::ceil(_startFrame); - } else { - nextIndex = (int)glm::ceil(_frame); - } - - // It can be quite possible for the user to set _startFrame and _endFrame to - // values before or past valid ranges. We clamp the frames here. - int frameCount = (int)_anim.size(); - prevIndex = std::min(std::max(0, prevIndex), frameCount - 1); - nextIndex = std::min(std::max(0, nextIndex), frameCount - 1); - - const AnimPoseVec& prevFrame = _mirrorFlag ? _mirrorAnim[prevIndex] : _anim[prevIndex]; - const AnimPoseVec& nextFrame = _mirrorFlag ? _mirrorAnim[nextIndex] : _anim[nextIndex]; - float alpha = glm::fract(_frame); - - ::blend(_poses.size(), &prevFrame[0], &nextFrame[0], alpha, &_poses[0]); - } - - processOutputJoints(triggersOut); - - return _poses; -} - -void AnimClip::loadURL(const QString& url) { - auto animCache = DependencyManager::get(); - _networkAnim = animCache->getAnimation(url); - _url = url; -} - -void AnimClip::setCurrentFrameInternal(float frame) { - // because dt is 0, we should not encounter any triggers - const float dt = 0.0f; - AnimVariantMap triggers; - _frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame + _startFrame, dt, _loopFlag, _id, triggers); -} - -static std::vector buildJointIndexMap(const AnimSkeleton& dstSkeleton, const AnimSkeleton& srcSkeleton) { - std::vector jointIndexMap; - int srcJointCount = srcSkeleton.getNumJoints(); - jointIndexMap.reserve(srcJointCount); - for (int srcJointIndex = 0; srcJointIndex < srcJointCount; srcJointIndex++) { - QString srcJointName = srcSkeleton.getJointName(srcJointIndex); - int dstJointIndex = dstSkeleton.nameToJointIndex(srcJointName); - jointIndexMap.push_back(dstJointIndex); - } - return jointIndexMap; -} - #ifdef USE_CUSTOM_ASSERT #undef ASSERT #define ASSERT(x) \ @@ -123,12 +29,44 @@ static std::vector buildJointIndexMap(const AnimSkeleton& dstSkeleton, cons #define ASSERT assert #endif -void AnimClip::copyFromNetworkAnim() { - assert(_networkAnim && _networkAnim->isLoaded() && _skeleton); - _anim.clear(); +static std::vector buildJointIndexMap(const AnimSkeleton& dstSkeleton, const AnimSkeleton& srcSkeleton) { + std::vector jointIndexMap; + int srcJointCount = srcSkeleton.getNumJoints(); + jointIndexMap.reserve(srcJointCount); + for (int srcJointIndex = 0; srcJointIndex < srcJointCount; srcJointIndex++) { + QString srcJointName = srcSkeleton.getJointName(srcJointIndex); + int dstJointIndex = dstSkeleton.nameToJointIndex(srcJointName); + jointIndexMap.push_back(dstJointIndex); + } + return jointIndexMap; +} - auto avatarSkeleton = getSkeleton(); - const HFMModel& animModel = _networkAnim->getHFMModel(); +static void bakeRelativeAnim(std::vector& anim, const AnimPoseVec& basePoses) { + + // invert all the basePoses + AnimPoseVec invBasePoses = basePoses; + for (auto&& invBasePose : invBasePoses) { + invBasePose = invBasePose.inverse(); + } + + // for each frame of the animation + for (auto&& animPoses : anim) { + ASSERT(animPoses.size() == basePoses.size()); + + // for each joint in animPoses + for (size_t i = 0; i < animPoses.size(); ++i) { + + // convert this AnimPose into a relative animation. + animPoses[i] = animPoses[i] * invBasePoses[i]; + } + } +} + +static std::vector copyAndRetargetFromNetworkAnim(AnimationPointer networkAnim, AnimSkeleton::ConstPointer avatarSkeleton) { + ASSERT(networkAnim && networkAnim->isLoaded() && avatarSkeleton); + std::vector anim; + + const HFMModel& animModel = networkAnim->getHFMModel(); AnimSkeleton animSkeleton(animModel); const int animJointCount = animSkeleton.getNumJoints(); const int avatarJointCount = avatarSkeleton->getNumJoints(); @@ -137,7 +75,7 @@ void AnimClip::copyFromNetworkAnim() { std::vector avatarToAnimJointIndexMap = buildJointIndexMap(animSkeleton, *avatarSkeleton); const int animFrameCount = animModel.animationFrames.size(); - _anim.resize(animFrameCount); + anim.resize(animFrameCount); // find the size scale factor for translation in the animation. float boneLengthScale = 1.0f; @@ -223,8 +161,8 @@ void AnimClip::copyFromNetworkAnim() { // convert avatar rotations into relative frame avatarSkeleton->convertAbsoluteRotationsToRelative(avatarRotations); - ASSERT(frame >= 0 && frame < (int)_anim.size()); - _anim[frame].reserve(avatarJointCount); + ASSERT(frame >= 0 && frame < (int)anim.size()); + anim[frame].reserve(avatarJointCount); for (int avatarJointIndex = 0; avatarJointIndex < avatarJointCount; avatarJointIndex++) { const AnimPose& avatarDefaultPose = avatarSkeleton->getRelativeDefaultPose(avatarJointIndex); @@ -251,14 +189,123 @@ void AnimClip::copyFromNetworkAnim() { // build the final pose ASSERT(avatarJointIndex >= 0 && avatarJointIndex < (int)avatarRotations.size()); - _anim[frame].push_back(AnimPose(relativeScale, avatarRotations[avatarJointIndex], relativeTranslation)); + anim[frame].push_back(AnimPose(relativeScale, avatarRotations[avatarJointIndex], relativeTranslation)); } } - // mirrorAnim will be re-built on demand, if needed. - _mirrorAnim.clear(); + return anim; +} - _poses.resize(avatarJointCount); +AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag, + bool isRelative, const QString& baseURL, float baseFrame) : + AnimNode(AnimNode::Type::Clip, id), + _startFrame(startFrame), + _endFrame(endFrame), + _timeScale(timeScale), + _loopFlag(loopFlag), + _mirrorFlag(mirrorFlag), + _frame(startFrame), + _isRelative(isRelative), + _baseFrame(baseFrame) +{ + loadURL(url); + + if (isRelative) { + auto animCache = DependencyManager::get(); + _baseNetworkAnim = animCache->getAnimation(baseURL); + _baseURL = baseURL; + } +} + +AnimClip::~AnimClip() { + +} + +const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& 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); + _mirrorFlag = animVars.lookup(_mirrorFlagVar, _mirrorFlag); + float frame = animVars.lookup(_frameVar, _frame); + + _frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame, dt, _loopFlag, _id, triggersOut); + + // poll network anim to see if it's finished loading yet. + if (_isRelative) { + if (_networkAnim && _networkAnim->isLoaded() && _skeleton) { + // loading is complete, copy & retarget animation. + _anim = copyAndRetargetFromNetworkAnim(_networkAnim, _skeleton); + + // we no longer need the actual animation resource anymore. + _networkAnim.reset(); + + // mirrorAnim will be re-built on demand, if needed. + _mirrorAnim.clear(); + + _poses.resize(_skeleton->getNumJoints()); + } + } else { + if (_networkAnim && _networkAnim->isLoaded() && _baseNetworkAnim && _baseNetworkAnim->isLoaded() && _skeleton) { + // loading is complete, copy & retarget animation. + _anim = copyAndRetargetFromNetworkAnim(_networkAnim, _skeleton); + + // we no longer need the actual animation resource anymore. + _networkAnim.reset(); + + // mirrorAnim will be re-built on demand, if needed. + _mirrorAnim.clear(); + + _poses.resize(_skeleton->getNumJoints()); + + // copy & retarget baseAnim! + auto baseAnim = copyAndRetargetFromNetworkAnim(_baseNetworkAnim, _skeleton); + + // make _anim relative to the baseAnim reference frame. + bakeRelativeAnim(_anim, baseAnim[(int)_baseFrame]); + } + } + + if (_anim.size()) { + + // lazy creation of mirrored animation frames. + if (_mirrorFlag && _anim.size() != _mirrorAnim.size()) { + buildMirrorAnim(); + } + + int prevIndex = (int)glm::floor(_frame); + int nextIndex; + if (_loopFlag && _frame >= _endFrame) { + nextIndex = (int)glm::ceil(_startFrame); + } else { + nextIndex = (int)glm::ceil(_frame); + } + + // It can be quite possible for the user to set _startFrame and _endFrame to + // values before or past valid ranges. We clamp the frames here. + int frameCount = (int)_anim.size(); + prevIndex = std::min(std::max(0, prevIndex), frameCount - 1); + nextIndex = std::min(std::max(0, nextIndex), frameCount - 1); + + const AnimPoseVec& prevFrame = _mirrorFlag ? _mirrorAnim[prevIndex] : _anim[prevIndex]; + const AnimPoseVec& nextFrame = _mirrorFlag ? _mirrorAnim[nextIndex] : _anim[nextIndex]; + float alpha = glm::fract(_frame); + + ::blend(_poses.size(), &prevFrame[0], &nextFrame[0], alpha, &_poses[0]); + } + + processOutputJoints(triggersOut); + + return _poses; +} + +void AnimClip::setCurrentFrameInternal(float frame) { + // because dt is 0, we should not encounter any triggers + const float dt = 0.0f; + AnimVariantMap triggers; + _frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame + _startFrame, dt, _loopFlag, _id, triggers); } void AnimClip::buildMirrorAnim() { @@ -275,3 +322,9 @@ void AnimClip::buildMirrorAnim() { const AnimPoseVec& AnimClip::getPosesInternal() const { return _poses; } + +void AnimClip::loadURL(const QString& url) { + auto animCache = DependencyManager::get(); + _networkAnim = animCache->getAnimation(url); + _url = url; +} diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index 92f4c01dcc..04a402356c 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -25,7 +25,8 @@ class AnimClip : public AnimNode { public: friend class AnimTests; - AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag); + AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag, + bool isRelative, const QString& baseURL, float baseFrame); virtual ~AnimClip() override; virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override; @@ -52,19 +53,20 @@ public: void setMirrorFlag(bool mirrorFlag) { _mirrorFlag = mirrorFlag; } float getFrame() const { return _frame; } - void loadURL(const QString& url); + protected: virtual void setCurrentFrameInternal(float frame) override; - void copyFromNetworkAnim(); void buildMirrorAnim(); // for AnimDebugDraw rendering virtual const AnimPoseVec& getPosesInternal() const override; AnimationPointer _networkAnim; + AnimationPointer _baseNetworkAnim; + AnimPoseVec _poses; // _anim[frame][joint] @@ -78,6 +80,9 @@ protected: bool _loopFlag; bool _mirrorFlag; float _frame; + bool _isRelative; + QString _baseURL; + float _baseFrame; QString _startFrameVar; QString _endFrameVar; diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index d43351fff9..9f223e08d1 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -374,6 +374,9 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& READ_FLOAT(timeScale, jsonObj, id, jsonUrl, nullptr); READ_BOOL(loopFlag, jsonObj, id, jsonUrl, nullptr); READ_OPTIONAL_BOOL(mirrorFlag, jsonObj, false); + READ_OPTIONAL_BOOL(isRelative, jsonObj, false); + READ_OPTIONAL_STRING(baseURL, jsonObj); + READ_OPTIONAL_FLOAT(baseFrame, jsonObj, 0.0f); READ_OPTIONAL_STRING(startFrameVar, jsonObj); READ_OPTIONAL_STRING(endFrameVar, jsonObj); @@ -381,11 +384,12 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& READ_OPTIONAL_STRING(loopFlagVar, jsonObj); READ_OPTIONAL_STRING(mirrorFlagVar, jsonObj); + // animation urls can be relative to the containing url document. auto tempUrl = QUrl(url); tempUrl = jsonUrl.resolved(tempUrl); - auto node = std::make_shared(id, tempUrl.toString(), startFrame, endFrame, timeScale, loopFlag, mirrorFlag); + auto node = std::make_shared(id, tempUrl.toString(), startFrame, endFrame, timeScale, loopFlag, mirrorFlag, isRelative, baseURL, baseFrame); if (!startFrameVar.isEmpty()) { node->setStartFrameVar(startFrameVar); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 211c54def8..2974212bf1 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -564,7 +564,7 @@ void Rig::overrideRoleAnimation(const QString& role, const QString& url, float f _origRoleAnimations[role] = node; const float REFERENCE_FRAMES_PER_SECOND = 30.0f; float timeScale = fps / REFERENCE_FRAMES_PER_SECOND; - auto clipNode = std::make_shared(role, url, firstFrame, lastFrame, timeScale, loop, false); + auto clipNode = std::make_shared(role, url, firstFrame, lastFrame, timeScale, loop, false, false, "", 0.0f); _roleAnimStates[role] = { role, url, fps, loop, firstFrame, lastFrame }; AnimNode::Pointer parent = node->getParent(); parent->replaceChild(node, clipNode); From f1afc9e8731692b8a757b9cbd4709aa563f3b9a1 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 23 Aug 2019 08:46:14 -0700 Subject: [PATCH 2/7] Bug fix & rename isRelative to relativeFlag --- libraries/animation/src/AnimClip.cpp | 9 +++++---- libraries/animation/src/AnimClip.h | 4 ++-- libraries/animation/src/AnimNodeLoader.cpp | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 3d045341ff..d58c3511cd 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -197,7 +197,7 @@ static std::vector copyAndRetargetFromNetworkAnim(AnimationPointer } AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag, - bool isRelative, const QString& baseURL, float baseFrame) : + bool relativeFlag, const QString& baseURL, float baseFrame) : AnimNode(AnimNode::Type::Clip, id), _startFrame(startFrame), _endFrame(endFrame), @@ -205,12 +205,12 @@ AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, floa _loopFlag(loopFlag), _mirrorFlag(mirrorFlag), _frame(startFrame), - _isRelative(isRelative), + _relativeFlag(relativeFlag), _baseFrame(baseFrame) { loadURL(url); - if (isRelative) { + if (relativeFlag) { auto animCache = DependencyManager::get(); _baseNetworkAnim = animCache->getAnimation(baseURL); _baseURL = baseURL; @@ -234,7 +234,7 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, const Anim _frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame, dt, _loopFlag, _id, triggersOut); // poll network anim to see if it's finished loading yet. - if (_isRelative) { + if (!_relativeFlag) { if (_networkAnim && _networkAnim->isLoaded() && _skeleton) { // loading is complete, copy & retarget animation. _anim = copyAndRetargetFromNetworkAnim(_networkAnim, _skeleton); @@ -256,6 +256,7 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, const Anim _networkAnim.reset(); // mirrorAnim will be re-built on demand, if needed. + // TODO: handle mirrored relative animations. _mirrorAnim.clear(); _poses.resize(_skeleton->getNumJoints()); diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index 04a402356c..2ecaffb8b1 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -26,7 +26,7 @@ public: friend class AnimTests; AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag, - bool isRelative, const QString& baseURL, float baseFrame); + bool relativeFlag, const QString& baseURL, float baseFrame); virtual ~AnimClip() override; virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override; @@ -80,7 +80,7 @@ protected: bool _loopFlag; bool _mirrorFlag; float _frame; - bool _isRelative; + bool _relativeFlag; QString _baseURL; float _baseFrame; diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 9f223e08d1..ebd8a6de5b 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -374,7 +374,7 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& READ_FLOAT(timeScale, jsonObj, id, jsonUrl, nullptr); READ_BOOL(loopFlag, jsonObj, id, jsonUrl, nullptr); READ_OPTIONAL_BOOL(mirrorFlag, jsonObj, false); - READ_OPTIONAL_BOOL(isRelative, jsonObj, false); + READ_OPTIONAL_BOOL(relativeFlag, jsonObj, false); READ_OPTIONAL_STRING(baseURL, jsonObj); READ_OPTIONAL_FLOAT(baseFrame, jsonObj, 0.0f); @@ -389,7 +389,7 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& auto tempUrl = QUrl(url); tempUrl = jsonUrl.resolved(tempUrl); - auto node = std::make_shared(id, tempUrl.toString(), startFrame, endFrame, timeScale, loopFlag, mirrorFlag, isRelative, baseURL, baseFrame); + auto node = std::make_shared(id, tempUrl.toString(), startFrame, endFrame, timeScale, loopFlag, mirrorFlag, relativeFlag, baseURL, baseFrame); if (!startFrameVar.isEmpty()) { node->setStartFrameVar(startFrameVar); From ed3ba876a840f2e670e209e41798b5fdf9e1957e Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 26 Aug 2019 17:02:15 -0700 Subject: [PATCH 3/7] AddRelative blending support (Absolute is still not working) --- libraries/animation/src/AnimBlendLinear.cpp | 23 +++++++++++++++--- libraries/animation/src/AnimBlendLinear.h | 3 ++- libraries/animation/src/AnimContext.h | 7 ++++++ libraries/animation/src/AnimNodeLoader.cpp | 26 +++++++++++++++++++-- libraries/animation/src/AnimUtil.cpp | 21 ++++++++++++++++- libraries/animation/src/AnimUtil.h | 3 +++ 6 files changed, 76 insertions(+), 7 deletions(-) diff --git a/libraries/animation/src/AnimBlendLinear.cpp b/libraries/animation/src/AnimBlendLinear.cpp index 17ca88123f..c740de72cb 100644 --- a/libraries/animation/src/AnimBlendLinear.cpp +++ b/libraries/animation/src/AnimBlendLinear.cpp @@ -14,9 +14,10 @@ #include "AnimUtil.h" #include "AnimClip.h" -AnimBlendLinear::AnimBlendLinear(const QString& id, float alpha) : +AnimBlendLinear::AnimBlendLinear(const QString& id, float alpha, AnimBlendType blendType) : AnimNode(AnimNode::Type::BlendLinear, id), - _alpha(alpha) { + _alpha(alpha), + _blendType(blendType) { } @@ -79,7 +80,23 @@ void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, c if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) { _poses.resize(prevPoses.size()); - ::blend(_poses.size(), &prevPoses[0], &nextPoses[0], alpha, &_poses[0]); + if (_blendType == AnimBlendType_Normal) { + ::blend(_poses.size(), &prevPoses[0], &nextPoses[0], alpha, &_poses[0]); + } else if (_blendType == AnimBlendType_AddRelative) { + ::blendAdd(_poses.size(), &prevPoses[0], &nextPoses[0], alpha, &_poses[0]); + } else if (_blendType == AnimBlendType_AddAbsolute) { + // onvert from relative to absolute + AnimPoseVec prev = prevPoses; + _skeleton->convertRelativePosesToAbsolute(prev); + AnimPoseVec next = nextPoses; + _skeleton->convertRelativePosesToAbsolute(next); + + // then blend + ::blendAdd(_poses.size(), &prevPoses[0], &nextPoses[0], alpha, &_poses[0]); + + // convert result back into relative + _skeleton->convertAbsolutePosesToRelative(_poses); + } } } } diff --git a/libraries/animation/src/AnimBlendLinear.h b/libraries/animation/src/AnimBlendLinear.h index d0fe2a8503..e6fb39438f 100644 --- a/libraries/animation/src/AnimBlendLinear.h +++ b/libraries/animation/src/AnimBlendLinear.h @@ -27,7 +27,7 @@ class AnimBlendLinear : public AnimNode { public: friend class AnimTests; - AnimBlendLinear(const QString& id, float alpha); + AnimBlendLinear(const QString& id, float alpha, AnimBlendType blendType); virtual ~AnimBlendLinear() override; virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override; @@ -43,6 +43,7 @@ protected: AnimPoseVec _poses; + AnimBlendType _blendType; float _alpha; QString _alphaVar; diff --git a/libraries/animation/src/AnimContext.h b/libraries/animation/src/AnimContext.h index 5f353fcae4..2ee8a30980 100644 --- a/libraries/animation/src/AnimContext.h +++ b/libraries/animation/src/AnimContext.h @@ -34,6 +34,13 @@ enum class AnimNodeType { NumTypes }; +enum AnimBlendType { + AnimBlendType_Normal, + AnimBlendType_AddRelative, + AnimBlendType_AddAbsolute, + AnimBlendType_NumTypes +}; + class AnimContext { public: AnimContext() {} diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index ebd8a6de5b..c00dabfb5d 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -161,6 +161,19 @@ static EasingType stringToEasingType(const QString& str) { } } +static AnimBlendType stringToAnimBlendType(const QString& str) { + if (str == "normal") { + return AnimBlendType_Normal; + } else if (str == "addRelative") { + return AnimBlendType_AddRelative; + } else if (str == "addAbsolute") { + return AnimBlendType_AddAbsolute; + } else { + return AnimBlendType_NumTypes; + } +} + + static const char* animManipulatorJointVarTypeToString(AnimManipulator::JointVar::Type type) { switch (type) { case AnimManipulator::JointVar::Type::Absolute: return "absolute"; @@ -413,10 +426,19 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr); - + READ_OPTIONAL_STRING(blendType, jsonObj); READ_OPTIONAL_STRING(alphaVar, jsonObj); - auto node = std::make_shared(id, alpha); + AnimBlendType blendTypeEnum = AnimBlendType_Normal; // default value + if (!blendType.isEmpty()) { + blendTypeEnum = stringToAnimBlendType(blendType); + if (blendTypeEnum == AnimBlendType_NumTypes) { + qCCritical(animation) << "AnimNodeLoader, bad blendType on blendLinear, id = " << id; + return nullptr; + } + } + + auto node = std::make_shared(id, alpha, blendTypeEnum); if (!alphaVar.isEmpty()) { node->setAlphaVar(alphaVar); diff --git a/libraries/animation/src/AnimUtil.cpp b/libraries/animation/src/AnimUtil.cpp index f4f922d704..621e714f97 100644 --- a/libraries/animation/src/AnimUtil.cpp +++ b/libraries/animation/src/AnimUtil.cpp @@ -15,7 +15,6 @@ // TODO: use restrict keyword // TODO: excellent candidate for simd vectorization. - void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result) { for (size_t i = 0; i < numPoses; i++) { const AnimPose& aPose = a[i]; @@ -27,6 +26,26 @@ void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, A } } +// additive blend +void blendAdd(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result) { + for (size_t i = 0; i < numPoses; i++) { + const AnimPose& aPose = a[i]; + const AnimPose& bPose = b[i]; + + result[i].scale() = lerp(aPose.scale(), bPose.scale(), alpha); + + // adjust sign of bPose.rot() if necessary + glm::quat bTemp = bPose.rot(); + float dot = glm::dot(aPose.rot(), bTemp); + if (dot < 0.0f) { + bTemp = -bTemp; + } + result[i].rot() = glm::normalize((alpha * bTemp) * aPose.rot()); + + result[i].trans() = aPose.trans() + (alpha * bPose.trans()); + } +} + glm::quat averageQuats(size_t numQuats, const glm::quat* quats) { if (numQuats == 0) { return glm::quat(); diff --git a/libraries/animation/src/AnimUtil.h b/libraries/animation/src/AnimUtil.h index 26dc19da06..5d67b27abb 100644 --- a/libraries/animation/src/AnimUtil.h +++ b/libraries/animation/src/AnimUtil.h @@ -16,6 +16,9 @@ // this is where the magic happens void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result); +// additive blending +void blendAdd(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result); + glm::quat averageQuats(size_t numQuats, const glm::quat* quats); float accumulateTime(float startFrame, float endFrame, float timeScale, float currentFrame, float dt, bool loopFlag, From adda7774d377ef3e635b81b55394e489efcabdf9 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 28 Aug 2019 14:08:21 -0700 Subject: [PATCH 4/7] WIP checkpoint addAbsolute still broken --- libraries/animation/src/AnimBlendLinear.cpp | 32 ++++++++++----- libraries/animation/src/AnimClip.cpp | 44 ++++++++++++++++++--- 2 files changed, 62 insertions(+), 14 deletions(-) diff --git a/libraries/animation/src/AnimBlendLinear.cpp b/libraries/animation/src/AnimBlendLinear.cpp index c740de72cb..65329dd167 100644 --- a/libraries/animation/src/AnimBlendLinear.cpp +++ b/libraries/animation/src/AnimBlendLinear.cpp @@ -85,17 +85,31 @@ void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, c } else if (_blendType == AnimBlendType_AddRelative) { ::blendAdd(_poses.size(), &prevPoses[0], &nextPoses[0], alpha, &_poses[0]); } else if (_blendType == AnimBlendType_AddAbsolute) { - // onvert from relative to absolute - AnimPoseVec prev = prevPoses; - _skeleton->convertRelativePosesToAbsolute(prev); - AnimPoseVec next = nextPoses; - _skeleton->convertRelativePosesToAbsolute(next); + // convert prev from relative to absolute + AnimPoseVec absPrev = prevPoses; + _skeleton->convertRelativePosesToAbsolute(absPrev); + + // rotate the offset rotations from next into the parent relative frame of each joint. + AnimPoseVec relOffsetPoses; + relOffsetPoses.reserve(nextPoses.size()); + for (size_t i = 0; i < nextPoses.size(); ++i) { + // copy translation and scale from nextPoses + AnimPose pose = nextPoses[i]; + + // AJT: HACK nuke trans + pose.trans() = glm::vec3(0.0f); + + int parentIndex = _skeleton->getParentIndex(i); + if (parentIndex >= 0) { + // but transform nextPoses rot into absPrev parent frame. + pose.rot() = glm::inverse(absPrev[parentIndex].rot()) * nextPoses[i].rot(); + } + + relOffsetPoses.push_back(pose); + } // then blend - ::blendAdd(_poses.size(), &prevPoses[0], &nextPoses[0], alpha, &_poses[0]); - - // convert result back into relative - _skeleton->convertAbsolutePosesToRelative(_poses); + ::blendAdd(_poses.size(), &prevPoses[0], &relOffsetPoses[0], alpha, &_poses[0]); } } } diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index d58c3511cd..3f386f2804 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -41,7 +41,7 @@ static std::vector buildJointIndexMap(const AnimSkeleton& dstSkeleton, cons return jointIndexMap; } -static void bakeRelativeAnim(std::vector& anim, const AnimPoseVec& basePoses) { +static void bakeRelativeDeltaAnim(std::vector& anim, const AnimPoseVec& basePoses) { // invert all the basePoses AnimPoseVec invBasePoses = basePoses; @@ -55,13 +55,42 @@ static void bakeRelativeAnim(std::vector& anim, const AnimPoseVec& // for each joint in animPoses for (size_t i = 0; i < animPoses.size(); ++i) { - - // convert this AnimPose into a relative animation. + // convert this relative AnimPose into a delta animation. animPoses[i] = animPoses[i] * invBasePoses[i]; } } } +void bakeAbsoluteDeltaAnim(std::vector& anim, const AnimPoseVec& basePoses, AnimSkeleton::ConstPointer skeleton) { + + // invert all the basePoses + AnimPoseVec invBasePoses = basePoses; + for (auto&& invBasePose : invBasePoses) { + invBasePose = invBasePose.inverse(); + } + + AnimPoseVec absBasePoses = basePoses; + skeleton->convertRelativePosesToAbsolute(absBasePoses); + + // for each frame of the animation + for (auto&& animPoses : anim) { + ASSERT(animPoses.size() == basePoses.size()); + + // for each joint in animPoses + for (size_t i = 0; i < animPoses.size(); ++i) { + + // scale and translation are relative frame + animPoses[i] = animPoses[i] * invBasePoses[i]; + + // but transform the rotation delta into the absolute frame. + int parentIndex = skeleton->getParentIndex(i); + if (parentIndex >= 0) { + animPoses[i].rot() = absBasePoses[parentIndex].rot() * animPoses[i].rot(); + } + } + } +} + static std::vector copyAndRetargetFromNetworkAnim(AnimationPointer networkAnim, AnimSkeleton::ConstPointer avatarSkeleton) { ASSERT(networkAnim && networkAnim->isLoaded() && avatarSkeleton); std::vector anim; @@ -264,8 +293,13 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, const Anim // copy & retarget baseAnim! auto baseAnim = copyAndRetargetFromNetworkAnim(_baseNetworkAnim, _skeleton); - // make _anim relative to the baseAnim reference frame. - bakeRelativeAnim(_anim, baseAnim[(int)_baseFrame]); +//#define BLEND_ADD_ABSOLUTE + +#ifdef BLEND_ADD_ABSOLUTE + bakeAbsoluteDeltaAnim(_anim, baseAnim[(int)_baseFrame], _skeleton); +#else + bakeRelativeDeltaAnim(_anim, baseAnim[(int)_baseFrame]); +#endif } } From 16a20a5ead5ce52e77a4b1c0816dfe4209e93764 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 28 Aug 2019 18:19:35 -0700 Subject: [PATCH 5/7] AddAbsolute now works! added blendType to AnimClip --- libraries/animation/src/AnimBlendLinear.cpp | 8 +++---- libraries/animation/src/AnimClip.cpp | 26 ++++++++++----------- libraries/animation/src/AnimClip.h | 4 ++-- libraries/animation/src/AnimNodeLoader.cpp | 14 +++++++++-- libraries/animation/src/Rig.cpp | 2 +- 5 files changed, 31 insertions(+), 23 deletions(-) diff --git a/libraries/animation/src/AnimBlendLinear.cpp b/libraries/animation/src/AnimBlendLinear.cpp index 65329dd167..4e8c42dad3 100644 --- a/libraries/animation/src/AnimBlendLinear.cpp +++ b/libraries/animation/src/AnimBlendLinear.cpp @@ -93,16 +93,14 @@ void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, c AnimPoseVec relOffsetPoses; relOffsetPoses.reserve(nextPoses.size()); for (size_t i = 0; i < nextPoses.size(); ++i) { + // copy translation and scale from nextPoses AnimPose pose = nextPoses[i]; - // AJT: HACK nuke trans - pose.trans() = glm::vec3(0.0f); - - int parentIndex = _skeleton->getParentIndex(i); + int parentIndex = _skeleton->getParentIndex((int)i); if (parentIndex >= 0) { // but transform nextPoses rot into absPrev parent frame. - pose.rot() = glm::inverse(absPrev[parentIndex].rot()) * nextPoses[i].rot(); + pose.rot() = glm::inverse(absPrev[parentIndex].rot()) * pose.rot() * absPrev[parentIndex].rot(); } relOffsetPoses.push_back(pose); diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 3f386f2804..96bc87d738 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -83,9 +83,9 @@ void bakeAbsoluteDeltaAnim(std::vector& anim, const AnimPoseVec& ba animPoses[i] = animPoses[i] * invBasePoses[i]; // but transform the rotation delta into the absolute frame. - int parentIndex = skeleton->getParentIndex(i); + int parentIndex = skeleton->getParentIndex((int)i); if (parentIndex >= 0) { - animPoses[i].rot() = absBasePoses[parentIndex].rot() * animPoses[i].rot(); + animPoses[i].rot() = absBasePoses[parentIndex].rot() * animPoses[i].rot() * glm::inverse(absBasePoses[parentIndex].rot()); } } } @@ -226,7 +226,7 @@ static std::vector copyAndRetargetFromNetworkAnim(AnimationPointer } AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag, - bool relativeFlag, const QString& baseURL, float baseFrame) : + AnimBlendType blendType, const QString& baseURL, float baseFrame) : AnimNode(AnimNode::Type::Clip, id), _startFrame(startFrame), _endFrame(endFrame), @@ -234,12 +234,12 @@ AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, floa _loopFlag(loopFlag), _mirrorFlag(mirrorFlag), _frame(startFrame), - _relativeFlag(relativeFlag), + _blendType(blendType), _baseFrame(baseFrame) { loadURL(url); - if (relativeFlag) { + if (blendType != AnimBlendType_Normal) { auto animCache = DependencyManager::get(); _baseNetworkAnim = animCache->getAnimation(baseURL); _baseURL = baseURL; @@ -263,7 +263,7 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, const Anim _frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame, dt, _loopFlag, _id, triggersOut); // poll network anim to see if it's finished loading yet. - if (!_relativeFlag) { + if (_blendType == AnimBlendType_Normal) { if (_networkAnim && _networkAnim->isLoaded() && _skeleton) { // loading is complete, copy & retarget animation. _anim = copyAndRetargetFromNetworkAnim(_networkAnim, _skeleton); @@ -277,6 +277,7 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, const Anim _poses.resize(_skeleton->getNumJoints()); } } else { + // an additive blend type if (_networkAnim && _networkAnim->isLoaded() && _baseNetworkAnim && _baseNetworkAnim->isLoaded() && _skeleton) { // loading is complete, copy & retarget animation. _anim = copyAndRetargetFromNetworkAnim(_networkAnim, _skeleton); @@ -293,13 +294,12 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, const Anim // copy & retarget baseAnim! auto baseAnim = copyAndRetargetFromNetworkAnim(_baseNetworkAnim, _skeleton); -//#define BLEND_ADD_ABSOLUTE - -#ifdef BLEND_ADD_ABSOLUTE - bakeAbsoluteDeltaAnim(_anim, baseAnim[(int)_baseFrame], _skeleton); -#else - bakeRelativeDeltaAnim(_anim, baseAnim[(int)_baseFrame]); -#endif + if (_blendType == AnimBlendType_AddAbsolute) { + bakeAbsoluteDeltaAnim(_anim, baseAnim[(int)_baseFrame], _skeleton); + } else { + // AnimBlendType_AddRelative + bakeRelativeDeltaAnim(_anim, baseAnim[(int)_baseFrame]); + } } } diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index 2ecaffb8b1..24acdcf20a 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -26,7 +26,7 @@ public: friend class AnimTests; AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag, - bool relativeFlag, const QString& baseURL, float baseFrame); + AnimBlendType blendType, const QString& baseURL, float baseFrame); virtual ~AnimClip() override; virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override; @@ -80,7 +80,7 @@ protected: bool _loopFlag; bool _mirrorFlag; float _frame; - bool _relativeFlag; + AnimBlendType _blendType; QString _baseURL; float _baseFrame; diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index c00dabfb5d..100269c55c 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -387,7 +387,7 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& READ_FLOAT(timeScale, jsonObj, id, jsonUrl, nullptr); READ_BOOL(loopFlag, jsonObj, id, jsonUrl, nullptr); READ_OPTIONAL_BOOL(mirrorFlag, jsonObj, false); - READ_OPTIONAL_BOOL(relativeFlag, jsonObj, false); + READ_OPTIONAL_STRING(blendType, jsonObj); READ_OPTIONAL_STRING(baseURL, jsonObj); READ_OPTIONAL_FLOAT(baseFrame, jsonObj, 0.0f); @@ -402,7 +402,17 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& auto tempUrl = QUrl(url); tempUrl = jsonUrl.resolved(tempUrl); - auto node = std::make_shared(id, tempUrl.toString(), startFrame, endFrame, timeScale, loopFlag, mirrorFlag, relativeFlag, baseURL, baseFrame); + // AJT: + AnimBlendType blendTypeEnum = AnimBlendType_Normal; // default value + if (!blendType.isEmpty()) { + blendTypeEnum = stringToAnimBlendType(blendType); + if (blendTypeEnum == AnimBlendType_NumTypes) { + qCCritical(animation) << "AnimNodeLoader, bad blendType on clip, id = " << id; + return nullptr; + } + } + + auto node = std::make_shared(id, tempUrl.toString(), startFrame, endFrame, timeScale, loopFlag, mirrorFlag, blendTypeEnum, baseURL, baseFrame); if (!startFrameVar.isEmpty()) { node->setStartFrameVar(startFrameVar); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 2974212bf1..d5594fca6c 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -564,7 +564,7 @@ void Rig::overrideRoleAnimation(const QString& role, const QString& url, float f _origRoleAnimations[role] = node; const float REFERENCE_FRAMES_PER_SECOND = 30.0f; float timeScale = fps / REFERENCE_FRAMES_PER_SECOND; - auto clipNode = std::make_shared(role, url, firstFrame, lastFrame, timeScale, loop, false, false, "", 0.0f); + auto clipNode = std::make_shared(role, url, firstFrame, lastFrame, timeScale, loop, false, AnimBlendType_Normal, "", 0.0f); _roleAnimStates[role] = { role, url, fps, loop, firstFrame, lastFrame }; AnimNode::Pointer parent = node->getParent(); parent->replaceChild(node, clipNode); From 52ed0bcb71469ed960af59ff29d862fa67ab54a0 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 29 Aug 2019 10:17:51 -0700 Subject: [PATCH 6/7] additive blending work --- libraries/animation/src/AnimBlendLinear.cpp | 13 +++++++++++++ libraries/animation/src/AnimUtil.cpp | 15 +++++++++------ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/libraries/animation/src/AnimBlendLinear.cpp b/libraries/animation/src/AnimBlendLinear.cpp index 4e8c42dad3..c066dc92eb 100644 --- a/libraries/animation/src/AnimBlendLinear.cpp +++ b/libraries/animation/src/AnimBlendLinear.cpp @@ -37,6 +37,19 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, con } else if (_children.size() == 1) { _poses = _children[0]->evaluate(animVars, context, dt, triggersOut); context.setDebugAlpha(_children[0]->getID(), parentDebugAlpha, _children[0]->getType()); + } else if (_children.size() == 2 && _blendType != AnimBlendType_Normal) { + // special case for additive blending + float alpha = glm::clamp(_alpha, 0.0f, 1.0f); + const size_t prevPoseIndex = 0; + const size_t nextPoseIndex = 1; + evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, dt); + + // for animation stack debugging + float weight2 = alpha; + float weight1 = 1.0f - weight2; + context.setDebugAlpha(_children[prevPoseIndex]->getID(), weight1 * parentDebugAlpha, _children[prevPoseIndex]->getType()); + context.setDebugAlpha(_children[nextPoseIndex]->getID(), weight2 * parentDebugAlpha, _children[nextPoseIndex]->getType()); + } else { float clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1)); size_t prevPoseIndex = glm::floor(clampedAlpha); diff --git a/libraries/animation/src/AnimUtil.cpp b/libraries/animation/src/AnimUtil.cpp index 621e714f97..e5f7cf4182 100644 --- a/libraries/animation/src/AnimUtil.cpp +++ b/libraries/animation/src/AnimUtil.cpp @@ -28,19 +28,22 @@ void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, A // additive blend void blendAdd(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result) { + + const glm::quat identity = glm::quat(); for (size_t i = 0; i < numPoses; i++) { const AnimPose& aPose = a[i]; const AnimPose& bPose = b[i]; result[i].scale() = lerp(aPose.scale(), bPose.scale(), alpha); - // adjust sign of bPose.rot() if necessary - glm::quat bTemp = bPose.rot(); - float dot = glm::dot(aPose.rot(), bTemp); - if (dot < 0.0f) { - bTemp = -bTemp; + // ensure that delta has the same "polarity" as the identity quat. + // we don't need to do a full dot product, just sign of w is sufficient. + glm::quat delta = bPose.rot(); + if (delta.w < 0.0f) { + delta = -delta; } - result[i].rot() = glm::normalize((alpha * bTemp) * aPose.rot()); + delta = glm::lerp(identity, delta, alpha); + result[i].rot() = glm::normalize(delta * aPose.rot()); result[i].trans() = aPose.trans() + (alpha * bPose.trans()); } From c814176d6a55f4875429cfe7f10c000ba9a90e61 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 29 Aug 2019 16:16:19 -0700 Subject: [PATCH 7/7] Fix initialization order of members warning --- libraries/animation/src/AnimBlendLinear.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/AnimBlendLinear.h b/libraries/animation/src/AnimBlendLinear.h index e6fb39438f..40a20adb2c 100644 --- a/libraries/animation/src/AnimBlendLinear.h +++ b/libraries/animation/src/AnimBlendLinear.h @@ -43,8 +43,8 @@ protected: AnimPoseVec _poses; - AnimBlendType _blendType; float _alpha; + AnimBlendType _blendType; QString _alphaVar;