diff --git a/Source/Editor/Surface/AnimGraphSurface.cs b/Source/Editor/Surface/AnimGraphSurface.cs index 3b704e26e..56764bafd 100644 --- a/Source/Editor/Surface/AnimGraphSurface.cs +++ b/Source/Editor/Surface/AnimGraphSurface.cs @@ -134,12 +134,7 @@ namespace FlaxEditor.Surface // Follow input node contexts path to verify if it matches with the path in the event var c = node.Context; for (int i = nodePathSize - 1; i >= 0 && c != null; i--) - { - if (c.OwnerNodeID != nodePath[i]) - c = null; - else - c = c.Parent; - } + c = c.OwnerNodeID == nodePath[i] ? c.Parent : null; if (c != null) { traceEvent = e; diff --git a/Source/Editor/Surface/VisjectSurface.Context.cs b/Source/Editor/Surface/VisjectSurface.Context.cs index 691ad4e50..0cb8e3ead 100644 --- a/Source/Editor/Surface/VisjectSurface.Context.cs +++ b/Source/Editor/Surface/VisjectSurface.Context.cs @@ -33,6 +33,30 @@ namespace FlaxEditor.Surface /// public event Action ContextChanged; + /// + /// Finds the surface context with the given owning nodes IDs path. + /// + /// The node ids path. + /// Found context or null if cannot. + public VisjectSurfaceContext FindContext(Span nodePath) + { + // Get size of the path + int nodePathSize = 0; + while (nodePathSize < nodePath.Length && nodePath[nodePathSize] != 0) + nodePathSize++; + + // Follow each context path to verify if it matches with the path in the input path + foreach (var e in _contextCache) + { + var c = e.Value; + for (int i = nodePathSize - 1; i >= 0 && c != null; i--) + c = c.OwnerNodeID == nodePath[i] ? c.Parent : null; + if (c != null) + return e.Value; + } + return null; + } + /// /// Creates the Visject surface context for the given surface data source context. /// diff --git a/Source/Editor/Windows/Assets/AnimationGraphWindow.cs b/Source/Editor/Windows/Assets/AnimationGraphWindow.cs index e0d8c9ded..93eea95e1 100644 --- a/Source/Editor/Windows/Assets/AnimationGraphWindow.cs +++ b/Source/Editor/Windows/Assets/AnimationGraphWindow.cs @@ -154,10 +154,11 @@ namespace FlaxEditor.Windows.Assets } [StructLayout(LayoutKind.Sequential)] - private struct AnimGraphDebugFlowInfo + private unsafe struct AnimGraphDebugFlowInfo { public uint NodeId; public int BoxId; + public fixed uint NodePath[8]; } private FlaxObjectRefPickerControl _debugPicker; @@ -252,25 +253,26 @@ namespace FlaxEditor.Windows.Assets return obj is AnimatedModel player && player.AnimationGraph == OriginalAsset; } - private void OnDebugFlow(Asset asset, Object obj, uint nodeId, uint boxId) + private unsafe void OnDebugFlow(Animations.DebugFlowInfo flowInfo) { // Filter the flow if (_debugPicker.Value != null) { - if (asset != OriginalAsset || _debugPicker.Value != obj) + if (flowInfo.Asset != OriginalAsset || _debugPicker.Value != flowInfo.Instance) return; } else { - if (asset != Asset || _preview.PreviewActor != obj) + if (flowInfo.Asset != Asset || _preview.PreviewActor != flowInfo.Instance) return; } // Register flow to show it in UI on a surface - var flowInfo = new AnimGraphDebugFlowInfo { NodeId = nodeId, BoxId = (int)boxId }; + var flow = new AnimGraphDebugFlowInfo { NodeId = flowInfo.NodeId, BoxId = (int)flowInfo.BoxId }; + Utils.MemoryCopy(new IntPtr(flow.NodePath), new IntPtr(flowInfo.NodePath0), sizeof(uint) * 8ul); lock (_debugFlows) { - _debugFlows.Add(flowInfo); + _debugFlows.Add(flow); } } @@ -394,7 +396,7 @@ namespace FlaxEditor.Windows.Assets } /// - public override void OnUpdate() + public override unsafe void OnUpdate() { // Extract animations playback state from the events tracing var debugActor = _debugPicker.Value as AnimatedModel; @@ -413,7 +415,8 @@ namespace FlaxEditor.Windows.Assets { foreach (var debugFlow in _debugFlows) { - var node = Surface.Context.FindNode(debugFlow.NodeId); + var context = Surface.FindContext(new Span(debugFlow.NodePath, 8)); + var node = context?.FindNode(debugFlow.NodeId); var box = node?.GetBox(debugFlow.BoxId); box?.HighlightConnections(); } diff --git a/Source/Engine/Animations/Animations.cpp b/Source/Engine/Animations/Animations.cpp index e571af162..59acc5860 100644 --- a/Source/Engine/Animations/Animations.cpp +++ b/Source/Engine/Animations/Animations.cpp @@ -52,7 +52,7 @@ namespace AnimationsService AnimationManagerInstance; TaskGraphSystem* Animations::System = nullptr; #if USE_EDITOR -Delegate Animations::DebugFlow; +Delegate Animations::DebugFlow; #endif AnimEvent::AnimEvent(const SpawnParams& params) @@ -127,7 +127,7 @@ void AnimationsSystem::Execute(TaskGraph* graph) #if USE_EDITOR // If debug flow is registered, then warm it up (eg. static cached method inside DebugFlow_ManagedWrapper) so it doesn't crash on highly multi-threaded code if (Animations::DebugFlow.IsBinded()) - Animations::DebugFlow(nullptr, nullptr, 0, 0); + Animations::DebugFlow(Animations::DebugFlowInfo()); #endif // Schedule work to update all animated models in async diff --git a/Source/Engine/Animations/Animations.h b/Source/Engine/Animations/Animations.h index d82b9d05f..038d20d9d 100644 --- a/Source/Engine/Animations/Animations.h +++ b/Source/Engine/Animations/Animations.h @@ -22,8 +22,25 @@ API_CLASS(Static) class FLAXENGINE_API Animations API_FIELD(ReadOnly) static TaskGraphSystem* System; #if USE_EDITOR - // Custom event that is called every time the Anim Graph signal flows over the graph (including the data connections). Can be used to read and visualize the animation blending logic. Args are: anim graph asset, animated object, node id, box id - API_EVENT() static Delegate DebugFlow; + // Data wrapper for the debug flow information. + API_STRUCT(NoDefault) struct DebugFlowInfo + { + DECLARE_SCRIPTING_TYPE_MINIMAL(DebugFlowInfo); + + // Anim Graph asset + API_FIELD() Asset* Asset = nullptr; + // Animated actor + API_FIELD() ScriptingObject* Instance = nullptr; + // Graph node id. + API_FIELD() uint32 NodeId = 0; + // Graph box id. + API_FIELD() uint32 BoxId = 0; + // Ids of graph nodes (call of hierarchy). + API_FIELD(Internal, NoArray) uint32 NodePath[8] = {}; + }; + + // Custom event that is called every time the Anim Graph signal flows over the graph (including the data connections). Can be used to read and visualize the animation blending logic. + API_EVENT() static Delegate DebugFlow; #endif /// diff --git a/Source/Engine/Animations/Graph/AnimGraph.cpp b/Source/Engine/Animations/Graph/AnimGraph.cpp index 9ec87c78f..40c33a3e8 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.cpp +++ b/Source/Engine/Animations/Graph/AnimGraph.cpp @@ -425,7 +425,15 @@ VisjectExecutor::Value AnimGraphExecutor::eatBox(Node* caller, Box* box) context.CallStack.Add(caller); #if USE_EDITOR - Animations::DebugFlow(_graph._owner, context.Data->Object, box->GetParent()->ID, box->ID); + Animations::DebugFlowInfo flowInfo; + flowInfo.Asset = _graph._owner; + flowInfo.Instance = context.Data->Object; + flowInfo.NodeId = box->GetParent()->ID; + flowInfo.BoxId = box->ID; + const auto* nodePath = context.NodePath.Get(); + for (int32 i = 0; i < context.NodePath.Count(); i++) + flowInfo.NodePath[i] = nodePath[i]; + Animations::DebugFlow(flowInfo); #endif // Call per group custom processing event diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index 3d729e80e..0fab934ca 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -227,7 +227,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode* trace.Asset = anim; trace.Value = animPos; trace.NodeId = node->ID; - auto* nodePath = context.NodePath.Get(); + const auto* nodePath = context.NodePath.Get(); for (int32 i = 0; i < context.NodePath.Count(); i++) trace.NodePath[i] = nodePath[i]; }