From da809efcd6d60f9531d392b23232397ad6a2bc4f Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 31 Jul 2015 19:06:50 -0700 Subject: [PATCH] WIP commit, DOES NOT BUILD. * Added AnimSkeleton class * Attempt to copy animation frames when _networkAnimation has finished loading. Fill in the holes with bind pose. --- libraries/animation/src/AnimClip.cpp | 92 +++++++++++++++++++--- libraries/animation/src/AnimClip.h | 11 ++- libraries/animation/src/AnimNode.h | 29 +++++-- libraries/animation/src/AnimNodeLoader.cpp | 10 +-- libraries/animation/src/AnimSkeleton.cpp | 36 +++++++++ libraries/animation/src/AnimSkeleton.h | 30 +++++++ libraries/shared/src/GLMHelpers.cpp | 1 - 7 files changed, 185 insertions(+), 24 deletions(-) create mode 100644 libraries/animation/src/AnimSkeleton.cpp create mode 100644 libraries/animation/src/AnimSkeleton.h diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index a819304d18..51b01560fb 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -27,7 +27,7 @@ AnimClip::~AnimClip() { } void AnimClip::setURL(const std::string& url) { - // TODO: + _networkAnim = DependencyManager::get()->getAnimation(QString::fromStdString(url)); _url = url; } @@ -43,11 +43,11 @@ void AnimClip::setLoopFlag(bool loopFlag) { _loopFlag = loopFlag; } -const AnimPose& AnimClip::evaluate(float dt) { +float AnimClip::accumulateTime(float frame, float dt) const { const float startFrame = std::min(_startFrame, _endFrame); if (startFrame == _endFrame) { // when startFrame >= endFrame - _frame = _endFrame; + frame = _endFrame; } else if (_timeScale > 0.0f) { // accumulate time, keeping track of loops and end of animation events. const float FRAMES_PER_SECOND = 30.0f; @@ -59,20 +59,94 @@ const AnimPose& AnimClip::evaluate(float dt) { // anim loop // TODO: trigger onLoop event framesRemaining -= framesTillEnd; - _frame = startFrame; + frame = startFrame; } else { // anim end // TODO: trigger onDone event - _frame = _endFrame; + frame = _endFrame; framesRemaining = 0.0f; } } else { - _frame += framesRemaining; + frame += framesRemaining; framesRemaining = 0.0f; } } } - // TODO: eval animation - - return _frame; + return frame; +} + +const std::vector& AnimClip::evaluate(float dt) { + _frame = accumulateTime(_frame, dt); + + if (!_anim && _networkAnim && _networkAnim->isLoaded() && _skeleton) { + copyFramesFromNetworkAnim(); + _networkAnim = nullptr; + } + + if (_anim) { + int frameCount = _anim.size(); + + int prevIndex = (int)glm::floor(_frame); + int nextIndex = (int)glm::ceil(_frame); + if (_loopFlag && nextIndex >= frameCount) { + nextIndex = 0; + } + + // 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. + prevIndex = std::min(std::max(0, prevIndex), frameCount - 1); + nextIndex = std::min(std::max(0, nextIndex), frameCount - 1); + + const std::vector& prevFrame = _anim[prevIndex]; + const std::vector& nextFrame = _anim[nextIndex]; + float alpha = glm::fract(_frame); + + for (size_t i = 0; i < _bones.size(); i++) { + const AnimBone& prevBone = prevFrame[i]; + const AnimBone& nextBone = nextFrame[i]; + _bones[i].scale = glm::lerp(prevBone.scale, nextBone.scale, alpha); + _bones[i].rot = glm::normalize(glm::lerp(prevBone.rot, nextBone.rot, alpha)); + _bones[i].trans = glm::lerp(prevBone.trans, nextBone.trans, alpha); + } + } + + return _bones; +} + +void AnimClip::copyFromNetworkAnim() { + assert(_networkAnim && _networkAnim->isLoaded() && _skeleton); + _anim.clear(); + + // build a mapping from animation joint indices to skeleton joint indices. + // by matching joints with the same name. + const FBXGeometry& geom = _networkAnim->getGeometry(); + const QVector& joints = geom.joints; + std::vector jointMap; + const int animJointCount = joints.count(); + jointMap.reserve(animJointCount); + for (int i = 0; i < animJointCount; i++) { + int skeletonJoint _skeleton.nameToJointIndex(joints.at(i).name); + jointMap.push_back(skeletonJoint); + } + + const int frameCount = geom.animationFrames.size(); + const int skeletonJointCount = _skeleton.jointCount(); + _anim.resize(frameCount); + for (int i = 0; i < frameCount; i++) { + + // init all joints in animation to bind pose + _anim[i].reserve(skeletonJointCount); + for (int j = 0; j < skeletonJointCount; j++) { + _anim[i].push_back(_skeleton.getBindPose(j)); + } + + // init over all joint animations + for (int j = 0; j < animJointCount; j++) { + int k = jointMap[j]; + if (k >= 0 && k < skeletonJointCount) { + // currently animations only have rotation. + _anim[i][k].rot = geom.animationFrames[i].rotations[j]; + } + } + } } diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index db3419332b..a43ee98194 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -36,10 +36,17 @@ public: void setLoopFlag(bool loopFlag); bool getLoopFlag() const { return _loopFlag; } - virtual const AnimPose& evaluate(float dt); + virtual const std::vector& evaluate(float dt); protected: - AnimationPointer _anim; + float accumulateTime(float frame, float dt) const; + void copyFromNetworkAnim(); + + AnimationPointer _networkAnim; + std::vector _bones; + + // _anim[frame][joint] + std::vector> _anim; std::string _url; float _startFrame; diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index af1fd8d5d5..36f5c29ef3 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -1,5 +1,5 @@ // -// AnimInterface.h +// AnimNode.h // // Copyright 2015 High Fidelity, Inc. // @@ -14,8 +14,18 @@ #include #include #include +#include +#include + +#include "AnimSkeleton.h" + +struct AnimBone { + AnimBone() {} + glm::vec3 scale; + glm::quat rot; + glm::vec3 trans; +}; -typedef float AnimPose; class QJsonObject; class AnimNode { @@ -24,33 +34,38 @@ public: ClipType = 0, NumTypes }; + typedef std::shared_ptr Pointer; AnimNode(Type type, const std::string& id) : _type(type), _id(id) {} const std::string& getID() const { return _id; } Type getType() const { return _type; } - void addChild(std::shared_ptr child) { _children.push_back(child); } - void removeChild(std::shared_ptr child) { + void addChild(Pointer child) { _children.push_back(child); } + void removeChild(Pointer child) { auto iter = std::find(_children.begin(), _children.end(), child); if (iter != _children.end()) { _children.erase(iter); } } - const std::shared_ptr& getChild(int i) const { + Pointer getChild(int i) const { assert(i >= 0 && i < (int)_children.size()); return _children[i]; } int getChildCount() const { return (int)_children.size(); } + void setSkeleton(AnimSkeleton::Pointer skeleton) { _skeleton = skeleton; } + AnimSkeleton::Pointer getSkeleton() const { return _skeleton; } + virtual ~AnimNode() {} - virtual const AnimPose& evaluate(float dt) = 0; + virtual const std::vector& evaluate(float dt) = 0; protected: std::string _id; Type _type; - std::vector> _children; + std::vector _children; + AnimSkeleton::Pointer _skeleton; // no copies AnimNode(const AnimNode&) = delete; diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index c2cb75364d..d9b34e3c86 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -28,9 +28,9 @@ static TypeInfo typeInfoArray[AnimNode::NumTypes] = { { AnimNode::ClipType, "clip" } }; -typedef std::shared_ptr (*NodeLoaderFunc)(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl); +typedef AnimNode::Pointer (*NodeLoaderFunc)(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl); -static std::shared_ptr loadClipNode(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl); +static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl); static NodeLoaderFunc nodeLoaderFuncs[AnimNode::NumTypes] = { loadClipNode @@ -75,7 +75,7 @@ static AnimNode::Type stringToEnum(const QString& str) { return AnimNode::NumTypes; } -static std::shared_ptr loadNode(const QJsonObject& jsonObj, const QString& jsonUrl) { +static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QString& jsonUrl) { auto idVal = jsonObj.value("id"); if (!idVal.isString()) { qCCritical(animation) << "AnimNodeLoader, bad string \"id\", url =" << jsonUrl; @@ -121,7 +121,7 @@ static std::shared_ptr loadNode(const QJsonObject& jsonObj, const QStr return node; } -static std::shared_ptr loadClipNode(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl) { +static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& id, const QString& jsonUrl) { READ_STRING(url, jsonObj, id, jsonUrl); READ_FLOAT(startFrame, jsonObj, id, jsonUrl); @@ -132,7 +132,7 @@ static std::shared_ptr loadClipNode(const QJsonObject& jsonObj, const return std::make_shared(id.toStdString(), url.toStdString(), startFrame, endFrame, timeScale, loopFlag); } -std::shared_ptr AnimNodeLoader::load(const std::string& filename) const { +AnimNode::Pointer AnimNodeLoader::load(const std::string& filename) const { // load entire file into a string. QString jsonUrl = QString::fromStdString(filename); QFile file; diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp new file mode 100644 index 0000000000..fbd31d55b9 --- /dev/null +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -0,0 +1,36 @@ +// +// AnimSkeleton.cpp +// +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "AnimSkeleton.h" + +AnimSkeleton::AnimSkeleton(const std::vector& joints) { + _joints = joints; +} + +int AnimSkeltion::nameToJointIndex(const QString& jointName) const { + for (int i = 0; i < _joints.size(); i++) { + if (_joints.name == jointName) { + return i; + } + } + return -1; +} + +int AnimSkeleton::getNumJoints() const { + return _joints.size(); +} + +AnimBone getBindPose(int jointIndex) const { + // TODO: what coordinate frame is the bindTransform in? local to the bones parent frame? or model? + return AnimBone bone(glm::vec3(1.0f, 1.0f, 1.0f), + glm::quat_cast(_joints[jointIndex].bindTransform), + glm::vec3(0.0f, 0.0f, 0.0f)); +} + + diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h new file mode 100644 index 0000000000..a48d7dfa50 --- /dev/null +++ b/libraries/animation/src/AnimSkeleton.h @@ -0,0 +1,30 @@ +// +// AnimSkeleton.h +// +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_AnimSkeleton +#define hifi_AnimSkeleton + +#include + +#include "FBXReader.h" + +class AnimSkeleton { +public: + typedef std::shared_ptr Pointer; + AnimSkeleton(const std::vector& joints); + int nameToJointIndex(const QString& jointName) const; + int getNumJoints() const; + AnimBone getBindPose(int jointIndex) const; +} + +protected: + std::vector _joints; +}; + +#endif diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index d5b2917369..6084bf9354 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -347,4 +347,3 @@ QRectF glmToRect(const glm::vec2 & pos, const glm::vec2 & size) { return result; } -