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.
This commit is contained in:
Anthony J. Thibault 2015-07-31 19:06:50 -07:00
parent 343b2ccf9d
commit da809efcd6
7 changed files with 185 additions and 24 deletions

View file

@ -27,7 +27,7 @@ AnimClip::~AnimClip() {
}
void AnimClip::setURL(const std::string& url) {
// TODO:
_networkAnim = DependencyManager::get<AnimationCache>()->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<AnimBone>& 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<AnimBone>& prevFrame = _anim[prevIndex];
const std::vector<AnimBone>& 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<FBXJoint>& joints = geom.joints;
std::vector<int> 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];
}
}
}
}

View file

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

View file

@ -1,5 +1,5 @@
//
// AnimInterface.h
// AnimNode.h
//
// Copyright 2015 High Fidelity, Inc.
//
@ -14,8 +14,18 @@
#include <vector>
#include <memory>
#include <cassert>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#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<AnimNode> 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<AnimNode> child) { _children.push_back(child); }
void removeChild(std::shared_ptr<AnimNode> 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<AnimNode>& 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<AnimBone>& evaluate(float dt) = 0;
protected:
std::string _id;
Type _type;
std::vector<std::shared_ptr<AnimNode>> _children;
std::vector<AnimNode::Pointer> _children;
AnimSkeleton::Pointer _skeleton;
// no copies
AnimNode(const AnimNode&) = delete;

View file

@ -28,9 +28,9 @@ static TypeInfo typeInfoArray[AnimNode::NumTypes] = {
{ AnimNode::ClipType, "clip" }
};
typedef std::shared_ptr<AnimNode> (*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<AnimNode> 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<AnimNode> 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<AnimNode> loadNode(const QJsonObject& jsonObj, const QStr
return node;
}
static std::shared_ptr<AnimNode> 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<AnimNode> loadClipNode(const QJsonObject& jsonObj, const
return std::make_shared<AnimClip>(id.toStdString(), url.toStdString(), startFrame, endFrame, timeScale, loopFlag);
}
std::shared_ptr<AnimNode> 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;

View file

@ -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<FBXJoint>& 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));
}

View file

@ -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 <vector>
#include "FBXReader.h"
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;
}
protected:
std::vector<FBXJoint> _joints;
};
#endif

View file

@ -347,4 +347,3 @@ QRectF glmToRect(const glm::vec2 & pos, const glm::vec2 & size) {
return result;
}