diff options
Diffstat (limited to 'editor')
20 files changed, 989 insertions, 151 deletions
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 75e7542abb..8fd1f5951e 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -3199,7 +3199,7 @@ void AnimationTrackEditor::update_keying() { } keying = keying_enabled; - //_update_menu(); + emit_signal("keying_changed"); } diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index 62b5911ac1..6507956d07 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -984,7 +984,7 @@ void ConnectionsDock::update_tree() { while (F && descr == String()) { for (int i = 0; i < F->get().signals.size(); i++) { if (F->get().signals[i].name == signal_name.operator String()) { - descr = DTR(F->get().signals[i].description.strip_edges()); + descr = DTR(F->get().signals[i].description); break; } } diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 1b423f69b7..a8ded44323 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -1583,9 +1583,9 @@ void EditorInspector::update_tree() { DocData *dd = EditorHelp::get_doc_data(); Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(type2); if (E) { - descr = E->get().brief_description; + descr = DTR(E->get().brief_description); } - class_descr_cache[type2] = DTR(descr); + class_descr_cache[type2] = descr; } category->set_tooltip(p.name + "::" + (class_descr_cache[type2] == "" ? "" : class_descr_cache[type2])); @@ -1755,7 +1755,7 @@ void EditorInspector::update_tree() { while (F && descr == String()) { for (int i = 0; i < F->get().properties.size(); i++) { if (F->get().properties[i].name == propname.operator String()) { - descr = DTR(F->get().properties[i].description.strip_edges()); + descr = DTR(F->get().properties[i].description); break; } } @@ -1765,7 +1765,7 @@ void EditorInspector::update_tree() { // Likely a theme property. for (int i = 0; i < F->get().theme_properties.size(); i++) { if (F->get().theme_properties[i].name == slices[1]) { - descr = DTR(F->get().theme_properties[i].description.strip_edges()); + descr = DTR(F->get().theme_properties[i].description); break; } } diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index a16605ab44..5f293f1fb3 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -503,17 +503,38 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("editors/grid_map/pick_distance", 5000.0); // 3D - _initial_set("editors/3d/primary_grid_color", Color(0.56, 0.56, 0.56)); - hints["editors/3d/primary_grid_color"] = PropertyInfo(Variant::COLOR, "editors/3d/primary_grid_color", PROPERTY_HINT_COLOR_NO_ALPHA, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); + _initial_set("editors/3d/primary_grid_color", Color(0.56, 0.56, 0.56, 0.5)); + hints["editors/3d/primary_grid_color"] = PropertyInfo(Variant::COLOR, "editors/3d/primary_grid_color", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT); - _initial_set("editors/3d/secondary_grid_color", Color(0.38, 0.38, 0.38)); - hints["editors/3d/secondary_grid_color"] = PropertyInfo(Variant::COLOR, "editors/3d/secondary_grid_color", PROPERTY_HINT_COLOR_NO_ALPHA, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); - - _initial_set("editors/3d/grid_size", 50); - hints["editors/3d/grid_size"] = PropertyInfo(Variant::INT, "editors/3d/grid_size", PROPERTY_HINT_RANGE, "1,500,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); + _initial_set("editors/3d/secondary_grid_color", Color(0.38, 0.38, 0.38, 0.5)); + hints["editors/3d/secondary_grid_color"] = PropertyInfo(Variant::COLOR, "editors/3d/secondary_grid_color", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT); + // If a line is a multiple of this, it uses the primary grid color. _initial_set("editors/3d/primary_grid_steps", 10); - hints["editors/3d/primary_grid_steps"] = PropertyInfo(Variant::INT, "editors/3d/primary_grid_steps", PROPERTY_HINT_RANGE, "1,100,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); + hints["editors/3d/primary_grid_steps"] = PropertyInfo(Variant::INT, "editors/3d/primary_grid_steps", PROPERTY_HINT_RANGE, "1,100,1", PROPERTY_USAGE_DEFAULT); + + // At 1000, the grid mostly looks like it has no edge. + _initial_set("editors/3d/grid_size", 200); + hints["editors/3d/grid_size"] = PropertyInfo(Variant::INT, "editors/3d/grid_size", PROPERTY_HINT_RANGE, "1,2000,1", PROPERTY_USAGE_DEFAULT); + + // Default largest grid size is 100m, 10^2 (primary grid lines are 1km apart when primary_grid_steps is 10). + _initial_set("editors/3d/grid_division_level_max", 2); + // Higher values produce graphical artifacts when far away unless View Z-Far + // is increased significantly more than it really should need to be. + hints["editors/3d/grid_division_level_max"] = PropertyInfo(Variant::INT, "editors/3d/grid_division_level_max", PROPERTY_HINT_RANGE, "-1,3,1", PROPERTY_USAGE_DEFAULT); + + // Default smallest grid size is 1cm, 10^-2. + _initial_set("editors/3d/grid_division_level_min", -2); + // Lower values produce graphical artifacts regardless of view clipping planes, so limit to -2 as a lower bound. + hints["editors/3d/grid_division_level_min"] = PropertyInfo(Variant::INT, "editors/3d/grid_division_level_min", PROPERTY_HINT_RANGE, "-2,2,1", PROPERTY_USAGE_DEFAULT); + + // -0.2 seems like a sensible default. -1.0 gives Blender-like behavior, 0.5 gives huge grids. + _initial_set("editors/3d/grid_division_level_bias", -0.2); + hints["editors/3d/grid_division_level_bias"] = PropertyInfo(Variant::FLOAT, "editors/3d/grid_division_level_bias", PROPERTY_HINT_RANGE, "-1.0,0.5,0.1", PROPERTY_USAGE_DEFAULT); + + _initial_set("editors/3d/grid_xz_plane", true); + _initial_set("editors/3d/grid_xy_plane", false); + _initial_set("editors/3d/grid_yz_plane", false); _initial_set("editors/3d/default_fov", 70.0); _initial_set("editors/3d/default_z_near", 0.05); diff --git a/editor/import/resource_importer_texture_atlas.cpp b/editor/import/resource_importer_texture_atlas.cpp index 0818655c4c..2848d13160 100644 --- a/editor/import/resource_importer_texture_atlas.cpp +++ b/editor/import/resource_importer_texture_atlas.cpp @@ -33,6 +33,7 @@ #include "atlas_import_failed.xpm" #include "core/io/image_loader.h" #include "core/io/resource_saver.h" +#include "core/math/geometry_2d.h" #include "core/os/file_access.h" #include "editor/editor_atlas_packer.h" #include "scene/resources/mesh.h" @@ -247,7 +248,7 @@ Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file chart.vertices = polygons[j]; chart.can_transpose = true; - Vector<int> poly = Geometry::triangulate_polygon(polygons[j]); + Vector<int> poly = Geometry2D::triangulate_polygon(polygons[j]); for (int i = 0; i < poly.size(); i += 3) { EditorAtlasPacker::Chart::Face f; f.vertex[0] = poly[i + 0]; diff --git a/editor/node_3d_editor_gizmos.cpp b/editor/node_3d_editor_gizmos.cpp index 0eb4e6036a..2ddcf3d877 100644 --- a/editor/node_3d_editor_gizmos.cpp +++ b/editor/node_3d_editor_gizmos.cpp @@ -30,7 +30,8 @@ #include "node_3d_editor_gizmos.h" -#include "core/math/geometry.h" +#include "core/math/geometry_2d.h" +#include "core/math/geometry_3d.h" #include "core/math/quick_hull.h" #include "scene/3d/audio_stream_player_3d.h" #include "scene/3d/baked_lightmap.h" @@ -482,7 +483,7 @@ bool EditorNode3DGizmo::intersect_frustum(const Camera3D *p_camera, const Vector transformed_frustum.push_back(it.xform(p_frustum[i])); } - Vector<Vector3> convex_points = Geometry::compute_convex_mesh_points(p_frustum.ptr(), p_frustum.size()); + Vector<Vector3> convex_points = Geometry3D::compute_convex_mesh_points(p_frustum.ptr(), p_frustum.size()); if (collision_mesh->inside_convex_shape(transformed_frustum.ptr(), transformed_frustum.size(), convex_points.ptr(), convex_points.size(), mesh_scale)) { return true; } @@ -616,7 +617,7 @@ bool EditorNode3DGizmo::intersect_ray(Camera3D *p_camera, const Point2 &p_point, s[0] = p_camera->unproject_position(a); s[1] = p_camera->unproject_position(b); - Vector2 p = Geometry::get_closest_point_to_segment_2d(p_point, s); + Vector2 p = Geometry2D::get_closest_point_to_segment(p_point, s); float pd = p.distance_to(p_point); @@ -831,7 +832,7 @@ static float _find_closest_angle_to_half_pi_arc(const Vector3 &p_from, const Vec Vector3 n = Vector3(Math::cos(an), 0, -Math::sin(an)) * p_arc_radius; Vector3 ra, rb; - Geometry::get_closest_points_between_segments(p, n, p_from, p_to, ra, rb); + Geometry3D::get_closest_points_between_segments(p, n, p_from, p_to, ra, rb); float d = ra.distance_to(rb); if (d < min_d) { @@ -857,7 +858,7 @@ void Light3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camer if (p_idx == 0) { if (Object::cast_to<SpotLight3D>(light)) { Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), Vector3(0, 0, -4096), s[0], s[1], ra, rb); + Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(0, 0, -4096), s[0], s[1], ra, rb); float d = -ra.z; if (Node3DEditor::get_singleton()->is_snap_enabled()) { @@ -1100,7 +1101,7 @@ void AudioStreamPlayer3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int Vector3 to(Math::sin(an), 0, -Math::cos(an)); Vector3 r1, r2; - Geometry::get_closest_points_between_segments(from, to, ray_from, ray_to, r1, r2); + Geometry3D::get_closest_points_between_segments(from, to, ray_from, ray_to, r1, r2); float d = r1.distance_to(r2); if (d < closest_dist) { closest_dist = d; @@ -1238,7 +1239,7 @@ void Camera3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Came camera->set("fov", CLAMP(a * 2.0, 1, 179)); } else { Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(0, 0, -1), Vector3(4096, 0, -1), s[0], s[1], ra, rb); + Geometry3D::get_closest_points_between_segments(Vector3(0, 0, -1), Vector3(4096, 0, -1), s[0], s[1], ra, rb); float d = ra.x * 2.0; if (Node3DEditor::get_singleton()->is_snap_enabled()) { d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); @@ -2169,7 +2170,7 @@ void VisibilityNotifier3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int if (move) { Vector3 ra, rb; - Geometry::get_closest_points_between_segments(ofs - axis * 4096, ofs + axis * 4096, sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(ofs - axis * 4096, ofs + axis * 4096, sg[0], sg[1], ra, rb); float d = ra[p_idx]; if (Node3DEditor::get_singleton()->is_snap_enabled()) { @@ -2181,7 +2182,7 @@ void VisibilityNotifier3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int } else { Vector3 ra, rb; - Geometry::get_closest_points_between_segments(ofs, ofs + axis * 4096, sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(ofs, ofs + axis * 4096, sg[0], sg[1], ra, rb); float d = ra[p_idx] - ofs[p_idx]; if (Node3DEditor::get_singleton()->is_snap_enabled()) { @@ -2360,7 +2361,7 @@ void GPUParticles3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx if (move) { Vector3 ra, rb; - Geometry::get_closest_points_between_segments(ofs - axis * 4096, ofs + axis * 4096, sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(ofs - axis * 4096, ofs + axis * 4096, sg[0], sg[1], ra, rb); float d = ra[p_idx]; if (Node3DEditor::get_singleton()->is_snap_enabled()) { @@ -2372,7 +2373,7 @@ void GPUParticles3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx } else { Vector3 ra, rb; - Geometry::get_closest_points_between_segments(ofs, ofs + axis * 4096, sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(ofs, ofs + axis * 4096, sg[0], sg[1], ra, rb); float d = ra[p_idx] - ofs[p_idx]; if (Node3DEditor::get_singleton()->is_snap_enabled()) { @@ -2523,7 +2524,7 @@ void ReflectionProbeGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_id axis[p_idx] = 1.0; Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb); float d = ra[p_idx]; if (Node3DEditor::get_singleton()->is_snap_enabled()) { d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); @@ -2550,7 +2551,7 @@ void ReflectionProbeGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_id axis[p_idx] = 1.0; Vector3 ra, rb; - Geometry::get_closest_points_between_segments(origin - axis * 16384, origin + axis * 16384, sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(origin - axis * 16384, origin + axis * 16384, sg[0], sg[1], ra, rb); // Adjust the actual position to account for the gizmo handle position float d = ra[p_idx] + 0.25; if (Node3DEditor::get_singleton()->is_snap_enabled()) { @@ -2701,7 +2702,7 @@ void DecalGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3 axis[p_idx] = 1.0; Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb); float d = ra[p_idx]; if (Node3DEditor::get_singleton()->is_snap_enabled()) { d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); @@ -2842,7 +2843,7 @@ void GIProbeGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camer axis[p_idx] = 1.0; Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb); float d = ra[p_idx]; if (Node3DEditor::get_singleton()->is_snap_enabled()) { d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); @@ -3354,7 +3355,7 @@ void CollisionShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_i if (Object::cast_to<SphereShape3D>(*s)) { Ref<SphereShape3D> ss = s; Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb); float d = ra.x; if (Node3DEditor::get_singleton()->is_snap_enabled()) { d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); @@ -3370,7 +3371,7 @@ void CollisionShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_i if (Object::cast_to<RayShape3D>(*s)) { Ref<RayShape3D> rs = s; Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), Vector3(0, 0, 4096), sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(0, 0, 4096), sg[0], sg[1], ra, rb); float d = ra.z; if (Node3DEditor::get_singleton()->is_snap_enabled()) { d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); @@ -3388,7 +3389,7 @@ void CollisionShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_i axis[p_idx] = 1.0; Ref<BoxShape3D> bs = s; Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); float d = ra[p_idx]; if (Node3DEditor::get_singleton()->is_snap_enabled()) { d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); @@ -3408,7 +3409,7 @@ void CollisionShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_i axis[p_idx == 0 ? 0 : 2] = 1.0; Ref<CapsuleShape3D> cs2 = s; Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); float d = axis.dot(ra); if (p_idx == 1) { d -= cs2->get_radius(); @@ -3434,7 +3435,7 @@ void CollisionShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_i axis[p_idx == 0 ? 0 : 1] = 1.0; Ref<CylinderShape3D> cs2 = s; Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); + Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); float d = axis.dot(ra); if (Node3DEditor::get_singleton()->is_snap_enabled()) { d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); @@ -3802,7 +3803,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { if (points.size() > 3) { Vector<Vector3> varr = Variant(points); - Geometry::MeshData md; + Geometry3D::MeshData md; Error err = QuickHull::build(varr, md); if (err == OK) { Vector<Vector3> points2; diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp index 1abba45f9e..b905c8db12 100644 --- a/editor/plugins/abstract_polygon_2d_editor.cpp +++ b/editor/plugins/abstract_polygon_2d_editor.cpp @@ -31,6 +31,7 @@ #include "abstract_polygon_2d_editor.h" #include "canvas_item_editor_plugin.h" +#include "core/math/geometry_2d.h" #include "core/os/keyboard.h" #include "editor/editor_scale.h" @@ -671,7 +672,7 @@ AbstractPolygon2DEditor::PosVertex AbstractPolygon2DEditor::closest_edge_point(c Vector2 segment[2] = { xform.xform(points[i] + offset), xform.xform(points[(i + 1) % n_points] + offset) }; - Vector2 cp = Geometry::get_closest_point_to_segment_2d(p_pos, segment); + Vector2 cp = Geometry2D::get_closest_point_to_segment(p_pos, segment); if (cp.distance_squared_to(segment[0]) < eps2 || cp.distance_squared_to(segment[1]) < eps2) { continue; //not valid to reuse point diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp index df67482dfb..a7d5c2207b 100644 --- a/editor/plugins/animation_blend_space_2d_editor.cpp +++ b/editor/plugins/animation_blend_space_2d_editor.cpp @@ -32,7 +32,7 @@ #include "core/input/input.h" #include "core/io/resource_loader.h" -#include "core/math/delaunay_2d.h" +#include "core/math/geometry_2d.h" #include "core/os/keyboard.h" #include "core/project_settings.h" #include "editor/editor_scale.h" @@ -165,7 +165,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven triangle.push_back(points[idx]); } - if (Geometry::is_point_in_triangle(mb->get_position(), triangle[0], triangle[1], triangle[2])) { + if (Geometry2D::is_point_in_triangle(mb->get_position(), triangle[0], triangle[1], triangle[2])) { selected_triangle = i; _update_tool_erase(); return; diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index 652754a146..a435b1c482 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -32,7 +32,7 @@ #include "core/input/input.h" #include "core/io/resource_loader.h" -#include "core/math/delaunay_2d.h" +#include "core/math/geometry_2d.h" #include "core/os/keyboard.h" #include "core/project_settings.h" #include "editor/editor_scale.h" @@ -191,7 +191,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv transition_lines[i].from, transition_lines[i].to }; - Vector2 cpoint = Geometry::get_closest_point_to_segment_2d(mb->get_position(), s); + Vector2 cpoint = Geometry2D::get_closest_point_to_segment(mb->get_position(), s); float d = cpoint.distance_to(mb->get_position()); if (d > transition_lines[i].width) { continue; diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 8f88612ffa..573876c488 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -31,6 +31,7 @@ #include "canvas_item_editor_plugin.h" #include "core/input/input.h" +#include "core/math/geometry_2d.h" #include "core/os/keyboard.h" #include "core/print_string.h" #include "core/project_settings.h" @@ -672,7 +673,7 @@ void CanvasItemEditor::_get_bones_at_pos(const Point2 &p_pos, Vector<_SelectResu } // Check if the point is inside the Polygon2D - if (Geometry::is_point_in_polygon(screen_pos, bone_shape)) { + if (Geometry2D::is_point_in_polygon(screen_pos, bone_shape)) { // Check if the item is already in the list bool duplicate = false; for (int i = 0; i < r_items.size(); i++) { @@ -5610,6 +5611,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { hb->add_child(pan_button); pan_button->set_toggle_mode(true); pan_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select), make_binds(TOOL_PAN)); + pan_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/pan_mode", TTR("Pan Mode"), KEY_G)); pan_button->set_tooltip(TTR("Pan Mode")); ruler_button = memnew(ToolButton); diff --git a/editor/plugins/collision_polygon_3d_editor_plugin.cpp b/editor/plugins/collision_polygon_3d_editor_plugin.cpp index c61d410d38..d9d9cf6a87 100644 --- a/editor/plugins/collision_polygon_3d_editor_plugin.cpp +++ b/editor/plugins/collision_polygon_3d_editor_plugin.cpp @@ -32,6 +32,7 @@ #include "canvas_item_editor_plugin.h" #include "core/input/input.h" +#include "core/math/geometry_2d.h" #include "core/os/file_access.h" #include "core/os/keyboard.h" #include "editor/editor_settings.h" @@ -196,7 +197,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con p_camera->unproject_position(gt.xform(Vector3(poly[(i + 1) % poly.size()].x, poly[(i + 1) % poly.size()].y, depth))) }; - Vector2 cp = Geometry::get_closest_point_to_segment_2d(gpoint, points); + Vector2 cp = Geometry2D::get_closest_point_to_segment(gpoint, points); if (cp.distance_squared_to(points[0]) < CMP_EPSILON2 || cp.distance_squared_to(points[1]) < CMP_EPSILON2) { continue; //not valid to reuse point } diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 3c12022854..f59b4171b4 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -328,17 +328,13 @@ void Node3DEditorViewport::_update_camera(float p_interp_delta) { //------- // Apply camera transform - float tolerance = 0.001; + real_t tolerance = 0.001; bool equal = true; - if (Math::abs(old_camera_cursor.x_rot - camera_cursor.x_rot) > tolerance || Math::abs(old_camera_cursor.y_rot - camera_cursor.y_rot) > tolerance) { + if (!Math::is_equal_approx(old_camera_cursor.x_rot, camera_cursor.x_rot, tolerance) || !Math::is_equal_approx(old_camera_cursor.y_rot, camera_cursor.y_rot, tolerance)) { equal = false; - } - - if (equal && old_camera_cursor.pos.distance_squared_to(camera_cursor.pos) > tolerance * tolerance) { + } else if (!old_camera_cursor.pos.is_equal_approx(camera_cursor.pos)) { equal = false; - } - - if (equal && Math::abs(old_camera_cursor.distance - camera_cursor.distance) > tolerance) { + } else if (!Math::is_equal_approx(old_camera_cursor.distance, camera_cursor.distance, tolerance)) { equal = false; } @@ -356,6 +352,7 @@ void Node3DEditorViewport::_update_camera(float p_interp_delta) { update_transform_gizmo_view(); rotation_control->update(); } + spatial_editor->update_grid(); } Transform Node3DEditorViewport::to_camera_transform(const Cursor &p_cursor) const { @@ -835,7 +832,7 @@ bool Node3DEditorViewport::_gizmo_select(const Vector2 &p_screenpos, bool p_high Vector3 r; - if (Geometry::segment_intersects_sphere(ray_pos, ray_pos + ray * MAX_Z, grabber_pos, grabber_radius, &r)) { + if (Geometry3D::segment_intersects_sphere(ray_pos, ray_pos + ray * MAX_Z, grabber_pos, grabber_radius, &r)) { float d = r.distance_to(ray_pos); if (d < col_d) { col_d = d; @@ -932,7 +929,7 @@ bool Node3DEditorViewport::_gizmo_select(const Vector2 &p_screenpos, bool p_high Vector3 r; - if (Geometry::segment_intersects_sphere(ray_pos, ray_pos + ray * MAX_Z, grabber_pos, grabber_radius, &r)) { + if (Geometry3D::segment_intersects_sphere(ray_pos, ray_pos + ray * MAX_Z, grabber_pos, grabber_radius, &r)) { float d = r.distance_to(ray_pos); if (d < col_d) { col_d = d; @@ -4929,8 +4926,10 @@ void Node3DEditor::_menu_item_pressed(int p_option) { for (int i = 0; i < 3; ++i) { if (grid_enable[i]) { - RenderingServer::get_singleton()->instance_set_visible(grid_instance[i], grid_enabled); grid_visible[i] = grid_enabled; + if (grid_instance[i].is_valid()) { + RenderingServer::get_singleton()->instance_set_visible(grid_instance[i], grid_enabled); + } } } @@ -5054,6 +5053,7 @@ void Node3DEditor::_init_indicators() { indicator_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); indicator_mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); indicator_mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); + indicator_mat->set_transparency(StandardMaterial3D::Transparency::TRANSPARENCY_ALPHA_DEPTH_PRE_PASS); Vector<Color> origin_colors; Vector<Vector3> origin_points; @@ -5082,12 +5082,27 @@ void Node3DEditor::_init_indicators() { origin_colors.push_back(origin_color); origin_colors.push_back(origin_color); - origin_points.push_back(axis * 4096); - origin_points.push_back(axis * -4096); - } - - grid_enable[1] = true; - grid_visible[1] = true; + origin_colors.push_back(origin_color); + origin_colors.push_back(origin_color); + origin_colors.push_back(origin_color); + origin_colors.push_back(origin_color); + // To both allow having a large origin size and avoid jitter + // at small scales, we should segment the line into pieces. + // 3 pieces seems to do the trick, and let's use powers of 2. + origin_points.push_back(axis * 1048576); + origin_points.push_back(axis * 1024); + origin_points.push_back(axis * 1024); + origin_points.push_back(axis * -1024); + origin_points.push_back(axis * -1024); + origin_points.push_back(axis * -1048576); + } + + grid_enable[0] = EditorSettings::get_singleton()->get("editors/3d/grid_xy_plane"); + grid_enable[1] = EditorSettings::get_singleton()->get("editors/3d/grid_yz_plane"); + grid_enable[2] = EditorSettings::get_singleton()->get("editors/3d/grid_xz_plane"); + grid_visible[0] = grid_enable[0]; + grid_visible[1] = grid_enable[1]; + grid_visible[2] = grid_enable[2]; _init_grid(); @@ -5418,6 +5433,15 @@ void Node3DEditor::_update_gizmos_menu_theme() { } void Node3DEditor::_init_grid() { + if (!grid_enabled) { + return; + } + Camera3D *camera = get_editor_viewport(0)->camera; + Vector3 camera_position = camera->get_translation(); + if (camera_position == Vector3()) { + return; // Camera3D is invalid, don't draw the grid. + } + Vector<Color> grid_colors[3]; Vector<Vector3> grid_points[3]; @@ -5426,52 +5450,111 @@ void Node3DEditor::_init_grid() { int grid_size = EditorSettings::get_singleton()->get("editors/3d/grid_size"); int primary_grid_steps = EditorSettings::get_singleton()->get("editors/3d/primary_grid_steps"); - for (int i = 0; i < 3; i++) { - Vector3 axis; - axis[i] = 1; - Vector3 axis_n1; - axis_n1[(i + 1) % 3] = 1; - Vector3 axis_n2; - axis_n2[(i + 2) % 3] = 1; - - for (int j = -grid_size; j <= grid_size; j++) { - Vector3 p1 = axis_n1 * j + axis_n2 * -grid_size; - Vector3 p1_dest = p1 * (-axis_n2 + axis_n1); - Vector3 p2 = axis_n2 * j + axis_n1 * -grid_size; - Vector3 p2_dest = p2 * (-axis_n1 + axis_n2); - - Color line_color = secondary_grid_color; - if (origin_enabled && j == 0) { - // Don't draw the center lines of the grid if the origin is enabled - // The origin would overlap the grid lines in this case, causing flickering - continue; - } else if (j % primary_grid_steps == 0) { - line_color = primary_grid_color; + // Which grid planes are enabled? Which should we generate? + grid_enable[0] = grid_visible[0] = EditorSettings::get_singleton()->get("editors/3d/grid_xy_plane"); + grid_enable[1] = grid_visible[1] = EditorSettings::get_singleton()->get("editors/3d/grid_yz_plane"); + grid_enable[2] = grid_visible[2] = EditorSettings::get_singleton()->get("editors/3d/grid_xz_plane"); + + // Offsets division_level for bigger or smaller grids. + // Default value is -0.2. -1.0 gives Blender-like behavior, 0.5 gives huge grids. + real_t division_level_bias = EditorSettings::get_singleton()->get("editors/3d/grid_division_level_bias"); + // Default largest grid size is 100m, 10^2 (default value is 2). + int division_level_max = EditorSettings::get_singleton()->get("editors/3d/grid_division_level_max"); + // Default smallest grid size is 1cm, 10^-2 (default value is -2). + int division_level_min = EditorSettings::get_singleton()->get("editors/3d/grid_division_level_min"); + ERR_FAIL_COND_MSG(division_level_max < division_level_min, "The 3D grid's maximum division level cannot be lower than its minimum division level."); + + if (primary_grid_steps != 10) { // Log10 of 10 is 1. + // Change of base rule, divide by ln(10). + real_t div = Math::log((real_t)primary_grid_steps) / (real_t)2.302585092994045901094; + // Trucation (towards zero) is intentional. + division_level_max = (int)(division_level_max / div); + division_level_min = (int)(division_level_min / div); + } + + for (int a = 0; a < 3; a++) { + if (!grid_enable[a]) { + continue; // If this grid plane is disabled, skip generation. + } + int b = (a + 1) % 3; + int c = (a + 2) % 3; + + real_t division_level = Math::log(Math::abs(camera_position[c])) / Math::log((double)primary_grid_steps) + division_level_bias; + division_level = CLAMP(division_level, division_level_min, division_level_max); + real_t division_level_floored = Math::floor(division_level); + real_t division_level_decimals = division_level - division_level_floored; + + real_t small_step_size = Math::pow(primary_grid_steps, division_level_floored); + real_t large_step_size = small_step_size * primary_grid_steps; + real_t center_a = large_step_size * (int)(camera_position[a] / large_step_size); + real_t center_b = large_step_size * (int)(camera_position[b] / large_step_size); + + real_t bgn_a = center_a - grid_size * small_step_size; + real_t end_a = center_a + grid_size * small_step_size; + real_t bgn_b = center_b - grid_size * small_step_size; + real_t end_b = center_b + grid_size * small_step_size; + + // In each iteration of this loop, draw one line in each direction (so two lines per loop, in each if statement). + for (int i = -grid_size; i <= grid_size; i++) { + Color line_color; + // Is this a primary line? Set the appropriate color. + if (i % primary_grid_steps == 0) { + line_color = primary_grid_color.lerp(secondary_grid_color, division_level_decimals); + } else { + line_color = secondary_grid_color; + line_color.a = line_color.a * (1 - division_level_decimals); + } + // Makes lines farther from the center fade out. + // Due to limitations of lines, any that come near the camera have full opacity always. + // This should eventually be replaced by some kind of "distance fade" system, outside of this function. + // But the effect is still somewhat convincing... + line_color.a *= 1 - (1 - division_level_decimals * 0.9) * (Math::abs(i / (float)grid_size)); + + real_t position_a = center_a + i * small_step_size; + real_t position_b = center_b + i * small_step_size; + + // Don't draw lines over the origin if it's enabled. + if (!(origin_enabled && Math::is_zero_approx(position_a))) { + Vector3 line_bgn = Vector3(); + Vector3 line_end = Vector3(); + line_bgn[a] = position_a; + line_end[a] = position_a; + line_bgn[b] = bgn_b; + line_end[b] = end_b; + grid_points[c].push_back(line_bgn); + grid_points[c].push_back(line_end); + grid_colors[c].push_back(line_color); + grid_colors[c].push_back(line_color); } - grid_points[i].push_back(p1); - grid_points[i].push_back(p1_dest); - grid_colors[i].push_back(line_color); - grid_colors[i].push_back(line_color); - - grid_points[i].push_back(p2); - grid_points[i].push_back(p2_dest); - grid_colors[i].push_back(line_color); - grid_colors[i].push_back(line_color); + if (!(origin_enabled && Math::is_zero_approx(position_b))) { + Vector3 line_bgn = Vector3(); + Vector3 line_end = Vector3(); + line_bgn[b] = position_b; + line_end[b] = position_b; + line_bgn[a] = bgn_a; + line_end[a] = end_a; + grid_points[c].push_back(line_bgn); + grid_points[c].push_back(line_end); + grid_colors[c].push_back(line_color); + grid_colors[c].push_back(line_color); + } } - grid[i] = RenderingServer::get_singleton()->mesh_create(); + // Create a mesh from the pushed vector points and colors. + grid[c] = RenderingServer::get_singleton()->mesh_create(); Array d; d.resize(RS::ARRAY_MAX); - d[RenderingServer::ARRAY_VERTEX] = grid_points[i]; - d[RenderingServer::ARRAY_COLOR] = grid_colors[i]; - RenderingServer::get_singleton()->mesh_add_surface_from_arrays(grid[i], RenderingServer::PRIMITIVE_LINES, d); - RenderingServer::get_singleton()->mesh_surface_set_material(grid[i], 0, indicator_mat->get_rid()); - grid_instance[i] = RenderingServer::get_singleton()->instance_create2(grid[i], get_tree()->get_root()->get_world_3d()->get_scenario()); + d[RenderingServer::ARRAY_VERTEX] = grid_points[c]; + d[RenderingServer::ARRAY_COLOR] = grid_colors[c]; + RenderingServer::get_singleton()->mesh_add_surface_from_arrays(grid[c], RenderingServer::PRIMITIVE_LINES, d); + RenderingServer::get_singleton()->mesh_surface_set_material(grid[c], 0, indicator_mat->get_rid()); + grid_instance[c] = RenderingServer::get_singleton()->instance_create2(grid[c], get_tree()->get_root()->get_world_3d()->get_scenario()); - RenderingServer::get_singleton()->instance_set_visible(grid_instance[i], grid_visible[i]); - RenderingServer::get_singleton()->instance_geometry_set_cast_shadows_setting(grid_instance[i], RS::SHADOW_CASTING_SETTING_OFF); - RS::get_singleton()->instance_set_layer_mask(grid_instance[i], 1 << Node3DEditorViewport::GIZMO_GRID_LAYER); + // Yes, the end of this line is supposed to be a. + RenderingServer::get_singleton()->instance_set_visible(grid_instance[c], grid_visible[a]); + RenderingServer::get_singleton()->instance_geometry_set_cast_shadows_setting(grid_instance[c], RS::SHADOW_CASTING_SETTING_OFF); + RS::get_singleton()->instance_set_layer_mask(grid_instance[c], 1 << Node3DEditorViewport::GIZMO_GRID_LAYER); } } @@ -5489,6 +5572,11 @@ void Node3DEditor::_finish_grid() { } } +void Node3DEditor::update_grid() { + _finish_grid(); + _init_grid(); +} + bool Node3DEditor::is_any_freelook_active() const { for (unsigned int i = 0; i < VIEWPORTS_COUNT; ++i) { if (viewports[i]->is_freelook_active()) { diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index 3d92e7e7e1..32b087c372 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -766,6 +766,7 @@ public: Ref<ArrayMesh> get_scale_gizmo(int idx) const { return scale_gizmo[idx]; } Ref<ArrayMesh> get_scale_plane_gizmo(int idx) const { return scale_plane_gizmo[idx]; } + void update_grid(); void update_transform_gizmo(); void update_all_gizmos(Node *p_node = nullptr); void snap_selected_nodes_to_floor(); diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp index a44fe69ff6..25cffa3d6c 100644 --- a/editor/plugins/path_3d_editor_plugin.cpp +++ b/editor/plugins/path_3d_editor_plugin.cpp @@ -30,6 +30,8 @@ #include "path_3d_editor_plugin.h" +#include "core/math/geometry_2d.h" +#include "core/math/geometry_3d.h" #include "core/os/keyboard.h" #include "node_3d_editor_plugin.h" #include "scene/resources/curve.h" @@ -344,7 +346,7 @@ bool Path3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref Vector2 s[2]; s[0] = p_camera->unproject_position(from); s[1] = p_camera->unproject_position(to); - Vector2 inters = Geometry::get_closest_point_to_segment_2d(mbpos, s); + Vector2 inters = Geometry2D::get_closest_point_to_segment(mbpos, s); float d = inters.distance_to(mbpos); if (d < 10 && d < closest_d) { @@ -354,7 +356,7 @@ bool Path3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref Vector3 ray_dir = p_camera->project_ray_normal(mbpos); Vector3 ra, rb; - Geometry::get_closest_points_between_segments(ray_from, ray_from + ray_dir * 4096, from, to, ra, rb); + Geometry3D::get_closest_points_between_segments(ray_from, ray_from + ray_dir * 4096, from, to, ra, rb); closest_seg_point = it.xform(rb); } diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index 7ee695b9fe..1f98c0139b 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -32,6 +32,7 @@ #include "canvas_item_editor_plugin.h" #include "core/input/input.h" +#include "core/math/geometry_2d.h" #include "core/os/file_access.h" #include "core/os/keyboard.h" #include "editor/editor_scale.h" @@ -693,7 +694,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { polys.write[j] = mtx.xform(points_prev[idx]); } - if (Geometry::is_point_in_polygon(Vector2(mb->get_position().x, mb->get_position().y), polys)) { + if (Geometry2D::is_point_in_polygon(Vector2(mb->get_position().x, mb->get_position().y), polys)) { erase_index = i; break; } diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index c256acd17b..321b4432ab 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -30,13 +30,385 @@ #include "skeleton_3d_editor_plugin.h" +#include "core/io/resource_saver.h" +#include "editor/editor_file_dialog.h" +#include "editor/editor_properties.h" +#include "editor/editor_scale.h" +#include "editor/plugins/animation_player_editor_plugin.h" #include "node_3d_editor_plugin.h" #include "scene/3d/collision_shape_3d.h" +#include "scene/3d/mesh_instance_3d.h" #include "scene/3d/physics_body_3d.h" #include "scene/3d/physics_joint_3d.h" #include "scene/resources/capsule_shape_3d.h" #include "scene/resources/sphere_shape_3d.h" +void BoneTransformEditor::create_editors() { + const Color section_color = get_theme_color("prop_subsection", "Editor"); + + section = memnew(EditorInspectorSection); + section->setup("trf_properties", label, this, section_color, true); + add_child(section); + + key_button = memnew(Button); + key_button->set_text(TTR("Key Transform")); + key_button->set_visible(keyable); + key_button->set_icon(get_theme_icon("Key", "EditorIcons")); + key_button->set_flat(true); + section->get_vbox()->add_child(key_button); + + enabled_checkbox = memnew(CheckBox(TTR("Pose Enabled"))); + enabled_checkbox->set_flat(true); + enabled_checkbox->set_visible(toggle_enabled); + section->get_vbox()->add_child(enabled_checkbox); + + Label *l1 = memnew(Label(TTR("Translation"))); + section->get_vbox()->add_child(l1); + + translation_grid = memnew(GridContainer()); + translation_grid->set_columns(TRANSLATION_COMPONENTS); + section->get_vbox()->add_child(translation_grid); + + Label *l2 = memnew(Label(TTR("Rotation Degrees"))); + section->get_vbox()->add_child(l2); + + rotation_grid = memnew(GridContainer()); + rotation_grid->set_columns(ROTATION_DEGREES_COMPONENTS); + section->get_vbox()->add_child(rotation_grid); + + Label *l3 = memnew(Label(TTR("Scale"))); + section->get_vbox()->add_child(l3); + + scale_grid = memnew(GridContainer()); + scale_grid->set_columns(SCALE_COMPONENTS); + section->get_vbox()->add_child(scale_grid); + + Label *l4 = memnew(Label(TTR("Transform"))); + section->get_vbox()->add_child(l4); + + transform_grid = memnew(GridContainer()); + transform_grid->set_columns(TRANSFORM_CONTROL_COMPONENTS); + section->get_vbox()->add_child(transform_grid); + + static const char *desc[TRANSFORM_COMPONENTS] = { "x", "y", "z", "x", "y", "z", "x", "y", "z", "x", "y", "z" }; + + for (int i = 0; i < TRANSFORM_CONTROL_COMPONENTS; ++i) { + translation_slider[i] = memnew(EditorSpinSlider()); + translation_slider[i]->set_label(desc[i]); + setup_spinner(translation_slider[i], false); + translation_grid->add_child(translation_slider[i]); + + rotation_slider[i] = memnew(EditorSpinSlider()); + rotation_slider[i]->set_label(desc[i]); + setup_spinner(rotation_slider[i], false); + rotation_grid->add_child(rotation_slider[i]); + + scale_slider[i] = memnew(EditorSpinSlider()); + scale_slider[i]->set_label(desc[i]); + setup_spinner(scale_slider[i], false); + scale_grid->add_child(scale_slider[i]); + } + + for (int i = 0; i < TRANSFORM_COMPONENTS; ++i) { + transform_slider[i] = memnew(EditorSpinSlider()); + transform_slider[i]->set_label(desc[i]); + setup_spinner(transform_slider[i], true); + transform_grid->add_child(transform_slider[i]); + } +} + +void BoneTransformEditor::setup_spinner(EditorSpinSlider *spinner, const bool is_transform_spinner) { + spinner->set_flat(true); + spinner->set_min(-10000); + spinner->set_max(10000); + spinner->set_step(0.001f); + spinner->set_hide_slider(true); + spinner->set_allow_greater(true); + spinner->set_allow_lesser(true); + spinner->set_h_size_flags(SIZE_EXPAND_FILL); + + spinner->connect_compat("value_changed", this, "_value_changed", varray(is_transform_spinner)); +} + +void BoneTransformEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + create_editors(); + key_button->connect_compat("pressed", this, "_key_button_pressed"); + enabled_checkbox->connect_compat("toggled", this, "_checkbox_toggled"); + [[fallthrough]]; + } + case NOTIFICATION_THEME_CHANGED: { + const Color base = get_theme_color("accent_color", "Editor"); + const Color bg_color = get_theme_color("property_color", "Editor"); + const Color bg_lbl_color(bg_color.r, bg_color.g, bg_color.b, 0.5); + + for (int i = 0; i < TRANSLATION_COMPONENTS; i++) { + Color c = base; + c.set_hsv(float(i % TRANSLATION_COMPONENTS) / TRANSLATION_COMPONENTS + 0.05, c.get_s() * 0.75, c.get_v()); + if (!translation_slider[i]) { + continue; + } + translation_slider[i]->set_custom_label_color(true, c); + } + + for (int i = 0; i < ROTATION_DEGREES_COMPONENTS; i++) { + Color c = base; + c.set_hsv(float(i % ROTATION_DEGREES_COMPONENTS) / ROTATION_DEGREES_COMPONENTS + 0.05, c.get_s() * 0.75, c.get_v()); + if (!rotation_slider[i]) { + continue; + } + rotation_slider[i]->set_custom_label_color(true, c); + } + + for (int i = 0; i < SCALE_COMPONENTS; i++) { + Color c = base; + c.set_hsv(float(i % SCALE_COMPONENTS) / SCALE_COMPONENTS + 0.05, c.get_s() * 0.75, c.get_v()); + if (!scale_slider[i]) { + continue; + } + scale_slider[i]->set_custom_label_color(true, c); + } + + for (int i = 0; i < TRANSFORM_COMPONENTS; i++) { + Color c = base; + c.set_hsv(float(i % TRANSFORM_COMPONENTS) / TRANSFORM_COMPONENTS + 0.05, c.get_s() * 0.75, c.get_v()); + if (!transform_slider[i]) { + continue; + } + transform_slider[i]->set_custom_label_color(true, c); + } + + break; + } + case NOTIFICATION_SORT_CHILDREN: { + const Ref<Font> font = get_theme_font("font", "Tree"); + + Point2 buffer; + buffer.x += get_theme_constant("inspector_margin", "Editor"); + buffer.y += font->get_height(); + buffer.y += get_theme_constant("vseparation", "Tree"); + + const float vector_height = translation_grid->get_size().y; + const float transform_height = transform_grid->get_size().y; + const float button_height = key_button->get_size().y; + + const float width = get_size().x - get_theme_constant("inspector_margin", "Editor"); + Vector<Rect2> input_rects; + if (keyable && section->get_vbox()->is_visible()) { + input_rects.push_back(Rect2(key_button->get_position() + buffer, Size2(width, button_height))); + } else { + input_rects.push_back(Rect2(0, 0, 0, 0)); + } + + if (section->get_vbox()->is_visible()) { + input_rects.push_back(Rect2(translation_grid->get_position() + buffer, Size2(width, vector_height))); + input_rects.push_back(Rect2(rotation_grid->get_position() + buffer, Size2(width, vector_height))); + input_rects.push_back(Rect2(scale_grid->get_position() + buffer, Size2(width, vector_height))); + input_rects.push_back(Rect2(transform_grid->get_position() + buffer, Size2(width, transform_height))); + } else { + const int32_t start = input_rects.size(); + const int32_t empty_input_rect_elements = 4; + const int32_t end = start + empty_input_rect_elements; + for (int i = start; i < end; ++i) { + input_rects.push_back(Rect2(0, 0, 0, 0)); + } + } + + for (int32_t i = 0; i < input_rects.size(); i++) { + background_rects[i] = input_rects[i]; + } + + update(); + break; + } + case NOTIFICATION_DRAW: { + const Color dark_color = get_theme_color("dark_color_2", "Editor"); + + for (int i = 0; i < 5; ++i) { + draw_rect(background_rects[i], dark_color); + } + + break; + } + } +} + +void BoneTransformEditor::_value_changed(const double p_value, const bool p_from_transform) { + if (updating) + return; + + if (property.get_slicec('/', 0) == "bones" && property.get_slicec('/', 2) == "custom_pose") { + const Transform tform = compute_transform(p_from_transform); + + undo_redo->create_action(TTR("Set Custom Bone Pose Transform"), UndoRedo::MERGE_ENDS); + undo_redo->add_undo_method(skeleton, "set_bone_custom_pose", property.get_slicec('/', 1).to_int(), skeleton->get_bone_custom_pose(property.get_slicec('/', 1).to_int())); + undo_redo->add_do_method(skeleton, "set_bone_custom_pose", property.get_slicec('/', 1).to_int(), tform); + undo_redo->commit_action(); + } else if (property.get_slicec('/', 0) == "bones") { + const Transform tform = compute_transform(p_from_transform); + + undo_redo->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS); + undo_redo->add_undo_property(skeleton, property, skeleton->get(property)); + undo_redo->add_do_property(skeleton, property, tform); + undo_redo->commit_action(); + } +} + +Transform BoneTransformEditor::compute_transform(const bool p_from_transform) const { + // Last modified was a raw transform column... + if (p_from_transform) { + Transform tform; + + for (int i = 0; i < BASIS_COMPONENTS; ++i) { + tform.basis[i / BASIS_SPLIT_COMPONENTS][i % BASIS_SPLIT_COMPONENTS] = transform_slider[i]->get_value(); + } + + for (int i = 0; i < TRANSLATION_COMPONENTS; ++i) { + tform.origin[i] = transform_slider[i + BASIS_COMPONENTS]->get_value(); + } + + return tform; + } + + return Transform( + Basis(Vector3(Math::deg2rad(rotation_slider[0]->get_value()), Math::deg2rad(rotation_slider[1]->get_value()), Math::deg2rad(rotation_slider[2]->get_value())), + Vector3(scale_slider[0]->get_value(), scale_slider[1]->get_value(), scale_slider[2]->get_value())), + Vector3(translation_slider[0]->get_value(), translation_slider[1]->get_value(), translation_slider[2]->get_value())); +} + +void BoneTransformEditor::update_enabled_checkbox() { + if (enabled_checkbox) { + const String path = "bones/" + property.get_slicec('/', 1) + "/enabled"; + const bool is_enabled = skeleton->get(path); + enabled_checkbox->set_pressed(is_enabled); + } +} + +void BoneTransformEditor::_bind_methods() { + ClassDB::bind_method(D_METHOD("_value_changed", "value"), &BoneTransformEditor::_value_changed); + ClassDB::bind_method(D_METHOD("_key_button_pressed"), &BoneTransformEditor::_key_button_pressed); + ClassDB::bind_method(D_METHOD("_checkbox_toggled", "toggled"), &BoneTransformEditor::_checkbox_toggled); +} + +void BoneTransformEditor::_update_properties() { + if (updating) + return; + + if (skeleton == nullptr) + return; + + updating = true; + + Transform tform = skeleton->get(property); + _update_transform_properties(tform); +} + +void BoneTransformEditor::_update_custom_pose_properties() { + if (updating) + return; + + if (skeleton == nullptr) + return; + + updating = true; + + Transform tform = skeleton->get_bone_custom_pose(property.to_int()); + _update_transform_properties(tform); +} + +void BoneTransformEditor::_update_transform_properties(Transform tform) { + Quat rot = tform.get_basis(); + Vector3 rot_rad = rot.get_euler(); + Vector3 rot_degrees = Vector3(Math::rad2deg(rot_rad.x), Math::rad2deg(rot_rad.y), Math::rad2deg(rot_rad.z)); + Vector3 tr = tform.get_origin(); + Vector3 scale = tform.basis.get_scale(); + + for (int i = 0; i < TRANSLATION_COMPONENTS; i++) { + translation_slider[i]->set_value(tr[i]); + } + + for (int i = 0; i < ROTATION_DEGREES_COMPONENTS; i++) { + rotation_slider[i]->set_value(rot_degrees[i]); + } + + for (int i = 0; i < SCALE_COMPONENTS; i++) { + scale_slider[i]->set_value(scale[i]); + } + + transform_slider[0]->set_value(tform.get_basis()[Vector3::AXIS_X].x); + transform_slider[1]->set_value(tform.get_basis()[Vector3::AXIS_X].y); + transform_slider[2]->set_value(tform.get_basis()[Vector3::AXIS_X].z); + transform_slider[3]->set_value(tform.get_basis()[Vector3::AXIS_Y].x); + transform_slider[4]->set_value(tform.get_basis()[Vector3::AXIS_Y].y); + transform_slider[5]->set_value(tform.get_basis()[Vector3::AXIS_Y].z); + transform_slider[6]->set_value(tform.get_basis()[Vector3::AXIS_Z].x); + transform_slider[7]->set_value(tform.get_basis()[Vector3::AXIS_Z].y); + transform_slider[8]->set_value(tform.get_basis()[Vector3::AXIS_Z].z); + + for (int i = 0; i < TRANSLATION_COMPONENTS; i++) { + transform_slider[BASIS_COMPONENTS + i]->set_value(tform.get_origin()[i]); + } + + update_enabled_checkbox(); + updating = false; +} + +BoneTransformEditor::BoneTransformEditor(Skeleton3D *p_skeleton) : + translation_slider(), + rotation_slider(), + scale_slider(), + transform_slider(), + skeleton(p_skeleton), + key_button(nullptr), + enabled_checkbox(nullptr), + keyable(false), + toggle_enabled(false), + updating(false) { + undo_redo = EditorNode::get_undo_redo(); +} + +void BoneTransformEditor::set_target(const String &p_prop) { + property = p_prop; +} + +void BoneTransformEditor::set_keyable(const bool p_keyable) { + keyable = p_keyable; + if (key_button) { + key_button->set_visible(p_keyable); + } +} + +void BoneTransformEditor::set_toggle_enabled(const bool p_enabled) { + toggle_enabled = p_enabled; + if (enabled_checkbox) { + enabled_checkbox->set_visible(p_enabled); + } +} + +void BoneTransformEditor::_key_button_pressed() { + if (skeleton == nullptr) + return; + + const BoneId bone_id = property.get_slicec('/', 1).to_int(); + const String name = skeleton->get_bone_name(bone_id); + + if (name.empty()) + return; + + // Need to normalize the basis before you key it + Transform tform = compute_transform(true); + tform.orthonormalize(); + AnimationPlayerEditor::singleton->get_track_editor()->insert_transform_key(skeleton, name, tform); +} + +void BoneTransformEditor::_checkbox_toggled(const bool p_toggled) { + if (enabled_checkbox) { + const String path = "bones/" + property.get_slicec('/', 1) + "/enabled"; + skeleton->set(path, p_toggled); + } +} + void Skeleton3DEditor::_on_click_option(int p_option) { if (!skeleton) { return; @@ -45,12 +417,14 @@ void Skeleton3DEditor::_on_click_option(int p_option) { switch (p_option) { case MENU_OPTION_CREATE_PHYSICAL_SKELETON: { create_physical_skeleton(); - } break; + break; + } } } void Skeleton3DEditor::create_physical_skeleton() { UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ERR_FAIL_COND(!get_tree()); Node *owner = skeleton == get_tree()->get_edited_scene_root() ? skeleton : skeleton->get_owner(); const int bc = skeleton->get_bone_count(); @@ -124,28 +498,164 @@ PhysicalBone3D *Skeleton3DEditor::create_physical_bone(int bone_id, int bone_chi return physical_bone; } -void Skeleton3DEditor::edit(Skeleton3D *p_node) { - skeleton = p_node; +Variant Skeleton3DEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) { + TreeItem *selected = joint_tree->get_selected(); + + if (!selected) + return Variant(); + + Ref<Texture> icon = selected->get_icon(0); + + VBoxContainer *vb = memnew(VBoxContainer); + HBoxContainer *hb = memnew(HBoxContainer); + TextureRect *tf = memnew(TextureRect); + tf->set_texture(icon); + tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); + hb->add_child(tf); + Label *label = memnew(Label(selected->get_text(0))); + hb->add_child(label); + vb->add_child(hb); + hb->set_modulate(Color(1, 1, 1, 1)); + + set_drag_preview(vb); + Dictionary drag_data; + drag_data["type"] = "nodes"; + drag_data["node"] = selected; + + return drag_data; } -void Skeleton3DEditor::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE) { - get_tree()->connect("node_removed", callable_mp(this, &Skeleton3DEditor::_node_removed)); +bool Skeleton3DEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { + TreeItem *target = joint_tree->get_item_at_position(p_point); + if (!target) + return false; + + const String path = target->get_metadata(0); + if (!path.begins_with("bones/")) + return false; + + TreeItem *selected = Object::cast_to<TreeItem>(Dictionary(p_data)["node"]); + if (target == selected) + return false; + + const String path2 = target->get_metadata(0); + if (!path2.begins_with("bones/")) + return false; + + return true; +} + +void Skeleton3DEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) { + if (!can_drop_data_fw(p_point, p_data, p_from)) + return; + + TreeItem *target = joint_tree->get_item_at_position(p_point); + TreeItem *selected = Object::cast_to<TreeItem>(Dictionary(p_data)["node"]); + + const BoneId target_boneidx = String(target->get_metadata(0)).get_slicec('/', 1).to_int(); + const BoneId selected_boneidx = String(selected->get_metadata(0)).get_slicec('/', 1).to_int(); + + move_skeleton_bone(skeleton->get_path(), selected_boneidx, target_boneidx); +} + +void Skeleton3DEditor::move_skeleton_bone(NodePath p_skeleton_path, int32_t p_selected_boneidx, int32_t p_target_boneidx) { + Node *node = get_node_or_null(p_skeleton_path); + Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node); + ERR_FAIL_NULL(skeleton); + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Set Bone Parentage")); + // If the target is a child of ourselves, we move only *us* and not our children + if (skeleton->is_bone_parent_of(p_target_boneidx, p_selected_boneidx)) { + const BoneId parent_idx = skeleton->get_bone_parent(p_selected_boneidx); + const int bone_count = skeleton->get_bone_count(); + for (BoneId i = 0; i < bone_count; ++i) { + if (skeleton->get_bone_parent(i) == p_selected_boneidx) { + ur->add_undo_method(skeleton, "set_bone_parent", i, skeleton->get_bone_parent(i)); + ur->add_do_method(skeleton, "set_bone_parent", i, parent_idx); + skeleton->set_bone_parent(i, parent_idx); + } + } } + ur->add_undo_method(skeleton, "set_bone_parent", p_selected_boneidx, skeleton->get_bone_parent(p_selected_boneidx)); + ur->add_do_method(skeleton, "set_bone_parent", p_selected_boneidx, p_target_boneidx); + skeleton->set_bone_parent(p_selected_boneidx, p_target_boneidx); + + update_joint_tree(); + ur->commit_action(); } -void Skeleton3DEditor::_node_removed(Node *p_node) { - if (p_node == skeleton) { - skeleton = nullptr; - options->hide(); +void Skeleton3DEditor::_joint_tree_selection_changed() { + TreeItem *selected = joint_tree->get_selected(); + const String path = selected->get_metadata(0); + + if (path.begins_with("bones/")) { + const int b_idx = path.get_slicec('/', 1).to_int(); + const String bone_path = "bones/" + itos(b_idx) + "/"; + + pose_editor->set_target(bone_path + "pose"); + rest_editor->set_target(bone_path + "rest"); + custom_pose_editor->set_target(bone_path + "custom_pose"); + + pose_editor->set_visible(true); + rest_editor->set_visible(true); + custom_pose_editor->set_visible(true); } } -void Skeleton3DEditor::_bind_methods() { +void Skeleton3DEditor::_joint_tree_rmb_select(const Vector2 &p_pos) { +} + +void Skeleton3DEditor::_update_properties() { + if (rest_editor) + rest_editor->_update_properties(); + if (pose_editor) + pose_editor->_update_properties(); + if (custom_pose_editor) + custom_pose_editor->_update_custom_pose_properties(); +} + +void Skeleton3DEditor::update_joint_tree() { + joint_tree->clear(); + + if (skeleton == nullptr) + return; + + TreeItem *root = joint_tree->create_item(); + + Map<int, TreeItem *> items; + + items.insert(-1, root); + + const Vector<int> &joint_porder = skeleton->get_bone_process_orders(); + + Ref<Texture> bone_icon = get_theme_icon("Skeleton3D", "EditorIcons"); + + for (int i = 0; i < joint_porder.size(); ++i) { + const int b_idx = joint_porder[i]; + + const int p_idx = skeleton->get_bone_parent(b_idx); + TreeItem *p_item = items.find(p_idx)->get(); + + TreeItem *joint_item = joint_tree->create_item(p_item); + items.insert(b_idx, joint_item); + + joint_item->set_text(0, skeleton->get_bone_name(b_idx)); + joint_item->set_icon(0, bone_icon); + joint_item->set_selectable(0, true); + joint_item->set_metadata(0, "bones/" + itos(b_idx)); + } +} + +void Skeleton3DEditor::update_editors() { } -Skeleton3DEditor::Skeleton3DEditor() { - skeleton = nullptr; +void Skeleton3DEditor::create_editors() { + set_h_size_flags(SIZE_EXPAND_FILL); + add_theme_constant_override("separation", 0); + + set_focus_mode(FOCUS_ALL); + + // Create Top Menu Bar options = memnew(MenuButton); Node3DEditor::get_singleton()->add_control_to_menu_panel(options); @@ -156,31 +666,119 @@ Skeleton3DEditor::Skeleton3DEditor() { options->get_popup()->connect("id_pressed", callable_mp(this, &Skeleton3DEditor::_on_click_option)); options->hide(); + + const Color section_color = get_theme_color("prop_subsection", "Editor"); + + EditorInspectorSection *bones_section = memnew(EditorInspectorSection); + bones_section->setup("bones", "Bones", skeleton, section_color, true); + add_child(bones_section); + bones_section->unfold(); + + ScrollContainer *s_con = memnew(ScrollContainer); + s_con->set_h_size_flags(SIZE_EXPAND_FILL); + s_con->set_custom_minimum_size(Size2(1, 350) * EDSCALE); + bones_section->get_vbox()->add_child(s_con); + + joint_tree = memnew(Tree); + joint_tree->set_columns(1); + joint_tree->set_focus_mode(Control::FocusMode::FOCUS_NONE); + joint_tree->set_select_mode(Tree::SELECT_SINGLE); + joint_tree->set_hide_root(true); + joint_tree->set_v_size_flags(SIZE_EXPAND_FILL); + joint_tree->set_h_size_flags(SIZE_EXPAND_FILL); + joint_tree->set_allow_rmb_select(true); + joint_tree->set_drag_forwarding(this); + s_con->add_child(joint_tree); + + pose_editor = memnew(BoneTransformEditor(skeleton)); + pose_editor->set_label(TTR("Bone Pose")); + pose_editor->set_keyable(AnimationPlayerEditor::singleton->get_track_editor()->has_keying()); + pose_editor->set_toggle_enabled(true); + pose_editor->set_visible(false); + add_child(pose_editor); + + rest_editor = memnew(BoneTransformEditor(skeleton)); + rest_editor->set_label(TTR("Bone Rest")); + rest_editor->set_visible(false); + add_child(rest_editor); + + custom_pose_editor = memnew(BoneTransformEditor(skeleton)); + custom_pose_editor->set_label(TTR("Bone Custom Pose")); + custom_pose_editor->set_visible(false); + add_child(custom_pose_editor); +} + +void Skeleton3DEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + create_editors(); + update_joint_tree(); + update_editors(); + + get_tree()->connect_compat("node_removed", this, "_node_removed", Vector<Variant>(), Object::CONNECT_ONESHOT); + joint_tree->connect_compat("item_selected", this, "_joint_tree_selection_changed"); + joint_tree->connect_compat("item_rmb_selected", this, "_joint_tree_rmb_select"); +#ifdef TOOLS_ENABLED + skeleton->connect_compat("pose_updated", this, "_update_properties"); +#endif // TOOLS_ENABLED + + break; + } + } } -Skeleton3DEditor::~Skeleton3DEditor() {} +void Skeleton3DEditor::_node_removed(Node *p_node) { + if (skeleton && p_node == skeleton) { + skeleton = nullptr; + options->hide(); + } -void Skeleton3DEditorPlugin::edit(Object *p_object) { - skeleton_editor->edit(Object::cast_to<Skeleton3D>(p_object)); + _update_properties(); } -bool Skeleton3DEditorPlugin::handles(Object *p_object) const { - return p_object->is_class("Skeleton3D"); +void Skeleton3DEditor::_bind_methods() { + ClassDB::bind_method(D_METHOD("_node_removed"), &Skeleton3DEditor::_node_removed); + ClassDB::bind_method(D_METHOD("_joint_tree_selection_changed"), &Skeleton3DEditor::_joint_tree_selection_changed); + ClassDB::bind_method(D_METHOD("_joint_tree_rmb_select"), &Skeleton3DEditor::_joint_tree_rmb_select); + ClassDB::bind_method(D_METHOD("_update_properties"), &Skeleton3DEditor::_update_properties); + ClassDB::bind_method(D_METHOD("_on_click_option"), &Skeleton3DEditor::_on_click_option); + + ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &Skeleton3DEditor::get_drag_data_fw); + ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &Skeleton3DEditor::can_drop_data_fw); + ClassDB::bind_method(D_METHOD("drop_data_fw"), &Skeleton3DEditor::drop_data_fw); + ClassDB::bind_method(D_METHOD("move_skeleton_bone"), &Skeleton3DEditor::move_skeleton_bone); +} + +Skeleton3DEditor::Skeleton3DEditor(EditorInspectorPluginSkeleton *e_plugin, EditorNode *p_editor, Skeleton3D *p_skeleton) : + editor(p_editor), + editor_plugin(e_plugin), + skeleton(p_skeleton) { } -void Skeleton3DEditorPlugin::make_visible(bool p_visible) { - if (p_visible) { - skeleton_editor->options->show(); - } else { - skeleton_editor->options->hide(); - skeleton_editor->edit(nullptr); +Skeleton3DEditor::~Skeleton3DEditor() { + if (options) { + Node3DEditor::get_singleton()->remove_control_from_menu_panel(options); } } +bool EditorInspectorPluginSkeleton::can_handle(Object *p_object) { + return Object::cast_to<Skeleton3D>(p_object) != nullptr; +} + +void EditorInspectorPluginSkeleton::parse_begin(Object *p_object) { + Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_object); + ERR_FAIL_COND(!skeleton); + + Skeleton3DEditor *skel_editor = memnew(Skeleton3DEditor(this, editor, skeleton)); + add_custom_control(skel_editor); +} + Skeleton3DEditorPlugin::Skeleton3DEditorPlugin(EditorNode *p_node) { editor = p_node; - skeleton_editor = memnew(Skeleton3DEditor); - editor->get_viewport()->add_child(skeleton_editor); -} -Skeleton3DEditorPlugin::~Skeleton3DEditorPlugin() {} + Ref<EditorInspectorPluginSkeleton> skeleton_plugin; + skeleton_plugin.instance(); + skeleton_plugin->editor = editor; + + EditorInspector::add_inspector_plugin(skeleton_plugin); +} diff --git a/editor/plugins/skeleton_3d_editor_plugin.h b/editor/plugins/skeleton_3d_editor_plugin.h index af9ebb6246..8b0639ed92 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.h +++ b/editor/plugins/skeleton_3d_editor_plugin.h @@ -35,11 +35,97 @@ #include "editor/editor_plugin.h" #include "scene/3d/skeleton_3d.h" +class EditorInspectorPluginSkeleton; +class Joint; class PhysicalBone3D; -class Joint3D; +class Skeleton3DEditorPlugin; +class Button; +class CheckBox; -class Skeleton3DEditor : public Node { - GDCLASS(Skeleton3DEditor, Node); +class BoneTransformEditor : public VBoxContainer { + GDCLASS(BoneTransformEditor, VBoxContainer); + + static const int32_t TRANSLATION_COMPONENTS = 3; + static const int32_t ROTATION_DEGREES_COMPONENTS = 3; + static const int32_t SCALE_COMPONENTS = 3; + static const int32_t BASIS_COMPONENTS = 9; + static const int32_t BASIS_SPLIT_COMPONENTS = 3; + static const int32_t TRANSFORM_COMPONENTS = 12; + static const int32_t TRANSFORM_SPLIT_COMPONENTS = 3; + static const int32_t TRANSFORM_CONTROL_COMPONENTS = 3; + + EditorInspectorSection *section; + + GridContainer *translation_grid; + GridContainer *rotation_grid; + GridContainer *scale_grid; + GridContainer *transform_grid; + + EditorSpinSlider *translation_slider[TRANSLATION_COMPONENTS]; + EditorSpinSlider *rotation_slider[ROTATION_DEGREES_COMPONENTS]; + EditorSpinSlider *scale_slider[SCALE_COMPONENTS]; + EditorSpinSlider *transform_slider[TRANSFORM_COMPONENTS]; + + Rect2 background_rects[5]; + + Skeleton3D *skeleton; + String property; + + UndoRedo *undo_redo; + + Button *key_button; + CheckBox *enabled_checkbox; + + bool keyable; + bool toggle_enabled; + bool updating; + + String label; + + void create_editors(); + void setup_spinner(EditorSpinSlider *spinner, const bool is_transform_spinner); + + void _value_changed(const double p_value, const bool p_from_transform); + + Transform compute_transform(const bool p_from_transform) const; + + void update_enabled_checkbox(); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + BoneTransformEditor(Skeleton3D *p_skeleton); + + // Which transform target to modify + void set_target(const String &p_prop); + void set_label(const String &p_label) { label = p_label; } + + void _update_properties(); + void _update_custom_pose_properties(); + void _update_transform_properties(Transform p_transform); + + // Can/cannot modify the spinner values for the Transform + void set_read_only(const bool p_read_only); + + // Transform can be keyed, whether or not to show the button + void set_keyable(const bool p_keyable); + + // Bone can be toggled enabled or disabled, whether or not to show the checkbox + void set_toggle_enabled(const bool p_enabled); + + // Key Transform Button pressed + void _key_button_pressed(); + + // Bone Enabled Checkbox toggled + void _checkbox_toggled(const bool p_toggled); +}; + +class Skeleton3DEditor : public VBoxContainer { + GDCLASS(Skeleton3DEditor, VBoxContainer); + + friend class Skeleton3DEditorPlugin; enum Menu { MENU_OPTION_CREATE_PHYSICAL_SKELETON @@ -51,44 +137,78 @@ class Skeleton3DEditor : public Node { BoneInfo() {} }; + EditorNode *editor; + EditorInspectorPluginSkeleton *editor_plugin; + Skeleton3D *skeleton; + Tree *joint_tree; + BoneTransformEditor *rest_editor; + BoneTransformEditor *pose_editor; + BoneTransformEditor *custom_pose_editor; + MenuButton *options; + EditorFileDialog *file_dialog; + + UndoRedo *undo_redo; void _on_click_option(int p_option); + void _file_selected(const String &p_file); - friend class Skeleton3DEditorPlugin; + EditorFileDialog *file_export_lib; + + void update_joint_tree(); + void update_editors(); + + void create_editors(); + + void create_physical_skeleton(); + PhysicalBone3D *create_physical_bone(int bone_id, int bone_child_id, const Vector<BoneInfo> &bones_infos); + + Variant get_drag_data_fw(const Point2 &p_point, Control *p_from); + bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; + void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); protected: void _notification(int p_what); void _node_removed(Node *p_node); static void _bind_methods(); - void create_physical_skeleton(); - PhysicalBone3D *create_physical_bone(int bone_id, int bone_child_id, const Vector<BoneInfo> &bones_infos); - public: - void edit(Skeleton3D *p_node); + void move_skeleton_bone(NodePath p_skeleton_path, int32_t p_selected_boneidx, int32_t p_target_boneidx); - Skeleton3DEditor(); + Skeleton3D *get_skeleton() const { return skeleton; }; + + void _joint_tree_selection_changed(); + void _joint_tree_rmb_select(const Vector2 &p_pos); + + void _update_properties(); + + Skeleton3DEditor(EditorInspectorPluginSkeleton *e_plugin, EditorNode *p_editor, Skeleton3D *skeleton); ~Skeleton3DEditor(); }; +class EditorInspectorPluginSkeleton : public EditorInspectorPlugin { + GDCLASS(EditorInspectorPluginSkeleton, EditorInspectorPlugin); + + friend class Skeleton3DEditorPlugin; + + EditorNode *editor; + +public: + virtual bool can_handle(Object *p_object); + virtual void parse_begin(Object *p_object); +}; + class Skeleton3DEditorPlugin : public EditorPlugin { GDCLASS(Skeleton3DEditorPlugin, EditorPlugin); EditorNode *editor; - Skeleton3DEditor *skeleton_editor; public: - virtual String get_name() const { return "Skeleton3D"; } - virtual bool has_main_screen() const { return false; } - virtual void edit(Object *p_object); - virtual bool handles(Object *p_object) const; - virtual void make_visible(bool p_visible); - Skeleton3DEditorPlugin(EditorNode *p_node); - ~Skeleton3DEditorPlugin(); + + virtual String get_name() const { return "Skeleton3D"; } }; #endif // SKELETON_3D_EDITOR_PLUGIN_H diff --git a/editor/plugins/sprite_2d_editor_plugin.cpp b/editor/plugins/sprite_2d_editor_plugin.cpp index b21586a6b0..f5fafb68a5 100644 --- a/editor/plugins/sprite_2d_editor_plugin.cpp +++ b/editor/plugins/sprite_2d_editor_plugin.cpp @@ -31,6 +31,7 @@ #include "sprite_2d_editor_plugin.h" #include "canvas_item_editor_plugin.h" +#include "core/math/geometry_2d.h" #include "editor/editor_scale.h" #include "scene/2d/collision_polygon_2d.h" #include "scene/2d/light_occluder_2d.h" @@ -233,7 +234,7 @@ void Sprite2DEditor::_update_mesh_data() { computed_vertices.push_back(vtx); } - Vector<int> poly = Geometry::triangulate_polygon(lines[j]); + Vector<int> poly = Geometry2D::triangulate_polygon(lines[j]); for (int i = 0; i < poly.size(); i += 3) { for (int k = 0; k < 3; k++) { diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp index 3010d72d81..6c037f94a0 100644 --- a/editor/plugins/tile_map_editor_plugin.cpp +++ b/editor/plugins/tile_map_editor_plugin.cpp @@ -1951,7 +1951,7 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) { toolbar->add_child(paint_button); bucket_fill_button = memnew(ToolButton); - bucket_fill_button->set_shortcut(ED_SHORTCUT("tile_map_editor/bucket_fill", TTR("Bucket Fill"), KEY_G)); + bucket_fill_button->set_shortcut(ED_SHORTCUT("tile_map_editor/bucket_fill", TTR("Bucket Fill"), KEY_B)); bucket_fill_button->connect("pressed", callable_mp(this, &TileMapEditor::_button_tool_select), make_binds(TOOL_BUCKET)); bucket_fill_button->set_toggle_mode(true); toolbar->add_child(bucket_fill_button); diff --git a/editor/property_selector.cpp b/editor/property_selector.cpp index 6888ebdc71..c6c93fae83 100644 --- a/editor/property_selector.cpp +++ b/editor/property_selector.cpp @@ -357,7 +357,7 @@ void PropertySelector::_item_selected() { if (E) { for (int i = 0; i < E->get().properties.size(); i++) { if (E->get().properties[i].name == name) { - text = E->get().properties[i].description; + text = DTR(E->get().properties[i].description); } } } @@ -372,7 +372,7 @@ void PropertySelector::_item_selected() { if (E) { for (int i = 0; i < E->get().methods.size(); i++) { if (E->get().methods[i].name == name) { - text = E->get().methods[i].description; + text = DTR(E->get().methods[i].description); } } } |