diff --git a/Source/Engine/Animations/Graph/AnimGraph.cpp b/Source/Engine/Animations/Graph/AnimGraph.cpp
index 7c4671a51..fa039c7e9 100644
--- a/Source/Engine/Animations/Graph/AnimGraph.cpp
+++ b/Source/Engine/Animations/Graph/AnimGraph.cpp
@@ -56,6 +56,7 @@ void AnimGraphInstanceData::ClearState()
State.Resize(0);
NodesPose.Resize(0);
Slots.Clear();
+ TraceEvents.Clear();
}
void AnimGraphInstanceData::Invalidate()
@@ -238,6 +239,7 @@ void AnimGraphExecutor::Update(AnimGraphInstanceData& data, float dt)
}
for (auto& e : data.ActiveEvents)
e.Hit = false;
+ data.TraceEvents.Clear();
// Init empty nodes data
context.EmptyNodes.RootMotion = Transform::Identity;
diff --git a/Source/Engine/Animations/Graph/AnimGraph.h b/Source/Engine/Animations/Graph/AnimGraph.h
index 30ac5a776..2a8a7f7ab 100644
--- a/Source/Engine/Animations/Graph/AnimGraph.h
+++ b/Source/Engine/Animations/Graph/AnimGraph.h
@@ -200,6 +200,21 @@ struct FLAXENGINE_API AnimGraphSlot
bool Reset = false;
};
+///
+/// The animation graph state container for a single node playback trace (eg. animation sample info or state transition). Can be used by Anim Graph debugging or custom scripting.
+///
+API_STRUCT() struct FLAXENGINE_API AnimGraphTraceEvent
+{
+ DECLARE_SCRIPTING_TYPE_MINIMAL(AnimGraphTraceEvent);
+
+ // Contextual asset used. For example, sampled animation.
+ API_FIELD() Asset* Asset = nullptr;
+ // Generic value contextual to playback type (eg. animation sample position).
+ API_FIELD() float Value = 0;
+ // Identifier of the node in the graph.
+ API_FIELD() uint32 NodeId = 0;
+};
+
///
/// The animation graph instance data storage. Required to update the animation graph.
///
@@ -358,6 +373,12 @@ public:
///
void InvokeAnimEvents();
+public:
+ // Anim Graph logic tracing feature that allows to collect insights of animations sampling and skeleton poses operations.
+ bool EnableTracing = false;
+ // Trace events collected when using EnableTracing option.
+ Array TraceEvents;
+
private:
struct OutgoingEvent
{
diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp
index fc7cf6dbc..598e55da1 100644
--- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp
+++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp
@@ -219,6 +219,16 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
const float animPos = GetAnimSamplePos(length, anim, pos, speed);
const float animPrevPos = GetAnimSamplePos(length, anim, prevPos, speed);
+ // Add to trace
+ auto& context = Context.Get();
+ if (context.Data->EnableTracing)
+ {
+ auto& trace = context.Data->TraceEvents.AddOne();
+ trace.Asset = anim;
+ trace.Value = animPos;
+ trace.NodeId = node->ID;
+ }
+
// Evaluate nested animations
bool hasNested = false;
if (anim->NestedAnims.Count() != 0)
diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp
index 17f117173..319def475 100644
--- a/Source/Engine/Level/Actors/AnimatedModel.cpp
+++ b/Source/Engine/Level/Actors/AnimatedModel.cpp
@@ -225,6 +225,17 @@ void AnimatedModel::SetMasterPoseModel(AnimatedModel* masterPose)
_masterPose->AnimationUpdated.Bind(this);
}
+const Array& AnimatedModel::GetTraceEvents() const
+{
+#if !BUILD_RELEASE
+ if (!GetEnableTracing())
+ {
+ LOG(Warning, "Accessing AnimatedModel.TraceEvents with tracing disabled.");
+ }
+#endif
+ return GraphInstance.TraceEvents;
+}
+
#define CHECK_ANIM_GRAPH_PARAM_ACCESS() \
if (!AnimationGraph) \
{ \
diff --git a/Source/Engine/Level/Actors/AnimatedModel.h b/Source/Engine/Level/Actors/AnimatedModel.h
index 029e17b62..bd03e824e 100644
--- a/Source/Engine/Level/Actors/AnimatedModel.h
+++ b/Source/Engine/Level/Actors/AnimatedModel.h
@@ -259,6 +259,27 @@ public:
/// The master pose actor to use.
API_FUNCTION() void SetMasterPoseModel(AnimatedModel* masterPose);
+ ///
+ /// Enables extracting animation playback insights for debugging or custom scripting.
+ ///
+ API_PROPERTY(Attributes="HideInEditor, NoSerialize") bool GetEnableTracing() const
+ {
+ return GraphInstance.EnableTracing;
+ }
+
+ ///
+ /// Enables extracting animation playback insights for debugging or custom scripting.
+ ///
+ API_PROPERTY() void SetEnableTracing(bool value)
+ {
+ GraphInstance.EnableTracing = value;
+ }
+
+ ///
+ /// Gets the trace events from the last animation update. Valid only when EnableTracing is active.
+ ///
+ API_PROPERTY(Attributes="HideInEditor, NoSerialize") const Array& GetTraceEvents() const;
+
public:
///
/// Gets the anim graph instance parameters collection.