From 8a4fec4397c08450bf7356332a46b64a2dedde2b Mon Sep 17 00:00:00 2001 From: "Silc Lizard (Tokage) Renew" <61938263+TokageItLab@users.noreply.github.com> Date: Tue, 30 Dec 2025 08:12:27 +0900 Subject: [PATCH] Fix IKModifier/ JointLimitation gizmo on root bone and dirty handling --- .../3d/gizmos/chain_ik_3d_gizmo_plugin.cpp | 60 +++++++++++-------- .../3d/gizmos/chain_ik_3d_gizmo_plugin.h | 2 +- scene/3d/ik_modifier_3d.cpp | 24 ++++++-- scene/3d/ik_modifier_3d.h | 1 + 4 files changed, 56 insertions(+), 31 deletions(-) diff --git a/editor/scene/3d/gizmos/chain_ik_3d_gizmo_plugin.cpp b/editor/scene/3d/gizmos/chain_ik_3d_gizmo_plugin.cpp index 98a2801a87..bbdc674fcd 100644 --- a/editor/scene/3d/gizmos/chain_ik_3d_gizmo_plugin.cpp +++ b/editor/scene/3d/gizmos/chain_ik_3d_gizmo_plugin.cpp @@ -98,12 +98,15 @@ void ChainIK3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { return; } - Ref mesh = get_joints_mesh(skeleton, ik, p_gizmo->is_selected()); + Ref skeleton_mesh; + Ref mesh; + get_joints_mesh(skeleton, ik, p_gizmo->is_selected(), skeleton_mesh, mesh); Transform3D skel_tr = ik->get_global_transform().inverse() * skeleton->get_global_transform(); - p_gizmo->add_mesh(mesh, Ref(), skel_tr, skeleton->register_skin(skeleton->create_skin_from_rest_transforms())); + p_gizmo->add_mesh(skeleton_mesh, Ref(), skel_tr, skeleton->register_skin(skeleton->create_skin_from_rest_transforms())); + p_gizmo->add_mesh(mesh, Ref(), skel_tr); } -Ref ChainIK3DGizmoPlugin::get_joints_mesh(Skeleton3D *p_skeleton, ChainIK3D *p_ik, bool p_is_selected) { +void ChainIK3DGizmoPlugin::get_joints_mesh(Skeleton3D *p_skeleton, ChainIK3D *p_ik, bool p_is_selected, Ref &r_skinned_mesh, Ref &r_mesh) { Color bone_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/ik_chain"); static const Color limitation_x_axis_color = Color(1, 0, 0, 1); static const Color limitation_z_axis_color = Color(0, 0, 1, 1); @@ -114,11 +117,17 @@ Ref ChainIK3DGizmoPlugin::get_joints_mesh(Skeleton3D *p_skeleton, Cha surface_tool.instantiate(); surface_tool->begin(Mesh::PRIMITIVE_LINES); + Ref surface_tool_without_skin; + surface_tool_without_skin.instantiate(); + surface_tool_without_skin->begin(Mesh::PRIMITIVE_LINES); + if (p_is_selected) { surface_tool->set_material(selection_materials.selected_mat); + surface_tool_without_skin->set_material(selection_materials.selected_mat); } else { selection_materials.unselected_mat->set_albedo(bone_color); surface_tool->set_material(selection_materials.unselected_mat); + surface_tool_without_skin->set_material(selection_materials.unselected_mat); } PackedInt32Array bones; @@ -164,25 +173,24 @@ Ref ChainIK3DGizmoPlugin::get_joints_mesh(Skeleton3D *p_skeleton, Cha // Draw parent limitation shape. Ref lim = it_ik->get_joint_limitation(i, prev_joint); - if (lim.is_valid()) { + if (lim.is_valid() && prev_bone >= 0) { // Limitation space should bind parent bone rest. - if (prev_bone >= 0) { - int parent = p_skeleton->get_bone_parent(prev_bone); - if (parent >= 0) { - bones.write[0] = parent; - surface_tool->set_bones(bones); - surface_tool->set_weights(weights); - } + int parent = p_skeleton->get_bone_parent(prev_bone); + Ref limitation_surface_tool = parent >= 0 ? surface_tool : surface_tool_without_skin; + if (parent >= 0) { + bones.write[0] = parent; + limitation_surface_tool->set_bones(bones); + limitation_surface_tool->set_weights(weights); } Transform3D tr = anc_global_pose; tr.basis *= it_ik->get_joint_limitation_space(i, prev_joint, bone_vector.normalized()); float sl = MIN(current_length, prev_length); - lim->draw_shape(surface_tool, tr, sl, bone_color); + lim->draw_shape(limitation_surface_tool, tr, sl, bone_color); sl *= 0.1; Vector3 x_axis = tr.basis.get_column(Vector3::AXIS_X).normalized() * sl; Vector3 z_axis = tr.basis.get_column(Vector3::AXIS_Z).normalized() * sl; - draw_line(surface_tool, tr.origin + x_axis * 2, tr.origin + x_axis * 3, limitation_x_axis_color); // Offset 20%. - draw_line(surface_tool, tr.origin + z_axis * 2, tr.origin + z_axis * 3, limitation_z_axis_color); // Offset 20%. + draw_line(limitation_surface_tool, tr.origin + x_axis * 2, tr.origin + x_axis * 3, limitation_x_axis_color); // Offset 20%. + draw_line(limitation_surface_tool, tr.origin + z_axis * 2, tr.origin + z_axis * 3, limitation_z_axis_color); // Offset 20%. } } prev_length = current_length; @@ -207,25 +215,24 @@ Ref ChainIK3DGizmoPlugin::get_joints_mesh(Skeleton3D *p_skeleton, Cha if (it_ik) { // Draw limitation shape. Ref lim = it_ik->get_joint_limitation(i, j); - if (lim.is_valid()) { + if (lim.is_valid() && current_bone >= 0) { // Limitation space should bind parent bone rest. - if (current_bone >= 0) { - int parent = p_skeleton->get_bone_parent(current_bone); - if (parent >= 0) { - bones.write[0] = parent; - surface_tool->set_bones(bones); - surface_tool->set_weights(weights); - } + int parent = p_skeleton->get_bone_parent(current_bone); + Ref limitation_surface_tool = parent >= 0 ? surface_tool : surface_tool_without_skin; + if (parent >= 0) { + bones.write[0] = parent; + limitation_surface_tool->set_bones(bones); + limitation_surface_tool->set_weights(weights); } Transform3D tr = anc_global_pose; tr.basis *= it_ik->get_joint_limitation_space(i, j, bone_vector.normalized()); float sl = MIN(current_length, prev_length); - lim->draw_shape(surface_tool, tr, sl, bone_color); + lim->draw_shape(limitation_surface_tool, tr, sl, bone_color); sl *= 0.1; Vector3 x_axis = tr.basis.get_column(Vector3::AXIS_X).normalized() * sl; Vector3 z_axis = tr.basis.get_column(Vector3::AXIS_Z).normalized() * sl; - draw_line(surface_tool, tr.origin + x_axis * 2, tr.origin + x_axis * 3, limitation_x_axis_color); // Offset 20%. - draw_line(surface_tool, tr.origin + z_axis * 2, tr.origin + z_axis * 3, limitation_z_axis_color); // Offset 20%. + draw_line(limitation_surface_tool, tr.origin + x_axis * 2, tr.origin + x_axis * 3, limitation_x_axis_color); // Offset 20%. + draw_line(limitation_surface_tool, tr.origin + z_axis * 2, tr.origin + z_axis * 3, limitation_z_axis_color); // Offset 20%. } } } else { @@ -257,7 +264,8 @@ Ref ChainIK3DGizmoPlugin::get_joints_mesh(Skeleton3D *p_skeleton, Cha } } - return surface_tool->commit(); + r_skinned_mesh = surface_tool->commit(); + r_mesh = surface_tool_without_skin->commit(); } void ChainIK3DGizmoPlugin::draw_line(Ref &p_surface_tool, const Vector3 &p_begin_pos, const Vector3 &p_end_pos, const Color &p_color) { diff --git a/editor/scene/3d/gizmos/chain_ik_3d_gizmo_plugin.h b/editor/scene/3d/gizmos/chain_ik_3d_gizmo_plugin.h index 611374aa87..d29d109cb2 100644 --- a/editor/scene/3d/gizmos/chain_ik_3d_gizmo_plugin.h +++ b/editor/scene/3d/gizmos/chain_ik_3d_gizmo_plugin.h @@ -46,7 +46,7 @@ class ChainIK3DGizmoPlugin : public EditorNode3DGizmoPlugin { static SelectionMaterials selection_materials; public: - static Ref get_joints_mesh(Skeleton3D *p_skeleton, ChainIK3D *p_ik, bool p_is_selected); + static void get_joints_mesh(Skeleton3D *p_skeleton, ChainIK3D *p_ik, bool p_is_selected, Ref &r_skinned_mesh, Ref &r_mesh); static void draw_line(Ref &p_surface_tool, const Vector3 &p_begin_pos, const Vector3 &p_end_pos, const Color &p_color); bool has_gizmo(Node3D *p_spatial) override; diff --git a/scene/3d/ik_modifier_3d.cpp b/scene/3d/ik_modifier_3d.cpp index 2ec5c00e73..0332611bc3 100644 --- a/scene/3d/ik_modifier_3d.cpp +++ b/scene/3d/ik_modifier_3d.cpp @@ -71,11 +71,11 @@ void IKModifier3D::_set_active(bool p_active) { } void IKModifier3D::_skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) { - if (p_old && p_old->is_connected(SNAME("rest_updated"), callable_mp(this, &IKModifier3D::_make_all_joints_dirty))) { - p_old->disconnect(SNAME("rest_updated"), callable_mp(this, &IKModifier3D::_make_all_joints_dirty)); + if (p_old && p_old->is_connected(SNAME("rest_updated"), callable_mp(this, &IKModifier3D::_rest_updated))) { + p_old->disconnect(SNAME("rest_updated"), callable_mp(this, &IKModifier3D::_rest_updated)); } - if (p_new && !p_new->is_connected(SNAME("rest_updated"), callable_mp(this, &IKModifier3D::_make_all_joints_dirty))) { - p_new->connect(SNAME("rest_updated"), callable_mp(this, &IKModifier3D::_make_all_joints_dirty)); + if (p_new && !p_new->is_connected(SNAME("rest_updated"), callable_mp(this, &IKModifier3D::_rest_updated))) { + p_new->connect(SNAME("rest_updated"), callable_mp(this, &IKModifier3D::_rest_updated)); } _make_all_joints_dirty(); } @@ -84,6 +84,22 @@ void IKModifier3D::_validate_bone_names() { // } +void IKModifier3D::_rest_updated() { + _make_all_joints_dirty(); + if (is_inside_tree()) { + Skeleton3D *skeleton = get_skeleton(); + if (skeleton) { + for (uint32_t i = 0; i < settings.size(); i++) { + _init_joints(skeleton, i); + } + } + } +#ifdef TOOLS_ENABLED + _update_mutable_info(); + _make_gizmo_dirty(); +#endif // TOOLS_ENABLED +} + void IKModifier3D::_make_all_joints_dirty() { // } diff --git a/scene/3d/ik_modifier_3d.h b/scene/3d/ik_modifier_3d.h index 1e2e670d9d..71b2d7f2a4 100644 --- a/scene/3d/ik_modifier_3d.h +++ b/scene/3d/ik_modifier_3d.h @@ -76,6 +76,7 @@ protected: virtual void _validate_bone_names() override; + void _rest_updated(); virtual void _make_all_joints_dirty(); virtual void _init_joints(Skeleton3D *p_skeleton, int p_index); virtual void _update_joints(int p_index);