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!
This commit is contained in:
Anthony J. Thibault 2015-07-31 22:08:39 -07:00 committed by Anthony J. Thibault
parent da809efcd6
commit f5dee717a1
10 changed files with 79 additions and 48 deletions

View file

@ -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<AnimationCache>()->getAnimation(QString::fromStdString(url));
auto animCache = DependencyManager::get<AnimationCache>();
_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<AnimBone>& AnimClip::evaluate(float dt) {
const std::vector<AnimPose>& 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<AnimBone>& 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<AnimBone>& prevFrame = _anim[prevIndex];
const std::vector<AnimBone>& nextFrame = _anim[nextIndex];
const std::vector<AnimPose>& prevFrame = _anim[prevIndex];
const std::vector<AnimPose>& 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

View file

@ -36,17 +36,17 @@ public:
void setLoopFlag(bool loopFlag);
bool getLoopFlag() const { return _loopFlag; }
virtual const std::vector<AnimBone>& evaluate(float dt);
virtual const std::vector<AnimPose>& evaluate(float dt);
protected:
float accumulateTime(float frame, float dt) const;
void copyFromNetworkAnim();
AnimationPointer _networkAnim;
std::vector<AnimBone> _bones;
std::vector<AnimPose> _poses;
// _anim[frame][joint]
std::vector<std::vector<AnimBone>> _anim;
std::vector<std::vector<AnimPose>> _anim;
std::string _url;
float _startFrame;

View file

@ -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<AnimBone>& evaluate(float dt) = 0;
virtual const std::vector<AnimPose>& evaluate(float dt) = 0;
protected:
std::string _id;
Type _type;
std::string _id;
std::vector<AnimNode::Pointer> _children;
AnimSkeleton::Pointer _skeleton;

View file

@ -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;

View file

@ -13,9 +13,9 @@ AnimSkeleton::AnimSkeleton(const std::vector<FBXJoint>& 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));
}

View file

@ -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<AnimSkeleton> Pointer;
AnimSkeleton(const std::vector<FBXJoint>& joints);
int nameToJointIndex(const QString& jointName) const;
int getNumJoints() const;
AnimBone getBindPose(int jointIndex) const;
}
AnimPose getBindPose(int jointIndex) const;
protected:
std::vector<FBXJoint> _joints;

View file

@ -150,4 +150,19 @@ T toNormalizedDeviceScale(const T& value, const T& size) {
#define PITCH(euler) euler.x
#define ROLL(euler) euler.z
template<typename T, glm::precision P>
glm::detail::tvec2<T, P> lerp(const glm::detail::tvec2<T, P>& x, const glm::detail::tvec2<T, P>& y, T a) {
return x * (T(1) - a) + (y * a);
}
template<typename T, glm::precision P>
glm::detail::tvec3<T, P> lerp(const glm::detail::tvec3<T, P>& x, const glm::detail::tvec3<T, P>& y, T a) {
return x * (T(1) - a) + (y * a);
}
template<typename T, glm::precision P>
glm::detail::tvec4<T, P> lerp(const glm::detail::tvec4<T, P>& x, const glm::detail::tvec4<T, P>& y, T a) {
return x * (T(1) - a) + (y * a);
}
#endif // hifi_GLMHelpers_h

View file

@ -19,6 +19,15 @@ QTEST_MAIN(AnimClipTests)
const float EPSILON = 0.001f;
void AnimClipTests::initTestCase() {
auto animationCache = DependencyManager::set<AnimationCache>();
auto resourceCacheSharedItems = DependencyManager::set<ResourceCacheSharedItems>();
}
void AnimClipTests::cleanupTestCase() {
DependencyManager::destroy<AnimationCache>();
}
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);

View file

@ -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();