mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-09 13:12:40 +02:00
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:
parent
da809efcd6
commit
f5dee717a1
10 changed files with 79 additions and 48 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue