From c83b74c85d14233e36a7c3728bdd97f53f2be339 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 Apr 2024 23:25:37 +0200 Subject: [PATCH] Fix blending nested animations to properly handle it per-node #2416 --- Source/Engine/Animations/Graph/AnimGraph.h | 3 ++- .../Animations/Graph/AnimGroup.Animation.cpp | 27 ++++++++++++++----- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/Source/Engine/Animations/Graph/AnimGraph.h b/Source/Engine/Animations/Graph/AnimGraph.h index ee84e0b71..ebee7e901 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.h +++ b/Source/Engine/Animations/Graph/AnimGraph.h @@ -5,6 +5,7 @@ #include "Engine/Visject/VisjectGraph.h" #include "Engine/Content/Assets/Animation.h" #include "Engine/Core/Collections/ChunkedArray.h" +#include "Engine/Core/Collections/BitArray.h" #include "Engine/Animations/AlphaBlend.h" #include "Engine/Core/Math/Matrix.h" #include "../Config.h" @@ -892,7 +893,7 @@ private: int32 GetRootNodeIndex(Animation* anim); void ProcessAnimEvents(AnimGraphNode* node, bool loop, float length, float animPos, float animPrevPos, Animation* anim, float speed); - void ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode* node, bool loop, float length, float pos, float prevPos, Animation* anim, float speed, float weight = 1.0f, ProcessAnimationMode mode = ProcessAnimationMode::Override); + void ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode* node, bool loop, float length, float pos, float prevPos, Animation* anim, float speed, float weight = 1.0f, ProcessAnimationMode mode = ProcessAnimationMode::Override, BitArray>* usedNodes = nullptr); Variant SampleAnimation(AnimGraphNode* node, bool loop, float length, float startTimePos, float prevTimePos, float& newTimePos, Animation* anim, float speed); Variant SampleAnimationsWithBlend(AnimGraphNode* node, bool loop, float length, float startTimePos, float prevTimePos, float& newTimePos, Animation* animA, Animation* animB, float speedA, float speedB, float alpha); Variant SampleAnimationsWithBlend(AnimGraphNode* node, bool loop, float length, float startTimePos, float prevTimePos, float& newTimePos, Animation* animA, Animation* animB, Animation* animC, float speedA, float speedB, float speedC, float alphaA, float alphaB, float alphaC); diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index 247c999e9..21eb44265 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -25,7 +25,7 @@ namespace { for (int32 i = 0; i < nodes->Nodes.Count(); i++) { - nodes->Nodes[i].Orientation.Normalize(); + nodes->Nodes.Get()[i].Orientation.Normalize(); } if (rootMotionMode != RootMotionExtraction::NoExtraction) { @@ -222,7 +222,7 @@ FORCE_INLINE void GetAnimSamplePos(bool loop, float length, float startTimePos, prevPos = GetAnimPos(prevTimePos, startTimePos, loop, length); } -void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode* node, bool loop, float length, float pos, float prevPos, Animation* anim, float speed, float weight, ProcessAnimationMode mode) +void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode* node, bool loop, float length, float pos, float prevPos, Animation* anim, float speed, float weight, ProcessAnimationMode mode, BitArray>* usedNodes) { PROFILE_CPU_ASSET(anim); @@ -240,9 +240,16 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode* } // Evaluate nested animations - bool hasNested = false; + BitArray> usedNodesThis; if (anim->NestedAnims.Count() != 0) { + if (usedNodes == nullptr) + { + // Per-channel bit to indicate which channels were used by nested + usedNodesThis.Resize(nodes->Nodes.Count()); + usedNodes = &usedNodesThis; + } + for (auto& e : anim->NestedAnims) { const auto& nestedAnim = e.Second; @@ -262,8 +269,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode* nestedAnimPrevPos = nestedAnimPrevPos * frameRateMatchScale; GetAnimSamplePos(nestedAnim.Loop, nestedAnimLength, nestedAnim.StartTime, nestedAnimPrevPos, nestedAnimPos, nestedAnimPos, nestedAnimPrevPos); - ProcessAnimation(nodes, node, true, nestedAnimLength, nestedAnimPos, nestedAnimPrevPos, nestedAnim.Anim, 1.0f, weight, mode); - hasNested = true; + ProcessAnimation(nodes, node, true, nestedAnimLength, nestedAnimPos, nestedAnimPrevPos, nestedAnim.Anim, 1.0f, weight, mode, usedNodes); } } } @@ -295,6 +301,15 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode* { RetargetSkeletonNode(mapping.SourceSkeleton->Skeleton, mapping.TargetSkeleton->Skeleton, sourceMapping, srcNode, i); } + + // Mark node as used + if (usedNodes) + usedNodes->Set(i, true); + } + else if (usedNodes && usedNodes != &usedNodesThis) + { + // Skip for nested animations so other one or top-level anim will update remaining nodes + continue; } // Blend node @@ -316,7 +331,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode* dstNode.Scale = srcNode.Scale * weight; dstNode.Orientation = srcNode.Orientation * weight; } - else if (!hasNested) + else { dstNode = srcNode; }