From 49e0cc937e6b43821bc221a18e8b2fc271a4e93a Mon Sep 17 00:00:00 2001 From: Olly Date: Wed, 14 May 2025 19:28:29 +1000 Subject: [PATCH 1/3] Added SetNodeTransformation with ModelBoneNode To get and set a series of bones based on their ID (cherry picked from commit e0a113483e910660e45c53e059502733ce1d6ad6) --- Source/Engine/Level/Actors/AnimatedModel.cpp | 40 +++++++++++++++++++- Source/Engine/Level/Actors/AnimatedModel.h | 26 ++++++++++++- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index 54fe6119a..d438f94c6 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -174,6 +174,14 @@ void AnimatedModel::GetNodeTransformation(const StringView& nodeName, Matrix& no GetNodeTransformation(SkinnedModel ? SkinnedModel->FindNode(nodeName) : -1, nodeTransformation, worldSpace); } +void AnimatedModel::GetNodeTransformation(Array& modelBoneNodes, bool worldSpace) const +{ + for (ModelBoneNode& item : modelBoneNodes) + { + GetNodeTransformation(item.NodeIndex, item.NodeMatrix, worldSpace); + } +} + void AnimatedModel::SetNodeTransformation(int32 nodeIndex, const Matrix& nodeTransformation, bool worldSpace) { if (GraphInstance.NodesPose.IsEmpty()) @@ -191,6 +199,33 @@ void AnimatedModel::SetNodeTransformation(int32 nodeIndex, const Matrix& nodeTra OnAnimationUpdated(); } +void AnimatedModel::SetNodeTransformation(Array& modelBoneNodes, bool worldSpace) +{ + if (GraphInstance.NodesPose.IsEmpty()) + const_cast(this)->PreInitSkinningData(); // Ensure to have valid nodes pose to return + + // Calculate it once, outside loop + Matrix invWorld; + if (worldSpace) + { + Matrix world; + GetLocalToWorldMatrix(world); + Matrix::Invert(world, invWorld); + } + + for (int i = 0; i < modelBoneNodes.Count(); i++) + { + int nodeIndex = modelBoneNodes[i].NodeIndex; + CHECK(nodeIndex >= 0 && nodeIndex < GraphInstance.NodesPose.Count()); + GraphInstance.NodesPose[nodeIndex] = modelBoneNodes[i].NodeMatrix; + if (worldSpace) + { + GraphInstance.NodesPose[nodeIndex] = GraphInstance.NodesPose[nodeIndex] * invWorld; + } + } + OnAnimationUpdated(); +} + void AnimatedModel::SetNodeTransformation(const StringView& nodeName, const Matrix& nodeTransformation, bool worldSpace) { SetNodeTransformation(SkinnedModel ? SkinnedModel->FindNode(nodeName) : -1, nodeTransformation, worldSpace); @@ -809,7 +844,10 @@ void AnimatedModel::OnAnimationUpdated_Async() _skinningData.OnDataChanged(!PerBoneMotionBlur); } - UpdateBounds(); + if (UpdateWhenOffscreen) + { + UpdateBounds(); + } } void AnimatedModel::OnAnimationUpdated_Sync() diff --git a/Source/Engine/Level/Actors/AnimatedModel.h b/Source/Engine/Level/Actors/AnimatedModel.h index 57224f83e..b7bf51108 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.h +++ b/Source/Engine/Level/Actors/AnimatedModel.h @@ -9,6 +9,14 @@ #include "Engine/Renderer/DrawCall.h" #include "Engine/Core/Delegate.h" +API_STRUCT() struct ModelBoneNode +{ + DECLARE_SCRIPTING_TYPE_MINIMAL(ModelBoneNode); + + API_FIELD() uint32 NodeIndex; + API_FIELD() Matrix NodeMatrix; +}; + /// /// Performs an animation and renders a skinned model. /// @@ -236,13 +244,21 @@ public: /// True if convert matrices into world-space, otherwise returned values will be in local-space of the actor. API_FUNCTION() void GetNodeTransformation(const StringView& nodeName, API_PARAM(Out) Matrix& nodeTransformation, bool worldSpace = false) const; + /// + /// Gets the node final transformation for a series of nodes. + /// + /// The series of nodes that will be returned + /// True if convert matrices into world-space, otherwise returned values will be in local-space of the actor. + /// + API_FUNCTION() void GetNodeTransformation(API_PARAM(Out) Array& modelBoneNodes, bool worldSpace = false) const; + /// /// Sets the node final transformation. If multiple nodes are to be set within a frame, do not use set worldSpace to true, and do the conversion yourself to avoid recalculation of inv matrices. /// /// The index of the skinned model skeleton node. /// The final node transformation matrix. /// True if convert matrices from world-space, otherwise values will be in local-space of the actor. - API_FUNCTION() void SetNodeTransformation(int32 nodeIndex, const Matrix& nodeTransformation, bool worldSpace = false); + API_FUNCTION() void SetNodeTransformation(int32 nodeIndex, const Matrix& modelBoneNodes, bool worldSpace = false); /// /// Sets the node final transformation. If multiple nodes are to be set within a frame, do not use set worldSpace to true, and do the conversion yourself to avoid recalculation of inv matrices. @@ -252,6 +268,14 @@ public: /// True if convert matrices from world-space, otherwise values will be in local-space of the actor. API_FUNCTION() void SetNodeTransformation(const StringView& nodeName, const Matrix& nodeTransformation, bool worldSpace = false); + /// + /// Sets a group of nodes final transformation. + /// + /// Array of the final node transformation matrix. + /// True if convert matrices from world-space, otherwise values will be in local-space of the actor. + /// + API_FUNCTION() void SetNodeTransformation(Array& nodesTransformations, bool worldSpace = false); + /// /// Finds the closest node to a given location. /// From 245d7de818571953668ad5c5ccadc1c28437d490 Mon Sep 17 00:00:00 2001 From: Olly Rybak Date: Wed, 14 May 2025 19:32:11 +1000 Subject: [PATCH 2/3] Fixed renamed parameters --- Source/Engine/Level/Actors/AnimatedModel.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Level/Actors/AnimatedModel.h b/Source/Engine/Level/Actors/AnimatedModel.h index b7bf51108..9e5a34c69 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.h +++ b/Source/Engine/Level/Actors/AnimatedModel.h @@ -258,7 +258,7 @@ public: /// The index of the skinned model skeleton node. /// The final node transformation matrix. /// True if convert matrices from world-space, otherwise values will be in local-space of the actor. - API_FUNCTION() void SetNodeTransformation(int32 nodeIndex, const Matrix& modelBoneNodes, bool worldSpace = false); + API_FUNCTION() void SetNodeTransformation(int32 nodeIndex, const Matrix& nodeTransformation, bool worldSpace = false); /// /// Sets the node final transformation. If multiple nodes are to be set within a frame, do not use set worldSpace to true, and do the conversion yourself to avoid recalculation of inv matrices. @@ -271,10 +271,10 @@ public: /// /// Sets a group of nodes final transformation. /// - /// Array of the final node transformation matrix. + /// Array of the final node transformation matrix. /// True if convert matrices from world-space, otherwise values will be in local-space of the actor. /// - API_FUNCTION() void SetNodeTransformation(Array& nodesTransformations, bool worldSpace = false); + API_FUNCTION() void SetNodeTransformation(Array& modelBoneNodes, bool worldSpace = false); /// /// Finds the closest node to a given location. From f8dadac453b9945806648c4f771177bde4399f6a Mon Sep 17 00:00:00 2001 From: Olly Rybak Date: Sat, 19 Jul 2025 22:37:35 +1000 Subject: [PATCH 3/3] Fixed up some names and added docs --- Source/Engine/Level/Actors/AnimatedModel.cpp | 12 +++---- Source/Engine/Level/Actors/AnimatedModel.h | 34 +++++++++++++------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index d438f94c6..82c9b163d 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -174,9 +174,9 @@ void AnimatedModel::GetNodeTransformation(const StringView& nodeName, Matrix& no GetNodeTransformation(SkinnedModel ? SkinnedModel->FindNode(nodeName) : -1, nodeTransformation, worldSpace); } -void AnimatedModel::GetNodeTransformation(Array& modelBoneNodes, bool worldSpace) const +void AnimatedModel::GetNodeTransformation(Array& nodeTransformations, bool worldSpace) const { - for (ModelBoneNode& item : modelBoneNodes) + for (NodeTransformation& item : nodeTransformations) { GetNodeTransformation(item.NodeIndex, item.NodeMatrix, worldSpace); } @@ -199,7 +199,7 @@ void AnimatedModel::SetNodeTransformation(int32 nodeIndex, const Matrix& nodeTra OnAnimationUpdated(); } -void AnimatedModel::SetNodeTransformation(Array& modelBoneNodes, bool worldSpace) +void AnimatedModel::SetNodeTransformation(const Array& nodeTransformations, bool worldSpace) { if (GraphInstance.NodesPose.IsEmpty()) const_cast(this)->PreInitSkinningData(); // Ensure to have valid nodes pose to return @@ -213,11 +213,11 @@ void AnimatedModel::SetNodeTransformation(Array& modelBoneNodes, Matrix::Invert(world, invWorld); } - for (int i = 0; i < modelBoneNodes.Count(); i++) + for (int i = 0; i < nodeTransformations.Count(); i++) { - int nodeIndex = modelBoneNodes[i].NodeIndex; + int nodeIndex = nodeTransformations[i].NodeIndex; CHECK(nodeIndex >= 0 && nodeIndex < GraphInstance.NodesPose.Count()); - GraphInstance.NodesPose[nodeIndex] = modelBoneNodes[i].NodeMatrix; + GraphInstance.NodesPose[nodeIndex] = nodeTransformations[i].NodeMatrix; if (worldSpace) { GraphInstance.NodesPose[nodeIndex] = GraphInstance.NodesPose[nodeIndex] * invWorld; diff --git a/Source/Engine/Level/Actors/AnimatedModel.h b/Source/Engine/Level/Actors/AnimatedModel.h index 9e5a34c69..50ab6be2e 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.h +++ b/Source/Engine/Level/Actors/AnimatedModel.h @@ -9,14 +9,6 @@ #include "Engine/Renderer/DrawCall.h" #include "Engine/Core/Delegate.h" -API_STRUCT() struct ModelBoneNode -{ - DECLARE_SCRIPTING_TYPE_MINIMAL(ModelBoneNode); - - API_FIELD() uint32 NodeIndex; - API_FIELD() Matrix NodeMatrix; -}; - /// /// Performs an animation and renders a skinned model. /// @@ -26,6 +18,24 @@ class FLAXENGINE_API AnimatedModel : public ModelInstanceActor DECLARE_SCENE_OBJECT(AnimatedModel); friend class AnimationsSystem; + /// + /// Keeps the data of a Node and its relevant Transform Matrix together when passing it between functions. + /// + API_STRUCT() struct NodeTransformation + { + DECLARE_SCRIPTING_TYPE_MINIMAL(NodeTransformation); + + /// + /// The index of the node in the node hierarchy. + /// + API_FIELD() uint32 NodeIndex; + + /// + /// The transformation matrix of the node + /// + API_FIELD() Matrix NodeMatrix; + }; + /// /// Describes the animation graph updates frequency for the animated model. /// @@ -247,10 +257,10 @@ public: /// /// Gets the node final transformation for a series of nodes. /// - /// The series of nodes that will be returned + /// The series of nodes that will be returned /// True if convert matrices into world-space, otherwise returned values will be in local-space of the actor. /// - API_FUNCTION() void GetNodeTransformation(API_PARAM(Out) Array& modelBoneNodes, bool worldSpace = false) const; + API_FUNCTION() void GetNodeTransformation(API_PARAM(Ref) Array& nodeTransformations, bool worldSpace = false) const; /// /// Sets the node final transformation. If multiple nodes are to be set within a frame, do not use set worldSpace to true, and do the conversion yourself to avoid recalculation of inv matrices. @@ -271,10 +281,10 @@ public: /// /// Sets a group of nodes final transformation. /// - /// Array of the final node transformation matrix. + /// Array of the final node transformation matrix. /// True if convert matrices from world-space, otherwise values will be in local-space of the actor. /// - API_FUNCTION() void SetNodeTransformation(Array& modelBoneNodes, bool worldSpace = false); + API_FUNCTION() void SetNodeTransformation(const Array& nodeTransformations, bool worldSpace = false); /// /// Finds the closest node to a given location.