mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 03:24:00 +02:00
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:
parent
343b2ccf9d
commit
da809efcd6
7 changed files with 185 additions and 24 deletions
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
36
libraries/animation/src/AnimSkeleton.cpp
Normal file
36
libraries/animation/src/AnimSkeleton.cpp
Normal 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));
|
||||
}
|
||||
|
||||
|
30
libraries/animation/src/AnimSkeleton.h
Normal file
30
libraries/animation/src/AnimSkeleton.h
Normal 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
|
|
@ -347,4 +347,3 @@ QRectF glmToRect(const glm::vec2 & pos, const glm::vec2 & size) {
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue