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. ///