diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 91eeb0c0d7..4e6aba85f9 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -166,6 +166,15 @@ void MyAvatar::update(float deltaTime) { if (_animNode) { static float t = 0.0f; _animVars.set("sine", 0.5f * sin(t) + 0.5f); + + if (glm::length(getVelocity()) > 0.01) { + _animVars.set("isMoving", true); + _animVars.set("isNotMoving", false); + } else { + _animVars.set("isMoving", false); + _animVars.set("isNotMoving", true); + } + t += deltaTime; _animNode->evaluate(_animVars, deltaTime); } @@ -1236,7 +1245,7 @@ void MyAvatar::setupNewAnimationSystem() { // load the anim graph // https://gist.github.com/hyperlogic/7d6a0892a7319c69e2b9 - auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/7d6a0892a7319c69e2b9/raw/c4a9223e97b1d00b423b87542a2a57895ca72d21/avatar.json"); + auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/7d6a0892a7319c69e2b9/raw/c684000794675bc84ed63efefc21870e47c58d6a/avatar.json"); _animLoader.reset(new AnimNodeLoader(graphUrl)); connect(_animLoader.get(), &AnimNodeLoader::success, [this](AnimNode::Pointer nodeIn) { _animNode = nodeIn; diff --git a/libraries/animation/src/AnimBlendLinear.cpp b/libraries/animation/src/AnimBlendLinear.cpp index 90dd85f313..c505984b87 100644 --- a/libraries/animation/src/AnimBlendLinear.cpp +++ b/libraries/animation/src/AnimBlendLinear.cpp @@ -13,7 +13,7 @@ #include "AnimUtil.h" AnimBlendLinear::AnimBlendLinear(const std::string& id, float alpha) : - AnimNode(AnimNode::BlendLinearType, id), + AnimNode(AnimNode::Type::BlendLinear, id), _alpha(alpha) { } diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 4dd89dd51a..e4eb63f23a 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -13,7 +13,7 @@ #include "AnimUtil.h" AnimClip::AnimClip(const std::string& id, const std::string& url, float startFrame, float endFrame, float timeScale, bool loopFlag) : - AnimNode(AnimNode::ClipType, id), + AnimNode(AnimNode::Type::Clip, id), _startFrame(startFrame), _endFrame(endFrame), _timeScale(timeScale), diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index b7c7826540..b6eedf9b2e 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -25,7 +25,7 @@ class QJsonObject; // Base class for all elements in the animation blend tree. // It provides the following categories of functions: // -// * id getter, id is a string name useful for debugging and searching. +// * id getter, id is used to identify a node, useful for debugging and node searching. // * type getter, helpful for determining the derived type of this node. // * hierarchy accessors, for adding, removing and iterating over child AnimNodes // * skeleton accessors, the skeleton is from the model whose bones we are going to manipulate @@ -33,15 +33,17 @@ class QJsonObject; class AnimNode { public: - friend class AnimDebugDraw; - - enum Type { - ClipType = 0, - BlendLinearType, - OverlayType, + enum class Type { + Clip = 0, + BlendLinear, + Overlay, + StateMachine, NumTypes }; - typedef std::shared_ptr Pointer; + using Pointer = std::shared_ptr; + + friend class AnimDebugDraw; + friend void buildChildMap(std::map& map, Pointer node); AnimNode(Type type, const std::string& id) : _type(type), _id(id) {} virtual ~AnimNode() {} diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 74bafb8630..e41c3550ad 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -18,31 +18,52 @@ #include "AnimationLogging.h" #include "AnimOverlay.h" #include "AnimNodeLoader.h" +#include "AnimStateMachine.h" -struct TypeInfo { - AnimNode::Type type; - const char* str; -}; - -// This will result in a compile error if someone adds a new -// item to the AnimNode::Type enum. This is by design. -static TypeInfo typeInfoArray[AnimNode::NumTypes] = { - { AnimNode::ClipType, "clip" }, - { AnimNode::BlendLinearType, "blendLinear" }, - { AnimNode::OverlayType, "overlay" } -}; - -typedef AnimNode::Pointer (*NodeLoaderFunc)(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); +using NodeLoaderFunc = AnimNode::Pointer (*)(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); +using NodeProcessFunc = bool (*)(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); +// factory functions static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); +static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); -static NodeLoaderFunc nodeLoaderFuncs[AnimNode::NumTypes] = { - loadClipNode, - loadBlendLinearNode, - loadOverlayNode -}; +// called after children have been loaded +static bool processClipNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } +static bool processBlendLinearNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } +static bool processOverlayNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } +static bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); + +static const char* animNodeTypeToString(AnimNode::Type type) { + switch (type) { + case AnimNode::Type::Clip: return "clip"; + case AnimNode::Type::BlendLinear: return "blendLinear"; + case AnimNode::Type::Overlay: return "overlay"; + case AnimNode::Type::StateMachine: return "stateMachine"; + }; + return nullptr; +} + +static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) { + switch (type) { + case AnimNode::Type::Clip: return loadClipNode; + case AnimNode::Type::BlendLinear: return loadBlendLinearNode; + case AnimNode::Type::Overlay: return loadOverlayNode; + case AnimNode::Type::StateMachine: return loadStateMachineNode; + }; + return nullptr; +} + +static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) { + switch (type) { + case AnimNode::Type::Clip: return processClipNode; + case AnimNode::Type::BlendLinear: return processBlendLinearNode; + case AnimNode::Type::Overlay: return processOverlayNode; + case AnimNode::Type::StateMachine: return processStateMachineNode; + }; + return nullptr; +} #define READ_STRING(NAME, JSON_OBJ, ID, URL) \ auto NAME##_VAL = JSON_OBJ.value(#NAME); \ @@ -82,12 +103,15 @@ static NodeLoaderFunc nodeLoaderFuncs[AnimNode::NumTypes] = { float NAME = (float)NAME##_VAL.toDouble() static AnimNode::Type stringToEnum(const QString& str) { - for (int i = 0; i < AnimNode::NumTypes; i++ ) { - if (str == typeInfoArray[i].str) { - return typeInfoArray[i].type; + // O(n), move to map when number of types becomes large. + const int NUM_TYPES = static_cast(AnimNode::Type::NumTypes); + for (int i = 0; i < NUM_TYPES; i++ ) { + AnimNode::Type type = static_cast(i); + if (str == animNodeTypeToString(type)) { + return type; } } - return AnimNode::NumTypes; + return AnimNode::Type::NumTypes; } static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QUrl& jsonUrl) { @@ -105,7 +129,7 @@ static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QUrl& jsonUr } QString typeStr = typeVal.toString(); AnimNode::Type type = stringToEnum(typeStr); - if (type == AnimNode::NumTypes) { + if (type == AnimNode::Type::NumTypes) { qCCritical(animation) << "AnimNodeLoader, unknown node type" << typeStr << ", id =" << id << ", url =" << jsonUrl.toDisplayString(); return nullptr; } @@ -117,23 +141,28 @@ static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QUrl& jsonUr } auto dataObj = dataValue.toObject(); - assert((int)type >= 0 && type < AnimNode::NumTypes); - auto node = nodeLoaderFuncs[type](dataObj, id, jsonUrl); + assert((int)type >= 0 && type < AnimNode::Type::NumTypes); + auto node = (animNodeTypeToLoaderFunc(type))(dataObj, id, jsonUrl); auto childrenValue = jsonObj.value("children"); if (!childrenValue.isArray()) { qCCritical(animation) << "AnimNodeLoader, bad array \"children\", id =" << id << ", url =" << jsonUrl.toDisplayString(); return nullptr; } - auto childrenAry = childrenValue.toArray(); - for (const auto& childValue : childrenAry) { + auto childrenArray = childrenValue.toArray(); + for (const auto& childValue : childrenArray) { if (!childValue.isObject()) { qCCritical(animation) << "AnimNodeLoader, bad object in \"children\", id =" << id << ", url =" << jsonUrl.toDisplayString(); return nullptr; } node->addChild(loadNode(childValue.toObject(), jsonUrl)); } - return node; + + if ((animNodeTypeToProcessFunc(type))(node, dataObj, id, jsonUrl)) { + return node; + } else { + return nullptr; + } } static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { @@ -230,6 +259,119 @@ static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QStri return node; } +static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { + auto node = std::make_shared(id.toStdString()); + return node; +} + +static void buildChildMap(std::map& map, AnimNode::Pointer node) { + for ( auto child : node->_children ) { + map.insert(std::pair(child->_id, child)); + } +} + +static bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& nodeId, const QUrl& jsonUrl) { + auto smNode = std::static_pointer_cast(node); + assert(smNode); + + READ_STRING(currentState, jsonObj, nodeId, jsonUrl); + + auto statesValue = jsonObj.value("states"); + if (!statesValue.isArray()) { + qCCritical(animation) << "AnimNodeLoader, bad array \"states\" in stateMachine node, id =" << nodeId << ", url =" << jsonUrl.toDisplayString(); + return nullptr; + } + + // build a map for all children by name. + std::map childMap; + buildChildMap(childMap, node); + + // first pass parse all the states and build up the state and transition map. + using StringPair = std::pair; + using TransitionMap = std::multimap; + TransitionMap transitionMap; + + using StateMap = std::map; + StateMap stateMap; + + auto statesArray = statesValue.toArray(); + for (const auto& stateValue : statesArray) { + if (!stateValue.isObject()) { + qCCritical(animation) << "AnimNodeLoader, bad state object in \"states\", id =" << nodeId << ", url =" << jsonUrl.toDisplayString(); + return nullptr; + } + auto stateObj = stateValue.toObject(); + + READ_STRING(id, stateObj, nodeId, jsonUrl); + READ_FLOAT(interpTarget, stateObj, nodeId, jsonUrl); + READ_FLOAT(interpDuration, stateObj, nodeId, jsonUrl); + + READ_OPTIONAL_STRING(interpTargetVar, stateObj); + READ_OPTIONAL_STRING(interpDurationVar, stateObj); + + auto stdId = id.toStdString(); + + auto iter = childMap.find(stdId); + if (iter == childMap.end()) { + qCCritical(animation) << "AnimNodeLoader, could not find stateMachine child (state) with nodeId =" << nodeId << "stateId =" << id << "url =" << jsonUrl.toDisplayString(); + return nullptr; + } + + auto statePtr = std::make_shared(stdId, iter->second, interpTarget, interpDuration); + assert(statePtr); + + if (!interpTargetVar.isEmpty()) { + statePtr->setInterpTargetVar(interpTargetVar.toStdString()); + } + if (!interpDurationVar.isEmpty()) { + statePtr->setInterpDurationVar(interpDurationVar.toStdString()); + } + + smNode->addState(statePtr); + stateMap.insert(StateMap::value_type(statePtr->getID(), statePtr)); + + auto transitionsValue = stateObj.value("transitions"); + if (!transitionsValue.isArray()) { + qCritical(animation) << "AnimNodeLoader, bad array \"transitions\" in stateMachine node, stateId =" << id << "nodeId =" << nodeId << "url =" << jsonUrl.toDisplayString(); + return nullptr; + } + + auto transitionsArray = transitionsValue.toArray(); + for (const auto& transitionValue : transitionsArray) { + if (!transitionValue.isObject()) { + qCritical(animation) << "AnimNodeLoader, bad transition object in \"transtions\", stateId =" << id << "nodeId =" << nodeId << "url =" << jsonUrl.toDisplayString(); + return nullptr; + } + auto transitionObj = transitionValue.toObject(); + + READ_STRING(var, transitionObj, nodeId, jsonUrl); + READ_STRING(state, transitionObj, nodeId, jsonUrl); + + transitionMap.insert(TransitionMap::value_type(statePtr, StringPair(var.toStdString(), state.toStdString()))); + } + } + + // second pass: now iterate thru all transitions and add them to the appropriate states. + for (auto& transition : transitionMap) { + AnimStateMachine::State::Pointer srcState = transition.first; + auto iter = stateMap.find(transition.second.second); + if (iter != stateMap.end()) { + srcState->addTransition(AnimStateMachine::State::Transition(transition.second.first, iter->second)); + } else { + qCCritical(animation) << "AnimNodeLoader, bad state machine transtion from srcState =" << srcState->_id.c_str() << "dstState =" << transition.second.second.c_str() << "nodeId =" << nodeId << "url = " << jsonUrl.toDisplayString(); + return nullptr; + } + } + + auto iter = stateMap.find(currentState.toStdString()); + if (iter == stateMap.end()) { + qCCritical(animation) << "AnimNodeLoader, bad currentState =" << currentState << "could not find child node" << "id =" << nodeId << "url = " << jsonUrl.toDisplayString(); + } + smNode->setCurrentState(iter->second); + + return true; +} + AnimNodeLoader::AnimNodeLoader(const QUrl& url) : _url(url), _resource(nullptr) { diff --git a/libraries/animation/src/AnimOverlay.cpp b/libraries/animation/src/AnimOverlay.cpp index 97db1711ca..21c893810a 100644 --- a/libraries/animation/src/AnimOverlay.cpp +++ b/libraries/animation/src/AnimOverlay.cpp @@ -12,7 +12,7 @@ #include AnimOverlay::AnimOverlay(const std::string& id, BoneSet boneSet, float alpha) : - AnimNode(AnimNode::OverlayType, id), _boneSet(boneSet), _alpha(alpha) { + AnimNode(AnimNode::Type::Overlay, id), _boneSet(boneSet), _alpha(alpha) { } AnimOverlay::~AnimOverlay() { diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index ef2d90fcea..3faade0dbd 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -39,12 +39,12 @@ inline QDebug operator<<(QDebug debug, const AnimPose& pose) { return debug; } -typedef std::vector AnimPoseVec; +using AnimPoseVec = std::vector; class AnimSkeleton { public: - typedef std::shared_ptr Pointer; - typedef std::shared_ptr ConstPointer; + using Pointer = std::shared_ptr; + using ConstPointer = std::shared_ptr; AnimSkeleton(const std::vector& joints); int nameToJointIndex(const QString& jointName) const; diff --git a/libraries/animation/src/AnimStateMachine.cpp b/libraries/animation/src/AnimStateMachine.cpp new file mode 100644 index 0000000000..2faea905de --- /dev/null +++ b/libraries/animation/src/AnimStateMachine.cpp @@ -0,0 +1,94 @@ +// +// AnimStateMachine.cpp +// +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "AnimStateMachine.h" +#include "AnimationLogging.h" + +AnimStateMachine::AnimStateMachine(const std::string& id) : + AnimNode(AnimNode::Type::StateMachine, id) { + +} + +AnimStateMachine::~AnimStateMachine() { + +} + + + +const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, float dt) { + + std::string desiredStateID = animVars.lookup(_currentStateVar, _currentState->getID()); + if (_currentState->getID() != desiredStateID) { + // switch states + bool foundState = false; + for (auto& state : _states) { + if (state->getID() == desiredStateID) { + switchState(state); + foundState = true; + break; + } + } + if (!foundState) { + qCCritical(animation) << "AnimStateMachine could not find state =" << desiredStateID.c_str() << ", referenced by _currentStateVar =" << _currentStateVar.c_str(); + } + } + + // evaluate currentState transitions + auto desiredState = evaluateTransitions(animVars); + if (desiredState != _currentState) { + switchState(desiredState); + } + + if (_duringInterp) { + // TODO: do interpolation + /* + const float FRAMES_PER_SECOND = 30.0f; + // blend betwen poses + blend(_poses.size(), _prevPose, _nextPose, _alpha, &_poses[0]); + */ + } else { + // eval current state + assert(_currentState); + auto currentStateNode = _currentState->getNode(); + assert(currentStateNode); + _poses = currentStateNode->evaluate(animVars, dt); + } + + qCDebug(animation) << "StateMachine::evalute"; + + return _poses; +} + +void AnimStateMachine::setCurrentState(State::Pointer state) { + _currentState = state; +} + +void AnimStateMachine::addState(State::Pointer state) { + _states.push_back(state); +} + +void AnimStateMachine::switchState(State::Pointer desiredState) { + qCDebug(animation) << "AnimStateMachine::switchState:" << _currentState->getID().c_str() << "->" << desiredState->getID().c_str(); + // TODO: interp. + _currentState = desiredState; +} + +AnimStateMachine::State::Pointer AnimStateMachine::evaluateTransitions(const AnimVariantMap& animVars) const { + assert(_currentState); + for (auto& transition : _currentState->_transitions) { + if (animVars.lookup(transition._var, false)) { + return transition._state; + } + } + return _currentState; +} + +const AnimPoseVec& AnimStateMachine::getPosesInternal() const { + return _poses; +} diff --git a/libraries/animation/src/AnimStateMachine.h b/libraries/animation/src/AnimStateMachine.h index 65c1bafdca..148429e722 100644 --- a/libraries/animation/src/AnimStateMachine.h +++ b/libraries/animation/src/AnimStateMachine.h @@ -1,66 +1,117 @@ +// +// AnimStateMachine.h +// +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// #ifndef hifi_AnimStateMachine_h #define hifi_AnimStateMachine_h -// AJT: post-pone state machine work. -#if 0 +#include +#include +#include "AnimNode.h" + class AnimStateMachine : public AnimNode { public: - friend class AnimDebugDraw; + friend class AnimNodeLoader; + friend bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& nodeId, const QUrl& jsonUrl); - AnimStateMachine(const std::string& id, float alpha); +protected: + class State { + public: + friend AnimStateMachine; + friend bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& nodeId, const QUrl& jsonUrl); + + using Pointer = std::shared_ptr; + using ConstPointer = std::shared_ptr; + + class Transition { + public: + friend AnimStateMachine; + Transition(const std::string& var, State::Pointer state) : _var(var), _state(state) {} + protected: + std::string _var; + State::Pointer _state; + }; + + State(const std::string& id, AnimNode::Pointer node, float interpTarget, float interpDuration) : + _id(id), + _node(node), + _interpTarget(interpTarget), + _interpDuration(interpDuration) {} + + void setInterpTargetVar(const std::string& interpTargetVar) { _interpTargetVar = interpTargetVar; } + void setInterpDurationVar(const std::string& interpDurationVar) { _interpDurationVar = interpDurationVar; } + + AnimNode::Pointer getNode() const { return _node; } + const std::string& getID() const { return _id; } + + protected: + + void setInterpTarget(float interpTarget) { _interpTarget = interpTarget; } + void setInterpDuration(float interpDuration) { _interpDuration = interpDuration; } + + void addTransition(const Transition& transition) { _transitions.push_back(transition); } + + std::string _id; + AnimNode::Pointer _node; + float _interpTarget; // frames + float _interpDuration; // frames + + std::string _interpTargetVar; + std::string _interpDurationVar; + + std::vector _transitions; + + private: + // no copies + State(const State&) = delete; + State& operator=(const State&) = delete; + }; + +public: + + AnimStateMachine(const std::string& id); 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); + void setCurrentStateVar(std::string& currentStateVar) { _currentStateVar = currentStateVar; } protected: + + void setCurrentState(State::Pointer state); + + void addState(State::Pointer state); + + void switchState(State::Pointer desiredState); + State::Pointer evaluateTransitions(const AnimVariantMap& animVars) const; + // for AnimDebugDraw rendering virtual const AnimPoseVec& getPosesInternal() const override; AnimPoseVec _poses; - std::vector _states; - AnimState::Pointer _currentState; - AnimState::Pointer _defaultState; + // interpolation state + bool _duringInterp = false; + float _interpFrame; + float _interpDuration; + float _alpha; + AnimPoseVec _prevPoses; + AnimPoseVec _nextPoses; + + State::Pointer _currentState; + std::vector _states; + + std::string _currentStateVar; private: // no copies - AnimStateMachine(const AnimBlendLinear&) = delete; - AnimStateMachine& operator=(const AnimBlendLinear&) = delete; + AnimStateMachine(const AnimStateMachine&) = delete; + AnimStateMachine& operator=(const AnimStateMachine&) = delete; }; -#endif -#endif // hifi_AnimBlendLinear_h +#endif // hifi_AnimStateMachine_h diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h index 7c14bfd096..15ab7e94b1 100644 --- a/libraries/animation/src/AnimVariant.h +++ b/libraries/animation/src/AnimVariant.h @@ -18,47 +18,53 @@ class AnimVariant { public: - enum Type { - BoolType = 0, - IntType, - FloatType, - Vec3Type, - QuatType, - Mat4Type, + enum class Type { + Bool = 0, + Int, + Float, + Vec3, + Quat, + Mat4, + String, NumTypes }; - AnimVariant() : _type(BoolType) { memset(&_val, 0, sizeof(_val)); } - AnimVariant(bool value) : _type(BoolType) { _val.boolVal = value; } - AnimVariant(int value) : _type(IntType) { _val.intVal = value; } - AnimVariant(float value) : _type(FloatType) { _val.floats[0] = value; } - AnimVariant(const glm::vec3& value) : _type(Vec3Type) { *reinterpret_cast(&_val) = value; } - AnimVariant(const glm::quat& value) : _type(QuatType) { *reinterpret_cast(&_val) = value; } - AnimVariant(const glm::mat4& value) : _type(Mat4Type) { *reinterpret_cast(&_val) = value; } + AnimVariant() : _type(Type::Bool) { memset(&_val, 0, sizeof(_val)); } + AnimVariant(bool value) : _type(Type::Bool) { _val.boolVal = value; } + AnimVariant(int value) : _type(Type::Int) { _val.intVal = value; } + AnimVariant(float value) : _type(Type::Float) { _val.floats[0] = value; } + AnimVariant(const glm::vec3& value) : _type(Type::Vec3) { *reinterpret_cast(&_val) = value; } + AnimVariant(const glm::quat& value) : _type(Type::Quat) { *reinterpret_cast(&_val) = value; } + AnimVariant(const glm::mat4& value) : _type(Type::Mat4) { *reinterpret_cast(&_val) = value; } + AnimVariant(const std::string& value) : _type(Type::String) { _stringVal = value; } - bool isBool() const { return _type == BoolType; } - bool isInt() const { return _type == IntType; } - bool isFloat() const { return _type == FloatType; } - bool isVec3() const { return _type == Vec3Type; } - bool isQuat() const { return _type == QuatType; } - bool isMat4() const { return _type == Mat4Type; } + bool isBool() const { return _type == Type::Bool; } + bool isInt() const { return _type == Type::Int; } + bool isFloat() const { return _type == Type::Float; } + bool isVec3() const { return _type == Type::Vec3; } + bool isQuat() const { return _type == Type::Quat; } + bool isMat4() const { return _type == Type::Mat4; } + bool isString() const { return _type == Type::String; } - void setBool(bool value) { assert(_type == BoolType); _val.boolVal = value; } - void setInt(int value) { assert(_type == IntType); _val.intVal = value; } - void setFloat(float value) { assert(_type == FloatType); _val.floats[0] = value; } - void setVec3(const glm::vec3& value) { assert(_type == Vec3Type); *reinterpret_cast(&_val) = value; } - 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; } + void setBool(bool value) { assert(_type == Type::Bool); _val.boolVal = value; } + void setInt(int value) { assert(_type == Type::Int); _val.intVal = value; } + void setFloat(float value) { assert(_type == Type::Float); _val.floats[0] = value; } + void setVec3(const glm::vec3& value) { assert(_type == Type::Vec3); *reinterpret_cast(&_val) = value; } + void setQuat(const glm::quat& value) { assert(_type == Type::Quat); *reinterpret_cast(&_val) = value; } + void setMat4(const glm::mat4& value) { assert(_type == Type::Mat4); *reinterpret_cast(&_val) = value; } + void setString(const std::string& value) { assert(_type == Type::String); _stringVal = value; } - 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); } + bool getBool() const { assert(_type == Type::Bool); return _val.boolVal; } + int getInt() const { assert(_type == Type::Int); return _val.intVal; } + float getFloat() const { assert(_type == Type::Float); return _val.floats[0]; } + const glm::vec3& getVec3() const { assert(_type == Type::Vec3); return *reinterpret_cast(&_val); } + const glm::quat& getQuat() const { assert(_type == Type::Quat); return *reinterpret_cast(&_val); } + const glm::mat4& getMat4() const { assert(_type == Type::Mat4); return *reinterpret_cast(&_val); } + const std::string& getString() const { assert(_type == Type::String); return _stringVal; } protected: Type _type; + std::string _stringVal; union { bool boolVal; int intVal; @@ -126,12 +132,22 @@ public: } } + const std::string& lookup(const std::string& key, const std::string& defaultValue) const { + if (key.empty()) { + return defaultValue; + } else { + auto iter = _map.find(key); + return iter != _map.end() ? iter->second.getString() : 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); } + void set(const std::string& key, const std::string& value) { _map[key] = AnimVariant(value); } void setTrigger(const std::string& key) { _triggers.insert(key); } void clearTirggers() { _triggers.clear(); } diff --git a/tests/animation/src/data/avatar.json b/tests/animation/src/data/avatar.json index f19ac04d56..7184e67a44 100644 --- a/tests/animation/src/data/avatar.json +++ b/tests/animation/src/data/avatar.json @@ -2,11 +2,27 @@ "version": "1.0", "root": { "id": "root", - "type": "overlay", + "type": "stateMachine", "data": { - "boneSet": "upperBody", - "alpha": 1.0, - "alphaVar": "sine" + "currentState": "idle", + "states": [ + { + "id": "idle", + "interpTarget": 3, + "interpDuration": 3, + "transitions": [ + { "var": "isMoving", "state": "walk" } + ] + }, + { + "id": "walk", + "interpTarget": 3, + "interpDuration": 3, + "transitions": [ + { "var": "isNotMoving", "state": "idle" } + ] + } + ] }, "children": [ {