From 3812c74eab1f78fe5fe87fa234d8bc9c5ce0708b Mon Sep 17 00:00:00 2001 From: "Silc Lizard (Tokage) Renew" <61938263+TokageItLab@users.noreply.github.com> Date: Sat, 15 Nov 2025 23:00:34 +0900 Subject: [PATCH] Add Deterministic option to IK --- doc/classes/IterateIK3D.xml | 4 ++++ scene/3d/iterate_ik_3d.cpp | 13 +++++++++++++ scene/3d/iterate_ik_3d.h | 5 +++++ 3 files changed, 22 insertions(+) diff --git a/doc/classes/IterateIK3D.xml b/doc/classes/IterateIK3D.xml index 76f7148f41a..1b380704cba 100644 --- a/doc/classes/IterateIK3D.xml +++ b/doc/classes/IterateIK3D.xml @@ -145,6 +145,10 @@ The maximum amount each bone can rotate in a single iteration. [b]Note:[/b] This limitation is applied during each iteration. For example, if [member max_iterations] is [code]4[/code] and [member angular_delta_limit] is [code]5[/code] degrees, the maximum rotation possible in a single frame is [code]20[/code] degrees. + + If [code]false[/code], the result is calculated from the previous frame's [IterateIK3D] result as the initial state. + If [code]true[/code], the previous frame's [IterateIK3D] result is discarded. At this point, the new result is calculated from the bone pose excluding the [IterateIK3D] as the initial state. This means the result will be always equal as long as the target position and the previous bone pose are the same. However, if [member angular_delta_limit] and [member max_iterations] are set too small, the end bone of the chain will never reach the target. + The number of iteration loops used by the IK solver to produce more accurate results. diff --git a/scene/3d/iterate_ik_3d.cpp b/scene/3d/iterate_ik_3d.cpp index 12c2d1e9fc4..8a86b867ced 100644 --- a/scene/3d/iterate_ik_3d.cpp +++ b/scene/3d/iterate_ik_3d.cpp @@ -190,6 +190,14 @@ double IterateIK3D::get_angular_delta_limit() const { return angular_delta_limit; } +void IterateIK3D::set_deterministic(bool p_deterministic) { + deterministic = p_deterministic; +} + +bool IterateIK3D::is_deterministic() const { + return deterministic; +} + // Setting. void IterateIK3D::set_target_node(int p_index, const NodePath &p_node_path) { @@ -362,6 +370,8 @@ void IterateIK3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_min_distance"), &IterateIK3D::get_min_distance); ClassDB::bind_method(D_METHOD("set_angular_delta_limit", "angular_delta_limit"), &IterateIK3D::set_angular_delta_limit); ClassDB::bind_method(D_METHOD("get_angular_delta_limit"), &IterateIK3D::get_angular_delta_limit); + ClassDB::bind_method(D_METHOD("set_deterministic", "deterministic"), &IterateIK3D::set_deterministic); + ClassDB::bind_method(D_METHOD("is_deterministic"), &IterateIK3D::is_deterministic); // Setting. ClassDB::bind_method(D_METHOD("set_target_node", "index", "target_node"), &IterateIK3D::set_target_node); @@ -384,6 +394,7 @@ void IterateIK3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "max_iterations", PROPERTY_HINT_RANGE, "0,100,or_greater"), "set_max_iterations", "get_max_iterations"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "min_distance", PROPERTY_HINT_RANGE, "0,1,0.001,or_greater"), "set_min_distance", "get_min_distance"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_delta_limit", PROPERTY_HINT_RANGE, "0,180,0.001,radians_as_degrees"), "set_angular_delta_limit", "get_angular_delta_limit"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deterministic"), "set_deterministic", "is_deterministic"); ADD_ARRAY_COUNT("Settings", "setting_count", "set_setting_count", "get_setting_count", "settings/"); } @@ -414,6 +425,8 @@ void IterateIK3D::_init_joints(Skeleton3D *p_skeleton, int p_index) { _clear_joints(p_index); setting->init_joints(p_skeleton, mutable_bone_axes); setting->simulation_dirty = false; + } else if (deterministic) { + setting->init_joints(p_skeleton, mutable_bone_axes); } if (mutable_bone_axes) { diff --git a/scene/3d/iterate_ik_3d.h b/scene/3d/iterate_ik_3d.h index 3716d1b5969..ea7f5a58c0b 100644 --- a/scene/3d/iterate_ik_3d.h +++ b/scene/3d/iterate_ik_3d.h @@ -279,6 +279,8 @@ protected: double min_distance_squared = min_distance * min_distance; // For cache. double angular_delta_limit = Math::deg_to_rad(2.0); // If the delta is too large, the results before and after iterating can change significantly, and divergence of calculations can easily occur. + bool deterministic = false; + bool _get(const StringName &p_path, Variant &r_ret) const; bool _set(const StringName &p_path, const Variant &p_value); void _get_property_list(List *p_list) const; @@ -325,6 +327,9 @@ public: void set_angular_delta_limit(double p_angular_delta_limit); double get_angular_delta_limit() const; + void set_deterministic(bool p_deterministic); + bool is_deterministic() const; + // Setting. void set_target_node(int p_index, const NodePath &p_target_node); NodePath get_target_node(int p_index) const;