AnimVariantMap is used in eval, MyAvatar loads avatar.json via url

This commit is contained in:
Anthony J. Thibault 2015-08-25 20:28:17 -07:00
parent 496c706bba
commit 4abf0cbd63
15 changed files with 361 additions and 151 deletions

View file

@ -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) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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

View file

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

View file

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

View file

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

View 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": []
}
]
}
}