diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index 7863578c6..02c31c8ac 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -176,6 +176,14 @@ void AnimatedModel::GetNodeTransformation(const StringView& nodeName, Matrix& no GetNodeTransformation(SkinnedModel ? SkinnedModel->FindNode(nodeName) : -1, nodeTransformation, worldSpace); } +void AnimatedModel::GetNodeTransformation(Array& nodeTransformations, bool worldSpace) const +{ + for (NodeTransformation& item : nodeTransformations) + { + GetNodeTransformation(item.NodeIndex, item.NodeMatrix, worldSpace); + } +} + void AnimatedModel::SetNodeTransformation(int32 nodeIndex, const Matrix& nodeTransformation, bool worldSpace) { if (GraphInstance.NodesPose.IsEmpty()) @@ -193,6 +201,33 @@ void AnimatedModel::SetNodeTransformation(int32 nodeIndex, const Matrix& nodeTra OnAnimationUpdated(); } +void AnimatedModel::SetNodeTransformation(const Array& nodeTransformations, 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 < nodeTransformations.Count(); i++) + { + int nodeIndex = nodeTransformations[i].NodeIndex; + CHECK(nodeIndex >= 0 && nodeIndex < GraphInstance.NodesPose.Count()); + GraphInstance.NodesPose[nodeIndex] = nodeTransformations[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); @@ -821,7 +856,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 859d89212..6c7bd19e5 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.h +++ b/Source/Engine/Level/Actors/AnimatedModel.h @@ -18,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. /// @@ -236,6 +254,14 @@ 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(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. /// @@ -252,6 +278,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(const Array& nodeTransformations, bool worldSpace = false); + /// /// Finds the closest node to a given location. ///