From 4abf0cbd63a5c7f178632cf493bdff892cba8450 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 25 Aug 2015 20:28:17 -0700 Subject: [PATCH] AnimVariantMap is used in eval, MyAvatar loads avatar.json via url --- interface/src/avatar/MyAvatar.cpp | 37 +++++---- interface/src/avatar/MyAvatar.h | 6 +- libraries/animation/src/AnimBlendLinear.cpp | 12 ++- libraries/animation/src/AnimBlendLinear.h | 20 +++-- libraries/animation/src/AnimClip.cpp | 45 +++++----- libraries/animation/src/AnimClip.h | 38 ++++----- libraries/animation/src/AnimNode.h | 17 ++-- libraries/animation/src/AnimOverlay.cpp | 24 ++++-- libraries/animation/src/AnimOverlay.h | 26 ++++-- libraries/animation/src/AnimSkeleton.h | 6 +- libraries/animation/src/AnimStateMachine.h | 66 +++++++++++++++ libraries/animation/src/AnimVariant.h | 81 ++++++++++++++++-- tests/animation/src/AnimTests.cpp | 92 ++++++++++++--------- tests/animation/src/AnimTests.h | 5 +- tests/animation/src/data/avatar.json | 37 +++++++++ 15 files changed, 361 insertions(+), 151 deletions(-) create mode 100644 libraries/animation/src/AnimStateMachine.h create mode 100644 tests/animation/src/data/avatar.json diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 15bde7fe2c..48fb2c1801 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -165,10 +165,7 @@ void MyAvatar::update(float deltaTime) { if (_animNode) { static float t = 0.0f; - /* - auto blend = std::static_pointer_cast(_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 joints; for (auto& joint : geom.joints) { joints.push_back(joint); } - auto skeleton = make_shared(joints); - AnimPose xform(_skeletonModel.getScale(), glm::quat(), _skeletonModel.getOffset()); - AnimDebugDraw::getInstance().addSkeleton("my-avatar", skeleton, xform); + _animSkeleton = make_shared(joints); - // create a overlay node - auto overlay = make_shared("overlay", AnimOverlay::UpperBodyBoneSet); - auto idle = make_shared("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("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) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 0adc901398..3946edbaba 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -16,8 +16,10 @@ #include #include +#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; + std::shared_ptr _animSkeleton; + std::unique_ptr _animLoader; AnimVariantMap _animVars; }; diff --git a/libraries/animation/src/AnimBlendLinear.cpp b/libraries/animation/src/AnimBlendLinear.cpp index 8ce6845e6f..90dd85f313 100644 --- a/libraries/animation/src/AnimBlendLinear.cpp +++ b/libraries/animation/src/AnimBlendLinear.cpp @@ -22,9 +22,9 @@ AnimBlendLinear::~AnimBlendLinear() { } -const std::vector& 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& 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& AnimBlendLinear::evaluate(const AnimVariantMap& ani } // for AnimDebugDraw rendering -const std::vector& AnimBlendLinear::getPosesInternal() const { +const AnimPoseVec& AnimBlendLinear::getPosesInternal() const { return _poses; } diff --git a/libraries/animation/src/AnimBlendLinear.h b/libraries/animation/src/AnimBlendLinear.h index 784d89b04c..85799ad3e3 100644 --- a/libraries/animation/src/AnimBlendLinear.h +++ b/libraries/animation/src/AnimBlendLinear.h @@ -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& 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& getPosesInternal() const override; + void setAlphaVar(const std::string& alphaVar) { _alphaVar = alphaVar; } - std::vector _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; diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 7653aabde8..51e32143be 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -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(); - _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& 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& 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& prevFrame = _anim[prevIndex]; - const std::vector& 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& AnimClip::evaluate(const AnimVariantMap& animVars, return _poses; } +void AnimClip::loadURL(const std::string& url) { + auto animCache = DependencyManager::get(); + _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& AnimClip::getPosesInternal() const { +const AnimPoseVec& AnimClip::getPosesInternal() const { return _poses; } diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index 0d4f104678..e4651e0ece 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -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& 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& getPosesInternal() const override; + virtual const AnimPoseVec& getPosesInternal() const override; AnimationPointer _networkAnim; - std::vector _poses; + AnimPoseVec _poses; // _anim[frame][joint] - std::vector> _anim; + std::vector _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; diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index c4edd9bb06..b7c7826540 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -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& evaluate(const AnimVariantMap& animVars, float dt) = 0; - virtual const std::vector& overlay(const AnimVariantMap& animVars, float dt, const std::vector& 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& getPosesInternal() const = 0; + virtual const AnimPoseVec& getPosesInternal() const = 0; Type _type; std::string _id; diff --git a/libraries/animation/src/AnimOverlay.cpp b/libraries/animation/src/AnimOverlay.cpp index 3a76903d0f..97db1711ca 100644 --- a/libraries/animation/src/AnimOverlay.cpp +++ b/libraries/animation/src/AnimOverlay.cpp @@ -11,16 +11,15 @@ #include "AnimUtil.h" #include -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& 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& 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& 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); } diff --git a/libraries/animation/src/AnimOverlay.h b/libraries/animation/src/AnimOverlay.h index 87d05e2b96..2fe25d430e 100644 --- a/libraries/animation/src/AnimOverlay.h +++ b/libraries/animation/src/AnimOverlay.h @@ -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& 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& getPosesInternal() const override; + virtual const AnimPoseVec& getPosesInternal() const override; virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override; - std::vector _poses; + AnimPoseVec _poses; BoneSet _boneSet; + float _alpha; std::vector _boneSetVec; + std::string _boneSetVar; + std::string _alphaVar; + void buildFullBodyBoneSet(); void buildUpperBodyBoneSet(); void buildLowerBodyBoneSet(); diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 468abdc359..02ecd5ea7e 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -35,6 +35,8 @@ inline QDebug operator<<(QDebug debug, const AnimPose& pose) { return debug; } +typedef std::vector AnimPoseVec; + class AnimSkeleton { public: typedef std::shared_ptr Pointer; @@ -55,8 +57,8 @@ public: protected: std::vector _joints; - std::vector _absoluteBindPoses; - std::vector _relativeBindPoses; + AnimPoseVec _absoluteBindPoses; + AnimPoseVec _relativeBindPoses; // no copies AnimSkeleton(const AnimSkeleton&) = delete; diff --git a/libraries/animation/src/AnimStateMachine.h b/libraries/animation/src/AnimStateMachine.h new file mode 100644 index 0000000000..65c1bafdca --- /dev/null +++ b/libraries/animation/src/AnimStateMachine.h @@ -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 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 _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 _states; + AnimState::Pointer _currentState; + AnimState::Pointer _defaultState; + +private: + // no copies + AnimStateMachine(const AnimBlendLinear&) = delete; + AnimStateMachine& operator=(const AnimBlendLinear&) = delete; +}; +#endif + +#endif // hifi_AnimBlendLinear_h diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h index 627e3210f4..36d189f85c 100644 --- a/libraries/animation/src/AnimVariant.h +++ b/libraries/animation/src/AnimVariant.h @@ -13,6 +13,7 @@ #include #include #include +#include class AnimVariant { public: @@ -48,12 +49,12 @@ public: void setQuat(const glm::quat& value) { assert(_type == QuatType); *reinterpret_cast(&_val) = value; } void setMat4(const glm::mat4& value) { assert(_type == Mat4Type); *reinterpret_cast(&_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(&_val); } - const glm::quat& getQuat() { assert(_type == QuatType); return *reinterpret_cast(&_val); } - const glm::mat4& getMat4() { assert(_type == Mat4Type); return *reinterpret_cast(&_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(&_val); } + const glm::quat& getQuat() const { assert(_type == QuatType); return *reinterpret_cast(&_val); } + const glm::mat4& getMat4() const { assert(_type == Mat4Type); return *reinterpret_cast(&_val); } protected: Type _type; @@ -64,6 +65,72 @@ protected: } _val; }; -typedef std::map 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 _map; +}; #endif // hifi_AnimVariant_h diff --git a/tests/animation/src/AnimTests.cpp b/tests/animation/src/AnimTests.cpp index a25ce4bc0a..91bb1ce5bb 100644 --- a/tests/animation/src/AnimTests.cpp +++ b/tests/animation/src/AnimTests.cpp @@ -29,7 +29,7 @@ void AnimTests::cleanupTestCase() { DependencyManager::destroy(); } -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(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(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(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() { diff --git a/tests/animation/src/AnimTests.h b/tests/animation/src/AnimTests.h index 460caa067d..e667444657 100644 --- a/tests/animation/src/AnimTests.h +++ b/tests/animation/src/AnimTests.h @@ -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(); }; diff --git a/tests/animation/src/data/avatar.json b/tests/animation/src/data/avatar.json new file mode 100644 index 0000000000..5dca9d3e16 --- /dev/null +++ b/tests/animation/src/data/avatar.json @@ -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": [] + } + ] + } +}