From f5dee717a1d64a677fe71b7e9a4596dfd0667b52 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 31 Jul 2015 22:08:39 -0700 Subject: [PATCH] Added fbx loading via animation cache. * added AnimPose::copyFromNetworkAnim() which should, re-map bone ids to match the current skeleton, and fill in missing bones with bind pose frames. * added ability to set a skeleton on a node. I might need to add a recursive version of this. * it compiles! * tests run! --- libraries/animation/src/AnimClip.cpp | 42 +++++++++++----------- libraries/animation/src/AnimClip.h | 6 ++-- libraries/animation/src/AnimNode.h | 11 ++---- libraries/animation/src/AnimNodeLoader.cpp | 2 +- libraries/animation/src/AnimSkeleton.cpp | 14 ++++---- libraries/animation/src/AnimSkeleton.h | 13 +++++-- libraries/shared/src/GLMHelpers.h | 15 ++++++++ tests/animation/src/AnimClipTests.cpp | 22 ++++++++---- tests/animation/src/AnimClipTests.h | 2 ++ tests/animation/src/{ => data}/test.json | 0 10 files changed, 79 insertions(+), 48 deletions(-) rename tests/animation/src/{ => data}/test.json (100%) diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 51b01560fb..d10430dec3 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -7,6 +7,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "GLMHelpers.h" #include "AnimClip.h" #include "AnimationLogging.h" @@ -16,8 +17,8 @@ AnimClip::AnimClip(const std::string& id, const std::string& url, float startFra _startFrame(startFrame), _endFrame(endFrame), _timeScale(timeScale), - _frame(startFrame), - _loopFlag(loopFlag) + _loopFlag(loopFlag), + _frame(startFrame) { } @@ -27,7 +28,8 @@ AnimClip::~AnimClip() { } void AnimClip::setURL(const std::string& url) { - _networkAnim = DependencyManager::get()->getAnimation(QString::fromStdString(url)); + auto animCache = DependencyManager::get(); + _networkAnim = animCache->getAnimation(QString::fromStdString(url)); _url = url; } @@ -75,15 +77,15 @@ float AnimClip::accumulateTime(float frame, float dt) const { return frame; } -const std::vector& AnimClip::evaluate(float dt) { +const std::vector& AnimClip::evaluate(float dt) { _frame = accumulateTime(_frame, dt); - if (!_anim && _networkAnim && _networkAnim->isLoaded() && _skeleton) { - copyFramesFromNetworkAnim(); - _networkAnim = nullptr; + if (_networkAnim && _networkAnim->isLoaded() && _skeleton) { + copyFromNetworkAnim(); + _networkAnim.reset(); } - if (_anim) { + if (_anim.size()) { int frameCount = _anim.size(); int prevIndex = (int)glm::floor(_frame); @@ -97,20 +99,20 @@ const std::vector& AnimClip::evaluate(float dt) { 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]; + 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); + for (size_t i = 0; i < _poses.size(); i++) { + const AnimPose& prevBone = prevFrame[i]; + const AnimPose& nextBone = nextFrame[i]; + _poses[i].scale = lerp(prevBone.scale, nextBone.scale, alpha); + _poses[i].rot = glm::normalize(glm::lerp(prevBone.rot, nextBone.rot, alpha)); + _poses[i].trans = lerp(prevBone.trans, nextBone.trans, alpha); } } - return _bones; + return _poses; } void AnimClip::copyFromNetworkAnim() { @@ -125,19 +127,19 @@ void AnimClip::copyFromNetworkAnim() { const int animJointCount = joints.count(); jointMap.reserve(animJointCount); for (int i = 0; i < animJointCount; i++) { - int skeletonJoint _skeleton.nameToJointIndex(joints.at(i).name); + int skeletonJoint = _skeleton->nameToJointIndex(joints.at(i).name); jointMap.push_back(skeletonJoint); } const int frameCount = geom.animationFrames.size(); - const int skeletonJointCount = _skeleton.jointCount(); + const int skeletonJointCount = _skeleton->getNumJoints(); _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)); + _anim[i].push_back(_skeleton->getBindPose(j)); } // init over all joint animations diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index a43ee98194..9858e5b524 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -36,17 +36,17 @@ public: void setLoopFlag(bool loopFlag); bool getLoopFlag() const { return _loopFlag; } - virtual const std::vector& evaluate(float dt); + virtual const std::vector& evaluate(float dt); protected: float accumulateTime(float frame, float dt) const; void copyFromNetworkAnim(); AnimationPointer _networkAnim; - std::vector _bones; + std::vector _poses; // _anim[frame][joint] - std::vector> _anim; + std::vector> _anim; std::string _url; float _startFrame; diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index 36f5c29ef3..a801b8b2bc 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -19,13 +19,6 @@ #include "AnimSkeleton.h" -struct AnimBone { - AnimBone() {} - glm::vec3 scale; - glm::quat rot; - glm::vec3 trans; -}; - class QJsonObject; class AnimNode { @@ -59,11 +52,11 @@ public: virtual ~AnimNode() {} - virtual const std::vector& evaluate(float dt) = 0; + virtual const std::vector& evaluate(float dt) = 0; protected: - std::string _id; Type _type; + std::string _id; std::vector _children; AnimSkeleton::Pointer _skeleton; diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index d9b34e3c86..2d3a5a010c 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -111,7 +111,7 @@ static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QString& jso return nullptr; } auto childrenAry = childrenValue.toArray(); - for (auto& childValue : childrenAry) { + for (const auto& childValue : childrenAry) { if (!childValue.isObject()) { qCCritical(animation) << "AnimNodeLoader, bad object in \"children\", id =" << id << ", url =" << jsonUrl; return nullptr; diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index fbd31d55b9..695dea0752 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -13,9 +13,9 @@ 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) { +int AnimSkeleton::nameToJointIndex(const QString& jointName) const { + for (size_t i = 0; i < _joints.size(); i++) { + if (_joints[i].name == jointName) { return i; } } @@ -26,11 +26,11 @@ int AnimSkeleton::getNumJoints() const { return _joints.size(); } -AnimBone getBindPose(int jointIndex) const { +AnimPose AnimSkeleton::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)); + return AnimPose(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 index a48d7dfa50..0966c2b5f7 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -14,14 +14,23 @@ #include "FBXReader.h" +struct AnimPose { + AnimPose() {} + AnimPose(const glm::vec3& scaleIn, const glm::quat& rotIn, const glm::vec3& transIn) : scale(scaleIn), rot(rotIn), trans(transIn) {} + + glm::vec3 scale; + glm::quat rot; + glm::vec3 trans; +}; + 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; -} + AnimPose getBindPose(int jointIndex) const; protected: std::vector _joints; diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 6874f3b391..1a61c426e5 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -150,4 +150,19 @@ T toNormalizedDeviceScale(const T& value, const T& size) { #define PITCH(euler) euler.x #define ROLL(euler) euler.z +template +glm::detail::tvec2 lerp(const glm::detail::tvec2& x, const glm::detail::tvec2& y, T a) { + return x * (T(1) - a) + (y * a); +} + +template +glm::detail::tvec3 lerp(const glm::detail::tvec3& x, const glm::detail::tvec3& y, T a) { + return x * (T(1) - a) + (y * a); +} + +template +glm::detail::tvec4 lerp(const glm::detail::tvec4& x, const glm::detail::tvec4& y, T a) { + return x * (T(1) - a) + (y * a); +} + #endif // hifi_GLMHelpers_h diff --git a/tests/animation/src/AnimClipTests.cpp b/tests/animation/src/AnimClipTests.cpp index bab52f17a3..aab49aa30d 100644 --- a/tests/animation/src/AnimClipTests.cpp +++ b/tests/animation/src/AnimClipTests.cpp @@ -19,6 +19,15 @@ QTEST_MAIN(AnimClipTests) const float EPSILON = 0.001f; +void AnimClipTests::initTestCase() { + auto animationCache = DependencyManager::set(); + auto resourceCacheSharedItems = DependencyManager::set(); +} + +void AnimClipTests::cleanupTestCase() { + DependencyManager::destroy(); +} + void AnimClipTests::testAccessors() { std::string id = "my anim clip"; std::string url = "foo"; @@ -57,11 +66,6 @@ void AnimClipTests::testAccessors() { QVERIFY(clip.getLoopFlag() == loopFlag2); } -static float secsToFrames(float secs) { - const float FRAMES_PER_SECOND = 30.0f; - return secs * FRAMES_PER_SECOND; -} - static float framesToSec(float secs) { const float FRAMES_PER_SECOND = 30.0f; return secs / FRAMES_PER_SECOND; @@ -92,7 +96,13 @@ void AnimClipTests::testEvaulate() { void AnimClipTests::testLoader() { AnimNodeLoader loader; - auto node = loader.load("../../../tests/animation/src/test.json"); + +#ifdef Q_OS_WIN + auto node = loader.load("../../../tests/animation/src/data/test.json"); +#else + auto node = loader.load("../../../../tests/animation/src/data/test.json"); +#endif + QVERIFY((bool)node); QVERIFY(node->getID() == "idle"); QVERIFY(node->getType() == AnimNode::ClipType); diff --git a/tests/animation/src/AnimClipTests.h b/tests/animation/src/AnimClipTests.h index 08f25c324f..f70ee31aa1 100644 --- a/tests/animation/src/AnimClipTests.h +++ b/tests/animation/src/AnimClipTests.h @@ -20,6 +20,8 @@ inline float getErrorDifference(float a, float b) { class AnimClipTests : public QObject { Q_OBJECT private slots: + void initTestCase(); + void cleanupTestCase(); void testAccessors(); void testEvaulate(); void testLoader(); diff --git a/tests/animation/src/test.json b/tests/animation/src/data/test.json similarity index 100% rename from tests/animation/src/test.json rename to tests/animation/src/data/test.json