From e6abc026c832e22c3e6c8b4342e59f4de5546c39 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 2 Feb 2016 17:02:29 -0800 Subject: [PATCH] AnimClip: mirror animation support --- libraries/animation/src/AnimClip.cpp | 26 +++++++++++-- libraries/animation/src/AnimClip.h | 8 +++- libraries/animation/src/AnimNodeLoader.cpp | 11 +++++- libraries/animation/src/AnimPose.cpp | 5 +++ libraries/animation/src/AnimPose.h | 1 + libraries/animation/src/AnimSkeleton.cpp | 45 +++++++++++++++++++++- libraries/animation/src/AnimSkeleton.h | 5 +++ libraries/animation/src/Rig.cpp | 4 +- 8 files changed, 97 insertions(+), 8 deletions(-) diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 90cd85e727..f8e045fc3b 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -15,12 +15,13 @@ bool AnimClip::usePreAndPostPoseFromAnim = false; -AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag) : +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); @@ -49,6 +50,12 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, float dt, } 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) { @@ -63,8 +70,8 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, float dt, prevIndex = std::min(std::max(0, prevIndex), frameCount - 1); nextIndex = std::min(std::max(0, nextIndex), frameCount - 1); - const AnimPoseVec& prevFrame = _anim[prevIndex]; - const AnimPoseVec& nextFrame = _anim[nextIndex]; + 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]); @@ -162,9 +169,22 @@ void AnimClip::copyFromNetworkAnim() { } } + // mirrorAnim will be re-built on demand, if needed. + _mirrorAnim.clear(); + _poses.resize(skeletonJointCount); } +void AnimClip::buildMirrorAnim() { + assert(_skeleton); + + _mirrorAnim.clear(); + _mirrorAnim.reserve(_anim.size()); + for (auto& relPoses : _anim) { + _mirrorAnim.push_back(relPoses); + _skeleton->mirrorRelativePoses(_mirrorAnim.back()); + } +} const AnimPoseVec& AnimClip::getPosesInternal() const { return _poses; diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index 7d58ae4f9a..bb4f61ffc0 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -27,7 +27,7 @@ public: static bool usePreAndPostPoseFromAnim; - AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag); + AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag); virtual ~AnimClip() override; virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; @@ -49,12 +49,16 @@ public: bool getLoopFlag() const { return _loopFlag; } void setLoopFlag(bool loopFlag) { _loopFlag = loopFlag; } + bool getMirrorFlag() const { return _mirrorFlag; } + void setMirrorFlag(bool mirrorFlag) { _mirrorFlag = mirrorFlag; } + 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; @@ -64,12 +68,14 @@ protected: // _anim[frame][joint] std::vector _anim; + std::vector _mirrorAnim; QString _url; float _startFrame; float _endFrame; float _timeScale; bool _loopFlag; + bool _mirrorFlag; float _frame; QString _startFrameVar; diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 568da8dd63..b26f34f531 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -145,6 +145,14 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) { } \ bool NAME = NAME##_VAL.toBool() +#define READ_OPTIONAL_BOOL(NAME, JSON_OBJ, DEFAULT) \ + auto NAME##_VAL = JSON_OBJ.value(#NAME); \ + bool NAME = DEFAULT; \ + if (NAME##_VAL.isBool()) { \ + NAME = NAME##_VAL.toBool(); \ + } \ + do {} while (0) + #define READ_FLOAT(NAME, JSON_OBJ, ID, URL, ERROR_RETURN) \ auto NAME##_VAL = JSON_OBJ.value(#NAME); \ if (!NAME##_VAL.isDouble()) { \ @@ -222,13 +230,14 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& READ_FLOAT(endFrame, jsonObj, id, jsonUrl, nullptr); READ_FLOAT(timeScale, jsonObj, id, jsonUrl, nullptr); READ_BOOL(loopFlag, jsonObj, id, jsonUrl, nullptr); + READ_OPTIONAL_BOOL(mirrorFlag, jsonObj, false); READ_OPTIONAL_STRING(startFrameVar, jsonObj); READ_OPTIONAL_STRING(endFrameVar, jsonObj); READ_OPTIONAL_STRING(timeScaleVar, jsonObj); READ_OPTIONAL_STRING(loopFlagVar, jsonObj); - auto node = std::make_shared(id, url, startFrame, endFrame, timeScale, loopFlag); + auto node = std::make_shared(id, url, startFrame, endFrame, timeScale, loopFlag, mirrorFlag); if (!startFrameVar.isEmpty()) { node->setStartFrameVar(startFrameVar); diff --git a/libraries/animation/src/AnimPose.cpp b/libraries/animation/src/AnimPose.cpp index 0c6af2d5bd..439ef5336f 100644 --- a/libraries/animation/src/AnimPose.cpp +++ b/libraries/animation/src/AnimPose.cpp @@ -51,6 +51,11 @@ AnimPose AnimPose::inverse() const { return AnimPose(glm::inverse(static_cast(*this))); } +// mirror about x-axis without applying negative scale. +AnimPose AnimPose::mirror() const { + return AnimPose(scale, glm::quat(rot.w, rot.x, -rot.y, -rot.z), glm::vec3(-trans.x, trans.y, trans.z)); +} + AnimPose::operator glm::mat4() const { glm::vec3 xAxis = rot * glm::vec3(scale.x, 0.0f, 0.0f); glm::vec3 yAxis = rot * glm::vec3(0.0f, scale.y, 0.0f); diff --git a/libraries/animation/src/AnimPose.h b/libraries/animation/src/AnimPose.h index 852d84ec1b..6ffa9bb321 100644 --- a/libraries/animation/src/AnimPose.h +++ b/libraries/animation/src/AnimPose.h @@ -30,6 +30,7 @@ struct AnimPose { AnimPose operator*(const AnimPose& rhs) const; AnimPose inverse() const; + AnimPose mirror() const; operator glm::mat4() const; glm::vec3 scale; diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 8f45b785d1..2d37be9b87 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -87,7 +87,8 @@ AnimPose AnimSkeleton::getAbsolutePose(int jointIndex, const AnimPoseVec& poses) void AnimSkeleton::convertRelativePosesToAbsolute(AnimPoseVec& poses) const { // poses start off relative and leave in absolute frame - for (int i = 0; i < (int)poses.size() && i < (int)_joints.size(); ++i) { + int lastIndex = std::min((int)poses.size(), (int)_joints.size()); + for (int i = 0; i < lastIndex; ++i) { int parentIndex = _joints[i].parentIndex; if (parentIndex != -1) { poses[i] = poses[parentIndex] * poses[i]; @@ -95,6 +96,30 @@ void AnimSkeleton::convertRelativePosesToAbsolute(AnimPoseVec& poses) const { } } +void AnimSkeleton::convertAbsolutePosesToRelative(AnimPoseVec& poses) const { + // poses start off absolute and leave in relative frame + int lastIndex = std::min((int)poses.size(), (int)_joints.size()); + for (int i = lastIndex - 1; i >= 0; --i) { + int parentIndex = _joints[i].parentIndex; + if (parentIndex != -1) { + poses[i] = poses[parentIndex].inverse() * poses[i]; + } + } +} + +void AnimSkeleton::mirrorRelativePoses(AnimPoseVec& poses) const { + convertRelativePosesToAbsolute(poses); + mirrorAbsolutePoses(poses); + convertAbsolutePosesToRelative(poses); +} + +void AnimSkeleton::mirrorAbsolutePoses(AnimPoseVec& poses) const { + AnimPoseVec temp = poses; + for (int i = 0; i < (int)poses.size(); i++) { + poses[_mirrorMap[i]] = temp[i].mirror(); + } +} + void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints) { _joints = joints; @@ -150,6 +175,24 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints) } } } + + // build mirror map. + _mirrorMap.reserve(_joints.size()); + for (int i = 0; i < (int)joints.size(); i++) { + int mirrorJointIndex = -1; + if (_joints[i].name.startsWith("Left")) { + QString mirrorJointName = QString(_joints[i].name).replace(0, 4, "Right"); + mirrorJointIndex = nameToJointIndex(mirrorJointName); + } else if (_joints[i].name.startsWith("Right")) { + QString mirrorJointName = QString(_joints[i].name).replace(0, 5, "Left"); + mirrorJointIndex = nameToJointIndex(mirrorJointName); + } + if (mirrorJointIndex >= 0) { + _mirrorMap.push_back(mirrorJointIndex); + } else { + _mirrorMap.push_back(i); + } + } } #ifndef NDEBUG diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 757f4e5c3e..fc246bc4c0 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -53,6 +53,10 @@ public: AnimPose getAbsolutePose(int jointIndex, const AnimPoseVec& poses) const; void convertRelativePosesToAbsolute(AnimPoseVec& poses) const; + void convertAbsolutePosesToRelative(AnimPoseVec& poses) const; + + void mirrorRelativePoses(AnimPoseVec& poses) const; + void mirrorAbsolutePoses(AnimPoseVec& poses) const; #ifndef NDEBUG void dump() const; @@ -69,6 +73,7 @@ protected: AnimPoseVec _absoluteDefaultPoses; AnimPoseVec _relativePreRotationPoses; AnimPoseVec _relativePostRotationPoses; + std::vector _mirrorMap; // no copies AnimSkeleton(const AnimSkeleton&) = delete; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 4d72bfbaea..1e75df0ccc 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -120,7 +120,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); + auto clipNode = std::make_shared(role, url, firstFrame, lastFrame, timeScale, loop, false); AnimNode::Pointer parent = node->getParent(); parent->replaceChild(node, clipNode); } else { @@ -152,7 +152,7 @@ void Rig::prefetchAnimation(const QString& url) { // This will begin loading the NetworkGeometry for the given URL. // which should speed us up if we request it later via overrideAnimation. - auto clipNode = std::make_shared("prefetch", url, 0, 0, 1.0, false); + auto clipNode = std::make_shared("prefetch", url, 0, 0, 1.0, false, false); _prefetchedAnimations.push_back(clipNode); }