mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-09 13:12:40 +02:00
AnimVariantMap is used in eval, MyAvatar loads avatar.json via url
This commit is contained in:
parent
496c706bba
commit
4abf0cbd63
15 changed files with 361 additions and 151 deletions
|
@ -165,10 +165,7 @@ void MyAvatar::update(float deltaTime) {
|
|||
|
||||
if (_animNode) {
|
||||
static float t = 0.0f;
|
||||
/*
|
||||
auto blend = std::static_pointer_cast<AnimBlendLinear>(_animNode);
|
||||
blend->setAlpha(0.5f * sin(t) + 0.5f);
|
||||
*/
|
||||
_animVars.set("sine", 0.5f * sin(t) + 0.5f);
|
||||
t += deltaTime;
|
||||
_animNode->evaluate(_animVars, deltaTime);
|
||||
}
|
||||
|
@ -1222,26 +1219,30 @@ void MyAvatar::initHeadBones() {
|
|||
|
||||
void MyAvatar::setupNewAnimationSystem() {
|
||||
|
||||
// create a skeleton and hand it over to the debug draw instance
|
||||
// create a skeleton
|
||||
auto geom = _skeletonModel.getGeometry()->getFBXGeometry();
|
||||
std::vector<FBXJoint> joints;
|
||||
for (auto& joint : geom.joints) {
|
||||
joints.push_back(joint);
|
||||
}
|
||||
auto skeleton = make_shared<AnimSkeleton>(joints);
|
||||
AnimPose xform(_skeletonModel.getScale(), glm::quat(), _skeletonModel.getOffset());
|
||||
AnimDebugDraw::getInstance().addSkeleton("my-avatar", skeleton, xform);
|
||||
_animSkeleton = make_shared<AnimSkeleton>(joints);
|
||||
|
||||
// create a overlay node
|
||||
auto overlay = make_shared<AnimOverlay>("overlay", AnimOverlay::UpperBodyBoneSet);
|
||||
auto idle = make_shared<AnimClip>("clip", "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx", 0.0f, 90.0f, 1.0f, true);
|
||||
auto walk = make_shared<AnimClip>("clip", "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_walk.fbx", 0.0f, 28.0f, 1.0f, true);
|
||||
overlay->addChild(idle);
|
||||
overlay->addChild(walk);
|
||||
_animNode = overlay;
|
||||
_animNode->setSkeleton(skeleton);
|
||||
xform.trans.z += 1.0f;
|
||||
AnimDebugDraw::getInstance().addAnimNode("blend", _animNode, xform);
|
||||
// add it to the debug renderer, so we can see it.
|
||||
//AnimPose xform(_skeletonModel.getScale(), glm::quat(), _skeletonModel.getOffset());
|
||||
//AnimDebugDraw::getInstance().addSkeleton("my-avatar", _animSkeleton, xform);
|
||||
|
||||
// load the anim graph
|
||||
auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/7d6a0892a7319c69e2b9/raw/a939eadee4f36248776913d42891954a8d009158/avatar.json");
|
||||
_animLoader.reset(new AnimNodeLoader(graphUrl));
|
||||
connect(_animLoader.get(), &AnimNodeLoader::success, [this](AnimNode::Pointer nodeIn) {
|
||||
_animNode = nodeIn;
|
||||
_animNode->setSkeleton(_animSkeleton);
|
||||
AnimPose xform(_skeletonModel.getScale(), glm::quat(), _skeletonModel.getOffset() + glm::vec3(0, 0, 1));
|
||||
AnimDebugDraw::getInstance().addAnimNode("node", _animNode, xform);
|
||||
});
|
||||
connect(_animLoader.get(), &AnimNodeLoader::error, [this, graphUrl](int error, QString str) {
|
||||
qCCritical(interfaceapp) << "Error loading" << graphUrl << "code = " << error << "str =" << str;
|
||||
});
|
||||
}
|
||||
|
||||
void MyAvatar::preRender(RenderArgs* renderArgs) {
|
||||
|
|
|
@ -16,8 +16,10 @@
|
|||
#include <DynamicCharacterController.h>
|
||||
#include <Rig.h>
|
||||
|
||||
#include "AnimNode.h"
|
||||
#include "AnimNodeLoader.h"
|
||||
|
||||
#include "Avatar.h"
|
||||
#include "AnimVariant.h"
|
||||
|
||||
class ModelItemID;
|
||||
class AnimNode;
|
||||
|
@ -316,6 +318,8 @@ private:
|
|||
bool _prevShouldDrawHead;
|
||||
|
||||
std::shared_ptr<AnimNode> _animNode;
|
||||
std::shared_ptr<AnimSkeleton> _animSkeleton;
|
||||
std::unique_ptr<AnimNodeLoader> _animLoader;
|
||||
AnimVariantMap _animVars;
|
||||
};
|
||||
|
||||
|
|
|
@ -22,9 +22,9 @@ AnimBlendLinear::~AnimBlendLinear() {
|
|||
|
||||
}
|
||||
|
||||
const std::vector<AnimPose>& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, float dt) {
|
||||
const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, float dt) {
|
||||
|
||||
// TODO: update _alpha from animVars
|
||||
_alpha = animVars.lookup(_alphaVar, _alpha);
|
||||
|
||||
if (_children.size() == 0) {
|
||||
for (auto&& pose : _poses) {
|
||||
|
@ -37,7 +37,11 @@ const std::vector<AnimPose>& AnimBlendLinear::evaluate(const AnimVariantMap& ani
|
|||
size_t prevPoseIndex = glm::floor(clampedAlpha);
|
||||
size_t nextPoseIndex = glm::ceil(clampedAlpha);
|
||||
float alpha = glm::fract(clampedAlpha);
|
||||
if (prevPoseIndex != nextPoseIndex) {
|
||||
if (prevPoseIndex == nextPoseIndex) {
|
||||
// this can happen if alpha is on an integer boundary
|
||||
_poses = _children[prevPoseIndex]->evaluate(animVars, dt);
|
||||
} else {
|
||||
// need to eval and blend between two children.
|
||||
auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, dt);
|
||||
auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, dt);
|
||||
|
||||
|
@ -52,6 +56,6 @@ const std::vector<AnimPose>& AnimBlendLinear::evaluate(const AnimVariantMap& ani
|
|||
}
|
||||
|
||||
// for AnimDebugDraw rendering
|
||||
const std::vector<AnimPose>& AnimBlendLinear::getPosesInternal() const {
|
||||
const AnimPoseVec& AnimBlendLinear::getPosesInternal() const {
|
||||
return _poses;
|
||||
}
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AnimNode.h"
|
||||
|
||||
#ifndef hifi_AnimBlendLinear_h
|
||||
#define hifi_AnimBlendLinear_h
|
||||
|
||||
#include "AnimNode.h"
|
||||
|
||||
// Linear blend between two AnimNodes.
|
||||
// the amount of blending is determined by the alpha parameter.
|
||||
// If the number of children is 2, then the alpha parameters should be between
|
||||
|
@ -24,23 +24,25 @@
|
|||
|
||||
class AnimBlendLinear : public AnimNode {
|
||||
public:
|
||||
friend class AnimTests;
|
||||
|
||||
AnimBlendLinear(const std::string& id, float alpha);
|
||||
virtual ~AnimBlendLinear() override;
|
||||
|
||||
virtual const std::vector<AnimPose>& evaluate(const AnimVariantMap& animVars, float dt) override;
|
||||
|
||||
void setAlpha(float alpha) { _alpha = alpha; }
|
||||
float getAlpha() const { return _alpha; }
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt) override;
|
||||
|
||||
protected:
|
||||
// for AnimDebugDraw rendering
|
||||
virtual const std::vector<AnimPose>& getPosesInternal() const override;
|
||||
void setAlphaVar(const std::string& alphaVar) { _alphaVar = alphaVar; }
|
||||
|
||||
std::vector<AnimPose> _poses;
|
||||
// for AnimDebugDraw rendering
|
||||
virtual const AnimPoseVec& getPosesInternal() const override;
|
||||
|
||||
AnimPoseVec _poses;
|
||||
|
||||
float _alpha;
|
||||
|
||||
std::string _alphaVar;
|
||||
|
||||
// no copies
|
||||
AnimBlendLinear(const AnimBlendLinear&) = delete;
|
||||
AnimBlendLinear& operator=(const AnimBlendLinear&) = delete;
|
||||
|
|
|
@ -20,38 +20,25 @@ AnimClip::AnimClip(const std::string& id, const std::string& url, float startFra
|
|||
_loopFlag(loopFlag),
|
||||
_frame(startFrame)
|
||||
{
|
||||
setURL(url);
|
||||
loadURL(url);
|
||||
}
|
||||
|
||||
AnimClip::~AnimClip() {
|
||||
|
||||
}
|
||||
|
||||
void AnimClip::setURL(const std::string& url) {
|
||||
auto animCache = DependencyManager::get<AnimationCache>();
|
||||
_networkAnim = animCache->getAnimation(QString::fromStdString(url));
|
||||
_url = url;
|
||||
}
|
||||
const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, float dt) {
|
||||
|
||||
void AnimClip::setStartFrame(float startFrame) {
|
||||
_startFrame = startFrame;
|
||||
}
|
||||
|
||||
void AnimClip::setEndFrame(float endFrame) {
|
||||
_endFrame = endFrame;
|
||||
}
|
||||
|
||||
void AnimClip::setLoopFlag(bool loopFlag) {
|
||||
_loopFlag = loopFlag;
|
||||
}
|
||||
|
||||
const std::vector<AnimPose>& AnimClip::evaluate(const AnimVariantMap& animVars, float dt) {
|
||||
|
||||
// TODO: update _frame, _startFrame, _endFrame, _timeScale, _loopFlag from animVars.
|
||||
|
||||
_frame = accumulateTime(_frame, dt);
|
||||
// lookup parameters from animVars, using current instance variables as defaults.
|
||||
_startFrame = animVars.lookup(_startFrameVar, _startFrame);
|
||||
_endFrame = animVars.lookup(_endFrameVar, _endFrame);
|
||||
_timeScale = animVars.lookup(_timeScaleVar, _timeScale);
|
||||
_loopFlag = animVars.lookup(_loopFlagVar, _loopFlag);
|
||||
_frame = accumulateTime(animVars.lookup(_frameVar, _frame), dt);
|
||||
|
||||
// poll network anim to see if it's finished loading yet.
|
||||
if (_networkAnim && _networkAnim->isLoaded() && _skeleton) {
|
||||
// loading is complete, copy animation frames from network animation, then throw it away.
|
||||
copyFromNetworkAnim();
|
||||
_networkAnim.reset();
|
||||
}
|
||||
|
@ -70,8 +57,8 @@ const std::vector<AnimPose>& AnimClip::evaluate(const AnimVariantMap& animVars,
|
|||
prevIndex = std::min(std::max(0, prevIndex), frameCount - 1);
|
||||
nextIndex = std::min(std::max(0, nextIndex), frameCount - 1);
|
||||
|
||||
const std::vector<AnimPose>& prevFrame = _anim[prevIndex];
|
||||
const std::vector<AnimPose>& nextFrame = _anim[nextIndex];
|
||||
const AnimPoseVec& prevFrame = _anim[prevIndex];
|
||||
const AnimPoseVec& nextFrame = _anim[nextIndex];
|
||||
float alpha = glm::fract(_frame);
|
||||
|
||||
blend(_poses.size(), &prevFrame[0], &nextFrame[0], alpha, &_poses[0]);
|
||||
|
@ -80,6 +67,12 @@ const std::vector<AnimPose>& AnimClip::evaluate(const AnimVariantMap& animVars,
|
|||
return _poses;
|
||||
}
|
||||
|
||||
void AnimClip::loadURL(const std::string& url) {
|
||||
auto animCache = DependencyManager::get<AnimationCache>();
|
||||
_networkAnim = animCache->getAnimation(QString::fromStdString(url));
|
||||
_url = url;
|
||||
}
|
||||
|
||||
void AnimClip::setCurrentFrameInternal(float frame) {
|
||||
const float dt = 0.0f;
|
||||
_frame = accumulateTime(frame, dt);
|
||||
|
@ -160,6 +153,6 @@ void AnimClip::copyFromNetworkAnim() {
|
|||
}
|
||||
|
||||
|
||||
const std::vector<AnimPose>& AnimClip::getPosesInternal() const {
|
||||
const AnimPoseVec& AnimClip::getPosesInternal() const {
|
||||
return _poses;
|
||||
}
|
||||
|
|
|
@ -27,46 +27,44 @@ public:
|
|||
AnimClip(const std::string& id, const std::string& url, float startFrame, float endFrame, float timeScale, bool loopFlag);
|
||||
virtual ~AnimClip() override;
|
||||
|
||||
void setURL(const std::string& url);
|
||||
const std::string& getURL() const { return _url; }
|
||||
|
||||
void setStartFrame(float startFrame);
|
||||
float getStartFrame() const { return _startFrame; }
|
||||
|
||||
void setEndFrame(float endFrame);
|
||||
float getEndFrame() const { return _endFrame; }
|
||||
|
||||
void setTimeScale(float timeScale) { _timeScale = timeScale; }
|
||||
float getTimeScale() const { return _timeScale; }
|
||||
|
||||
void setLoopFlag(bool loopFlag);
|
||||
bool getLoopFlag() const { return _loopFlag; }
|
||||
|
||||
virtual const std::vector<AnimPose>& evaluate(const AnimVariantMap& animVars, float dt) override;
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt) override;
|
||||
|
||||
protected:
|
||||
void loadURL(const std::string& url);
|
||||
|
||||
void setStartFrameVar(const std::string& startFrameVar) { _startFrameVar = startFrameVar; }
|
||||
void setEndFrameVar(const std::string& endFrameVar) { _endFrameVar = endFrameVar; }
|
||||
void setTimeScaleVar(const std::string& timeScaleVar) { _timeScaleVar = timeScaleVar; }
|
||||
void setLoopFlagVar(const std::string& loopFlagVar) { _loopFlagVar = loopFlagVar; }
|
||||
void setFrameVar(const std::string& frameVar) { _frameVar = frameVar; }
|
||||
|
||||
virtual void setCurrentFrameInternal(float frame) override;
|
||||
|
||||
float accumulateTime(float frame, float dt) const;
|
||||
void copyFromNetworkAnim();
|
||||
|
||||
// for AnimDebugDraw rendering
|
||||
virtual const std::vector<AnimPose>& getPosesInternal() const override;
|
||||
virtual const AnimPoseVec& getPosesInternal() const override;
|
||||
|
||||
AnimationPointer _networkAnim;
|
||||
std::vector<AnimPose> _poses;
|
||||
AnimPoseVec _poses;
|
||||
|
||||
// _anim[frame][joint]
|
||||
std::vector<std::vector<AnimPose>> _anim;
|
||||
std::vector<AnimPoseVec> _anim;
|
||||
|
||||
std::string _url;
|
||||
float _startFrame;
|
||||
float _endFrame;
|
||||
float _timeScale;
|
||||
bool _loopFlag;
|
||||
|
||||
float _frame;
|
||||
|
||||
std::string _startFrameVar;
|
||||
std::string _endFrameVar;
|
||||
std::string _timeScaleVar;
|
||||
std::string _loopFlagVar;
|
||||
std::string _frameVar;
|
||||
|
||||
// no copies
|
||||
AnimClip(const AnimClip&) = delete;
|
||||
AnimClip& operator=(const AnimClip&) = delete;
|
||||
|
|
|
@ -63,6 +63,7 @@ public:
|
|||
}
|
||||
int getChildCount() const { return (int)_children.size(); }
|
||||
|
||||
// pair this AnimNode graph with a skeleton.
|
||||
void setSkeleton(const AnimSkeleton::Pointer skeleton) {
|
||||
setSkeletonInternal(skeleton);
|
||||
for (auto&& child : _children) {
|
||||
|
@ -72,6 +73,13 @@ public:
|
|||
|
||||
AnimSkeleton::ConstPointer getSkeleton() const { return _skeleton; }
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt) = 0;
|
||||
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, float dt, const AnimPoseVec& underPoses) {
|
||||
return evaluate(animVars, dt);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void setCurrentFrame(float frame) {
|
||||
setCurrentFrameInternal(frame);
|
||||
for (auto&& child : _children) {
|
||||
|
@ -79,20 +87,13 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
virtual const std::vector<AnimPose>& evaluate(const AnimVariantMap& animVars, float dt) = 0;
|
||||
virtual const std::vector<AnimPose>& overlay(const AnimVariantMap& animVars, float dt, const std::vector<AnimPose>& underPoses) {
|
||||
return evaluate(animVars, dt);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
virtual void setCurrentFrameInternal(float frame) {}
|
||||
virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) {
|
||||
_skeleton = skeleton;
|
||||
}
|
||||
|
||||
// for AnimDebugDraw rendering
|
||||
virtual const std::vector<AnimPose>& getPosesInternal() const = 0;
|
||||
virtual const AnimPoseVec& getPosesInternal() const = 0;
|
||||
|
||||
Type _type;
|
||||
std::string _id;
|
||||
|
|
|
@ -11,16 +11,15 @@
|
|||
#include "AnimUtil.h"
|
||||
#include <queue>
|
||||
|
||||
AnimOverlay::AnimOverlay(const std::string& id, BoneSet boneSet) :
|
||||
AnimNode(AnimNode::OverlayType, id), _boneSet(boneSet) {
|
||||
AnimOverlay::AnimOverlay(const std::string& id, BoneSet boneSet, float alpha) :
|
||||
AnimNode(AnimNode::OverlayType, id), _boneSet(boneSet), _alpha(alpha) {
|
||||
}
|
||||
|
||||
AnimOverlay::~AnimOverlay() {
|
||||
|
||||
}
|
||||
|
||||
void AnimOverlay::setBoneSet(BoneSet boneSet) {
|
||||
_boneSet = boneSet;
|
||||
void AnimOverlay::buildBoneSet(BoneSet boneSet) {
|
||||
assert(_skeleton);
|
||||
switch (boneSet) {
|
||||
case FullBodyBoneSet: buildFullBodyBoneSet(); break;
|
||||
|
@ -37,7 +36,16 @@ void AnimOverlay::setBoneSet(BoneSet boneSet) {
|
|||
}
|
||||
}
|
||||
|
||||
const std::vector<AnimPose>& AnimOverlay::evaluate(const AnimVariantMap& animVars, float dt) {
|
||||
const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, float dt) {
|
||||
|
||||
// lookup parameters from animVars, using current instance variables as defaults.
|
||||
// NOTE: switching bonesets can be an expensive operation, let's try to avoid it.
|
||||
auto prevBoneSet = _boneSet;
|
||||
_boneSet = (BoneSet)animVars.lookup(_boneSetVar, (int)_boneSet);
|
||||
if (_boneSet != prevBoneSet && _skeleton) {
|
||||
buildBoneSet(_boneSet);
|
||||
}
|
||||
_alpha = animVars.lookup(_alphaVar, _alpha);
|
||||
|
||||
if (_children.size() >= 2) {
|
||||
auto underPoses = _children[1]->evaluate(animVars, dt);
|
||||
|
@ -48,7 +56,7 @@ const std::vector<AnimPose>& AnimOverlay::evaluate(const AnimVariantMap& animVar
|
|||
assert(_boneSetVec.size() == _poses.size());
|
||||
|
||||
for (size_t i = 0; i < _poses.size(); i++) {
|
||||
float alpha = _boneSetVec[i];
|
||||
float alpha = _boneSetVec[i] * _alpha;
|
||||
blend(1, &underPoses[i], &overPoses[i], alpha, &_poses[i]);
|
||||
}
|
||||
}
|
||||
|
@ -160,7 +168,7 @@ void AnimOverlay::buildEmptyBoneSet() {
|
|||
}
|
||||
|
||||
// for AnimDebugDraw rendering
|
||||
const std::vector<AnimPose>& AnimOverlay::getPosesInternal() const {
|
||||
const AnimPoseVec& AnimOverlay::getPosesInternal() const {
|
||||
return _poses;
|
||||
}
|
||||
|
||||
|
@ -168,5 +176,5 @@ void AnimOverlay::setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) {
|
|||
_skeleton = skeleton;
|
||||
|
||||
// we have to re-build the bone set when the skeleton changes.
|
||||
setBoneSet(_boneSet);
|
||||
buildBoneSet(_boneSet);
|
||||
}
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AnimNode.h"
|
||||
|
||||
#ifndef hifi_AnimOverlay_h
|
||||
#define hifi_AnimOverlay_h
|
||||
|
||||
|
@ -17,9 +15,13 @@
|
|||
// Overlay the AnimPoses from one AnimNode on top of another AnimNode.
|
||||
// child[0] is overlayed on top of child[1]. The boneset is used
|
||||
// to control blending on a per-bone bases.
|
||||
// alpha gives the ability to fade in and fade out overlays.
|
||||
// alpha of 0, will have no overlay, final pose will be 100% from child[1].
|
||||
// alpha of 1, will be a full overlay.
|
||||
|
||||
class AnimOverlay : public AnimNode {
|
||||
public:
|
||||
friend class AnimDebugDraw;
|
||||
|
||||
enum BoneSet {
|
||||
FullBodyBoneSet = 0,
|
||||
|
@ -35,23 +37,29 @@ public:
|
|||
NumBoneSets,
|
||||
};
|
||||
|
||||
AnimOverlay(const std::string& id, BoneSet boneSet);
|
||||
AnimOverlay(const std::string& id, BoneSet boneSet, float alpha);
|
||||
virtual ~AnimOverlay() override;
|
||||
|
||||
void setBoneSet(BoneSet boneSet);
|
||||
BoneSet getBoneSet() const { return _boneSet; }
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt) override;
|
||||
|
||||
virtual const std::vector<AnimPose>& evaluate(const AnimVariantMap& animVars, float dt) override;
|
||||
protected:
|
||||
void buildBoneSet(BoneSet boneSet);
|
||||
|
||||
void setBoneSetVar(const std::string& boneSetVar) { _boneSetVar = boneSetVar; }
|
||||
void setAlphaVar(const std::string& alphaVar) { _alphaVar = alphaVar; }
|
||||
|
||||
protected:
|
||||
// for AnimDebugDraw rendering
|
||||
virtual const std::vector<AnimPose>& getPosesInternal() const override;
|
||||
virtual const AnimPoseVec& getPosesInternal() const override;
|
||||
virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override;
|
||||
|
||||
std::vector<AnimPose> _poses;
|
||||
AnimPoseVec _poses;
|
||||
BoneSet _boneSet;
|
||||
float _alpha;
|
||||
std::vector<float> _boneSetVec;
|
||||
|
||||
std::string _boneSetVar;
|
||||
std::string _alphaVar;
|
||||
|
||||
void buildFullBodyBoneSet();
|
||||
void buildUpperBodyBoneSet();
|
||||
void buildLowerBodyBoneSet();
|
||||
|
|
|
@ -35,6 +35,8 @@ inline QDebug operator<<(QDebug debug, const AnimPose& pose) {
|
|||
return debug;
|
||||
}
|
||||
|
||||
typedef std::vector<AnimPose> AnimPoseVec;
|
||||
|
||||
class AnimSkeleton {
|
||||
public:
|
||||
typedef std::shared_ptr<AnimSkeleton> Pointer;
|
||||
|
@ -55,8 +57,8 @@ public:
|
|||
|
||||
protected:
|
||||
std::vector<FBXJoint> _joints;
|
||||
std::vector<AnimPose> _absoluteBindPoses;
|
||||
std::vector<AnimPose> _relativeBindPoses;
|
||||
AnimPoseVec _absoluteBindPoses;
|
||||
AnimPoseVec _relativeBindPoses;
|
||||
|
||||
// no copies
|
||||
AnimSkeleton(const AnimSkeleton&) = delete;
|
||||
|
|
66
libraries/animation/src/AnimStateMachine.h
Normal file
66
libraries/animation/src/AnimStateMachine.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
|
||||
#ifndef hifi_AnimStateMachine_h
|
||||
#define hifi_AnimStateMachine_h
|
||||
|
||||
// AJT: post-pone state machine work.
|
||||
#if 0
|
||||
class AnimStateMachine : public AnimNode {
|
||||
public:
|
||||
friend class AnimDebugDraw;
|
||||
|
||||
AnimStateMachine(const std::string& id, float alpha);
|
||||
virtual ~AnimStateMachine() override;
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt) override;
|
||||
|
||||
class AnimTransition;
|
||||
|
||||
class AnimState {
|
||||
public:
|
||||
typedef std::shared_ptr<AnimState> Pointer;
|
||||
AnimState(const std::string& name, AnimNode::Pointer node, float interpFrame, float interpDuration) :
|
||||
_name(name),
|
||||
_node(node),
|
||||
_interpFrame(interpFrame),
|
||||
_interpDuration(interpDuration) {}
|
||||
protected:
|
||||
std::string _name;
|
||||
AnimNode::Pointer _node;
|
||||
float _interpFrame;
|
||||
float _interpDuration;
|
||||
std::vector<AnimTransition> _transitions;
|
||||
private:
|
||||
// no copies
|
||||
AnimState(const AnimState&) = delete;
|
||||
AnimState& operator=(const AnimState&) = delete;
|
||||
};
|
||||
|
||||
class AnimTransition {
|
||||
AnimTransition(const std::string& trigger, AnimState::Pointer state) : _trigger(trigger), _state(state) {}
|
||||
protected:
|
||||
std::string _trigger;
|
||||
AnimState::Pointer _state;
|
||||
};
|
||||
|
||||
void addState(AnimState::Pointer state);
|
||||
void removeState(AnimState::Pointer state);
|
||||
void setCurrentState(AnimState::Pointer state);
|
||||
|
||||
protected:
|
||||
// for AnimDebugDraw rendering
|
||||
virtual const AnimPoseVec& getPosesInternal() const override;
|
||||
|
||||
AnimPoseVec _poses;
|
||||
|
||||
std::vector<AnimState::Pointer> _states;
|
||||
AnimState::Pointer _currentState;
|
||||
AnimState::Pointer _defaultState;
|
||||
|
||||
private:
|
||||
// no copies
|
||||
AnimStateMachine(const AnimBlendLinear&) = delete;
|
||||
AnimStateMachine& operator=(const AnimBlendLinear&) = delete;
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // hifi_AnimBlendLinear_h
|
|
@ -13,6 +13,7 @@
|
|||
#include <cassert>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <map>
|
||||
|
||||
class AnimVariant {
|
||||
public:
|
||||
|
@ -48,12 +49,12 @@ public:
|
|||
void setQuat(const glm::quat& value) { assert(_type == QuatType); *reinterpret_cast<glm::quat*>(&_val) = value; }
|
||||
void setMat4(const glm::mat4& value) { assert(_type == Mat4Type); *reinterpret_cast<glm::mat4*>(&_val) = value; }
|
||||
|
||||
bool getBool() { assert(_type == BoolType); return _val.boolVal; }
|
||||
int getInt() { assert(_type == IntType); return _val.intVal; }
|
||||
float getFloat() { assert(_type == FloatType); return _val.floats[0]; }
|
||||
const glm::vec3& getVec3() { assert(_type == Vec3Type); return *reinterpret_cast<glm::vec3*>(&_val); }
|
||||
const glm::quat& getQuat() { assert(_type == QuatType); return *reinterpret_cast<glm::quat*>(&_val); }
|
||||
const glm::mat4& getMat4() { assert(_type == Mat4Type); return *reinterpret_cast<glm::mat4*>(&_val); }
|
||||
bool getBool() const { assert(_type == BoolType); return _val.boolVal; }
|
||||
int getInt() const { assert(_type == IntType); return _val.intVal; }
|
||||
float getFloat() const { assert(_type == FloatType); return _val.floats[0]; }
|
||||
const glm::vec3& getVec3() const { assert(_type == Vec3Type); return *reinterpret_cast<const glm::vec3*>(&_val); }
|
||||
const glm::quat& getQuat() const { assert(_type == QuatType); return *reinterpret_cast<const glm::quat*>(&_val); }
|
||||
const glm::mat4& getMat4() const { assert(_type == Mat4Type); return *reinterpret_cast<const glm::mat4*>(&_val); }
|
||||
|
||||
protected:
|
||||
Type _type;
|
||||
|
@ -64,6 +65,72 @@ protected:
|
|||
} _val;
|
||||
};
|
||||
|
||||
typedef std::map<std::string, AnimVariant> AnimVariantMap;
|
||||
class AnimVariantMap {
|
||||
public:
|
||||
|
||||
bool lookup(const std::string& key, bool defaultValue) const {
|
||||
if (key.empty()) {
|
||||
return defaultValue;
|
||||
} else {
|
||||
auto iter = _map.find(key);
|
||||
return iter != _map.end() ? iter->second.getBool() : defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
int lookup(const std::string& key, int defaultValue) const {
|
||||
if (key.empty()) {
|
||||
return defaultValue;
|
||||
} else {
|
||||
auto iter = _map.find(key);
|
||||
return iter != _map.end() ? iter->second.getInt() : defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
float lookup(const std::string& key, float defaultValue) const {
|
||||
if (key.empty()) {
|
||||
return defaultValue;
|
||||
} else {
|
||||
auto iter = _map.find(key);
|
||||
return iter != _map.end() ? iter->second.getFloat() : defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
const glm::vec3& lookup(const std::string& key, const glm::vec3& defaultValue) const {
|
||||
if (key.empty()) {
|
||||
return defaultValue;
|
||||
} else {
|
||||
auto iter = _map.find(key);
|
||||
return iter != _map.end() ? iter->second.getVec3() : defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
const glm::quat& lookup(const std::string& key, const glm::quat& defaultValue) const {
|
||||
if (key.empty()) {
|
||||
return defaultValue;
|
||||
} else {
|
||||
auto iter = _map.find(key);
|
||||
return iter != _map.end() ? iter->second.getQuat() : defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
const glm::mat4& lookup(const std::string& key, const glm::mat4& defaultValue) const {
|
||||
if (key.empty()) {
|
||||
return defaultValue;
|
||||
} else {
|
||||
auto iter = _map.find(key);
|
||||
return iter != _map.end() ? iter->second.getMat4() : defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
void set(const std::string& key, bool value) { _map[key] = AnimVariant(value); }
|
||||
void set(const std::string& key, int value) { _map[key] = AnimVariant(value); }
|
||||
void set(const std::string& key, float value) { _map[key] = AnimVariant(value); }
|
||||
void set(const std::string& key, const glm::vec3& value) { _map[key] = AnimVariant(value); }
|
||||
void set(const std::string& key, const glm::quat& value) { _map[key] = AnimVariant(value); }
|
||||
void set(const std::string& key, const glm::mat4& value) { _map[key] = AnimVariant(value); }
|
||||
|
||||
protected:
|
||||
std::map<std::string, AnimVariant> _map;
|
||||
};
|
||||
|
||||
#endif // hifi_AnimVariant_h
|
||||
|
|
|
@ -29,7 +29,7 @@ void AnimTests::cleanupTestCase() {
|
|||
DependencyManager::destroy<AnimationCache>();
|
||||
}
|
||||
|
||||
void AnimTests::testAccessors() {
|
||||
void AnimTests::testClipInternalState() {
|
||||
std::string id = "my anim clip";
|
||||
std::string url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx";
|
||||
float startFrame = 2.0f;
|
||||
|
@ -42,29 +42,11 @@ void AnimTests::testAccessors() {
|
|||
QVERIFY(clip.getID() == id);
|
||||
QVERIFY(clip.getType() == AnimNode::ClipType);
|
||||
|
||||
QVERIFY(clip.getURL() == url);
|
||||
QVERIFY(clip.getStartFrame() == startFrame);
|
||||
QVERIFY(clip.getEndFrame() == endFrame);
|
||||
QVERIFY(clip.getTimeScale() == timeScale);
|
||||
QVERIFY(clip.getLoopFlag() == loopFlag);
|
||||
|
||||
std::string url2 = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_walk.fbx";
|
||||
float startFrame2 = 22.0f;
|
||||
float endFrame2 = 100.0f;
|
||||
float timeScale2 = 1.2f;
|
||||
bool loopFlag2 = false;
|
||||
|
||||
clip.setURL(url2);
|
||||
clip.setStartFrame(startFrame2);
|
||||
clip.setEndFrame(endFrame2);
|
||||
clip.setTimeScale(timeScale2);
|
||||
clip.setLoopFlag(loopFlag2);
|
||||
|
||||
QVERIFY(clip.getURL() == url2);
|
||||
QVERIFY(clip.getStartFrame() == startFrame2);
|
||||
QVERIFY(clip.getEndFrame() == endFrame2);
|
||||
QVERIFY(clip.getTimeScale() == timeScale2);
|
||||
QVERIFY(clip.getLoopFlag() == loopFlag2);
|
||||
QVERIFY(clip._url == url);
|
||||
QVERIFY(clip._startFrame == startFrame);
|
||||
QVERIFY(clip._endFrame == endFrame);
|
||||
QVERIFY(clip._timeScale == timeScale);
|
||||
QVERIFY(clip._loopFlag == loopFlag);
|
||||
}
|
||||
|
||||
static float framesToSec(float secs) {
|
||||
|
@ -72,7 +54,7 @@ static float framesToSec(float secs) {
|
|||
return secs / FRAMES_PER_SECOND;
|
||||
}
|
||||
|
||||
void AnimTests::testEvaulate() {
|
||||
void AnimTests::testClipEvaulate() {
|
||||
std::string id = "my clip node";
|
||||
std::string url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx";
|
||||
float startFrame = 2.0f;
|
||||
|
@ -81,6 +63,7 @@ void AnimTests::testEvaulate() {
|
|||
float loopFlag = true;
|
||||
|
||||
auto vars = AnimVariantMap();
|
||||
vars.set("FalseVar", false);
|
||||
|
||||
AnimClip clip(id, url, startFrame, endFrame, timeScale, loopFlag);
|
||||
|
||||
|
@ -92,11 +75,46 @@ void AnimTests::testEvaulate() {
|
|||
QCOMPARE_WITH_ABS_ERROR(clip._frame, 3.0f, EPSILON);
|
||||
|
||||
// does it pause at end?
|
||||
clip.setLoopFlag(false);
|
||||
clip.setLoopFlagVar("FalseVar");
|
||||
clip.evaluate(vars, framesToSec(20.0f));
|
||||
QCOMPARE_WITH_ABS_ERROR(clip._frame, 22.0f, EPSILON);
|
||||
}
|
||||
|
||||
void AnimTests::testClipEvaulateWithVars() {
|
||||
std::string id = "my clip node";
|
||||
std::string url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx";
|
||||
float startFrame = 2.0f;
|
||||
float endFrame = 22.0f;
|
||||
float timeScale = 1.0f;
|
||||
float loopFlag = true;
|
||||
|
||||
float startFrame2 = 22.0f;
|
||||
float endFrame2 = 100.0f;
|
||||
float timeScale2 = 1.2f;
|
||||
bool loopFlag2 = false;
|
||||
|
||||
auto vars = AnimVariantMap();
|
||||
vars.set("startFrame2", startFrame2);
|
||||
vars.set("endFrame2", endFrame2);
|
||||
vars.set("timeScale2", timeScale2);
|
||||
vars.set("loopFlag2", loopFlag2);
|
||||
|
||||
AnimClip clip(id, url, startFrame, endFrame, timeScale, loopFlag);
|
||||
clip.setStartFrameVar("startFrame2");
|
||||
clip.setEndFrameVar("endFrame2");
|
||||
clip.setTimeScaleVar("timeScale2");
|
||||
clip.setLoopFlagVar("loopFlag2");
|
||||
|
||||
clip.evaluate(vars, framesToSec(0.1f));
|
||||
|
||||
// verify that the values from the AnimVariantMap made it into the clipNode's
|
||||
// internal state
|
||||
QVERIFY(clip._startFrame == startFrame2);
|
||||
QVERIFY(clip._endFrame == endFrame2);
|
||||
QVERIFY(clip._timeScale == timeScale2);
|
||||
QVERIFY(clip._loopFlag == loopFlag2);
|
||||
}
|
||||
|
||||
void AnimTests::testLoader() {
|
||||
auto url = QUrl("https://gist.githubusercontent.com/hyperlogic/857129fe04567cbe670f/raw/8ba57a8f0a76f88b39a11f77f8d9df04af9cec95/test.json");
|
||||
AnimNodeLoader loader(url);
|
||||
|
@ -126,7 +144,7 @@ void AnimTests::testLoader() {
|
|||
QVERIFY(node->getType() == AnimNode::BlendLinearType);
|
||||
|
||||
auto blend = std::static_pointer_cast<AnimBlendLinear>(node);
|
||||
QVERIFY(blend->getAlpha() == 0.5f);
|
||||
QVERIFY(blend->_alpha == 0.5f);
|
||||
|
||||
QVERIFY(node->getChildCount() == 3);
|
||||
|
||||
|
@ -140,18 +158,18 @@ void AnimTests::testLoader() {
|
|||
QVERIFY(nodes[2]->getChildCount() == 0);
|
||||
|
||||
auto test01 = std::static_pointer_cast<AnimClip>(nodes[0]);
|
||||
QVERIFY(test01->getURL() == "test01.fbx");
|
||||
QVERIFY(test01->getStartFrame() == 1.0f);
|
||||
QVERIFY(test01->getEndFrame() == 20.0f);
|
||||
QVERIFY(test01->getTimeScale() == 1.0f);
|
||||
QVERIFY(test01->getLoopFlag() == false);
|
||||
QVERIFY(test01->_url == "test01.fbx");
|
||||
QVERIFY(test01->_startFrame == 1.0f);
|
||||
QVERIFY(test01->_endFrame == 20.0f);
|
||||
QVERIFY(test01->_timeScale == 1.0f);
|
||||
QVERIFY(test01->_loopFlag == false);
|
||||
|
||||
auto test02 = std::static_pointer_cast<AnimClip>(nodes[1]);
|
||||
QVERIFY(test02->getURL() == "test02.fbx");
|
||||
QVERIFY(test02->getStartFrame() == 2.0f);
|
||||
QVERIFY(test02->getEndFrame() == 21.0f);
|
||||
QVERIFY(test02->getTimeScale() == 0.9f);
|
||||
QVERIFY(test02->getLoopFlag() == true);
|
||||
QVERIFY(test02->_url == "test02.fbx");
|
||||
QVERIFY(test02->_startFrame == 2.0f);
|
||||
QVERIFY(test02->_endFrame == 21.0f);
|
||||
QVERIFY(test02->_timeScale == 0.9f);
|
||||
QVERIFY(test02->_loopFlag == true);
|
||||
}
|
||||
|
||||
void AnimTests::testVariant() {
|
||||
|
|
|
@ -18,8 +18,9 @@ class AnimTests : public QObject {
|
|||
private slots:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
void testAccessors();
|
||||
void testEvaulate();
|
||||
void testClipInternalState();
|
||||
void testClipEvaulate();
|
||||
void testClipEvaulateWithVars();
|
||||
void testLoader();
|
||||
void testVariant();
|
||||
};
|
||||
|
|
37
tests/animation/src/data/avatar.json
Normal file
37
tests/animation/src/data/avatar.json
Normal file
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"version": "1.0",
|
||||
"root": {
|
||||
"id": "root",
|
||||
"type": "overlay",
|
||||
"data": {
|
||||
"boneSet": "upperBody",
|
||||
"alpha": 1.0
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "idle",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 90.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "walk",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_walk.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 28.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue