From 030fd71da23dc8e2c73febb966e4d6caf4f9ddbb Mon Sep 17 00:00:00 2001 From: Jeronimo Schreyer Date: Sun, 10 Dec 2023 18:26:14 -0300 Subject: [PATCH 1/2] add support for visualizing bones in Advanced Import Settings When an imported model Skeleton3D type node is selected, the bones are drawn using lines or octahedrons to provide a clearer reference to their position. Refactored Skeleton3DGizmoPlugin::redraw now uses a static function to generate bone meshes --- editor/import/3d/scene_import_settings.cpp | 23 +++++++- editor/import/3d/scene_import_settings.h | 2 + editor/plugins/skeleton_3d_editor_plugin.cpp | 61 ++++++++++++-------- editor/plugins/skeleton_3d_editor_plugin.h | 11 +++- 4 files changed, 68 insertions(+), 29 deletions(-) diff --git a/editor/import/3d/scene_import_settings.cpp b/editor/import/3d/scene_import_settings.cpp index 7cd5279b635..955eb8a8dce 100644 --- a/editor/import/3d/scene_import_settings.cpp +++ b/editor/import/3d/scene_import_settings.cpp @@ -37,6 +37,7 @@ #include "editor/editor_settings.h" #include "editor/editor_string_names.h" #include "editor/gui/editor_file_dialog.h" +#include "editor/plugins/skeleton_3d_editor_plugin.h" #include "editor/themes/editor_scale.h" #include "scene/3d/importer_mesh_instance_3d.h" #include "scene/animation/animation_player.h" @@ -419,7 +420,9 @@ void SceneImportSettingsDialog::_fill_scene(Node *p_node, TreeItem *p_parent_ite animation_player->connect(SceneStringName(animation_finished), callable_mp(this, &SceneImportSettingsDialog::_animation_finished)); } else if (Object::cast_to(p_node)) { category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE; - skeletons.push_back(Object::cast_to(p_node)); + Skeleton3D *skeleton = Object::cast_to(p_node); + skeleton->connect(SNAME("tree_entered"), callable_mp(this, &SceneImportSettingsDialog::_skeleton_tree_entered).bind(skeleton)); + skeletons.push_back(skeleton); } else { category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_NODE; } @@ -480,6 +483,11 @@ void SceneImportSettingsDialog::_fill_scene(Node *p_node, TreeItem *p_parent_ite contents_aabb.merge_with(aabb); } } + + Skeleton3D *skeleton = Object::cast_to(p_node); + if (skeleton) { + bones_mesh_preview->set_mesh(Skeleton3DGizmoPlugin::get_bones_mesh(skeleton, -1, true)); + } } void SceneImportSettingsDialog::_update_scene() { @@ -800,6 +808,7 @@ void SceneImportSettingsDialog::_select(Tree *p_from, const String &p_type, cons mesh_preview->hide(); _reset_animation(); + bones_mesh_preview->hide(); if (Object::cast_to(scene)) { Object::cast_to(scene)->show(); } @@ -834,6 +843,7 @@ void SceneImportSettingsDialog::_select(Tree *p_from, const String &p_type, cons scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE; } else if (Object::cast_to(nd.node)) { scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE; + bones_mesh_preview->show(); } else { scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_NODE; scene_import_settings_data->hide_options = editing_animation; @@ -1055,6 +1065,10 @@ void SceneImportSettingsDialog::_animation_slider_value_changed(double p_value) animation_player->seek(p_value * animation_map[selected_id].animation->get_length(), true); } +void SceneImportSettingsDialog::_skeleton_tree_entered(Skeleton3D *skeleton) { + bones_mesh_preview->set_skeleton_path(skeleton->get_path()); +} + void SceneImportSettingsDialog::_animation_finished(const StringName &p_name) { Animation::LoopMode loop_mode = animation_loop_mode; @@ -1800,6 +1814,13 @@ SceneImportSettingsDialog::SceneImportSettingsDialog() { collider_mat->set_albedo(Color(0.5, 0.5, 1.0)); } + { + bones_mesh_preview = memnew(MeshInstance3D); + bones_mesh_preview->set_cast_shadows_setting(GeometryInstance3D::SHADOW_CASTING_SETTING_OFF); + bones_mesh_preview->set_skeleton_path(NodePath()); + base_viewport->add_child(bones_mesh_preview); + } + inspector = memnew(EditorInspector); inspector->set_custom_minimum_size(Size2(300 * EDSCALE, 0)); inspector->connect(SNAME("property_edited"), callable_mp(this, &SceneImportSettingsDialog::_inspector_property_edited)); diff --git a/editor/import/3d/scene_import_settings.h b/editor/import/3d/scene_import_settings.h index c2a5151432d..a1c537ddc8d 100644 --- a/editor/import/3d/scene_import_settings.h +++ b/editor/import/3d/scene_import_settings.h @@ -113,6 +113,7 @@ class SceneImportSettingsDialog : public ConfirmationDialog { bool animation_pingpong = false; bool previous_import_as_skeleton = false; bool previous_rest_as_reset = false; + MeshInstance3D *bones_mesh_preview = nullptr; Ref collider_mat; @@ -190,6 +191,7 @@ class SceneImportSettingsDialog : public ConfirmationDialog { void _material_tree_selected(); void _mesh_tree_selected(); void _scene_tree_selected(); + void _skeleton_tree_entered(Skeleton3D *skeleton); void _cleanup(); void _on_light_1_switch_pressed(); void _on_light_2_switch_pressed(); diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index b340dd976e0..9ac5051eb2a 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -1180,16 +1180,18 @@ int Skeleton3DEditor::get_selected_bone() const { return selected_bone; } -Skeleton3DGizmoPlugin::Skeleton3DGizmoPlugin() { - unselected_mat = Ref(memnew(StandardMaterial3D)); - unselected_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); - unselected_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); - unselected_mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); - unselected_mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); - unselected_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true); +Skeleton3DGizmoPlugin::SelectionMaterials Skeleton3DGizmoPlugin::selection_materials; - selected_mat = Ref(memnew(ShaderMaterial)); - selected_sh = Ref(memnew(Shader)); +Skeleton3DGizmoPlugin::Skeleton3DGizmoPlugin() { + selection_materials.unselected_mat.instantiate(); + selection_materials.unselected_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + selection_materials.unselected_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); + selection_materials.unselected_mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + selection_materials.unselected_mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); + selection_materials.unselected_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true); + + selection_materials.selected_mat.instantiate(); + Ref selected_sh = Ref(memnew(Shader)); selected_sh->set_code(R"( // Skeleton 3D gizmo bones shader. @@ -1208,7 +1210,7 @@ void fragment() { ALPHA = COLOR.a; } )"); - selected_mat->set_shader(selected_sh); + selection_materials.selected_mat->set_shader(selected_sh); // Register properties in editor settings. EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/skeleton", Color(1, 0.8, 0.4)); @@ -1218,6 +1220,11 @@ void fragment() { EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "editors/3d_gizmos/gizmo_settings/bone_shape", PROPERTY_HINT_ENUM, "Wire,Octahedron")); } +Skeleton3DGizmoPlugin::~Skeleton3DGizmoPlugin() { + selection_materials.unselected_mat.unref(); + selection_materials.selected_mat.unref(); +} + bool Skeleton3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { return Object::cast_to(p_spatial) != nullptr; } @@ -1354,6 +1361,11 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { selected = se->get_selected_bone(); } + Ref m = get_bones_mesh(skeleton, selected, p_gizmo->is_selected()); + p_gizmo->add_mesh(m, Ref(), Transform3D(), skeleton->register_skin(skeleton->create_skin_from_rest_transforms())); +} + +Ref Skeleton3DGizmoPlugin::get_bones_mesh(Skeleton3D *p_skeleton, int p_selected, bool p_is_selected) { Color bone_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/skeleton"); Color selected_bone_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/selected_bone"); real_t bone_axis_length = EDITOR_GET("editors/3d_gizmos/gizmo_settings/bone_axis_length"); @@ -1367,11 +1379,11 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { Ref surface_tool(memnew(SurfaceTool)); surface_tool->begin(Mesh::PRIMITIVE_LINES); - if (p_gizmo->is_selected()) { - surface_tool->set_material(selected_mat); + if (p_is_selected) { + surface_tool->set_material(selection_materials.selected_mat); } else { - unselected_mat->set_albedo(bone_color); - surface_tool->set_material(unselected_mat); + selection_materials.unselected_mat->set_albedo(bone_color); + surface_tool->set_material(selection_materials.unselected_mat); } LocalVector bones; @@ -1385,16 +1397,16 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { weights[0] = 1; int current_bone_index = 0; - Vector bones_to_process = skeleton->get_parentless_bones(); + Vector bones_to_process = p_skeleton->get_parentless_bones(); while (bones_to_process.size() > current_bone_index) { int current_bone_idx = bones_to_process[current_bone_index]; current_bone_index++; - Color current_bone_color = (current_bone_idx == selected) ? selected_bone_color : bone_color; + Color current_bone_color = (current_bone_idx == p_selected) ? selected_bone_color : bone_color; Vector child_bones_vector; - child_bones_vector = skeleton->get_bone_children(current_bone_idx); + child_bones_vector = p_skeleton->get_bone_children(current_bone_idx); int child_bones_size = child_bones_vector.size(); for (int i = 0; i < child_bones_size; i++) { @@ -1405,8 +1417,8 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { int child_bone_idx = child_bones_vector[i]; - Vector3 v0 = skeleton->get_bone_global_rest(current_bone_idx).origin; - Vector3 v1 = skeleton->get_bone_global_rest(child_bone_idx).origin; + Vector3 v0 = p_skeleton->get_bone_global_rest(current_bone_idx).origin; + Vector3 v1 = p_skeleton->get_bone_global_rest(child_bone_idx).origin; Vector3 d = (v1 - v0).normalized(); real_t dist = v0.distance_to(v1); @@ -1414,7 +1426,7 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { int closest = -1; real_t closest_d = 0.0; for (int j = 0; j < 3; j++) { - real_t dp = Math::abs(skeleton->get_bone_global_rest(current_bone_idx).basis[j].normalized().dot(d)); + real_t dp = Math::abs(p_skeleton->get_bone_global_rest(current_bone_idx).basis[j].normalized().dot(d)); if (j == 0 || dp > closest_d) { closest = j; } @@ -1441,7 +1453,7 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { for (int j = 0; j < 3; j++) { Vector3 axis; if (first == Vector3()) { - axis = d.cross(d.cross(skeleton->get_bone_global_rest(current_bone_idx).basis[j])).normalized(); + axis = d.cross(d.cross(p_skeleton->get_bone_global_rest(current_bone_idx).basis[j])).normalized(); first = axis; } else { axis = d.cross(first).normalized(); @@ -1496,7 +1508,7 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { surface_tool->add_vertex(v0); surface_tool->set_bones(bones); surface_tool->set_weights(weights); - surface_tool->add_vertex(v0 + (skeleton->get_bone_global_rest(current_bone_idx).basis.inverse())[j].normalized() * dist * bone_axis_length); + surface_tool->add_vertex(v0 + (p_skeleton->get_bone_global_rest(current_bone_idx).basis.inverse())[j].normalized() * dist * bone_axis_length); if (j == closest) { continue; @@ -1513,7 +1525,7 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { surface_tool->add_vertex(v1); surface_tool->set_bones(bones); surface_tool->set_weights(weights); - surface_tool->add_vertex(v1 + (skeleton->get_bone_global_rest(child_bone_idx).basis.inverse())[j].normalized() * dist * bone_axis_length); + surface_tool->add_vertex(v1 + (p_skeleton->get_bone_global_rest(child_bone_idx).basis.inverse())[j].normalized() * dist * bone_axis_length); if (j == closest) { continue; @@ -1526,6 +1538,5 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { } } - Ref m = surface_tool->commit(); - p_gizmo->add_mesh(m, Ref(), Transform3D(), skeleton->register_skin(skeleton->create_skin_from_rest_transforms())); + return surface_tool->commit(); } diff --git a/editor/plugins/skeleton_3d_editor_plugin.h b/editor/plugins/skeleton_3d_editor_plugin.h index 79dc16ae2f6..42beb842a71 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.h +++ b/editor/plugins/skeleton_3d_editor_plugin.h @@ -252,11 +252,15 @@ public: class Skeleton3DGizmoPlugin : public EditorNode3DGizmoPlugin { GDCLASS(Skeleton3DGizmoPlugin, EditorNode3DGizmoPlugin); - Ref unselected_mat; - Ref selected_mat; - Ref selected_sh; + struct SelectionMaterials { + Ref unselected_mat; + Ref selected_mat; + }; + static SelectionMaterials selection_materials; public: + static Ref get_bones_mesh(Skeleton3D *p_skeleton, int p_selected, bool p_is_selected); + bool has_gizmo(Node3D *p_spatial) override; String get_gizmo_name() const override; int get_priority() const override; @@ -269,6 +273,7 @@ public: void redraw(EditorNode3DGizmo *p_gizmo) override; Skeleton3DGizmoPlugin(); + ~Skeleton3DGizmoPlugin(); }; #endif // SKELETON_3D_EDITOR_PLUGIN_H From 6532eff7a72ff7675f96c564c2a522f263358efd Mon Sep 17 00:00:00 2001 From: Saracen Date: Sun, 25 Aug 2024 20:47:14 +0100 Subject: [PATCH 2/2] Various improvements to advanced importer skeleton preview. Better size calculation in advanced importer preview. Uses the skeleton mesh to calculate the scene's bounding box. This improves some situations where a mesh instances' scale does not match its visual representation when a skeleton is applied. Advanced importer skeletal preview UX improvement. Make the visibility of the skeletal preview in the advanced importer when selecting an animation dependent on a new dedicated toggle button rather than carrying over whether a skeletal node was or was not previously selected before selecting the animation. Advanced importer skeletal preview fix. Fixes the preview on scaled skeletons in the advanced importer by applying the node's scale to the preview and generating a skin for it. --- editor/import/3d/scene_import_settings.cpp | 53 ++++++++++++++++++++-- editor/import/3d/scene_import_settings.h | 4 +- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/editor/import/3d/scene_import_settings.cpp b/editor/import/3d/scene_import_settings.cpp index 955eb8a8dce..ef4571230fd 100644 --- a/editor/import/3d/scene_import_settings.cpp +++ b/editor/import/3d/scene_import_settings.cpp @@ -421,7 +421,7 @@ void SceneImportSettingsDialog::_fill_scene(Node *p_node, TreeItem *p_parent_ite } else if (Object::cast_to(p_node)) { category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE; Skeleton3D *skeleton = Object::cast_to(p_node); - skeleton->connect(SNAME("tree_entered"), callable_mp(this, &SceneImportSettingsDialog::_skeleton_tree_entered).bind(skeleton)); + skeleton->connect(SceneStringName(tree_entered), callable_mp(this, &SceneImportSettingsDialog::_skeleton_tree_entered).bind(skeleton)); skeletons.push_back(skeleton); } else { category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_NODE; @@ -486,7 +486,27 @@ void SceneImportSettingsDialog::_fill_scene(Node *p_node, TreeItem *p_parent_ite Skeleton3D *skeleton = Object::cast_to(p_node); if (skeleton) { - bones_mesh_preview->set_mesh(Skeleton3DGizmoPlugin::get_bones_mesh(skeleton, -1, true)); + Ref bones_mesh = Skeleton3DGizmoPlugin::get_bones_mesh(skeleton, -1, true); + + bones_mesh_preview->set_mesh(bones_mesh); + + Transform3D accum_xform; + Node3D *base = skeleton; + while (base) { + accum_xform = base->get_transform() * accum_xform; + base = Object::cast_to(base->get_parent()); + } + + bones_mesh_preview->set_transform(accum_xform * skeleton->get_transform()); + + AABB aabb = accum_xform.xform(bones_mesh->get_aabb()); + + if (first_aabb) { + contents_aabb = aabb; + first_aabb = false; + } else { + contents_aabb.merge_with(aabb); + } } } @@ -803,12 +823,12 @@ void SceneImportSettingsDialog::_select(Tree *p_from, const String &p_type, cons selecting = true; scene_import_settings_data->hide_options = false; + bones_mesh_preview->hide(); if (p_type == "Node") { node_selected->hide(); // Always hide just in case. mesh_preview->hide(); _reset_animation(); - bones_mesh_preview->hide(); if (Object::cast_to(scene)) { Object::cast_to(scene)->show(); } @@ -863,6 +883,8 @@ void SceneImportSettingsDialog::_select(Tree *p_from, const String &p_type, cons scene_import_settings_data->settings = &ad.settings; scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_ANIMATION; + + _animation_update_skeleton_visibility(); } else if (p_type == "Mesh") { node_selected->hide(); if (Object::cast_to(scene)) { @@ -1065,8 +1087,9 @@ void SceneImportSettingsDialog::_animation_slider_value_changed(double p_value) animation_player->seek(p_value * animation_map[selected_id].animation->get_length(), true); } -void SceneImportSettingsDialog::_skeleton_tree_entered(Skeleton3D *skeleton) { - bones_mesh_preview->set_skeleton_path(skeleton->get_path()); +void SceneImportSettingsDialog::_skeleton_tree_entered(Skeleton3D *p_skeleton) { + bones_mesh_preview->set_skeleton_path(p_skeleton->get_path()); + bones_mesh_preview->set_skin(p_skeleton->register_skin(p_skeleton->create_skin_from_rest_transforms())); } void SceneImportSettingsDialog::_animation_finished(const StringName &p_name) { @@ -1094,6 +1117,14 @@ void SceneImportSettingsDialog::_animation_finished(const StringName &p_name) { } } +void SceneImportSettingsDialog::_animation_update_skeleton_visibility() { + if (animation_toggle_skeleton_visibility->is_pressed()) { + bones_mesh_preview->show(); + } else { + bones_mesh_preview->hide(); + } +} + void SceneImportSettingsDialog::_material_tree_selected() { if (selecting) { return; @@ -1296,6 +1327,8 @@ void SceneImportSettingsDialog::_notification(int p_what) { light_1_switch->set_icon(theme_cache.light_1_icon); light_2_switch->set_icon(theme_cache.light_2_icon); light_rotate_switch->set_icon(theme_cache.rotate_icon); + + animation_toggle_skeleton_visibility->set_icon(get_editor_theme_icon(SNAME("Skeleton3D"))); } break; case NOTIFICATION_PROCESS: { @@ -1675,6 +1708,7 @@ SceneImportSettingsDialog::SceneImportSettingsDialog() { animation_hbox->add_child(animation_stop_button); animation_stop_button->set_flat(true); animation_stop_button->set_focus_mode(Control::FOCUS_NONE); + animation_stop_button->set_tooltip_text(TTR("Selected Animation Stop")); animation_stop_button->connect(SceneStringName(pressed), callable_mp(this, &SceneImportSettingsDialog::_stop_current_animation)); animation_slider = memnew(HSlider); @@ -1687,6 +1721,15 @@ SceneImportSettingsDialog::SceneImportSettingsDialog() { animation_slider->set_focus_mode(Control::FOCUS_NONE); animation_slider->connect(SceneStringName(value_changed), callable_mp(this, &SceneImportSettingsDialog::_animation_slider_value_changed)); + animation_toggle_skeleton_visibility = memnew(Button); + animation_hbox->add_child(animation_toggle_skeleton_visibility); + animation_toggle_skeleton_visibility->set_toggle_mode(true); + animation_toggle_skeleton_visibility->set_flat(true); + animation_toggle_skeleton_visibility->set_focus_mode(Control::FOCUS_NONE); + animation_toggle_skeleton_visibility->set_tooltip_text(TTR("Toggle Animation Skeleton Visibility")); + + animation_toggle_skeleton_visibility->connect(SceneStringName(pressed), callable_mp(this, &SceneImportSettingsDialog::_animation_update_skeleton_visibility)); + base_viewport->set_use_own_world_3d(true); HBoxContainer *viewport_hbox = memnew(HBoxContainer); diff --git a/editor/import/3d/scene_import_settings.h b/editor/import/3d/scene_import_settings.h index a1c537ddc8d..c1600c34b89 100644 --- a/editor/import/3d/scene_import_settings.h +++ b/editor/import/3d/scene_import_settings.h @@ -109,6 +109,7 @@ class SceneImportSettingsDialog : public ConfirmationDialog { HSlider *animation_slider = nullptr; Button *animation_play_button = nullptr; Button *animation_stop_button = nullptr; + Button *animation_toggle_skeleton_visibility = nullptr; Animation::LoopMode animation_loop_mode = Animation::LOOP_NONE; bool animation_pingpong = false; bool previous_import_as_skeleton = false; @@ -188,10 +189,11 @@ class SceneImportSettingsDialog : public ConfirmationDialog { void _reset_animation(const String &p_animation_name = ""); void _animation_slider_value_changed(double p_value); void _animation_finished(const StringName &p_name); + void _animation_update_skeleton_visibility(); void _material_tree_selected(); void _mesh_tree_selected(); void _scene_tree_selected(); - void _skeleton_tree_entered(Skeleton3D *skeleton); + void _skeleton_tree_entered(Skeleton3D *p_skeleton); void _cleanup(); void _on_light_1_switch_pressed(); void _on_light_2_switch_pressed();