From 6274ab4aa7a2bdca59b2152b4a3b41965bcb94ae Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Sat, 31 Aug 2019 09:39:52 -0700 Subject: [PATCH] 3x3 blend wip --- .../animation/src/AnimBlendDirectional.cpp | 160 ++++++++++++++++++ .../animation/src/AnimBlendDirectional.h | 67 ++++++++ libraries/animation/src/AnimContext.h | 1 + libraries/animation/src/AnimNode.h | 9 + libraries/animation/src/AnimNodeLoader.cpp | 37 ++++ 5 files changed, 274 insertions(+) create mode 100644 libraries/animation/src/AnimBlendDirectional.cpp create mode 100644 libraries/animation/src/AnimBlendDirectional.h diff --git a/libraries/animation/src/AnimBlendDirectional.cpp b/libraries/animation/src/AnimBlendDirectional.cpp new file mode 100644 index 0000000000..36175c30d5 --- /dev/null +++ b/libraries/animation/src/AnimBlendDirectional.cpp @@ -0,0 +1,160 @@ +// +// AnimBlendDirectional.cpp +// +// Created by Anthony J. Thibault on Augest 30 2019. +// Copyright (c) 2019 High Fidelity, Inc. All rights reserved. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "AnimBlendDirectional.h" +#include "GLMHelpers.h" +#include "AnimationLogging.h" +#include "AnimUtil.h" + +AnimBlendDirectional::AnimBlendDirectional(const QString& id, float alpha, const QString& centerId, + const QString& upId, const QString& downId, const QString& leftId, const QString& rightId, + const QString& upLeftId, const QString& upRightId, const QString& downLeftId, const QString& downRightId) : + AnimNode(AnimNode::Type::BlendDirectional, id), + _alpha(alpha), + _upId(upId), + _downId(downId), + _leftId(leftId), + _rightId(rightId), + _upLeftId(upLeftId), + _upRightId(upRightId), + _downLeftId(downLeftId), + _downRightId(downRightId) { + +} + +AnimBlendDirectional::~AnimBlendDirectional() { + +} + +const AnimPoseVec& AnimBlendDirectional::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) { + + _alpha = animVars.lookup(_alphaVar, _alpha); + float parentDebugAlpha = context.getDebugAlpha(_id); + + /* + if (_children.size() == 0) { + for (auto&& pose : _poses) { + pose = AnimPose::identity; + } + } else if (_children.size() == 1) { + _poses = _children[0]->evaluate(animVars, context, dt, triggersOut); + context.setDebugAlpha(_children[0]->getID(), parentDebugAlpha, _children[0]->getType()); + } else if (_children.size() == 2 && _blendType != AnimBlendType_Normal) { + // special case for additive blending + float alpha = glm::clamp(_alpha, 0.0f, 1.0f); + const size_t prevPoseIndex = 0; + const size_t nextPoseIndex = 1; + evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, dt); + + // for animation stack debugging + float weight2 = alpha; + float weight1 = 1.0f - weight2; + context.setDebugAlpha(_children[prevPoseIndex]->getID(), weight1 * parentDebugAlpha, _children[prevPoseIndex]->getType()); + context.setDebugAlpha(_children[nextPoseIndex]->getID(), weight2 * parentDebugAlpha, _children[nextPoseIndex]->getType()); + + } else { + float clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1)); + size_t prevPoseIndex = glm::floor(clampedAlpha); + size_t nextPoseIndex = glm::ceil(clampedAlpha); + auto alpha = glm::fract(clampedAlpha); + evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, dt); + + // weights are for animation stack debug purposes only. + float weight1 = 0.0f; + float weight2 = 0.0f; + if (prevPoseIndex == nextPoseIndex) { + weight2 = 1.0f; + context.setDebugAlpha(_children[nextPoseIndex]->getID(), weight2 * parentDebugAlpha, _children[nextPoseIndex]->getType()); + } else { + weight2 = alpha; + weight1 = 1.0f - weight2; + context.setDebugAlpha(_children[prevPoseIndex]->getID(), weight1 * parentDebugAlpha, _children[prevPoseIndex]->getType()); + context.setDebugAlpha(_children[nextPoseIndex]->getID(), weight2 * parentDebugAlpha, _children[nextPoseIndex]->getType()); + } + } + processOutputJoints(triggersOut); + */ + + return _poses; +} + +// for AnimDebugDraw rendering +const AnimPoseVec& AnimBlendDirectional::getPosesInternal() const { + return _poses; +} + +/* +void AnimBlendDirectional::evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, AnimVariantMap& triggersOut, float alpha, + size_t prevPoseIndex, size_t nextPoseIndex, float dt) { + if (prevPoseIndex == nextPoseIndex) { + // this can happen if alpha is on an integer boundary + _poses = _children[prevPoseIndex]->evaluate(animVars, context, dt, triggersOut); + } else { + // need to eval and blend between two children. + auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, context, dt, triggersOut); + auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, context, dt, triggersOut); + + if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) { + _poses.resize(prevPoses.size()); + + if (_blendType == AnimBlendType_Normal) { + ::blend(_poses.size(), &prevPoses[0], &nextPoses[0], alpha, &_poses[0]); + } else if (_blendType == AnimBlendType_AddRelative) { + ::blendAdd(_poses.size(), &prevPoses[0], &nextPoses[0], alpha, &_poses[0]); + } else if (_blendType == AnimBlendType_AddAbsolute) { + // convert prev from relative to absolute + AnimPoseVec absPrev = prevPoses; + _skeleton->convertRelativePosesToAbsolute(absPrev); + + // rotate the offset rotations from next into the parent relative frame of each joint. + AnimPoseVec relOffsetPoses; + relOffsetPoses.reserve(nextPoses.size()); + for (size_t i = 0; i < nextPoses.size(); ++i) { + + // copy translation and scale from nextPoses + AnimPose pose = nextPoses[i]; + + int parentIndex = _skeleton->getParentIndex((int)i); + if (parentIndex >= 0) { + // but transform nextPoses rot into absPrev parent frame. + pose.rot() = glm::inverse(absPrev[parentIndex].rot()) * pose.rot() * absPrev[parentIndex].rot(); + } + + relOffsetPoses.push_back(pose); + } + + // then blend + ::blendAdd(_poses.size(), &prevPoses[0], &relOffsetPoses[0], alpha, &_poses[0]); + } + } + } +} +*/ + +bool AnimBlendDirectional::lookupChildIds() { + _center = findChildIndexByName(_centerId); + _up = findChildIndexByName(_upId); + _down = findChildIndexByName(_downId); + _left = findChildIndexByName(_leftId); + _right = findChildIndexByName(_rightId); + _upLeft = findChildIndexByName(_upLeftId); + _upRight = findChildIndexByName(_upRightId); + _downLeft = findChildIndexByName(_downLeftId); + _downRight = findChildIndexByName(_downRightId); + + // manditory children + // TODO: currently all are manditory. + if (_center == -1 || _up == -1 || _down == -1 || _left == -1 || _right== -1 || + _upLeft == -1 || _upRight == -1 || _downLeft == -1 || _downRight == -1) { + return false; + } else { + return true; + } +} diff --git a/libraries/animation/src/AnimBlendDirectional.h b/libraries/animation/src/AnimBlendDirectional.h new file mode 100644 index 0000000000..0691168de5 --- /dev/null +++ b/libraries/animation/src/AnimBlendDirectional.h @@ -0,0 +1,67 @@ +// +// AnimBlendDirectonal.h +// +// Created by Anthony J. Thibault on Augest 30 2019. +// Copyright (c) 2019 High Fidelity, Inc. All rights reserved. +// +// 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_AnimBlendDirectional_h +#define hifi_AnimBlendDirectional_h + +#include "AnimNode.h" + +// blend between up to nine AnimNodes. + +class AnimBlendDirectional : public AnimNode { +public: + friend class AnimTests; + + AnimBlendDirectional(const QString& id, float alpha, const QString& centerId, + const QString& upId, const QString& downId, const QString& leftId, const QString& rightId, + const QString& upLeftId, const QString& upRightId, const QString& downLeftId, const QString& downRightId); + virtual ~AnimBlendDirectional() override; + + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override; + + void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; } + + bool lookupChildIds(); + +protected: + // for AnimDebugDraw rendering + virtual const AnimPoseVec& getPosesInternal() const override; + + AnimPoseVec _poses; + + float _alpha; + QString _centerId; + QString _upId; + QString _downId; + QString _leftId; + QString _rightId; + QString _upLeftId; + QString _upRightId; + QString _downLeftId; + QString _downRightId; + + QString _alphaVar; + + int _center; + int _up; + int _down; + int _left; + int _right; + int _upLeft; + int _upRight; + int _downLeft; + int _downRight; + + // no copies + AnimBlendDirectional(const AnimBlendDirectional&) = delete; + AnimBlendDirectional& operator=(const AnimBlendDirectional&) = delete; +}; + +#endif // hifi_AnimBlendDirectional_h diff --git a/libraries/animation/src/AnimContext.h b/libraries/animation/src/AnimContext.h index 2ee8a30980..68b44c8f56 100644 --- a/libraries/animation/src/AnimContext.h +++ b/libraries/animation/src/AnimContext.h @@ -31,6 +31,7 @@ enum class AnimNodeType { TwoBoneIK, SplineIK, PoleVectorConstraint, + BlendDirectional, NumTypes }; diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index a0fb51e891..3dfe9dfe8c 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -99,6 +99,15 @@ public: return result; } + int findChildIndexByName(const QString& id) { + for (int i = 0; i < _children.size(); ++i) { + if (_children[i]->getID() == id) { + return i; + } + } + return -1; + } + const AnimPoseVec& getPoses() const { return getPosesInternal(); } protected: diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 100269c55c..6cb7bb47bd 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -29,6 +29,7 @@ #include "AnimTwoBoneIK.h" #include "AnimSplineIK.h" #include "AnimPoleVectorConstraint.h" +#include "AnimBlendDirectional.h" #include "AnimUtil.h" using NodeLoaderFunc = AnimNode::Pointer (*)(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); @@ -47,6 +48,7 @@ static AnimNode::Pointer loadDefaultPoseNode(const QJsonObject& jsonObj, const Q static AnimNode::Pointer loadTwoBoneIKNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static AnimNode::Pointer loadSplineIKNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static AnimNode::Pointer loadPoleVectorConstraintNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); +static AnimNode::Pointer loadBlendDirectionalNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static const float ANIM_GRAPH_LOAD_PRIORITY = 10.0f; @@ -55,6 +57,7 @@ static const float ANIM_GRAPH_LOAD_PRIORITY = 10.0f; static bool processDoNothing(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); bool processRandomSwitchStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); +bool processBlendDirectionalNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static const char* animNodeTypeToString(AnimNode::Type type) { switch (type) { @@ -70,6 +73,7 @@ static const char* animNodeTypeToString(AnimNode::Type type) { case AnimNode::Type::TwoBoneIK: return "twoBoneIK"; case AnimNode::Type::SplineIK: return "splineIK"; case AnimNode::Type::PoleVectorConstraint: return "poleVectorConstraint"; + case AnimNode::Type::BlendDirectional: return "blendDirectional"; case AnimNode::Type::NumTypes: return nullptr; }; return nullptr; @@ -211,6 +215,7 @@ static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) { case AnimNode::Type::TwoBoneIK: return loadTwoBoneIKNode; case AnimNode::Type::SplineIK: return loadSplineIKNode; case AnimNode::Type::PoleVectorConstraint: return loadPoleVectorConstraintNode; + case AnimNode::Type::BlendDirectional: return loadBlendDirectionalNode; case AnimNode::Type::NumTypes: return nullptr; }; return nullptr; @@ -230,6 +235,7 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) { case AnimNode::Type::TwoBoneIK: return processDoNothing; case AnimNode::Type::SplineIK: return processDoNothing; case AnimNode::Type::PoleVectorConstraint: return processDoNothing; + case AnimNode::Type::BlendDirectional: return processBlendDirectionalNode; case AnimNode::Type::NumTypes: return nullptr; }; return nullptr; @@ -772,6 +778,32 @@ static AnimNode::Pointer loadPoleVectorConstraintNode(const QJsonObject& jsonObj return node; } +static AnimNode::Pointer loadBlendDirectionalNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { + + READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr); + READ_OPTIONAL_STRING(alphaVar, jsonObj); + + READ_OPTIONAL_STRING(centerId, jsonObj); + READ_OPTIONAL_STRING(upId, jsonObj); + READ_OPTIONAL_STRING(downId, jsonObj); + READ_OPTIONAL_STRING(leftId, jsonObj); + READ_OPTIONAL_STRING(rightId, jsonObj); + READ_OPTIONAL_STRING(upLeftId, jsonObj); + READ_OPTIONAL_STRING(upRightId, jsonObj); + READ_OPTIONAL_STRING(downLeftId, jsonObj); + READ_OPTIONAL_STRING(downRightId, jsonObj); + + auto node = std::make_shared(id, alpha, centerId, + upId, downId, leftId, rightId, + upLeftId, upRightId, downLeftId, downRightId); + + if (!alphaVar.isEmpty()) { + node->setAlphaVar(alphaVar); + } + + return node; +} + void buildChildMap(std::map& map, AnimNode::Pointer node) { for (int i = 0; i < (int)node->getChildCount(); ++i) { map.insert(std::pair(node->getChild(i)->getID(), i)); @@ -1042,7 +1074,12 @@ bool processRandomSwitchStateMachineNode(AnimNode::Pointer node, const QJsonObje return true; } +bool processBlendDirectionalNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& nodeId, const QUrl& jsonUrl) { + auto blendNode = std::static_pointer_cast(node); + assert(blendNode); + return blendNode->lookupChildIds(); +} AnimNodeLoader::AnimNodeLoader(const QUrl& url) : _url(url)