diff --git a/src/engine/scene/node/modelnode.h b/src/engine/scene/node/modelnode.h index 4d74d4da..8f2f8688 100644 --- a/src/engine/scene/node/modelnode.h +++ b/src/engine/scene/node/modelnode.h @@ -18,6 +18,7 @@ #pragma once #include +#include #include #include @@ -38,7 +39,6 @@ namespace reone { namespace scene { constexpr float kDefaultDrawDistance = 1024.0f; -constexpr int kNumAnimationChannels = 8; class ModelSceneNode : public SceneNode { public: @@ -108,14 +108,15 @@ private: AnimationProperties properties; float time { 0.0f }; std::unordered_map stateByName; - - // Flags - - bool finished { false }; /**< channel contains a fire-and-forget animation that has finished playing */ - bool transition { false }; /**< when computing states, use animation transition time as channel time */ bool freeze { false }; /**< channel time is not to be updated */ + bool transition { false }; /**< when computing states, use animation transition time as channel time */ + bool finished { false }; /**< finished channels will be erased from the queue */ - // END Flags + AnimationChannel(std::shared_ptr anim, std::shared_ptr lipAnim, AnimationProperties properties) : + anim(std::move(anim)), + lipAnim(std::move(lipAnim)), + properties(std::move(properties)) { + } }; std::shared_ptr _model; @@ -133,7 +134,7 @@ private: // Animation - AnimationChannel _animChannels[kNumAnimationChannels]; + std::deque _animChannels; AnimationBlendMode _animBlendMode { AnimationBlendMode::Single }; std::set _inanimateNodes; /**< names of nodes that are not to be animated */ @@ -148,7 +149,6 @@ private: // Animation - void resetAnimationChannel(AnimationChannel &channel, std::shared_ptr anim = nullptr, std::shared_ptr lipAnim = nullptr, AnimationProperties properties = AnimationProperties()); void updateAnimations(float dt); void updateAnimationChannel(AnimationChannel &channel, float dt); void computeAnimationStates(AnimationChannel &channel, float time, const graphics::ModelNode &modelNode); diff --git a/src/engine/scene/node/modelnode_animation.cpp b/src/engine/scene/node/modelnode_animation.cpp index d3f89a5b..c2080d7c 100644 --- a/src/engine/scene/node/modelnode_animation.cpp +++ b/src/engine/scene/node/modelnode_animation.cpp @@ -17,9 +17,13 @@ #include "modelnode.h" +#include + #include "glm/gtx/matrix_decompose.hpp" #include "glm/gtx/transform.hpp" +#include "../../common/collectionutil.h" + using namespace std; using namespace reone::graphics; @@ -43,40 +47,33 @@ void ModelSceneNode::playAnimation(shared_ptr anim, shared_ptr 0; --i) { - _animChannels[i] = _animChannels[i - 1]; - } - } else { - for (int i = 1; i < kNumAnimationChannels; ++i) { - resetAnimationChannel(_animChannels[i]); - } + // In Overlay mode, clear channels only if previous mode is not + // Overlay and add animation on top + if (_animBlendMode != AnimationBlendMode::Overlay) { + _animChannels.clear(); } - resetAnimationChannel(_animChannels[0], anim, lipAnim, properties); + _animChannels.push_front(AnimationChannel(anim, lipAnim, properties)); break; default: @@ -101,18 +98,9 @@ ModelSceneNode::AnimationBlendMode ModelSceneNode::getAnimationBlendMode(int fla ((flags & AnimationFlags::overlay) ? AnimationBlendMode::Overlay : AnimationBlendMode::Single); } -void ModelSceneNode::resetAnimationChannel(AnimationChannel &channel, shared_ptr anim, shared_ptr lipAnim, AnimationProperties properties) { - channel.anim = move(anim); - channel.lipAnim = move(lipAnim); - channel.properties = move(properties); - channel.time = 0.0f; - channel.stateByName.clear(); - channel.finished = false; - channel.transition = false; - channel.freeze = false; -} - void ModelSceneNode::updateAnimations(float dt) { + if (_animChannels.empty()) return; + for (auto &channel : _animChannels) { updateAnimationChannel(channel, dt); } @@ -122,13 +110,13 @@ void ModelSceneNode::updateAnimations(float dt) { applyAnimationStates(*_model->rootNode()); computeBoneTransforms(); } + + // Erase finished channels + auto channelsToErase = remove_if(_animChannels.begin(), _animChannels.end(), [](auto &channel) { return channel.finished; }); + _animChannels.erase(channelsToErase, _animChannels.end()); } void ModelSceneNode::updateAnimationChannel(AnimationChannel &channel, float dt) { - // Do not update if there is no animation, freezed or a finished animation - // in the channel - if (!channel.anim || channel.freeze || channel.finished) return; - // Take length from the lip animation, if any float length = channel.lipAnim ? channel.lipAnim->length() : channel.anim->length(); @@ -248,20 +236,25 @@ void ModelSceneNode::applyAnimationStates(const ModelNode &modelNode) { switch (_animBlendMode) { case AnimationBlendMode::Single: case AnimationBlendMode::Blend: { + auto state1 = getFromLookupOrElse(_animChannels[0].stateByName, modelNode.name(), AnimationState()); bool blend = _animBlendMode == AnimationBlendMode::Blend && _animChannels[0].transition; - auto state1 = _animChannels[0].stateByName.count(modelNode.name()) > 0 ? _animChannels[0].stateByName[modelNode.name()] : AnimationState(); - auto state2 = _animChannels[1].stateByName.count(modelNode.name()) > 0 ? _animChannels[1].stateByName[modelNode.name()] : AnimationState(); - if (blend && state1.flags & AnimationStateFlags::transform && state2.flags & AnimationStateFlags::transform) { - float factor = glm::min(1.0f, _animChannels[0].time / _animChannels[0].anim->transitionTime()); - glm::vec3 scale1, scale2, translation1, translation2, skew; - glm::quat orientation1, orientation2; - glm::vec4 perspective; - glm::decompose(state1.transform, scale1, orientation1, translation1, skew, perspective); - glm::decompose(state2.transform, scale2, orientation2, translation2, skew, perspective); - combined.flags |= AnimationStateFlags::transform; - combined.transform *= glm::scale(glm::mix(scale2, scale1, factor)); - combined.transform *= glm::translate(glm::mix(translation2, translation1, factor)); - combined.transform *= glm::mat4_cast(glm::slerp(orientation2, orientation1, factor)); + if (blend) { + auto state2 = getFromLookupOrElse(_animChannels[1].stateByName, modelNode.name(), AnimationState()); + if (state1.flags & AnimationStateFlags::transform && state2.flags & AnimationStateFlags::transform) { + float factor = glm::min(1.0f, _animChannels[0].time / _animChannels[0].anim->transitionTime()); + glm::vec3 scale1, scale2, translation1, translation2, skew; + glm::quat orientation1, orientation2; + glm::vec4 perspective; + glm::decompose(state1.transform, scale1, orientation1, translation1, skew, perspective); + glm::decompose(state2.transform, scale2, orientation2, translation2, skew, perspective); + combined.flags |= AnimationStateFlags::transform; + combined.transform *= glm::scale(glm::mix(scale2, scale1, factor)); + combined.transform *= glm::translate(glm::mix(translation2, translation1, factor)); + combined.transform *= glm::mat4_cast(glm::slerp(orientation2, orientation1, factor)); + } else if (state1.flags & AnimationStateFlags::transform) { + combined.flags |= AnimationStateFlags::transform; + combined.transform = state1.transform; + } } else if (state1.flags & AnimationStateFlags::transform) { combined.flags |= AnimationStateFlags::transform; combined.transform = state1.transform; @@ -277,22 +270,21 @@ void ModelSceneNode::applyAnimationStates(const ModelNode &modelNode) { break; } case AnimationBlendMode::Overlay: - for (int i = 0; i < kNumAnimationChannels; ++i) { - auto maybeState = _animChannels[i].stateByName.find(modelNode.name()); - if (maybeState != _animChannels[i].stateByName.end()) { - const AnimationState &state = maybeState->second; - if ((state.flags & AnimationStateFlags::transform) && !(combined.flags & AnimationStateFlags::transform)) { - combined.flags |= AnimationStateFlags::transform; - combined.transform = state.transform; - } - if ((state.flags & AnimationStateFlags::alpha) && !(combined.flags & AnimationStateFlags::alpha)) { - combined.flags |= AnimationStateFlags::alpha; - combined.alpha = state.alpha; - } - if ((state.flags & AnimationStateFlags::selfIllumColor) && !(combined.flags & AnimationStateFlags::selfIllumColor)) { - combined.flags |= AnimationStateFlags::selfIllumColor; - combined.selfIllumColor = state.selfIllumColor; - } + for (auto &channel : _animChannels) { + auto maybeState = channel.stateByName.find(modelNode.name()); + if (maybeState == channel.stateByName.end()) continue; + const AnimationState &state = maybeState->second; + if ((state.flags & AnimationStateFlags::transform) && !(combined.flags & AnimationStateFlags::transform)) { + combined.flags |= AnimationStateFlags::transform; + combined.transform = state.transform; + } + if ((state.flags & AnimationStateFlags::alpha) && !(combined.flags & AnimationStateFlags::alpha)) { + combined.flags |= AnimationStateFlags::alpha; + combined.alpha = state.alpha; + } + if ((state.flags & AnimationStateFlags::selfIllumColor) && !(combined.flags & AnimationStateFlags::selfIllumColor)) { + combined.flags |= AnimationStateFlags::selfIllumColor; + combined.selfIllumColor = state.selfIllumColor; } } break; @@ -326,7 +318,7 @@ void ModelSceneNode::computeBoneTransforms() { } bool ModelSceneNode::isAnimationFinished() const { - return _animChannels[0].anim && _animChannels[0].finished; + return _animChannels.empty(); } } // namespace scene