diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp
index 842621e1d..bcbce5986 100644
--- a/Source/Engine/Level/Actors/AnimatedModel.cpp
+++ b/Source/Engine/Level/Actors/AnimatedModel.cpp
@@ -147,6 +147,24 @@ void AnimatedModel::GetNodeTransformation(const StringView& nodeName, Matrix& no
GetNodeTransformation(SkinnedModel ? SkinnedModel->FindNode(nodeName) : -1, nodeTransformation, worldSpace);
}
+int32 AnimatedModel::FindClosestNode(const Vector3& location, bool worldSpace) const
+{
+ const Vector3 pos = worldSpace ? _transform.WorldToLocal(location) : location;
+ int32 result = -1;
+ float closest = MAX_float;
+ for (int32 nodeIndex = 0; nodeIndex < GraphInstance.NodesPose.Count(); nodeIndex++)
+ {
+ const Vector3 node = GraphInstance.NodesPose[nodeIndex].GetTranslation();
+ const float dst = Vector3::DistanceSquared(node, pos);
+ if (dst < closest)
+ {
+ closest = dst;
+ result = nodeIndex;
+ }
+ }
+ return result;
+}
+
#define CHECK_ANIM_GRAPH_PARAM_ACCESS() \
if (!AnimationGraph) \
{ \
@@ -234,6 +252,8 @@ void AnimatedModel::SetParameterValue(const Guid& id, const Variant& value)
LOG(Warning, "Failed to set animated model '{0}' missing parameter '{1}'", ToString(), id.ToString());
}
+#undef CHECK_ANIM_GRAPH_PARAM_ACCESS
+
float AnimatedModel::GetBlendShapeWeight(const StringView& name)
{
for (auto& e : _blendShapes.Weights)
diff --git a/Source/Engine/Level/Actors/AnimatedModel.h b/Source/Engine/Level/Actors/AnimatedModel.h
index bbced6878..748e58fbe 100644
--- a/Source/Engine/Level/Actors/AnimatedModel.h
+++ b/Source/Engine/Level/Actors/AnimatedModel.h
@@ -208,6 +208,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;
+ ///
+ /// Finds the closest node to a given location.
+ ///
+ /// The text location (in local-space of the actor or world-space depending on ).
+ /// True if convert input location is in world-space, otherwise it's in local-space of the actor.
+ /// The zero-based index of the found node. Returns -1 if skeleton is missing.
+ API_FUNCTION() int32 FindClosestNode(const Vector3& location, bool worldSpace = false) const;
+
public:
///