diff --git a/Source/Editor/Surface/AnimGraphSurface.cs b/Source/Editor/Surface/AnimGraphSurface.cs index ccd78412a..3b704e26e 100644 --- a/Source/Editor/Surface/AnimGraphSurface.cs +++ b/Source/Editor/Surface/AnimGraphSurface.cs @@ -115,16 +115,36 @@ namespace FlaxEditor.Surface internal AnimGraphTraceEvent[] LastTraceEvents; - internal bool TryGetTraceEvent(SurfaceNode node, out AnimGraphTraceEvent traceEvent) + internal unsafe bool TryGetTraceEvent(SurfaceNode node, out AnimGraphTraceEvent traceEvent) { if (LastTraceEvents != null) { foreach (var e in LastTraceEvents) { + // Node IDs must match if (e.NodeId == node.ID) { - traceEvent = e; - return true; + uint* nodePath = e.NodePath0; + + // Get size of the path + int nodePathSize = 0; + while (nodePathSize < 8 && nodePath[nodePathSize] != 0) + nodePathSize++; + + // 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; + } + if (c != null) + { + traceEvent = e; + return true; + } } } } diff --git a/Source/Editor/Surface/VisjectSurface.Context.cs b/Source/Editor/Surface/VisjectSurface.Context.cs index 1d8b97729..691ad4e50 100644 --- a/Source/Editor/Surface/VisjectSurface.Context.cs +++ b/Source/Editor/Surface/VisjectSurface.Context.cs @@ -62,6 +62,8 @@ namespace FlaxEditor.Surface surfaceContext = CreateContext(_context, context); _context?.Children.Add(surfaceContext); _contextCache.Add(contextHandle, surfaceContext); + if (context is SurfaceNode asNode) + surfaceContext.OwnerNodeID = asNode.ID; context.OnContextCreated(surfaceContext); diff --git a/Source/Editor/Surface/VisjectSurfaceContext.cs b/Source/Editor/Surface/VisjectSurfaceContext.cs index 0886996b6..9902f224a 100644 --- a/Source/Editor/Surface/VisjectSurfaceContext.cs +++ b/Source/Editor/Surface/VisjectSurfaceContext.cs @@ -156,6 +156,11 @@ namespace FlaxEditor.Surface /// public event Action ControlDeleted; + /// + /// Identifier of the node that 'owns' this context (eg. State Machine which created this graph of state nodes). + /// + public uint OwnerNodeID; + /// /// Initializes a new instance of the class. /// diff --git a/Source/Engine/Animations/Graph/AnimGraph.cpp b/Source/Engine/Animations/Graph/AnimGraph.cpp index f7b883bd7..9ec87c78f 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.cpp +++ b/Source/Engine/Animations/Graph/AnimGraph.cpp @@ -215,6 +215,7 @@ void AnimGraphExecutor::Update(AnimGraphInstanceData& data, float dt) // Prepare context data for the evaluation context.GraphStack.Clear(); context.GraphStack.Push((Graph*)&_graph); + context.NodePath.Clear(); context.Data = &data; context.DeltaTime = dt; context.CurrentFrameIndex = ++data.CurrentFrame; diff --git a/Source/Engine/Animations/Graph/AnimGraph.h b/Source/Engine/Animations/Graph/AnimGraph.h index a6dc68596..d8acae234 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.h +++ b/Source/Engine/Animations/Graph/AnimGraph.h @@ -215,6 +215,8 @@ API_STRUCT(NoDefault) struct FLAXENGINE_API AnimGraphTraceEvent API_FIELD() float Value = 0; // Identifier of the node in the graph. API_FIELD() uint32 NodeId = 0; + // Ids of graph nodes (call of hierarchy). + API_FIELD(Internal, NoArray) uint32 NodePath[8] = {}; }; /// @@ -796,6 +798,7 @@ struct AnimGraphContext AnimGraphTransitionData TransitionData; Array> CallStack; Array> GraphStack; + Array > NodePath; Dictionary Functions; ChunkedArray PoseCache; int32 PoseCacheSize; @@ -891,7 +894,7 @@ private: 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); Variant Blend(AnimGraphNode* node, const Value& poseA, const Value& poseB, float alpha, AlphaBlendMode alphaMode); - Variant SampleState(AnimGraphNode* state); + Variant SampleState(AnimGraphContext& context, const AnimGraphNode* state); void InitStateTransition(AnimGraphContext& context, AnimGraphInstanceData::StateMachineBucket& stateMachineBucket, AnimGraphStateTransition* transition = nullptr); AnimGraphStateTransition* UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, AnimGraphNode* state, AnimGraphNode* ignoreState = nullptr); void UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, AnimGraphInstanceData::StateMachineBucket& stateMachineBucket, const AnimGraphNode::StateBaseData& stateData); diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index a9d29cda3..3d729e80e 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -227,6 +227,9 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode* trace.Asset = anim; trace.Value = animPos; trace.NodeId = node->ID; + auto* nodePath = context.NodePath.Get(); + for (int32 i = 0; i < context.NodePath.Count(); i++) + trace.NodePath[i] = nodePath[i]; } // Evaluate nested animations @@ -494,14 +497,17 @@ Variant AnimGraphExecutor::Blend(AnimGraphNode* node, const Value& poseA, const return nodes; } -Variant AnimGraphExecutor::SampleState(AnimGraphNode* state) +Variant AnimGraphExecutor::SampleState(AnimGraphContext& context, const AnimGraphNode* state) { auto& data = state->Data.State; if (data.Graph == nullptr || data.Graph->GetRootNode() == nullptr) return Value::Null; ANIM_GRAPH_PROFILE_EVENT("Evaluate State"); + context.NodePath.Add(state->ID); auto rootNode = data.Graph->GetRootNode(); - return eatBox((Node*)rootNode, &rootNode->Boxes[0]); + auto result = eatBox((Node*)rootNode, &rootNode->Boxes[0]); + context.NodePath.Pop(); + return result; } void AnimGraphExecutor::InitStateTransition(AnimGraphContext& context, AnimGraphInstanceData::StateMachineBucket& stateMachineBucket, AnimGraphStateTransition* transition) @@ -537,7 +543,7 @@ AnimGraphStateTransition* AnimGraphExecutor::UpdateStateTransitions(AnimGraphCon } // Evaluate source state transition data (position, length, etc.) - const Value sourceStatePtr = SampleState(state); + const Value sourceStatePtr = SampleState(context, state); auto& transitionData = context.TransitionData; // Note: this could support nested transitions but who uses state machine inside transition rule? if (ANIM_GRAPH_IS_VALID_PTR(sourceStatePtr)) { @@ -1660,6 +1666,8 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu bucket.CurrentState = bucket.ActiveTransition->Destination; \ InitStateTransition(context, bucket) + context.NodePath.Push(node->ID); + // Update the active transition if (bucket.ActiveTransition) { @@ -1767,11 +1775,11 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu if (bucket.BaseTransitionState) { // Sample the other state (eg. when blending from interrupted state to the another state from the old destination) - value = SampleState(bucket.BaseTransitionState); + value = SampleState(context, bucket.BaseTransitionState); if (bucket.BaseTransition) { // Evaluate the base pose from the time when transition was interrupted - const auto destinationState = SampleState(bucket.BaseTransition->Destination); + const auto destinationState = SampleState(context, bucket.BaseTransition->Destination); const float alpha = bucket.BaseTransitionPosition / bucket.BaseTransition->BlendDuration; value = Blend(node, value, destinationState, alpha, bucket.BaseTransition->BlendMode); } @@ -1779,14 +1787,14 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu else { // Sample the current state - value = SampleState(bucket.CurrentState); + value = SampleState(context, bucket.CurrentState); } // Handle active transition blending if (bucket.ActiveTransition) { // Sample the active transition destination state - const auto destinationState = SampleState(bucket.ActiveTransition->Destination); + const auto destinationState = SampleState(context, bucket.ActiveTransition->Destination); // Perform blending const float alpha = bucket.TransitionPosition / bucket.ActiveTransition->BlendDuration; @@ -1794,6 +1802,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu } bucket.LastUpdateFrame = context.CurrentFrameIndex; + context.NodePath.Pop(); #undef END_TRANSITION break; }