summaryrefslogtreecommitdiff
path: root/editor/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'editor/plugins')
-rw-r--r--editor/plugins/abstract_polygon_2d_editor.cpp8
-rw-r--r--editor/plugins/animation_blend_space_2d_editor.cpp15
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.cpp85
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.h18
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp96
-rw-r--r--editor/plugins/animation_player_editor_plugin.h15
-rw-r--r--editor/plugins/animation_tree_editor_plugin.cpp2
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp20
-rw-r--r--editor/plugins/asset_library_editor_plugin.h2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp100
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h2
-rw-r--r--editor/plugins/collision_polygon_3d_editor_plugin.cpp30
-rw-r--r--editor/plugins/collision_polygon_3d_editor_plugin.h4
-rw-r--r--editor/plugins/collision_shape_2d_editor_plugin.cpp79
-rw-r--r--editor/plugins/collision_shape_2d_editor_plugin.h3
-rw-r--r--editor/plugins/cpu_particles_2d_editor_plugin.cpp2
-rw-r--r--editor/plugins/curve_editor_plugin.cpp6
-rw-r--r--editor/plugins/curve_editor_plugin.h4
-rw-r--r--editor/plugins/editor_preview_plugins.cpp65
-rw-r--r--editor/plugins/font_editor_plugin.cpp247
-rw-r--r--editor/plugins/font_editor_plugin.h33
-rw-r--r--editor/plugins/gpu_particles_2d_editor_plugin.cpp2
-rw-r--r--editor/plugins/gpu_particles_3d_editor_plugin.cpp2
-rw-r--r--editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp2
-rw-r--r--editor/plugins/item_list_editor_plugin.cpp12
-rw-r--r--editor/plugins/item_list_editor_plugin.h2
-rw-r--r--editor/plugins/lightmap_gi_editor_plugin.cpp4
-rw-r--r--editor/plugins/material_editor_plugin.cpp12
-rw-r--r--editor/plugins/mesh_editor_plugin.cpp8
-rw-r--r--editor/plugins/mesh_editor_plugin.h3
-rw-r--r--editor/plugins/mesh_instance_3d_editor_plugin.cpp3
-rw-r--r--editor/plugins/mesh_library_editor_plugin.cpp59
-rw-r--r--editor/plugins/mesh_library_editor_plugin.h14
-rw-r--r--editor/plugins/node_3d_editor_gizmos.cpp598
-rw-r--r--editor/plugins/node_3d_editor_gizmos.h68
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp303
-rw-r--r--editor/plugins/node_3d_editor_plugin.h13
-rw-r--r--editor/plugins/occluder_instance_3d_editor_plugin.cpp4
-rw-r--r--editor/plugins/packed_scene_translation_parser_plugin.cpp49
-rw-r--r--editor/plugins/packed_scene_translation_parser_plugin.h4
-rw-r--r--editor/plugins/path_2d_editor_plugin.cpp2
-rw-r--r--editor/plugins/path_3d_editor_plugin.cpp31
-rw-r--r--editor/plugins/path_3d_editor_plugin.h2
-rw-r--r--editor/plugins/physical_bone_3d_editor_plugin.cpp1
-rw-r--r--editor/plugins/polygon_2d_editor_plugin.cpp28
-rw-r--r--editor/plugins/resource_preloader_editor_plugin.cpp4
-rw-r--r--editor/plugins/resource_preloader_editor_plugin.h2
-rw-r--r--editor/plugins/script_editor_plugin.cpp423
-rw-r--r--editor/plugins/script_editor_plugin.h25
-rw-r--r--editor/plugins/script_text_editor.cpp63
-rw-r--r--editor/plugins/script_text_editor.h4
-rw-r--r--editor/plugins/shader_editor_plugin.cpp6
-rw-r--r--editor/plugins/skeleton_2d_editor_plugin.cpp5
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp941
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.h135
-rw-r--r--editor/plugins/sprite_2d_editor_plugin.cpp4
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp128
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.h3
-rw-r--r--editor/plugins/text_editor.cpp9
-rw-r--r--editor/plugins/text_editor.h5
-rw-r--r--editor/plugins/texture_3d_editor_plugin.cpp6
-rw-r--r--editor/plugins/texture_3d_editor_plugin.h1
-rw-r--r--editor/plugins/texture_editor_plugin.cpp27
-rw-r--r--editor/plugins/texture_editor_plugin.h2
-rw-r--r--editor/plugins/texture_layered_editor_plugin.cpp9
-rw-r--r--editor/plugins/texture_layered_editor_plugin.h2
-rw-r--r--editor/plugins/texture_region_editor_plugin.cpp125
-rw-r--r--editor/plugins/texture_region_editor_plugin.h4
-rw-r--r--editor/plugins/theme_editor_plugin.cpp12
-rw-r--r--editor/plugins/theme_editor_preview.cpp4
-rw-r--r--editor/plugins/tiles/atlas_merging_dialog.cpp12
-rw-r--r--editor/plugins/tiles/tile_atlas_view.cpp53
-rw-r--r--editor/plugins/tiles/tile_atlas_view.h2
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp251
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp348
-rw-r--r--editor/plugins/tiles/tile_map_editor.h3
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp335
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.h1
-rw-r--r--editor/plugins/tiles/tile_set_editor.cpp225
-rw-r--r--editor/plugins/tiles/tile_set_editor.h1
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.cpp4
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp28
-rw-r--r--editor/plugins/visual_shader_editor_plugin.h2
-rw-r--r--editor/plugins/voxel_gi_editor_plugin.cpp2
84 files changed, 3370 insertions, 1908 deletions
diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp
index ef3b0588b8..36a814c30a 100644
--- a/editor/plugins/abstract_polygon_2d_editor.cpp
+++ b/editor/plugins/abstract_polygon_2d_editor.cpp
@@ -367,7 +367,7 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
edge_point = PosVertex();
return true;
} else {
- const real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius");
+ const real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
if (!_is_line() && wip.size() > 1 && xform.xform(wip[0]).distance_to(xform.xform(cpoint)) < grab_threshold) {
//wip closed
@@ -502,7 +502,7 @@ void AbstractPolygon2DEditor::forward_canvas_draw_over_viewport(Control *p_overl
offset = _get_offset(j);
}
- if (!wip_active && j == edited_point.polygon && EDITOR_GET("editors/poly_editor/show_previous_outline")) {
+ if (!wip_active && j == edited_point.polygon && EDITOR_GET("editors/polygon_editor/show_previous_outline")) {
const Color col = Color(0.5, 0.5, 0.5); // FIXME polygon->get_outline_color();
const int n = pre_move_edit.size();
for (int i = 0; i < n - (is_closed ? 0 : 1); i++) {
@@ -625,7 +625,7 @@ AbstractPolygon2DEditor::Vertex AbstractPolygon2DEditor::get_active_point() cons
}
AbstractPolygon2DEditor::PosVertex AbstractPolygon2DEditor::closest_point(const Vector2 &p_pos) const {
- const real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius");
+ const real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
const int n_polygons = _get_polygon_count();
const Transform2D xform = canvas_item_editor->get_canvas_transform() * _get_node()->get_global_transform();
@@ -653,7 +653,7 @@ AbstractPolygon2DEditor::PosVertex AbstractPolygon2DEditor::closest_point(const
}
AbstractPolygon2DEditor::PosVertex AbstractPolygon2DEditor::closest_edge_point(const Vector2 &p_pos) const {
- const real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius");
+ const real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
const real_t eps = grab_threshold * 2;
const real_t eps2 = eps * eps;
diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp
index 49fcac512b..686a35e442 100644
--- a/editor/plugins/animation_blend_space_2d_editor.cpp
+++ b/editor/plugins/animation_blend_space_2d_editor.cpp
@@ -129,8 +129,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven
add_point_pos += blend_space->get_min_space();
if (snap->is_pressed()) {
- add_point_pos.x = Math::snapped(add_point_pos.x, blend_space->get_snap().x);
- add_point_pos.y = Math::snapped(add_point_pos.y, blend_space->get_snap().y);
+ add_point_pos = add_point_pos.snapped(blend_space->get_snap());
}
}
@@ -215,8 +214,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven
Vector2 point = blend_space->get_blend_point_position(selected_point);
point += drag_ofs;
if (snap->is_pressed()) {
- point.x = Math::snapped(point.x, blend_space->get_snap().x);
- point.y = Math::snapped(point.y, blend_space->get_snap().y);
+ point = point.snapped(blend_space->get_snap());
}
updating = true;
@@ -467,8 +465,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_draw() {
if (dragging_selected && selected_point == point_idx) {
point += drag_ofs;
if (snap->is_pressed()) {
- point.x = Math::snapped(point.x, blend_space->get_snap().x);
- point.y = Math::snapped(point.y, blend_space->get_snap().y);
+ point = point.snapped(blend_space->get_snap());
}
}
point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space());
@@ -503,8 +500,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_draw() {
if (dragging_selected && selected_point == i) {
point += drag_ofs;
if (snap->is_pressed()) {
- point.x = Math::snapped(point.x, blend_space->get_snap().x);
- point.y = Math::snapped(point.y, blend_space->get_snap().y);
+ point = point.snapped(blend_space->get_snap());
}
}
point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space());
@@ -702,8 +698,7 @@ void AnimationNodeBlendSpace2DEditor::_update_edited_point_pos() {
if (dragging_selected) {
pos += drag_ofs;
if (snap->is_pressed()) {
- pos.x = Math::snapped(pos.x, blend_space->get_snap().x);
- pos.y = Math::snapped(pos.y, blend_space->get_snap().y);
+ pos = pos.snapped(blend_space->get_snap());
}
}
updating = true;
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp
index 030d90eeca..55ffbf9477 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp
@@ -66,9 +66,13 @@ void AnimationNodeBlendTreeEditor::remove_custom_type(const Ref<Script> &p_scrip
_update_options_menu();
}
-void AnimationNodeBlendTreeEditor::_update_options_menu() {
+void AnimationNodeBlendTreeEditor::_update_options_menu(bool p_has_input_ports) {
add_node->get_popup()->clear();
+ add_node->get_popup()->set_size(Size2i(-1, -1));
for (int i = 0; i < add_options.size(); i++) {
+ if (p_has_input_ports && add_options[i].input_port_count == 0) {
+ continue;
+ }
add_node->get_popup()->add_item(add_options[i].name, i);
}
@@ -309,6 +313,11 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) {
return;
}
+ if (!from_node.is_empty() && anode->get_input_count() == 0) {
+ from_node = "";
+ return;
+ }
+
Point2 instance_pos = graph->get_scroll_ofs();
if (use_popup_menu_position) {
instance_pos += popup_menu_position;
@@ -328,11 +337,51 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) {
undo_redo->create_action(TTR("Add Node to BlendTree"));
undo_redo->add_do_method(blend_tree.ptr(), "add_node", name, anode, instance_pos / EDSCALE);
undo_redo->add_undo_method(blend_tree.ptr(), "remove_node", name);
+
+ if (!from_node.is_empty()) {
+ undo_redo->add_do_method(blend_tree.ptr(), "connect_node", name, 0, from_node);
+ from_node = "";
+ }
+ if (!to_node.is_empty() && to_slot != -1) {
+ undo_redo->add_do_method(blend_tree.ptr(), "connect_node", to_node, to_slot, name);
+ to_node = "";
+ to_slot = -1;
+ }
+
undo_redo->add_do_method(this, "_update_graph");
undo_redo->add_undo_method(this, "_update_graph");
undo_redo->commit_action();
}
+void AnimationNodeBlendTreeEditor::_popup(bool p_has_input_ports, const Vector2 &p_popup_position, const Vector2 &p_node_position) {
+ _update_options_menu(p_has_input_ports);
+ use_popup_menu_position = true;
+ popup_menu_position = p_popup_position;
+ add_node->get_popup()->set_position(p_node_position);
+ add_node->get_popup()->popup();
+}
+
+void AnimationNodeBlendTreeEditor::_popup_request(const Vector2 &p_position) {
+ _popup(false, graph->get_local_mouse_position(), p_position);
+}
+
+void AnimationNodeBlendTreeEditor::_connection_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_position) {
+ Ref<AnimationNode> node = blend_tree->get_node(p_from);
+ if (node.is_valid()) {
+ from_node = p_from;
+ _popup(true, p_release_position, graph->get_global_mouse_position());
+ }
+}
+
+void AnimationNodeBlendTreeEditor::_connection_from_empty(const String &p_to, int p_to_slot, const Vector2 &p_release_position) {
+ Ref<AnimationNode> node = blend_tree->get_node(p_to);
+ if (node.is_valid()) {
+ to_node = p_to;
+ to_slot = p_to_slot;
+ _popup(false, p_release_position, graph->get_global_mouse_position());
+ }
+}
+
void AnimationNodeBlendTreeEditor::_node_dragged(const Vector2 &p_from, const Vector2 &p_to, const StringName &p_which) {
updating = true;
undo_redo->create_action(TTR("Node Moved"));
@@ -431,14 +480,6 @@ void AnimationNodeBlendTreeEditor::_delete_nodes_request() {
undo_redo->commit_action();
}
-void AnimationNodeBlendTreeEditor::_popup_request(const Vector2 &p_position) {
- _update_options_menu();
- use_popup_menu_position = true;
- popup_menu_position = graph->get_local_mouse_position();
- add_node->get_popup()->set_position(p_position);
- add_node->get_popup()->popup();
-}
-
void AnimationNodeBlendTreeEditor::_node_selected(Object *p_node) {
GraphNode *gn = Object::cast_to<GraphNode>(p_node);
ERR_FAIL_COND(!gn);
@@ -735,16 +776,16 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) {
}
if (player) {
- for (Map<StringName, ProgressBar *>::Element *E = animations.front(); E; E = E->next()) {
- Ref<AnimationNodeAnimation> an = blend_tree->get_node(E->key());
+ for (const KeyValue<StringName, ProgressBar *> &E : animations) {
+ Ref<AnimationNodeAnimation> an = blend_tree->get_node(E.key);
if (an.is_valid()) {
if (player->has_animation(an->get_animation())) {
Ref<Animation> anim = player->get_animation(an->get_animation());
if (anim.is_valid()) {
- E->get()->set_max(anim->get_length());
+ E.value->set_max(anim->get_length());
//StringName path = AnimationTreeEditor::get_singleton()->get_base_path() + E.input_node;
- StringName time_path = AnimationTreeEditor::get_singleton()->get_base_path() + String(E->key()) + "/time";
- E->get()->set_value(AnimationTreeEditor::get_singleton()->get_tree()->get(time_path));
+ StringName time_path = AnimationTreeEditor::get_singleton()->get_base_path() + String(E.key) + "/time";
+ E.value->set_value(AnimationTreeEditor::get_singleton()->get_tree()->get(time_path));
}
}
}
@@ -890,6 +931,8 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() {
graph->connect("scroll_offset_changed", callable_mp(this, &AnimationNodeBlendTreeEditor::_scroll_changed));
graph->connect("delete_nodes_request", callable_mp(this, &AnimationNodeBlendTreeEditor::_delete_nodes_request));
graph->connect("popup_request", callable_mp(this, &AnimationNodeBlendTreeEditor::_popup_request));
+ graph->connect("connection_to_empty", callable_mp(this, &AnimationNodeBlendTreeEditor::_connection_to_empty));
+ graph->connect("connection_from_empty", callable_mp(this, &AnimationNodeBlendTreeEditor::_connection_from_empty));
float graph_minimap_opacity = EditorSettings::get_singleton()->get("editors/visual_editors/minimap_opacity");
graph->set_minimap_opacity(graph_minimap_opacity);
@@ -905,13 +948,13 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() {
add_node->connect("about_to_popup", callable_mp(this, &AnimationNodeBlendTreeEditor::_update_options_menu));
add_options.push_back(AddOption("Animation", "AnimationNodeAnimation"));
- add_options.push_back(AddOption("OneShot", "AnimationNodeOneShot"));
- add_options.push_back(AddOption("Add2", "AnimationNodeAdd2"));
- add_options.push_back(AddOption("Add3", "AnimationNodeAdd3"));
- add_options.push_back(AddOption("Blend2", "AnimationNodeBlend2"));
- add_options.push_back(AddOption("Blend3", "AnimationNodeBlend3"));
- add_options.push_back(AddOption("Seek", "AnimationNodeTimeSeek"));
- add_options.push_back(AddOption("TimeScale", "AnimationNodeTimeScale"));
+ add_options.push_back(AddOption("OneShot", "AnimationNodeOneShot", 2));
+ add_options.push_back(AddOption("Add2", "AnimationNodeAdd2", 2));
+ add_options.push_back(AddOption("Add3", "AnimationNodeAdd3", 3));
+ add_options.push_back(AddOption("Blend2", "AnimationNodeBlend2", 2));
+ add_options.push_back(AddOption("Blend3", "AnimationNodeBlend3", 3));
+ add_options.push_back(AddOption("Seek", "AnimationNodeTimeSeek", 1));
+ add_options.push_back(AddOption("TimeScale", "AnimationNodeTimeScale", 1));
add_options.push_back(AddOption("Transition", "AnimationNodeTransition"));
add_options.push_back(AddOption("BlendTree", "AnimationNodeBlendTree"));
add_options.push_back(AddOption("BlendSpace1D", "AnimationNodeBlendSpace1D"));
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.h b/editor/plugins/animation_blend_tree_editor_plugin.h
index 9f09069719..0fcafad40e 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.h
+++ b/editor/plugins/animation_blend_tree_editor_plugin.h
@@ -64,22 +64,28 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin {
Map<StringName, ProgressBar *> animations;
Vector<EditorProperty *> visible_properties;
+ String to_node = "";
+ int to_slot = -1;
+ String from_node = "";
+
void _update_graph();
struct AddOption {
String name;
String type;
Ref<Script> script;
- AddOption(const String &p_name = String(), const String &p_type = String()) :
+ int input_port_count;
+ AddOption(const String &p_name = String(), const String &p_type = String(), bool p_input_port_count = 0) :
name(p_name),
- type(p_type) {
+ type(p_type),
+ input_port_count(p_input_port_count) {
}
};
Vector<AddOption> add_options;
void _add_node(int p_idx);
- void _update_options_menu();
+ void _update_options_menu(bool p_has_input_ports = false);
static AnimationNodeBlendTreeEditor *singleton;
@@ -98,7 +104,6 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin {
void _anim_selected(int p_index, Array p_options, const String &p_node);
void _delete_request(const String &p_which);
void _delete_nodes_request();
- void _popup_request(const Vector2 &p_position);
bool _update_filters(const Ref<AnimationNode> &anode);
void _edit_filters(const String &p_which);
@@ -106,6 +111,11 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin {
void _filter_toggled();
Ref<AnimationNode> _filter_edit;
+ void _popup(bool p_has_input_ports, const Vector2 &p_popup_position, const Vector2 &p_node_position);
+ void _popup_request(const Vector2 &p_position);
+ void _connection_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_position);
+ void _connection_from_empty(const String &p_to, int p_to_slot, const Vector2 &p_release_position);
+
void _property_changed(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing);
void _removed_from_graph();
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index b4e9f468de..ea025dad3e 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -120,7 +120,7 @@ void AnimationPlayerEditor::_notification(int p_what) {
Ref<Image> autoplay_img = autoplay_icon->get_image();
Ref<Image> reset_img = reset_icon->get_image();
Ref<Image> autoplay_reset_img;
- Size2 icon_size = Size2(autoplay_img->get_width(), autoplay_img->get_height());
+ Size2 icon_size = autoplay_img->get_size();
autoplay_reset_img.instantiate();
autoplay_reset_img->create(icon_size.x * 2, icon_size.y, false, autoplay_img->get_format());
autoplay_reset_img->blit_rect(autoplay_img, Rect2(Point2(), icon_size), Point2());
@@ -298,7 +298,7 @@ void AnimationPlayerEditor::_animation_selected(int p_which) {
autoplay->set_pressed(current == player->get_autoplay());
- AnimationPlayerEditor::singleton->get_track_editor()->update_keying();
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->update_keying();
EditorNode::get_singleton()->update_keying();
_animation_key_editor_seek(timeline_position, false);
}
@@ -345,7 +345,7 @@ void AnimationPlayerEditor::_animation_rename() {
void AnimationPlayerEditor::_animation_load() {
ERR_FAIL_COND(!player);
- file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
+ file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILES);
file->clear_filters();
List<String> extensions;
@@ -355,7 +355,6 @@ void AnimationPlayerEditor::_animation_load() {
}
file->popup_file_dialog();
- current_option = RESOURCE_LOAD;
}
void AnimationPlayerEditor::_animation_save_in_path(const Ref<Resource> &p_resource, const String &p_path) {
@@ -416,7 +415,6 @@ void AnimationPlayerEditor::_animation_save_as(const Ref<Resource> &p_resource)
file->set_current_path(path);
file->set_title(TTR("Save Resource As..."));
file->popup_file_dialog();
- current_option = RESOURCE_SAVE;
}
void AnimationPlayerEditor::_animation_remove() {
@@ -718,44 +716,48 @@ void AnimationPlayerEditor::_animation_edit() {
}
}
-void AnimationPlayerEditor::_dialog_action(String p_path) {
- switch (current_option) {
- case RESOURCE_LOAD: {
- ERR_FAIL_COND(!player);
+void AnimationPlayerEditor::_save_animation(String p_file) {
+ String current = animation->get_item_text(animation->get_selected());
+ if (current != "") {
+ Ref<Animation> anim = player->get_animation(current);
- Ref<Resource> res = ResourceLoader::load(p_path, "Animation");
- ERR_FAIL_COND_MSG(res.is_null(), "Cannot load Animation from file '" + p_path + "'.");
- ERR_FAIL_COND_MSG(!res->is_class("Animation"), "Loaded resource from file '" + p_path + "' is not Animation.");
+ ERR_FAIL_COND(!Object::cast_to<Resource>(*anim));
- String anim_name = p_path.get_file();
- int ext_pos = anim_name.rfind(".");
- if (ext_pos != -1) {
- anim_name = anim_name.substr(0, ext_pos);
- }
+ RES current_res = RES(Object::cast_to<Resource>(*anim));
- undo_redo->create_action(TTR("Load Animation"));
- undo_redo->add_do_method(player, "add_animation", anim_name, res);
- undo_redo->add_undo_method(player, "remove_animation", anim_name);
- if (player->has_animation(anim_name)) {
- undo_redo->add_undo_method(player, "add_animation", anim_name, player->get_animation(anim_name));
- }
- undo_redo->add_do_method(this, "_animation_player_changed", player);
- undo_redo->add_undo_method(this, "_animation_player_changed", player);
- undo_redo->commit_action();
- break;
- }
- case RESOURCE_SAVE: {
- String current = animation->get_item_text(animation->get_selected());
- if (current != "") {
- Ref<Animation> anim = player->get_animation(current);
+ _animation_save_in_path(current_res, p_file);
+ }
+}
- ERR_FAIL_COND(!Object::cast_to<Resource>(*anim));
+void AnimationPlayerEditor::_load_animations(Vector<String> p_files) {
+ ERR_FAIL_COND(!player);
- RES current_res = RES(Object::cast_to<Resource>(*anim));
+ for (int i = 0; i < p_files.size(); i++) {
+ String file = p_files[i];
- _animation_save_in_path(current_res, p_path);
- }
+ Ref<Resource> res = ResourceLoader::load(file, "Animation");
+ ERR_FAIL_COND_MSG(res.is_null(), "Cannot load Animation from file '" + file + "'.");
+ ERR_FAIL_COND_MSG(!res->is_class("Animation"), "Loaded resource from file '" + file + "' is not Animation.");
+ if (file.rfind("/") != -1) {
+ file = file.substr(file.rfind("/") + 1, file.length());
+ }
+ if (file.rfind("\\") != -1) {
+ file = file.substr(file.rfind("\\") + 1, file.length());
}
+
+ if (file.find(".") != -1) {
+ file = file.substr(0, file.find("."));
+ }
+
+ undo_redo->create_action(TTR("Load Animation"));
+ undo_redo->add_do_method(player, "add_animation", file, res);
+ undo_redo->add_undo_method(player, "remove_animation", file);
+ if (player->has_animation(file)) {
+ undo_redo->add_undo_method(player, "add_animation", file, player->get_animation(file));
+ }
+ undo_redo->add_do_method(this, "_animation_player_changed", player);
+ undo_redo->add_undo_method(this, "_animation_player_changed", player);
+ undo_redo->commit_action();
}
}
@@ -824,7 +826,7 @@ void AnimationPlayerEditor::_update_player() {
pin->set_disabled(player == nullptr);
if (!player) {
- AnimationPlayerEditor::singleton->get_track_editor()->update_keying();
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->update_keying();
EditorNode::get_singleton()->update_keying();
return;
}
@@ -902,7 +904,7 @@ void AnimationPlayerEditor::edit(AnimationPlayer *p_player) {
}
}
-void AnimationPlayerEditor::forward_canvas_force_draw_over_viewport(Control *p_overlay) {
+void AnimationPlayerEditor::forward_force_draw_over_viewport(Control *p_overlay) {
if (!onion.can_overlay) {
return;
}
@@ -1220,7 +1222,7 @@ void AnimationPlayerEditor::_onion_skinning_menu(int p_option) {
}
}
-void AnimationPlayerEditor::_unhandled_key_input(const Ref<InputEvent> &p_ev) {
+void AnimationPlayerEditor::unhandled_key_input(const Ref<InputEvent> &p_ev) {
ERR_FAIL_COND(p_ev.is_null());
Ref<InputEventKey> k = p_ev;
@@ -1464,15 +1466,15 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() {
}
void AnimationPlayerEditor::_start_onion_skinning() {
- // FIXME: Using "idle_frame" makes onion layers update one frame behind the current.
- if (!get_tree()->is_connected("idle_frame", callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_1_deferred))) {
- get_tree()->connect("idle_frame", callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_1_deferred));
+ // FIXME: Using "process_frame" makes onion layers update one frame behind the current.
+ if (!get_tree()->is_connected("process_frame", callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_1_deferred))) {
+ get_tree()->connect("process_frame", callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_1_deferred));
}
}
void AnimationPlayerEditor::_stop_onion_skinning() {
- if (get_tree()->is_connected("idle_frame", callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_1_deferred))) {
- get_tree()->disconnect("idle_frame", callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_1_deferred));
+ if (get_tree()->is_connected("process_frame", callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_1_deferred))) {
+ get_tree()->disconnect("process_frame", callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_1_deferred));
_free_onion_layers();
@@ -1497,7 +1499,6 @@ void AnimationPlayerEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_animation_player_changed"), &AnimationPlayerEditor::_animation_player_changed);
ClassDB::bind_method(D_METHOD("_list_changed"), &AnimationPlayerEditor::_list_changed);
ClassDB::bind_method(D_METHOD("_animation_duplicate"), &AnimationPlayerEditor::_animation_duplicate);
- ClassDB::bind_method(D_METHOD("_unhandled_key_input"), &AnimationPlayerEditor::_unhandled_key_input);
ClassDB::bind_method(D_METHOD("_prepare_onion_layers_1"), &AnimationPlayerEditor::_prepare_onion_layers_1);
ClassDB::bind_method(D_METHOD("_prepare_onion_layers_2"), &AnimationPlayerEditor::_prepare_onion_layers_2);
@@ -1696,7 +1697,8 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay
animation->connect("item_selected", callable_mp(this, &AnimationPlayerEditor::_animation_selected));
- file->connect("file_selected", callable_mp(this, &AnimationPlayerEditor::_dialog_action));
+ file->connect("file_selected", callable_mp(this, &AnimationPlayerEditor::_save_animation));
+ file->connect("files_selected", callable_mp(this, &AnimationPlayerEditor::_load_animations));
frame->connect("value_changed", callable_mp(this, &AnimationPlayerEditor::_seek_value_changed), make_binds(true, false));
scale->connect("text_submitted", callable_mp(this, &AnimationPlayerEditor::_scale_changed));
@@ -1736,6 +1738,8 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay
onion.capture.shader = Ref<Shader>(memnew(Shader));
onion.capture.shader->set_code(R"(
+// Animation editor onion skinning shader.
+
shader_type canvas_item;
uniform vec4 bkg_color;
diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h
index 5c2348f86b..eb8db2eaba 100644
--- a/editor/plugins/animation_player_editor_plugin.h
+++ b/editor/plugins/animation_player_editor_plugin.h
@@ -112,7 +112,6 @@ class AnimationPlayerEditor : public VBoxContainer {
EditorFileDialog *file;
ConfirmationDialog *delete_dialog;
- int current_option;
struct BlendEditor {
AcceptDialog *dialog = nullptr;
@@ -129,6 +128,7 @@ class AnimationPlayerEditor : public VBoxContainer {
bool updating_blends;
AnimationTrackEditor *track_editor;
+ static AnimationPlayerEditor *singleton;
// Onion skinning.
struct {
@@ -185,7 +185,8 @@ class AnimationPlayerEditor : public VBoxContainer {
void _animation_duplicate();
void _animation_resource_edit();
void _scale_changed(const String &p_scale);
- void _dialog_action(String p_file);
+ void _save_animation(String p_file);
+ void _load_animations(Vector<String> p_files);
void _seek_frame_changed(const String &p_frame);
void _seek_value_changed(float p_value, bool p_set = false, bool p_timeline_only = false);
void _blend_editor_next_changed(const int p_idx);
@@ -200,7 +201,7 @@ class AnimationPlayerEditor : public VBoxContainer {
void _animation_key_editor_seek(float p_pos, bool p_drag, bool p_timeline_only = false);
void _animation_key_editor_anim_len_changed(float p_len);
- void _unhandled_key_input(const Ref<InputEvent> &p_ev);
+ virtual void unhandled_key_input(const Ref<InputEvent> &p_ev) override;
void _animation_tool_menu(int p_option);
void _onion_skinning_menu(int p_option);
@@ -226,7 +227,8 @@ protected:
public:
AnimationPlayer *get_player() const;
- static AnimationPlayerEditor *singleton;
+
+ static AnimationPlayerEditor *get_singleton() { return singleton; }
bool is_pinned() const { return pin->is_pressed(); }
void unpin() { pin->set_pressed(false); }
@@ -238,7 +240,7 @@ public:
void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; }
void edit(AnimationPlayer *p_player);
- void forward_canvas_force_draw_over_viewport(Control *p_overlay);
+ void forward_force_draw_over_viewport(Control *p_overlay);
AnimationPlayerEditor(EditorNode *p_editor, AnimationPlayerEditorPlugin *p_plugin);
};
@@ -262,7 +264,8 @@ public:
virtual bool handles(Object *p_object) const override;
virtual void make_visible(bool p_visible) override;
- virtual void forward_canvas_force_draw_over_viewport(Control *p_overlay) override { anim_editor->forward_canvas_force_draw_over_viewport(p_overlay); }
+ virtual void forward_canvas_force_draw_over_viewport(Control *p_overlay) override { anim_editor->forward_force_draw_over_viewport(p_overlay); }
+ virtual void forward_spatial_force_draw_over_viewport(Control *p_overlay) override { anim_editor->forward_force_draw_over_viewport(p_overlay); }
AnimationPlayerEditorPlugin(EditorNode *p_node);
~AnimationPlayerEditorPlugin();
diff --git a/editor/plugins/animation_tree_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp
index cd84be0c25..6c5606fbfd 100644
--- a/editor/plugins/animation_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_tree_editor_plugin.cpp
@@ -55,7 +55,7 @@ void AnimationTreeEditor::edit(AnimationTree *p_tree) {
tree = p_tree;
Vector<String> path;
- if (tree->has_meta("_tree_edit_path")) {
+ if (tree && tree->has_meta("_tree_edit_path")) {
path = tree->get_meta("_tree_edit_path");
edit_path(path);
} else {
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index 785bab42cf..aacfc3e305 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -230,7 +230,7 @@ void EditorAssetLibraryItemDescription::configure(const String &p_title, int p_a
description->add_text(TTR("View Files"));
description->pop();
description->add_text("\n" + TTR("Description:") + "\n\n");
- description->append_bbcode(p_description);
+ description->append_text(p_description);
set_title(p_title);
}
@@ -614,7 +614,7 @@ void EditorAssetLibrary::_update_repository_options() {
}
}
-void EditorAssetLibrary::_unhandled_key_input(const Ref<InputEvent> &p_event) {
+void EditorAssetLibrary::unhandled_key_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
const Ref<InputEventKey> key = p_event;
@@ -830,9 +830,9 @@ void EditorAssetLibrary::_update_image_queue() {
int current_images = 0;
List<int> to_delete;
- for (Map<int, ImageQueue>::Element *E = image_queue.front(); E; E = E->next()) {
- if (!E->get().active && current_images < max_images) {
- String cache_filename_base = EditorPaths::get_singleton()->get_cache_dir().plus_file("assetimage_" + E->get().image_url.md5_text());
+ for (KeyValue<int, ImageQueue> &E : image_queue) {
+ if (!E.value.active && current_images < max_images) {
+ String cache_filename_base = EditorPaths::get_singleton()->get_cache_dir().plus_file("assetimage_" + E.value.image_url.md5_text());
Vector<String> headers;
if (FileAccess::exists(cache_filename_base + ".etag") && FileAccess::exists(cache_filename_base + ".data")) {
@@ -844,14 +844,14 @@ void EditorAssetLibrary::_update_image_queue() {
}
}
- Error err = E->get().request->request(E->get().image_url, headers);
+ Error err = E.value.request->request(E.value.image_url, headers);
if (err != OK) {
- to_delete.push_back(E->key());
+ to_delete.push_back(E.key);
} else {
- E->get().active = true;
+ E.value.active = true;
}
current_images++;
- } else if (E->get().active) {
+ } else if (E.value.active) {
current_images++;
}
}
@@ -1322,8 +1322,6 @@ void EditorAssetLibrary::disable_community_support() {
}
void EditorAssetLibrary::_bind_methods() {
- ClassDB::bind_method("_unhandled_key_input", &EditorAssetLibrary::_unhandled_key_input);
-
ADD_SIGNAL(MethodInfo("install_asset", PropertyInfo(Variant::STRING, "zip_path"), PropertyInfo(Variant::STRING, "name")));
}
diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h
index c6ca1ecd4f..286546f962 100644
--- a/editor/plugins/asset_library_editor_plugin.h
+++ b/editor/plugins/asset_library_editor_plugin.h
@@ -299,7 +299,7 @@ class EditorAssetLibrary : public PanelContainer {
protected:
static void _bind_methods();
void _notification(int p_what);
- void _unhandled_key_input(const Ref<InputEvent> &p_event);
+ virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override;
public:
void disable_community_support();
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 477e066e87..ef872bcead 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -387,7 +387,7 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, unsig
// Self center
if ((is_snap_active && snap_node_center && (p_modes & SNAP_NODE_CENTER)) || (p_forced_modes & SNAP_NODE_CENTER)) {
if (p_self_canvas_item->_edit_use_rect()) {
- Point2 center = p_self_canvas_item->get_global_transform_with_canvas().xform(p_self_canvas_item->_edit_get_rect().get_position() + p_self_canvas_item->_edit_get_rect().get_size() / 2.0);
+ Point2 center = p_self_canvas_item->get_global_transform_with_canvas().xform(p_self_canvas_item->_edit_get_rect().get_center());
_snap_if_closer_point(p_target, output, snap_target, center, SNAP_TARGET_SELF, rotation);
} else {
Point2 position = p_self_canvas_item->get_global_transform_with_canvas().xform(Point2());
@@ -472,7 +472,7 @@ real_t CanvasItemEditor::snap_angle(real_t p_target, real_t p_start) const {
}
}
-void CanvasItemEditor::_unhandled_key_input(const Ref<InputEvent> &p_ev) {
+void CanvasItemEditor::unhandled_key_input(const Ref<InputEvent> &p_ev) {
ERR_FAIL_COND(p_ev.is_null());
Ref<InputEventKey> k = p_ev;
@@ -513,7 +513,7 @@ Object *CanvasItemEditor::_get_editor_data(Object *p_what) {
}
void CanvasItemEditor::_keying_changed() {
- if (AnimationPlayerEditor::singleton->get_track_editor()->is_visible_in_tree()) {
+ if (AnimationPlayerEditor::get_singleton()->get_track_editor()->is_visible_in_tree()) {
animation_hb->show();
} else {
animation_hb->hide();
@@ -525,7 +525,7 @@ Rect2 CanvasItemEditor::_get_encompassing_rect_from_list(List<CanvasItem *> p_li
// Handles the first element
CanvasItem *canvas_item = p_list.front()->get();
- Rect2 rect = Rect2(canvas_item->get_global_transform_with_canvas().xform(canvas_item->_edit_get_rect().position + canvas_item->_edit_get_rect().size / 2), Size2());
+ Rect2 rect = Rect2(canvas_item->get_global_transform_with_canvas().xform(canvas_item->_edit_get_rect().get_center()), Size2());
// Expand with the other ones
for (CanvasItem *canvas_item2 : p_list) {
@@ -564,7 +564,7 @@ void CanvasItemEditor::_expand_encompassing_rect_using_children(Rect2 &r_rect, c
Transform2D xform = p_parent_xform * p_canvas_xform * canvas_item->get_transform();
Rect2 rect = canvas_item->_edit_get_rect();
if (r_first) {
- r_rect = Rect2(xform.xform(rect.position + rect.size / 2), Size2());
+ r_rect = Rect2(xform.xform(rect.get_center()), Size2());
r_first = false;
}
r_rect.expand_to(xform.xform(rect.position));
@@ -590,7 +590,7 @@ void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_no
return;
}
- const real_t grab_distance = EDITOR_GET("editors/poly_editor/point_grab_radius");
+ const real_t grab_distance = EDITOR_GET("editors/polygon_editor/point_grab_radius");
CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node);
for (int i = p_node->get_child_count() - 1; i >= 0; i--) {
@@ -748,8 +748,8 @@ bool CanvasItemEditor::_select_click_on_item(CanvasItem *item, Point2 p_click_po
List<CanvasItem *> CanvasItemEditor::_get_edited_canvas_items(bool retreive_locked, bool remove_canvas_item_if_parent_in_selection) {
List<CanvasItem *> selection;
- for (Map<Node *, Object *>::Element *E = editor_selection->get_selection().front(); E; E = E->next()) {
- CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->key());
+ for (const KeyValue<Node *, Object *> &E : editor_selection->get_selection()) {
+ CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E.key);
if (canvas_item && canvas_item->is_visible_in_tree() && canvas_item->get_viewport() == EditorNode::get_singleton()->get_scene_root() && (retreive_locked || !_is_node_locked(canvas_item))) {
CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
if (se) {
@@ -1295,7 +1295,7 @@ bool CanvasItemEditor::_gui_input_pivot(const Ref<InputEvent> &p_event) {
// Drag the pivot (in pivot mode / with V key)
if (drag_type == DRAG_NONE) {
if ((b.is_valid() && b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_LEFT && tool == TOOL_EDIT_PIVOT) ||
- (k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_keycode() == KEY_V)) {
+ (k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_keycode() == KEY_V && tool == TOOL_SELECT && k->get_modifiers_mask() == 0)) {
List<CanvasItem *> selection = _get_edited_canvas_items();
// Filters the selection with nodes that allow setting the pivot
@@ -1460,8 +1460,8 @@ bool CanvasItemEditor::_gui_input_open_scene_on_double_click(const Ref<InputEven
List<CanvasItem *> selection = _get_edited_canvas_items();
if (selection.size() == 1) {
CanvasItem *canvas_item = selection[0];
- if (canvas_item->get_filename() != "" && canvas_item != editor->get_edited_scene()) {
- editor->open_request(canvas_item->get_filename());
+ if (canvas_item->get_scene_file_path() != "" && canvas_item != editor->get_edited_scene()) {
+ editor->open_request(canvas_item->get_scene_file_path());
return true;
}
}
@@ -2927,7 +2927,7 @@ void CanvasItemEditor::_draw_ruler_tool() {
viewport->draw_string(font, text_pos, TS->format_number(vformat("%.1f px", length_vector.length())), HALIGN_LEFT, -1, font_size, font_color, outline_size, outline_color);
if (draw_secondary_lines) {
- const real_t horizontal_angle_rad = atan2(length_vector.y, length_vector.x);
+ const real_t horizontal_angle_rad = length_vector.angle();
const real_t vertical_angle_rad = Math_PI / 2.0 - horizontal_angle_rad;
const int horizontal_angle = round(180 * horizontal_angle_rad / Math_PI);
const int vertical_angle = round(180 * vertical_angle_rad / Math_PI);
@@ -3782,8 +3782,8 @@ void CanvasItemEditor::_notification(int p_what) {
}
// Update the viewport if bones changes
- for (Map<BoneKey, BoneList>::Element *E = bone_list.front(); E; E = E->next()) {
- Object *b = ObjectDB::get_instance(E->key().from);
+ for (KeyValue<BoneKey, BoneList> &E : bone_list) {
+ Object *b = ObjectDB::get_instance(E.key.from);
if (!b) {
viewport->update();
break;
@@ -3796,14 +3796,14 @@ void CanvasItemEditor::_notification(int p_what) {
Transform2D global_xform = b2->get_global_transform();
- if (global_xform != E->get().xform) {
- E->get().xform = global_xform;
+ if (global_xform != E.value.xform) {
+ E.value.xform = global_xform;
viewport->update();
}
Bone2D *bone = Object::cast_to<Bone2D>(b);
- if (bone && bone->get_length() != E->get().length) {
- E->get().length = bone->get_length();
+ if (bone && bone->get_length() != E.value.length) {
+ E.value.length = bone->get_length();
viewport->update();
}
}
@@ -3816,7 +3816,7 @@ void CanvasItemEditor::_notification(int p_what) {
select_sb->set_default_margin(Side(i), 4);
}
- AnimationPlayerEditor::singleton->get_track_editor()->connect("visibility_changed", callable_mp(this, &CanvasItemEditor::_keying_changed));
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->connect("visibility_changed", callable_mp(this, &CanvasItemEditor::_keying_changed));
_keying_changed();
} else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
@@ -3968,7 +3968,7 @@ void CanvasItemEditor::edit(CanvasItem *p_canvas_item) {
void CanvasItemEditor::_update_context_menu_stylebox() {
// This must be called when the theme changes to follow the new accent color.
Ref<StyleBoxFlat> context_menu_stylebox = memnew(StyleBoxFlat);
- const Color accent_color = EditorNode::get_singleton()->get_gui_base()->get_theme_color("accent_color", "Editor");
+ const Color accent_color = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("accent_color"), SNAME("Editor"));
context_menu_stylebox->set_bg_color(accent_color * Color(1, 1, 1, 0.1));
// Add an underline to the StyleBox, but prevent its minimum vertical size from changing.
context_menu_stylebox->set_border_color(accent_color);
@@ -4195,6 +4195,7 @@ void CanvasItemEditor::_zoom_on_position(real_t p_zoom, Point2 p_position) {
p_zoom = CLAMP(p_zoom, MIN_ZOOM, MAX_ZOOM);
if (p_zoom == zoom) {
+ zoom_widget->set_zoom(p_zoom);
return;
}
@@ -4262,8 +4263,8 @@ void CanvasItemEditor::_button_tool_select(int p_index) {
void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation, bool p_scale, bool p_on_existing) {
Map<Node *, Object *> &selection = editor_selection->get_selection();
- for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) {
- CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->key());
+ for (const KeyValue<Node *, Object *> &E : selection) {
+ CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E.key);
if (!canvas_item || !canvas_item->is_visible_in_tree()) {
continue;
}
@@ -4276,13 +4277,13 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation,
Node2D *n2d = Object::cast_to<Node2D>(canvas_item);
if (key_pos && p_location) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "position", n2d->get_position(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(n2d, "position", n2d->get_position(), p_on_existing);
}
if (key_rot && p_rotation) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "rotation", n2d->get_rotation(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(n2d, "rotation", n2d->get_rotation(), p_on_existing);
}
if (key_scale && p_scale) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "scale", n2d->get_scale(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(n2d, "scale", n2d->get_scale(), p_on_existing);
}
if (n2d->has_meta("_edit_bone_") && n2d->get_parent_item()) {
@@ -4308,13 +4309,13 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation,
if (has_chain && ik_chain.size()) {
for (Node2D *&F : ik_chain) {
if (key_pos) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F, "position", F->get_position(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(F, "position", F->get_position(), p_on_existing);
}
if (key_rot) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F, "rotation", F->get_rotation(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(F, "rotation", F->get_rotation(), p_on_existing);
}
if (key_scale) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F, "scale", F->get_scale(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(F, "scale", F->get_scale(), p_on_existing);
}
}
}
@@ -4324,13 +4325,13 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation,
Control *ctrl = Object::cast_to<Control>(canvas_item);
if (key_pos) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_position", ctrl->get_position(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(ctrl, "rect_position", ctrl->get_position(), p_on_existing);
}
if (key_rot) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_rotation", ctrl->get_rotation(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(ctrl, "rect_rotation", ctrl->get_rotation(), p_on_existing);
}
if (key_scale) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_size", ctrl->get_size(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(ctrl, "rect_size", ctrl->get_size(), p_on_existing);
}
}
}
@@ -4694,8 +4695,8 @@ void CanvasItemEditor::_popup_callback(int p_op) {
Map<Node *, Object *> &selection = editor_selection->get_selection();
- for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) {
- CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->key());
+ for (const KeyValue<Node *, Object *> &E : selection) {
+ CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E.key);
if (!canvas_item || !canvas_item->is_visible_in_tree()) {
continue;
}
@@ -4740,8 +4741,8 @@ void CanvasItemEditor::_popup_callback(int p_op) {
case ANIM_CLEAR_POSE: {
Map<Node *, Object *> &selection = editor_selection->get_selection();
- for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) {
- CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->key());
+ for (const KeyValue<Node *, Object *> &E : selection) {
+ CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E.key);
if (!canvas_item || !canvas_item->is_visible_in_tree()) {
continue;
}
@@ -4770,7 +4771,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
}
/*
if (key_scale)
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl,"rect/size",ctrl->get_size());
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(ctrl,"rect/size",ctrl->get_size());
*/
}
}
@@ -4815,8 +4816,8 @@ void CanvasItemEditor::_popup_callback(int p_op) {
Node *editor_root = EditorNode::get_singleton()->get_edited_scene()->get_tree()->get_edited_scene_root();
undo_redo->create_action(TTR("Create Custom Bone2D(s) from Node(s)"));
- for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) {
- Node2D *n2d = Object::cast_to<Node2D>(E->key());
+ for (const KeyValue<Node *, Object *> &E : selection) {
+ Node2D *n2d = Object::cast_to<Node2D>(E.key);
Bone2D *new_bone = memnew(Bone2D);
String new_bone_name = n2d->get_name();
@@ -4860,8 +4861,8 @@ void CanvasItemEditor::_focus_selection(int p_op) {
int count = 0;
Map<Node *, Object *> &selection = editor_selection->get_selection();
- for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) {
- CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->key());
+ for (const KeyValue<Node *, Object *> &E : selection) {
+ CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E.key);
if (!canvas_item) {
continue;
}
@@ -4895,10 +4896,9 @@ void CanvasItemEditor::_focus_selection(int p_op) {
};
if (p_op == VIEW_CENTER_TO_SELECTION) {
- center = rect.position + rect.size / 2;
+ center = rect.get_center();
Vector2 offset = viewport->get_size() / 2 - editor->get_scene_root()->get_global_canvas_transform().xform(center);
- view_offset.x -= Math::round(offset.x / zoom);
- view_offset.y -= Math::round(offset.y / zoom);
+ view_offset -= (offset / zoom).round();
update_viewport();
} else { // VIEW_FRAME_TO_SELECTION
@@ -4918,7 +4918,7 @@ void CanvasItemEditor::_focus_selection(int p_op) {
void CanvasItemEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update_override_camera_button", "game_running"), &CanvasItemEditor::_update_override_camera_button);
ClassDB::bind_method("_get_editor_data", &CanvasItemEditor::_get_editor_data);
- ClassDB::bind_method("_unhandled_key_input", &CanvasItemEditor::_unhandled_key_input);
+
ClassDB::bind_method(D_METHOD("set_state"), &CanvasItemEditor::set_state);
ClassDB::bind_method(D_METHOD("update_viewport"), &CanvasItemEditor::update_viewport);
ClassDB::bind_method(D_METHOD("_zoom_on_position"), &CanvasItemEditor::_zoom_on_position);
@@ -5203,7 +5203,9 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
snap_rotation = false;
snap_scale = false;
snap_relative = false;
- snap_pixel = false;
+ // Enable pixel snapping even if pixel snap rendering is disabled in the Project Settings.
+ // This results in crisper visuals by preventing 2D nodes from being placed at subpixel coordinates.
+ snap_pixel = true;
snap_target[0] = SNAP_TARGET_NONE;
snap_target[1] = SNAP_TARGET_NONE;
@@ -5798,7 +5800,7 @@ void CanvasItemEditorViewport::_remove_preview() {
}
bool CanvasItemEditorViewport::_cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node) {
- if (p_desired_node->get_filename() == p_target_scene_path) {
+ if (p_desired_node->get_scene_file_path() == p_target_scene_path) {
return true;
}
@@ -5895,14 +5897,14 @@ bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, cons
return false;
}
- if (editor->get_edited_scene()->get_filename() != "") { // cyclical instancing
- if (_cyclical_dependency_exists(editor->get_edited_scene()->get_filename(), instantiated_scene)) {
+ if (editor->get_edited_scene()->get_scene_file_path() != "") { // cyclical instancing
+ if (_cyclical_dependency_exists(editor->get_edited_scene()->get_scene_file_path(), instantiated_scene)) {
memdelete(instantiated_scene);
return false;
}
}
- instantiated_scene->set_filename(ProjectSettings::get_singleton()->localize_path(path));
+ instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(path));
editor_data->get_undo_redo().add_do_method(parent, "add_child", instantiated_scene);
editor_data->get_undo_redo().add_do_method(instantiated_scene, "set_owner", editor->get_edited_scene());
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index bff580315e..1965efbf30 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -455,7 +455,7 @@ private:
void _keying_changed();
- void _unhandled_key_input(const Ref<InputEvent> &p_ev);
+ virtual void unhandled_key_input(const Ref<InputEvent> &p_ev) override;
void _draw_text_at_position(Point2 p_position, String p_string, Side p_side);
void _draw_margin_at_position(int p_value, Point2 p_position, Side p_side);
diff --git a/editor/plugins/collision_polygon_3d_editor_plugin.cpp b/editor/plugins/collision_polygon_3d_editor_plugin.cpp
index 5d5f78e0dc..1ee834a974 100644
--- a/editor/plugins/collision_polygon_3d_editor_plugin.cpp
+++ b/editor/plugins/collision_polygon_3d_editor_plugin.cpp
@@ -103,9 +103,9 @@ void CollisionPolygon3DEditor::_wip_close() {
undo_redo->commit_action();
}
-bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
+EditorPlugin::AfterGUIInput CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
if (!node) {
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
Transform3D gt = node->get_global_transform();
@@ -124,7 +124,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
Vector3 spoint;
if (!p.intersects_ray(ray_from, ray_dir, &spoint)) {
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
spoint = gi.xform(spoint);
@@ -138,7 +138,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
Vector<Vector2> poly = node->call("get_polygon");
//first check if a point is to be added (segment split)
- real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius");
+ real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
switch (mode) {
case MODE_CREATE: {
@@ -151,19 +151,19 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
snap_ignore = false;
_polygon_draw();
edited_point = 1;
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else {
if (wip.size() > 1 && p_camera->unproject_position(gt.xform(Vector3(wip[0].x, wip[0].y, depth))).distance_to(gpoint) < grab_threshold) {
//wip closed
_wip_close();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else {
wip.push_back(cpoint);
edited_point = wip.size();
snap_ignore = false;
_polygon_draw();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
} else if (mb->get_button_index() == MOUSE_BUTTON_RIGHT && mb->is_pressed() && wip_active) {
@@ -184,7 +184,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
undo_redo->add_do_method(this, "_polygon_draw");
undo_redo->add_undo_method(this, "_polygon_draw");
undo_redo->commit_action();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
//search edges
@@ -219,7 +219,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
_polygon_draw();
snap_ignore = true;
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
} else {
//look for points to move
@@ -244,7 +244,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
edited_point_pos = poly[closest_idx];
_polygon_draw();
snap_ignore = false;
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
} else {
@@ -253,7 +253,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
if (edited_point != -1) {
//apply
- ERR_FAIL_INDEX_V(edited_point, poly.size(), false);
+ ERR_FAIL_INDEX_V(edited_point, poly.size(), EditorPlugin::AFTER_GUI_INPUT_PASS);
poly.write[edited_point] = edited_point_pos;
undo_redo->create_action(TTR("Edit Poly"));
undo_redo->add_do_method(node, "set_polygon", poly);
@@ -263,7 +263,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
undo_redo->commit_action();
edited_point = -1;
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
}
@@ -290,7 +290,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
undo_redo->add_do_method(this, "_polygon_draw");
undo_redo->add_undo_method(this, "_polygon_draw");
undo_redo->commit_action();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
@@ -310,7 +310,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
Vector3 spoint;
if (!p.intersects_ray(ray_from, ray_dir, &spoint)) {
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
spoint = gi.xform(spoint);
@@ -332,7 +332,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
}
}
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
float CollisionPolygon3DEditor::_get_depth() {
diff --git a/editor/plugins/collision_polygon_3d_editor_plugin.h b/editor/plugins/collision_polygon_3d_editor_plugin.h
index 5db0f7308a..10b0adf76c 100644
--- a/editor/plugins/collision_polygon_3d_editor_plugin.h
+++ b/editor/plugins/collision_polygon_3d_editor_plugin.h
@@ -88,7 +88,7 @@ protected:
static void _bind_methods();
public:
- virtual bool forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event);
+ virtual EditorPlugin::AfterGUIInput forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event);
void edit(Node *p_collision_polygon);
CollisionPolygon3DEditor(EditorNode *p_editor);
~CollisionPolygon3DEditor();
@@ -101,7 +101,7 @@ class Polygon3DEditorPlugin : public EditorPlugin {
EditorNode *editor;
public:
- virtual bool forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override { return collision_polygon_editor->forward_spatial_gui_input(p_camera, p_event); }
+ virtual EditorPlugin::AfterGUIInput forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override { return collision_polygon_editor->forward_spatial_gui_input(p_camera, p_event); }
virtual String get_name() const override { return "Polygon3DEditor"; }
bool has_main_screen() const override { return false; }
diff --git a/editor/plugins/collision_shape_2d_editor_plugin.cpp b/editor/plugins/collision_shape_2d_editor_plugin.cpp
index c2684305ef..fb32d7b1fd 100644
--- a/editor/plugins/collision_shape_2d_editor_plugin.cpp
+++ b/editor/plugins/collision_shape_2d_editor_plugin.cpp
@@ -38,7 +38,8 @@
#include "scene/resources/convex_polygon_shape_2d.h"
#include "scene/resources/rectangle_shape_2d.h"
#include "scene/resources/segment_shape_2d.h"
-#include "scene/resources/world_margin_shape_2d.h"
+#include "scene/resources/separation_ray_shape_2d.h"
+#include "scene/resources/world_boundary_shape_2d.h"
void CollisionShape2DEditor::_node_removed(Node *p_node) {
if (p_node == node) {
@@ -69,13 +70,22 @@ Variant CollisionShape2DEditor::get_handle_value(int idx) const {
case CONVEX_POLYGON_SHAPE: {
} break;
- case WORLD_MARGIN_SHAPE: {
- Ref<WorldMarginShape2D> line = node->get_shape();
+ case WORLD_BOUNDARY_SHAPE: {
+ Ref<WorldBoundaryShape2D> world_boundary = node->get_shape();
if (idx == 0) {
- return line->get_distance();
+ return world_boundary->get_distance();
} else {
- return line->get_normal();
+ return world_boundary->get_normal();
+ }
+
+ } break;
+
+ case SEPARATION_RAY_SHAPE: {
+ Ref<SeparationRayShape2D> ray = node->get_shape();
+
+ if (idx == 0) {
+ return ray->get_length();
}
} break;
@@ -137,14 +147,14 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) {
case CONVEX_POLYGON_SHAPE: {
} break;
- case WORLD_MARGIN_SHAPE: {
+ case WORLD_BOUNDARY_SHAPE: {
if (idx < 2) {
- Ref<WorldMarginShape2D> line = node->get_shape();
+ Ref<WorldBoundaryShape2D> world_boundary = node->get_shape();
if (idx == 0) {
- line->set_distance(p_point.length());
+ world_boundary->set_distance(p_point.length());
} else {
- line->set_normal(p_point.normalized());
+ world_boundary->set_normal(p_point.normalized());
}
canvas_item_editor->update_viewport();
@@ -152,6 +162,15 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) {
} break;
+ case SEPARATION_RAY_SHAPE: {
+ Ref<SeparationRayShape2D> ray = node->get_shape();
+
+ ray->set_length(Math::abs(p_point.y));
+
+ canvas_item_editor->update_viewport();
+
+ } break;
+
case RECTANGLE_SHAPE: {
if (idx < 8) {
Ref<RectangleShape2D> rect = node->get_shape();
@@ -236,23 +255,33 @@ void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) {
// Cannot be edited directly, use CollisionPolygon2D instead.
} break;
- case WORLD_MARGIN_SHAPE: {
- Ref<WorldMarginShape2D> line = node->get_shape();
+ case WORLD_BOUNDARY_SHAPE: {
+ Ref<WorldBoundaryShape2D> world_boundary = node->get_shape();
if (idx == 0) {
- undo_redo->add_do_method(line.ptr(), "set_distance", line->get_distance());
+ undo_redo->add_do_method(world_boundary.ptr(), "set_distance", world_boundary->get_distance());
undo_redo->add_do_method(canvas_item_editor, "update_viewport");
- undo_redo->add_undo_method(line.ptr(), "set_distance", p_org);
+ undo_redo->add_undo_method(world_boundary.ptr(), "set_distance", p_org);
undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
} else {
- undo_redo->add_do_method(line.ptr(), "set_normal", line->get_normal());
+ undo_redo->add_do_method(world_boundary.ptr(), "set_normal", world_boundary->get_normal());
undo_redo->add_do_method(canvas_item_editor, "update_viewport");
- undo_redo->add_undo_method(line.ptr(), "set_normal", p_org);
+ undo_redo->add_undo_method(world_boundary.ptr(), "set_normal", p_org);
undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
}
} break;
+ case SEPARATION_RAY_SHAPE: {
+ Ref<SeparationRayShape2D> ray = node->get_shape();
+
+ undo_redo->add_do_method(ray.ptr(), "set_length", ray->get_length());
+ undo_redo->add_do_method(canvas_item_editor, "update_viewport");
+ undo_redo->add_undo_method(ray.ptr(), "set_length", p_org);
+ undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
+
+ } break;
+
case RECTANGLE_SHAPE: {
Ref<RectangleShape2D> rect = node->get_shape();
@@ -392,8 +421,10 @@ void CollisionShape2DEditor::_get_current_shape_type() {
shape_type = CONCAVE_POLYGON_SHAPE;
} else if (Object::cast_to<ConvexPolygonShape2D>(*s)) {
shape_type = CONVEX_POLYGON_SHAPE;
- } else if (Object::cast_to<WorldMarginShape2D>(*s)) {
- shape_type = WORLD_MARGIN_SHAPE;
+ } else if (Object::cast_to<WorldBoundaryShape2D>(*s)) {
+ shape_type = WORLD_BOUNDARY_SHAPE;
+ } else if (Object::cast_to<SeparationRayShape2D>(*s)) {
+ shape_type = SEPARATION_RAY_SHAPE;
} else if (Object::cast_to<RectangleShape2D>(*s)) {
shape_type = RECTANGLE_SHAPE;
} else if (Object::cast_to<SegmentShape2D>(*s)) {
@@ -459,8 +490,8 @@ void CollisionShape2DEditor::forward_canvas_draw_over_viewport(Control *p_overla
case CONVEX_POLYGON_SHAPE: {
} break;
- case WORLD_MARGIN_SHAPE: {
- Ref<WorldMarginShape2D> shape = node->get_shape();
+ case WORLD_BOUNDARY_SHAPE: {
+ Ref<WorldBoundaryShape2D> shape = node->get_shape();
handles.resize(2);
handles.write[0] = shape->get_normal() * shape->get_distance();
@@ -471,6 +502,16 @@ void CollisionShape2DEditor::forward_canvas_draw_over_viewport(Control *p_overla
} break;
+ case SEPARATION_RAY_SHAPE: {
+ Ref<SeparationRayShape2D> shape = node->get_shape();
+
+ handles.resize(1);
+ handles.write[0] = Point2(0, shape->get_length());
+
+ p_overlay->draw_texture(h, gt.xform(handles[0]) - size);
+
+ } break;
+
case RECTANGLE_SHAPE: {
Ref<RectangleShape2D> shape = node->get_shape();
diff --git a/editor/plugins/collision_shape_2d_editor_plugin.h b/editor/plugins/collision_shape_2d_editor_plugin.h
index 056e1b5b7d..ab95600a52 100644
--- a/editor/plugins/collision_shape_2d_editor_plugin.h
+++ b/editor/plugins/collision_shape_2d_editor_plugin.h
@@ -46,7 +46,8 @@ class CollisionShape2DEditor : public Control {
CIRCLE_SHAPE,
CONCAVE_POLYGON_SHAPE,
CONVEX_POLYGON_SHAPE,
- WORLD_MARGIN_SHAPE,
+ WORLD_BOUNDARY_SHAPE,
+ SEPARATION_RAY_SHAPE,
RECTANGLE_SHAPE,
SEGMENT_SHAPE
};
diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.cpp b/editor/plugins/cpu_particles_2d_editor_plugin.cpp
index 6f246c1661..fb9f8696fe 100644
--- a/editor/plugins/cpu_particles_2d_editor_plugin.cpp
+++ b/editor/plugins/cpu_particles_2d_editor_plugin.cpp
@@ -83,7 +83,7 @@ void CPUParticles2DEditorPlugin::_generate_emission_mask() {
}
img->convert(Image::FORMAT_RGBA8);
ERR_FAIL_COND(img->get_format() != Image::FORMAT_RGBA8);
- Size2i s = Size2(img->get_width(), img->get_height());
+ Size2i s = img->get_size();
ERR_FAIL_COND(s.width == 0 || s.height == 0);
Vector<Point2> valid_positions;
diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp
index 07ff0eb346..4a22dc5b62 100644
--- a/editor/plugins/curve_editor_plugin.cpp
+++ b/editor/plugins/curve_editor_plugin.cpp
@@ -101,7 +101,7 @@ void CurveEditor::_notification(int p_what) {
}
}
-void CurveEditor::on_gui_input(const Ref<InputEvent> &p_event) {
+void CurveEditor::gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mb_ref = p_event;
if (mb_ref.is_valid()) {
const InputEventMouseButton &mb = **mb_ref;
@@ -757,10 +757,6 @@ void CurveEditor::_draw() {
}
}
-void CurveEditor::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_gui_input"), &CurveEditor::on_gui_input);
-}
-
//---------------
bool EditorInspectorPluginCurve::can_handle(Object *p_object) {
diff --git a/editor/plugins/curve_editor_plugin.h b/editor/plugins/curve_editor_plugin.h
index 2e8dd43d7e..c351f6ebe9 100644
--- a/editor/plugins/curve_editor_plugin.h
+++ b/editor/plugins/curve_editor_plugin.h
@@ -74,10 +74,8 @@ public:
protected:
void _notification(int p_what);
- static void _bind_methods();
-
private:
- void on_gui_input(const Ref<InputEvent> &p_event);
+ virtual void gui_input(const Ref<InputEvent> &p_event) override;
void on_preset_item_selected(int preset_id);
void _curve_changed();
void on_context_menu_item_selected(int action_id);
diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp
index 95f68d5f7f..4cb2c0a76b 100644
--- a/editor/plugins/editor_preview_plugins.cpp
+++ b/editor/plugins/editor_preview_plugins.cpp
@@ -718,7 +718,7 @@ Ref<Texture2D> EditorMeshPreviewPlugin::generate(const RES &p_from, const Size2
RS::get_singleton()->instance_set_base(mesh_instance, mesh->get_rid());
AABB aabb = mesh->get_aabb();
- Vector3 ofs = aabb.position + aabb.size * 0.5;
+ Vector3 ofs = aabb.get_center();
aabb.position -= ofs;
Transform3D xform;
xform.basis = Basis().rotated(Vector3(0, 1, 0), -Math_PI * 0.125);
@@ -826,55 +826,6 @@ bool EditorFontPreviewPlugin::handles(const String &p_type) const {
return ClassDB::is_parent_class(p_type, "FontData") || ClassDB::is_parent_class(p_type, "Font");
}
-struct FSample {
- String script;
- String sample;
-};
-
-static FSample _samples[] = {
- { "hani", U"漢字" },
- { "armn", U"Աբ" },
- { "copt", U"Αα" },
- { "cyrl", U"Аб" },
- { "grek", U"Αα" },
- { "hebr", U"אב" },
- { "arab", U"اب" },
- { "syrc", U"ܐܒ" },
- { "thaa", U"ހށ" },
- { "deva", U"आ" },
- { "beng", U"আ" },
- { "guru", U"ਆ" },
- { "gujr", U"આ" },
- { "orya", U"ଆ" },
- { "taml", U"ஆ" },
- { "telu", U"ఆ" },
- { "knda", U"ಆ" },
- { "mylm", U"ആ" },
- { "sinh", U"ආ" },
- { "thai", U"กิ" },
- { "laoo", U"ກິ" },
- { "tibt", U"ༀ" },
- { "mymr", U"က" },
- { "geor", U"Ⴀა" },
- { "hang", U"한글" },
- { "ethi", U"ሀ" },
- { "cher", U"Ꭳ" },
- { "cans", U"ᐁ" },
- { "ogam", U"ᚁ" },
- { "runr", U"ᚠ" },
- { "tglg", U"ᜀ" },
- { "hano", U"ᜠ" },
- { "buhd", U"ᝀ" },
- { "tagb", U"ᝠ" },
- { "khmr", U"ក" },
- { "mong", U"ᠠ" },
- { "limb", U"ᤁ" },
- { "tale", U"ᥐ" },
- { "latn", U"Ab" },
- { "zyyy", U"😀" },
- { "", U"" }
-};
-
Ref<Texture2D> EditorFontPreviewPlugin::generate_from_path(const String &p_path, const Size2 &p_size) const {
RES res = ResourceLoader::load(p_path);
Ref<Font> sampled_font;
@@ -886,15 +837,15 @@ Ref<Texture2D> EditorFontPreviewPlugin::generate_from_path(const String &p_path,
}
String sample;
- for (int j = 0; j < sampled_font->get_data_count(); j++) {
- for (int i = 0; _samples[i].script != String(); i++) {
- if (sampled_font->get_data(j)->is_script_supported(_samples[i].script)) {
- if (sampled_font->get_data(j)->has_char(_samples[i].sample[0])) {
- sample += _samples[i].sample;
- }
- }
+ static const String sample_base = U"12漢字ԱբΑαАбΑαאבابܐܒހށआআਆઆଆஆఆಆആආกิກິༀကႠა한글ሀᎣᐁᚁᚠᜀᜠᝀᝠកᠠᤁᥐAb😀";
+ for (int i = 0; i < sample_base.length(); i++) {
+ if (sampled_font->has_char(sample_base[i])) {
+ sample += sample_base[i];
}
}
+ if (sample.is_empty()) {
+ sample = sampled_font->get_supported_chars().substr(0, 6);
+ }
Vector2 size = sampled_font->get_string_size(sample, 50);
Vector2 pos;
diff --git a/editor/plugins/font_editor_plugin.cpp b/editor/plugins/font_editor_plugin.cpp
index 22c9cc9ab1..52fb5b69ea 100644
--- a/editor/plugins/font_editor_plugin.cpp
+++ b/editor/plugins/font_editor_plugin.cpp
@@ -50,70 +50,24 @@ Size2 FontDataPreview::get_minimum_size() const {
return Vector2(64, 64) * EDSCALE;
}
-struct FSample {
- String script;
- String sample;
-};
-
-static FSample _samples[] = {
- { "hani", U"漢字" },
- { "armn", U"Աբ" },
- { "copt", U"Αα" },
- { "cyrl", U"Аб" },
- { "grek", U"Αα" },
- { "hebr", U"אב" },
- { "arab", U"اب" },
- { "syrc", U"ܐܒ" },
- { "thaa", U"ހށ" },
- { "deva", U"आ" },
- { "beng", U"আ" },
- { "guru", U"ਆ" },
- { "gujr", U"આ" },
- { "orya", U"ଆ" },
- { "taml", U"ஆ" },
- { "telu", U"ఆ" },
- { "knda", U"ಆ" },
- { "mylm", U"ആ" },
- { "sinh", U"ආ" },
- { "thai", U"กิ" },
- { "laoo", U"ກິ" },
- { "tibt", U"ༀ" },
- { "mymr", U"က" },
- { "geor", U"Ⴀა" },
- { "hang", U"한글" },
- { "ethi", U"ሀ" },
- { "cher", U"Ꭳ" },
- { "cans", U"ᐁ" },
- { "ogam", U"ᚁ" },
- { "runr", U"ᚠ" },
- { "tglg", U"ᜀ" },
- { "hano", U"ᜠ" },
- { "buhd", U"ᝀ" },
- { "tagb", U"ᝠ" },
- { "khmr", U"ក" },
- { "mong", U"ᠠ" },
- { "limb", U"ᤁ" },
- { "tale", U"ᥐ" },
- { "latn", U"Ab" },
- { "zyyy", U"😀" },
- { "", U"" }
-};
-
void FontDataPreview::set_data(const Ref<FontData> &p_data) {
Ref<Font> f = memnew(Font);
f->add_data(p_data);
line->clear();
-
- String sample;
- for (int i = 0; _samples[i].script != String(); i++) {
- if (p_data->is_script_supported(_samples[i].script)) {
- if (p_data->has_char(_samples[i].sample[0])) {
- sample += _samples[i].sample;
+ if (p_data.is_valid()) {
+ String sample;
+ static const String sample_base = U"12漢字ԱբΑαАбΑαאבابܐܒހށआআਆઆଆஆఆಆആආกิກິༀကႠა한글ሀᎣᐁᚁᚠᜀᜠᝀᝠកᠠᤁᥐAb😀";
+ for (int i = 0; i < sample_base.length(); i++) {
+ if (p_data->has_char(sample_base[i])) {
+ sample += sample_base[i];
}
}
+ if (sample.is_empty()) {
+ sample = p_data->get_supported_chars().substr(0, 6);
+ }
+ line->add_string(sample, f, 72);
}
- line->add_string(sample, f, 72);
update();
}
@@ -124,159 +78,6 @@ FontDataPreview::FontDataPreview() {
/*************************************************************************/
-void FontDataEditor::_notification(int p_what) {
- if (p_what == NOTIFICATION_SORT_CHILDREN) {
- int split_width = get_name_split_ratio() * get_size().width;
- button->set_size(Size2(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))->get_width(), get_size().height));
- if (is_layout_rtl()) {
- if (le != nullptr) {
- fit_child_in_rect(le, Rect2(Vector2(split_width, 0), Size2(split_width, get_size().height)));
- }
- fit_child_in_rect(chk, Rect2(Vector2(split_width - chk->get_size().x, 0), Size2(chk->get_size().x, get_size().height)));
- fit_child_in_rect(button, Rect2(Vector2(0, 0), Size2(button->get_size().width, get_size().height)));
- } else {
- if (le != nullptr) {
- fit_child_in_rect(le, Rect2(Vector2(0, 0), Size2(split_width, get_size().height)));
- }
- fit_child_in_rect(chk, Rect2(Vector2(split_width, 0), Size2(chk->get_size().x, get_size().height)));
- fit_child_in_rect(button, Rect2(Vector2(get_size().width - button->get_size().width, 0), Size2(button->get_size().width, get_size().height)));
- }
- update();
- }
- if (p_what == NOTIFICATION_DRAW) {
- int split_width = get_name_split_ratio() * get_size().width;
- Color dark_color = get_theme_color(SNAME("dark_color_2"), SNAME("Editor"));
- if (is_layout_rtl()) {
- draw_rect(Rect2(Vector2(0, 0), Size2(split_width, get_size().height)), dark_color);
- } else {
- draw_rect(Rect2(Vector2(split_width, 0), Size2(split_width, get_size().height)), dark_color);
- }
- }
- if (p_what == NOTIFICATION_THEME_CHANGED) {
- if (le != nullptr) {
- button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
- } else {
- button->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
- }
- queue_sort();
- }
- if (p_what == NOTIFICATION_RESIZED) {
- queue_sort();
- }
-}
-
-void FontDataEditor::update_property() {
- if (le == nullptr) {
- bool c = get_edited_object()->get(get_edited_property());
- chk->set_pressed(c);
- chk->set_disabled(is_read_only());
- }
-}
-
-Size2 FontDataEditor::get_minimum_size() const {
- return Size2(0, 60);
-}
-
-void FontDataEditor::_bind_methods() {
-}
-
-void FontDataEditor::init_lang_add() {
- le = memnew(LineEdit);
- le->set_placeholder("Language code");
- le->set_custom_minimum_size(Size2(get_size().width / 2, 0));
- le->set_editable(true);
- add_child(le);
-
- button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
- button->connect("pressed", callable_mp(this, &FontDataEditor::add_lang));
-}
-
-void FontDataEditor::init_lang_edit() {
- button->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
- button->connect("pressed", callable_mp(this, &FontDataEditor::remove_lang));
- chk->connect("toggled", callable_mp(this, &FontDataEditor::toggle_lang));
-}
-
-void FontDataEditor::init_script_add() {
- le = memnew(LineEdit);
- le->set_placeholder("Script code");
- le->set_custom_minimum_size(Size2(get_size().width / 2, 0));
- le->set_editable(true);
- add_child(le);
-
- button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
- button->connect("pressed", callable_mp(this, &FontDataEditor::add_script));
-}
-
-void FontDataEditor::init_script_edit() {
- button->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
- button->connect("pressed", callable_mp(this, &FontDataEditor::remove_script));
- chk->connect("toggled", callable_mp(this, &FontDataEditor::toggle_script));
-}
-
-void FontDataEditor::add_lang() {
- FontData *fd = Object::cast_to<FontData>(get_edited_object());
- if (fd != nullptr && !le->get_text().is_empty()) {
- fd->set_language_support_override(le->get_text(), chk->is_pressed());
- le->set_text("");
- chk->set_pressed(false);
- }
-}
-
-void FontDataEditor::add_script() {
- FontData *fd = Object::cast_to<FontData>(get_edited_object());
- if (fd != nullptr && le->get_text().length() == 4) {
- fd->set_script_support_override(le->get_text(), chk->is_pressed());
- le->set_text("");
- chk->set_pressed(false);
- }
-}
-
-void FontDataEditor::toggle_lang(bool p_pressed) {
- FontData *fd = Object::cast_to<FontData>(get_edited_object());
- if (fd != nullptr) {
- String lang = String(get_edited_property()).replace("language_support_override/", "");
- fd->set_language_support_override(lang, p_pressed);
- }
-}
-
-void FontDataEditor::toggle_script(bool p_pressed) {
- FontData *fd = Object::cast_to<FontData>(get_edited_object());
- if (fd != nullptr) {
- String script = String(get_edited_property()).replace("script_support_override/", "");
- fd->set_script_support_override(script, p_pressed);
- }
-}
-
-void FontDataEditor::remove_lang() {
- FontData *fd = Object::cast_to<FontData>(get_edited_object());
- if (fd != nullptr) {
- String lang = String(get_edited_property()).replace("language_support_override/", "");
- fd->remove_language_support_override(lang);
- }
-}
-
-void FontDataEditor::remove_script() {
- FontData *fd = Object::cast_to<FontData>(get_edited_object());
- if (fd != nullptr) {
- String script = String(get_edited_property()).replace("script_support_override/", "");
- fd->remove_script_support_override(script);
- }
-}
-
-FontDataEditor::FontDataEditor() {
- chk = memnew(CheckBox);
- chk->set_text(TTR("On"));
- chk->set_flat(true);
- add_child(chk);
-
- button = memnew(Button);
- button->set_flat(true);
- add_child(button);
-}
-
-/*************************************************************************/
-
bool EditorInspectorPluginFont::can_handle(Object *p_object) {
return Object::cast_to<FontData>(p_object) != nullptr;
}
@@ -291,34 +92,6 @@ void EditorInspectorPluginFont::parse_begin(Object *p_object) {
}
bool EditorInspectorPluginFont::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const uint32_t p_usage, const bool p_wide) {
- if (p_path.begins_with("language_support_override/") && p_object->is_class("FontData")) {
- String lang = p_path.replace("language_support_override/", "");
-
- FontDataEditor *editor = memnew(FontDataEditor);
- if (lang != "_new") {
- editor->init_lang_edit();
- } else {
- editor->init_lang_add();
- }
- add_property_editor(p_path, editor);
-
- return true;
- }
-
- if (p_path.begins_with("script_support_override/") && p_object->is_class("FontData")) {
- String script = p_path.replace("script_support_override/", "");
-
- FontDataEditor *editor = memnew(FontDataEditor);
- if (script != "_new") {
- editor->init_script_edit();
- } else {
- editor->init_script_add();
- }
- add_property_editor(p_path, editor);
-
- return true;
- }
-
return false;
}
diff --git a/editor/plugins/font_editor_plugin.h b/editor/plugins/font_editor_plugin.h
index 71464003a0..3530815872 100644
--- a/editor/plugins/font_editor_plugin.h
+++ b/editor/plugins/font_editor_plugin.h
@@ -55,39 +55,6 @@ public:
/*************************************************************************/
-class FontDataEditor : public EditorProperty {
- GDCLASS(FontDataEditor, EditorProperty);
-
- LineEdit *le = nullptr;
- CheckBox *chk = nullptr;
- Button *button = nullptr;
-
- void toggle_lang(bool p_pressed);
- void toggle_script(bool p_pressed);
- void add_lang();
- void add_script();
- void remove_lang();
- void remove_script();
-
-protected:
- void _notification(int p_what);
-
- static void _bind_methods();
-
-public:
- virtual Size2 get_minimum_size() const override;
- virtual void update_property() override;
-
- void init_lang_add();
- void init_lang_edit();
- void init_script_add();
- void init_script_edit();
-
- FontDataEditor();
-};
-
-/*************************************************************************/
-
class EditorInspectorPluginFont : public EditorInspectorPlugin {
GDCLASS(EditorInspectorPluginFont, EditorInspectorPlugin);
diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.cpp b/editor/plugins/gpu_particles_2d_editor_plugin.cpp
index dd91df747a..44c789b145 100644
--- a/editor/plugins/gpu_particles_2d_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_2d_editor_plugin.cpp
@@ -158,7 +158,7 @@ void GPUParticles2DEditorPlugin::_generate_emission_mask() {
}
img->convert(Image::FORMAT_RGBA8);
ERR_FAIL_COND(img->get_format() != Image::FORMAT_RGBA8);
- Size2i s = Size2(img->get_width(), img->get_height());
+ Size2i s = img->get_size();
ERR_FAIL_COND(s.width == 0 || s.height == 0);
Vector<Point2> valid_positions;
diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.cpp b/editor/plugins/gpu_particles_3d_editor_plugin.cpp
index 903a3689b0..5ac58795d1 100644
--- a/editor/plugins/gpu_particles_3d_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_3d_editor_plugin.cpp
@@ -362,6 +362,7 @@ void GPUParticles3DEditor::_generate_emission_points() {
Ref<ImageTexture> tex;
tex.instantiate();
+ tex->create_from_image(image);
Ref<ParticlesMaterial> material = node->get_process_material();
ERR_FAIL_COND(material.is_null());
@@ -390,6 +391,7 @@ void GPUParticles3DEditor::_generate_emission_points() {
Ref<ImageTexture> tex2;
tex2.instantiate();
+ tex2->create_from_image(image2);
material->set_emission_normal_texture(tex2);
} else {
diff --git a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp
index a4436525fb..6df2e34ceb 100644
--- a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp
@@ -33,7 +33,7 @@
void GPUParticlesCollisionSDFEditorPlugin::_bake() {
if (col_sdf) {
if (col_sdf->get_texture().is_null() || !col_sdf->get_texture()->get_path().is_resource_file()) {
- String path = get_tree()->get_edited_scene_root()->get_filename();
+ String path = get_tree()->get_edited_scene_root()->get_scene_file_path();
if (path == String()) {
path = "res://" + col_sdf->get_name() + "_data.exr";
} else {
diff --git a/editor/plugins/item_list_editor_plugin.cpp b/editor/plugins/item_list_editor_plugin.cpp
index 3207a989bd..16cafda899 100644
--- a/editor/plugins/item_list_editor_plugin.cpp
+++ b/editor/plugins/item_list_editor_plugin.cpp
@@ -244,6 +244,7 @@ void ItemListEditor::_node_removed(Node *p_node) {
void ItemListEditor::_notification(int p_notification) {
if (p_notification == NOTIFICATION_ENTER_TREE || p_notification == NOTIFICATION_THEME_CHANGED) {
add_button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
+ clear_button->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
del_button->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
} else if (p_notification == NOTIFICATION_READY) {
get_tree()->connect("node_removed", callable_mp(this, &ItemListEditor::_node_removed));
@@ -258,6 +259,12 @@ void ItemListEditor::_add_pressed() {
item_plugins[selected_idx]->add_item();
}
+void ItemListEditor::_clear_pressed() {
+ for (int i = item_plugins[selected_idx]->get_item_count() - 1; i >= 0; i--) {
+ item_plugins[selected_idx]->erase(i);
+ }
+}
+
void ItemListEditor::_delete_pressed() {
if (selected_idx == -1) {
return;
@@ -350,6 +357,11 @@ ItemListEditor::ItemListEditor() {
hbc->add_spacer();
+ clear_button = memnew(Button);
+ clear_button->set_text(TTR("Delete All"));
+ hbc->add_child(clear_button);
+ clear_button->connect("pressed", callable_mp(this, &ItemListEditor::_clear_pressed));
+
del_button = memnew(Button);
del_button->set_text(TTR("Delete"));
hbc->add_child(del_button);
diff --git a/editor/plugins/item_list_editor_plugin.h b/editor/plugins/item_list_editor_plugin.h
index 8c77f3d952..8f61aef083 100644
--- a/editor/plugins/item_list_editor_plugin.h
+++ b/editor/plugins/item_list_editor_plugin.h
@@ -204,6 +204,7 @@ class ItemListEditor : public HBoxContainer {
Tree *tree;
Button *add_button;
Button *del_button;
+ Button *clear_button;
int selected_idx;
@@ -213,6 +214,7 @@ class ItemListEditor : public HBoxContainer {
void _add_pressed();
void _delete_pressed();
+ void _clear_pressed();
void _node_removed(Node *p_node);
diff --git a/editor/plugins/lightmap_gi_editor_plugin.cpp b/editor/plugins/lightmap_gi_editor_plugin.cpp
index b4a70cd31d..123087446c 100644
--- a/editor/plugins/lightmap_gi_editor_plugin.cpp
+++ b/editor/plugins/lightmap_gi_editor_plugin.cpp
@@ -43,9 +43,9 @@ void LightmapGIEditorPlugin::_bake_select_file(const String &p_file) {
switch (err) {
case LightmapGI::BAKE_ERROR_NO_SAVE_PATH: {
- String scene_path = lightmap->get_filename();
+ String scene_path = lightmap->get_scene_file_path();
if (scene_path == String()) {
- scene_path = lightmap->get_owner()->get_filename();
+ scene_path = lightmap->get_owner()->get_scene_file_path();
}
if (scene_path == String()) {
EditorNode::get_singleton()->show_warning(TTR("Can't determine a save path for lightmap images.\nSave your scene and try again."));
diff --git a/editor/plugins/material_editor_plugin.cpp b/editor/plugins/material_editor_plugin.cpp
index 94966d4fe6..30945826bb 100644
--- a/editor/plugins/material_editor_plugin.cpp
+++ b/editor/plugins/material_editor_plugin.cpp
@@ -278,6 +278,8 @@ Ref<Resource> StandardMaterial3DConversionPlugin::convert(const Ref<Resource> &p
}
smat->set_render_priority(mat->get_render_priority());
+ smat->set_local_to_scene(mat->is_local_to_scene());
+ smat->set_name(mat->get_name());
return smat;
}
@@ -315,6 +317,8 @@ Ref<Resource> ParticlesMaterialConversionPlugin::convert(const Ref<Resource> &p_
}
smat->set_render_priority(mat->get_render_priority());
+ smat->set_local_to_scene(mat->is_local_to_scene());
+ smat->set_name(mat->get_name());
return smat;
}
@@ -352,6 +356,8 @@ Ref<Resource> CanvasItemMaterialConversionPlugin::convert(const Ref<Resource> &p
}
smat->set_render_priority(mat->get_render_priority());
+ smat->set_local_to_scene(mat->is_local_to_scene());
+ smat->set_name(mat->get_name());
return smat;
}
@@ -389,6 +395,8 @@ Ref<Resource> ProceduralSkyMaterialConversionPlugin::convert(const Ref<Resource>
}
smat->set_render_priority(mat->get_render_priority());
+ smat->set_local_to_scene(mat->is_local_to_scene());
+ smat->set_name(mat->get_name());
return smat;
}
@@ -426,6 +434,8 @@ Ref<Resource> PanoramaSkyMaterialConversionPlugin::convert(const Ref<Resource> &
}
smat->set_render_priority(mat->get_render_priority());
+ smat->set_local_to_scene(mat->is_local_to_scene());
+ smat->set_name(mat->get_name());
return smat;
}
@@ -463,5 +473,7 @@ Ref<Resource> PhysicalSkyMaterialConversionPlugin::convert(const Ref<Resource> &
}
smat->set_render_priority(mat->get_render_priority());
+ smat->set_local_to_scene(mat->is_local_to_scene());
+ smat->set_name(mat->get_name());
return smat;
}
diff --git a/editor/plugins/mesh_editor_plugin.cpp b/editor/plugins/mesh_editor_plugin.cpp
index 39ab3215ff..dc16a7a325 100644
--- a/editor/plugins/mesh_editor_plugin.cpp
+++ b/editor/plugins/mesh_editor_plugin.cpp
@@ -32,7 +32,7 @@
#include "editor/editor_scale.h"
-void MeshEditor::_gui_input(Ref<InputEvent> p_event) {
+void MeshEditor::gui_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
Ref<InputEventMouseMotion> mm = p_event;
@@ -80,7 +80,7 @@ void MeshEditor::edit(Ref<Mesh> p_mesh) {
_update_rotation();
AABB aabb = mesh->get_aabb();
- Vector3 ofs = aabb.position + aabb.size * 0.5;
+ Vector3 ofs = aabb.get_center();
float m = aabb.get_longest_axis_size();
if (m != 0) {
m = 1.0 / m;
@@ -103,10 +103,6 @@ void MeshEditor::_button_pressed(Node *p_button) {
}
}
-void MeshEditor::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_gui_input"), &MeshEditor::_gui_input);
-}
-
MeshEditor::MeshEditor() {
viewport = memnew(SubViewport);
Ref<World3D> world_3d;
diff --git a/editor/plugins/mesh_editor_plugin.h b/editor/plugins/mesh_editor_plugin.h
index 455fcb5fe9..1e88b70202 100644
--- a/editor/plugins/mesh_editor_plugin.h
+++ b/editor/plugins/mesh_editor_plugin.h
@@ -64,8 +64,7 @@ class MeshEditor : public SubViewportContainer {
protected:
void _notification(int p_what);
- void _gui_input(Ref<InputEvent> p_event);
- static void _bind_methods();
+ void gui_input(const Ref<InputEvent> &p_event) override;
public:
void edit(Ref<Mesh> p_mesh);
diff --git a/editor/plugins/mesh_instance_3d_editor_plugin.cpp b/editor/plugins/mesh_instance_3d_editor_plugin.cpp
index 9a2b222f21..574d3ef27e 100644
--- a/editor/plugins/mesh_instance_3d_editor_plugin.cpp
+++ b/editor/plugins/mesh_instance_3d_editor_plugin.cpp
@@ -202,7 +202,8 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
return;
}
- Vector<Ref<Shape3D>> shapes = mesh->convex_decompose();
+ Mesh::ConvexDecompositionSettings settings;
+ Vector<Ref<Shape3D>> shapes = mesh->convex_decompose(settings);
if (!shapes.size()) {
err_dialog->set_text(TTR("Couldn't create any collision shapes."));
diff --git a/editor/plugins/mesh_library_editor_plugin.cpp b/editor/plugins/mesh_library_editor_plugin.cpp
index b3f92c9d95..18e7480287 100644
--- a/editor/plugins/mesh_library_editor_plugin.cpp
+++ b/editor/plugins/mesh_library_editor_plugin.cpp
@@ -47,23 +47,25 @@ void MeshLibraryEditor::edit(const Ref<MeshLibrary> &p_mesh_library) {
}
}
-void MeshLibraryEditor::_menu_confirm() {
+void MeshLibraryEditor::_menu_remove_confirm() {
switch (option) {
case MENU_OPTION_REMOVE_ITEM: {
mesh_library->remove_item(to_erase);
} break;
- case MENU_OPTION_UPDATE_FROM_SCENE: {
- String existing = mesh_library->get_meta("_editor_source_scene");
- ERR_FAIL_COND(existing == "");
- _import_scene_cbk(existing);
-
- } break;
default: {
};
}
}
-void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library, bool p_merge) {
+void MeshLibraryEditor::_menu_update_confirm(bool p_apply_xforms) {
+ cd_update->hide();
+ apply_xforms = p_apply_xforms;
+ String existing = mesh_library->get_meta("_editor_source_scene");
+ ERR_FAIL_COND(existing == "");
+ _import_scene_cbk(existing);
+}
+
+void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library, bool p_merge, bool p_apply_xforms) {
if (!p_merge) {
p_library->clear();
}
@@ -108,6 +110,13 @@ void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library,
}
p_library->set_item_mesh(id, mesh);
+
+ if (p_apply_xforms) {
+ p_library->set_item_mesh_transform(id, mi->get_transform());
+ } else {
+ p_library->set_item_mesh_transform(id, Transform3D());
+ }
+
mesh_instances[id] = mi;
Vector<MeshLibrary::ShapeData> collisions;
@@ -197,15 +206,16 @@ void MeshLibraryEditor::_import_scene_cbk(const String &p_str) {
ERR_FAIL_COND_MSG(!scene, "Cannot create an instance from PackedScene '" + p_str + "'.");
- _import_scene(scene, mesh_library, option == MENU_OPTION_UPDATE_FROM_SCENE);
+ _import_scene(scene, mesh_library, option == MENU_OPTION_UPDATE_FROM_SCENE, apply_xforms);
memdelete(scene);
mesh_library->set_meta("_editor_source_scene", p_str);
+
menu->get_popup()->set_item_disabled(menu->get_popup()->get_item_index(MENU_OPTION_UPDATE_FROM_SCENE), false);
}
-Error MeshLibraryEditor::update_library_file(Node *p_base_scene, Ref<MeshLibrary> ml, bool p_merge) {
- _import_scene(p_base_scene, ml, p_merge);
+Error MeshLibraryEditor::update_library_file(Node *p_base_scene, Ref<MeshLibrary> ml, bool p_merge, bool p_apply_xforms) {
+ _import_scene(p_base_scene, ml, p_merge, p_apply_xforms);
return OK;
}
@@ -219,16 +229,21 @@ void MeshLibraryEditor::_menu_cbk(int p_option) {
String p = editor->get_inspector()->get_selected_path();
if (p.begins_with("/MeshLibrary/item") && p.get_slice_count("/") >= 3) {
to_erase = p.get_slice("/", 3).to_int();
- cd->set_text(vformat(TTR("Remove item %d?"), to_erase));
- cd->popup_centered(Size2(300, 60));
+ cd_remove->set_text(vformat(TTR("Remove item %d?"), to_erase));
+ cd_remove->popup_centered(Size2(300, 60));
}
} break;
case MENU_OPTION_IMPORT_FROM_SCENE: {
+ apply_xforms = false;
+ file->popup_file_dialog();
+ } break;
+ case MENU_OPTION_IMPORT_FROM_SCENE_APPLY_XFORMS: {
+ apply_xforms = true;
file->popup_file_dialog();
} break;
case MENU_OPTION_UPDATE_FROM_SCENE: {
- cd->set_text(vformat(TTR("Update from existing scene?:\n%s"), String(mesh_library->get_meta("_editor_source_scene"))));
- cd->popup_centered(Size2(500, 60));
+ cd_update->set_text(vformat(TTR("Update from existing scene?:\n%s"), String(mesh_library->get_meta("_editor_source_scene"))));
+ cd_update->popup_centered(Size2(500, 60));
} break;
}
}
@@ -258,16 +273,22 @@ MeshLibraryEditor::MeshLibraryEditor(EditorNode *p_editor) {
menu->get_popup()->add_item(TTR("Add Item"), MENU_OPTION_ADD_ITEM);
menu->get_popup()->add_item(TTR("Remove Selected Item"), MENU_OPTION_REMOVE_ITEM);
menu->get_popup()->add_separator();
- menu->get_popup()->add_item(TTR("Import from Scene"), MENU_OPTION_IMPORT_FROM_SCENE);
+ menu->get_popup()->add_item(TTR("Import from Scene (Ignore Transforms)"), MENU_OPTION_IMPORT_FROM_SCENE);
+ menu->get_popup()->add_item(TTR("Import from Scene (Apply Transforms)"), MENU_OPTION_IMPORT_FROM_SCENE_APPLY_XFORMS);
menu->get_popup()->add_item(TTR("Update from Scene"), MENU_OPTION_UPDATE_FROM_SCENE);
menu->get_popup()->set_item_disabled(menu->get_popup()->get_item_index(MENU_OPTION_UPDATE_FROM_SCENE), true);
menu->get_popup()->connect("id_pressed", callable_mp(this, &MeshLibraryEditor::_menu_cbk));
menu->hide();
editor = p_editor;
- cd = memnew(ConfirmationDialog);
- add_child(cd);
- cd->get_ok_button()->connect("pressed", callable_mp(this, &MeshLibraryEditor::_menu_confirm));
+ cd_remove = memnew(ConfirmationDialog);
+ add_child(cd_remove);
+ cd_remove->get_ok_button()->connect("pressed", callable_mp(this, &MeshLibraryEditor::_menu_remove_confirm));
+ cd_update = memnew(ConfirmationDialog);
+ add_child(cd_update);
+ cd_update->get_ok_button()->set_text("Apply without Transforms");
+ cd_update->get_ok_button()->connect("pressed", callable_mp(this, &MeshLibraryEditor::_menu_update_confirm), varray(false));
+ cd_update->add_button("Apply with Transforms")->connect("pressed", callable_mp(this, &MeshLibraryEditor::_menu_update_confirm), varray(true));
}
void MeshLibraryEditorPlugin::edit(Object *p_node) {
diff --git a/editor/plugins/mesh_library_editor_plugin.h b/editor/plugins/mesh_library_editor_plugin.h
index 6c33c8bb9e..9e225ffb9b 100644
--- a/editor/plugins/mesh_library_editor_plugin.h
+++ b/editor/plugins/mesh_library_editor_plugin.h
@@ -41,23 +41,27 @@ class MeshLibraryEditor : public Control {
EditorNode *editor;
MenuButton *menu;
- ConfirmationDialog *cd;
+ ConfirmationDialog *cd_remove;
+ ConfirmationDialog *cd_update;
EditorFileDialog *file;
+ bool apply_xforms;
int to_erase;
enum {
MENU_OPTION_ADD_ITEM,
MENU_OPTION_REMOVE_ITEM,
MENU_OPTION_UPDATE_FROM_SCENE,
- MENU_OPTION_IMPORT_FROM_SCENE
+ MENU_OPTION_IMPORT_FROM_SCENE,
+ MENU_OPTION_IMPORT_FROM_SCENE_APPLY_XFORMS
};
int option;
void _import_scene_cbk(const String &p_str);
void _menu_cbk(int p_option);
- void _menu_confirm();
+ void _menu_remove_confirm();
+ void _menu_update_confirm(bool p_apply_xforms);
- static void _import_scene(Node *p_scene, Ref<MeshLibrary> p_library, bool p_merge);
+ static void _import_scene(Node *p_scene, Ref<MeshLibrary> p_library, bool p_merge, bool p_apply_xforms);
protected:
static void _bind_methods();
@@ -66,7 +70,7 @@ public:
MenuButton *get_menu_button() const { return menu; }
void edit(const Ref<MeshLibrary> &p_mesh_library);
- static Error update_library_file(Node *p_base_scene, Ref<MeshLibrary> ml, bool p_merge = true);
+ static Error update_library_file(Node *p_base_scene, Ref<MeshLibrary> ml, bool p_merge = true, bool p_apply_xforms = false);
MeshLibraryEditor(EditorNode *p_editor);
};
diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp
index a42f94ed3d..4d2fc29fe0 100644
--- a/editor/plugins/node_3d_editor_gizmos.cpp
+++ b/editor/plugins/node_3d_editor_gizmos.cpp
@@ -34,6 +34,7 @@
#include "core/math/geometry_2d.h"
#include "core/math/geometry_3d.h"
#include "editor/plugins/node_3d_editor_plugin.h"
+#include "scene/3d/audio_listener_3d.h"
#include "scene/3d/audio_stream_player_3d.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/collision_polygon_3d.h"
@@ -42,18 +43,17 @@
#include "scene/3d/decal.h"
#include "scene/3d/gpu_particles_3d.h"
#include "scene/3d/gpu_particles_collision_3d.h"
+#include "scene/3d/joint_3d.h"
#include "scene/3d/light_3d.h"
#include "scene/3d/lightmap_gi.h"
#include "scene/3d/lightmap_probe.h"
-#include "scene/3d/listener_3d.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/navigation_region_3d.h"
#include "scene/3d/occluder_instance_3d.h"
-#include "scene/3d/physics_joint_3d.h"
#include "scene/3d/position_3d.h"
#include "scene/3d/ray_cast_3d.h"
#include "scene/3d/reflection_probe.h"
-#include "scene/3d/soft_body_3d.h"
+#include "scene/3d/soft_dynamic_body_3d.h"
#include "scene/3d/spring_arm_3d.h"
#include "scene/3d/sprite_3d.h"
#include "scene/3d/vehicle_body_3d.h"
@@ -66,9 +66,10 @@
#include "scene/resources/cylinder_shape_3d.h"
#include "scene/resources/height_map_shape_3d.h"
#include "scene/resources/primitive_meshes.h"
+#include "scene/resources/separation_ray_shape_3d.h"
#include "scene/resources/sphere_shape_3d.h"
#include "scene/resources/surface_tool.h"
-#include "scene/resources/world_margin_shape_3d.h"
+#include "scene/resources/world_boundary_shape_3d.h"
#define HANDLE_HALF_SIZE 9.5
@@ -105,9 +106,7 @@ void EditorNode3DGizmo::clear() {
}
void EditorNode3DGizmo::redraw() {
- if (get_script_instance() && get_script_instance()->has_method("_redraw")) {
- get_script_instance()->call("_redraw");
- } else {
+ if (!GDVIRTUAL_CALL(_redraw)) {
ERR_FAIL_COND(!gizmo_plugin);
gizmo_plugin->redraw(this);
}
@@ -118,8 +117,9 @@ void EditorNode3DGizmo::redraw() {
}
String EditorNode3DGizmo::get_handle_name(int p_id) const {
- if (get_script_instance() && get_script_instance()->has_method("_get_handle_name")) {
- return get_script_instance()->call("_get_handle_name", p_id);
+ String ret;
+ if (GDVIRTUAL_CALL(_get_handle_name, p_id, ret)) {
+ return ret;
}
ERR_FAIL_COND_V(!gizmo_plugin, "");
@@ -127,8 +127,9 @@ String EditorNode3DGizmo::get_handle_name(int p_id) const {
}
bool EditorNode3DGizmo::is_handle_highlighted(int p_id) const {
- if (get_script_instance() && get_script_instance()->has_method("_is_handle_highlighted")) {
- return get_script_instance()->call("_is_handle_highlighted", p_id);
+ bool success;
+ if (GDVIRTUAL_CALL(_is_handle_highlighted, p_id, success)) {
+ return success;
}
ERR_FAIL_COND_V(!gizmo_plugin, false);
@@ -136,8 +137,9 @@ bool EditorNode3DGizmo::is_handle_highlighted(int p_id) const {
}
Variant EditorNode3DGizmo::get_handle_value(int p_id) const {
- if (get_script_instance() && get_script_instance()->has_method("_get_handle_value")) {
- return get_script_instance()->call("_get_handle_value", p_id);
+ Variant value;
+ if (GDVIRTUAL_CALL(_get_handle_value, p_id, value)) {
+ return value;
}
ERR_FAIL_COND_V(!gizmo_plugin, Variant());
@@ -145,8 +147,7 @@ Variant EditorNode3DGizmo::get_handle_value(int p_id) const {
}
void EditorNode3DGizmo::set_handle(int p_id, Camera3D *p_camera, const Point2 &p_point) {
- if (get_script_instance() && get_script_instance()->has_method("_set_handle")) {
- get_script_instance()->call("_set_handle", p_id, p_camera, p_point);
+ if (GDVIRTUAL_CALL(_set_handle, p_id, p_camera, p_point)) {
return;
}
@@ -155,8 +156,7 @@ void EditorNode3DGizmo::set_handle(int p_id, Camera3D *p_camera, const Point2 &p
}
void EditorNode3DGizmo::commit_handle(int p_id, const Variant &p_restore, bool p_cancel) {
- if (get_script_instance() && get_script_instance()->has_method("_commit_handle")) {
- get_script_instance()->call("_commit_handle", p_id, p_restore, p_cancel);
+ if (GDVIRTUAL_CALL(_commit_handle, p_id, p_restore, p_cancel)) {
return;
}
@@ -165,8 +165,9 @@ void EditorNode3DGizmo::commit_handle(int p_id, const Variant &p_restore, bool p
}
int EditorNode3DGizmo::subgizmos_intersect_ray(Camera3D *p_camera, const Vector2 &p_point) const {
- if (get_script_instance() && get_script_instance()->has_method("_subgizmos_intersect_ray")) {
- return get_script_instance()->call("_subgizmos_intersect_ray", p_camera, p_point);
+ int id;
+ if (GDVIRTUAL_CALL(_subgizmos_intersect_ray, p_camera, p_point, id)) {
+ return id;
}
ERR_FAIL_COND_V(!gizmo_plugin, -1);
@@ -174,12 +175,14 @@ int EditorNode3DGizmo::subgizmos_intersect_ray(Camera3D *p_camera, const Vector2
}
Vector<int> EditorNode3DGizmo::subgizmos_intersect_frustum(const Camera3D *p_camera, const Vector<Plane> &p_frustum) const {
- if (get_script_instance() && get_script_instance()->has_method("_subgizmos_intersect_frustum")) {
- Array frustum;
- for (int i = 0; i < p_frustum.size(); i++) {
- frustum[i] = p_frustum[i];
- }
- return get_script_instance()->call("_subgizmos_intersect_frustum", p_camera, frustum);
+ TypedArray<Plane> frustum;
+ frustum.resize(p_frustum.size());
+ for (int i = 0; i < p_frustum.size(); i++) {
+ frustum[i] = p_frustum[i];
+ }
+ Vector<int> ret;
+ if (GDVIRTUAL_CALL(_subgizmos_intersect_frustum, p_camera, frustum, ret)) {
+ return ret;
}
ERR_FAIL_COND_V(!gizmo_plugin, Vector<int>());
@@ -187,8 +190,9 @@ Vector<int> EditorNode3DGizmo::subgizmos_intersect_frustum(const Camera3D *p_cam
}
Transform3D EditorNode3DGizmo::get_subgizmo_transform(int p_id) const {
- if (get_script_instance() && get_script_instance()->has_method("_get_subgizmo_transform")) {
- return get_script_instance()->call("_get_subgizmo_transform", p_id);
+ Transform3D ret;
+ if (GDVIRTUAL_CALL(_get_subgizmo_transform, p_id, ret)) {
+ return ret;
}
ERR_FAIL_COND_V(!gizmo_plugin, Transform3D());
@@ -196,8 +200,7 @@ Transform3D EditorNode3DGizmo::get_subgizmo_transform(int p_id) const {
}
void EditorNode3DGizmo::set_subgizmo_transform(int p_id, Transform3D p_transform) {
- if (get_script_instance() && get_script_instance()->has_method("_set_subgizmo_transform")) {
- get_script_instance()->call("_set_subgizmo_transform", p_id, p_transform);
+ if (GDVIRTUAL_CALL(_set_subgizmo_transform, p_id, p_transform)) {
return;
}
@@ -206,18 +209,13 @@ void EditorNode3DGizmo::set_subgizmo_transform(int p_id, Transform3D p_transform
}
void EditorNode3DGizmo::commit_subgizmos(const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) {
- if (get_script_instance() && get_script_instance()->has_method("_commit_subgizmos")) {
- Array ids;
- for (int i = 0; i < p_ids.size(); i++) {
- ids[i] = p_ids[i];
- }
-
- Array restore;
- for (int i = 0; i < p_restore.size(); i++) {
- restore[i] = p_restore[i];
- }
+ TypedArray<Transform3D> restore;
+ restore.resize(p_restore.size());
+ for (int i = 0; i < p_restore.size(); i++) {
+ restore[i] = p_restore[i];
+ }
- get_script_instance()->call("_commit_subgizmos", ids, restore, p_cancel);
+ if (GDVIRTUAL_CALL(_commit_subgizmos, p_ids, restore, p_cancel)) {
return;
}
@@ -834,29 +832,22 @@ void EditorNode3DGizmo::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_plugin"), &EditorNode3DGizmo::get_plugin);
ClassDB::bind_method(D_METHOD("clear"), &EditorNode3DGizmo::clear);
ClassDB::bind_method(D_METHOD("set_hidden", "hidden"), &EditorNode3DGizmo::set_hidden);
- ClassDB::bind_method(D_METHOD("is_subgizmo_selected"), &EditorNode3DGizmo::is_subgizmo_selected);
+ ClassDB::bind_method(D_METHOD("is_subgizmo_selected", "id"), &EditorNode3DGizmo::is_subgizmo_selected);
ClassDB::bind_method(D_METHOD("get_subgizmo_selection"), &EditorNode3DGizmo::get_subgizmo_selection);
- BIND_VMETHOD(MethodInfo("_redraw"));
- BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_handle_name", PropertyInfo(Variant::INT, "id")));
- BIND_VMETHOD(MethodInfo(Variant::BOOL, "_is_handle_highlighted", PropertyInfo(Variant::INT, "id")));
+ GDVIRTUAL_BIND(_redraw);
+ GDVIRTUAL_BIND(_get_handle_name, "id");
+ GDVIRTUAL_BIND(_is_handle_highlighted, "id");
- MethodInfo hvget(Variant::NIL, "_get_handle_value", PropertyInfo(Variant::INT, "id"));
- hvget.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- BIND_VMETHOD(hvget);
+ GDVIRTUAL_BIND(_get_handle_value, "id");
+ GDVIRTUAL_BIND(_set_handle, "id", "camera", "point");
+ GDVIRTUAL_BIND(_commit_handle, "id", "restore", "cancel");
- BIND_VMETHOD(MethodInfo("_set_handle", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera3D"), PropertyInfo(Variant::VECTOR2, "point")));
- MethodInfo cm = MethodInfo("_commit_handle", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::NIL, "restore"), PropertyInfo(Variant::BOOL, "cancel"));
- cm.default_arguments.push_back(false);
- BIND_VMETHOD(cm);
-
- BIND_VMETHOD(MethodInfo(Variant::INT, "_subgizmos_intersect_ray", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera3D"), PropertyInfo(Variant::VECTOR2, "point")));
- BIND_VMETHOD(MethodInfo(Variant::PACKED_INT32_ARRAY, "_subgizmos_intersect_frustum", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera3D"), PropertyInfo(Variant::ARRAY, "frustum")));
- BIND_VMETHOD(MethodInfo(Variant::TRANSFORM3D, "_get_subgizmo_transform", PropertyInfo(Variant::INT, "id")));
- BIND_VMETHOD(MethodInfo("_set_subgizmo_transform", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::TRANSFORM3D, "transform")));
- MethodInfo cs = MethodInfo("_commit_subgizmos", PropertyInfo(Variant::PACKED_INT32_ARRAY, "ids"), PropertyInfo(Variant::ARRAY, "restore"), PropertyInfo(Variant::BOOL, "cancel"));
- cs.default_arguments.push_back(false);
- BIND_VMETHOD(cs);
+ GDVIRTUAL_BIND(_subgizmos_intersect_ray, "camera", "point");
+ GDVIRTUAL_BIND(_subgizmos_intersect_frustum, "camera", "frustum");
+ GDVIRTUAL_BIND(_set_subgizmo_transform, "id", "transform");
+ GDVIRTUAL_BIND(_get_subgizmo_transform, "id");
+ GDVIRTUAL_BIND(_commit_subgizmos, "ids", "restores", "cancel");
}
EditorNode3DGizmo::EditorNode3DGizmo() {
@@ -1012,7 +1003,9 @@ String EditorNode3DGizmoPlugin::get_gizmo_name() const {
if (get_script_instance() && get_script_instance()->has_method("_get_gizmo_name")) {
return get_script_instance()->call("_get_gizmo_name");
}
- return TTR("Nameless gizmo");
+
+ WARN_PRINT_ONCE("A 3D editor gizmo has no name defined (it will appear as \"Unnamed Gizmo\" in the \"View > Gizmos\" menu). To resolve this, override the `_get_gizmo_name()` function to return a String in the script that extends EditorNode3DGizmoPlugin.");
+ return TTR("Unnamed Gizmo");
}
int EditorNode3DGizmoPlugin::get_priority() const {
@@ -1042,11 +1035,6 @@ Ref<EditorNode3DGizmo> EditorNode3DGizmoPlugin::get_gizmo(Node3D *p_spatial) {
}
void EditorNode3DGizmoPlugin::_bind_methods() {
-#define GIZMO_REF PropertyInfo(Variant::OBJECT, "gizmo", PROPERTY_HINT_RESOURCE_TYPE, "EditorNode3DGizmo")
-
- BIND_VMETHOD(MethodInfo(Variant::BOOL, "_has_gizmo", PropertyInfo(Variant::OBJECT, "spatial", PROPERTY_HINT_RESOURCE_TYPE, "Node3D")));
- BIND_VMETHOD(MethodInfo(GIZMO_REF, "_create_gizmo", PropertyInfo(Variant::OBJECT, "spatial", PROPERTY_HINT_RESOURCE_TYPE, "Node3D")));
-
ClassDB::bind_method(D_METHOD("create_material", "name", "color", "billboard", "on_top", "use_vertex_color"), &EditorNode3DGizmoPlugin::create_material, DEFVAL(false), DEFVAL(false), DEFVAL(false));
ClassDB::bind_method(D_METHOD("create_icon_material", "name", "texture", "on_top", "color"), &EditorNode3DGizmoPlugin::create_icon_material, DEFVAL(false), DEFVAL(Color(1, 1, 1, 1)));
ClassDB::bind_method(D_METHOD("create_handle_material", "name", "billboard", "texture"), &EditorNode3DGizmoPlugin::create_handle_material, DEFVAL(false), DEFVAL(Variant()));
@@ -1054,45 +1042,42 @@ void EditorNode3DGizmoPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_material", "name", "gizmo"), &EditorNode3DGizmoPlugin::get_material, DEFVAL(Ref<EditorNode3DGizmo>()));
- BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_gizmo_name"));
- BIND_VMETHOD(MethodInfo(Variant::INT, "_get_priority"));
- BIND_VMETHOD(MethodInfo(Variant::BOOL, "_can_be_hidden"));
- BIND_VMETHOD(MethodInfo(Variant::BOOL, "_is_selectable_when_hidden"));
-
- BIND_VMETHOD(MethodInfo("_redraw", GIZMO_REF));
- BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_handle_name", GIZMO_REF, PropertyInfo(Variant::INT, "id")));
- BIND_VMETHOD(MethodInfo(Variant::BOOL, "_is_handle_highlighted", GIZMO_REF, PropertyInfo(Variant::INT, "id")));
+ GDVIRTUAL_BIND(_has_gizmo, "for_node_3d");
+ GDVIRTUAL_BIND(_create_gizmo, "for_node_3d");
- MethodInfo hvget(Variant::NIL, "_get_handle_value", GIZMO_REF, PropertyInfo(Variant::INT, "id"));
- hvget.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- BIND_VMETHOD(hvget);
+ GDVIRTUAL_BIND(_get_gizmo_name);
+ GDVIRTUAL_BIND(_get_priority);
+ GDVIRTUAL_BIND(_can_be_hidden);
+ GDVIRTUAL_BIND(_is_selectable_when_hidden);
- BIND_VMETHOD(MethodInfo("_set_handle", GIZMO_REF, PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera3D"), PropertyInfo(Variant::VECTOR2, "point")));
- MethodInfo cm = MethodInfo("_commit_handle", GIZMO_REF, PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::NIL, "restore"), PropertyInfo(Variant::BOOL, "cancel"));
- cm.default_arguments.push_back(false);
- BIND_VMETHOD(cm);
+ GDVIRTUAL_BIND(_redraw, "gizmo");
+ GDVIRTUAL_BIND(_get_handle_name, "gizmo", "handle_id");
+ GDVIRTUAL_BIND(_is_handle_highlighted, "gizmo", "handle_id");
+ GDVIRTUAL_BIND(_get_handle_value, "gizmo", "handle_id");
- BIND_VMETHOD(MethodInfo(Variant::INT, "_subgizmos_intersect_ray", GIZMO_REF, PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera3D"), PropertyInfo(Variant::VECTOR2, "point")));
- BIND_VMETHOD(MethodInfo(Variant::PACKED_INT32_ARRAY, "_subgizmos_intersect_frustum", GIZMO_REF, PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera3D"), PropertyInfo(Variant::ARRAY, "frustum")));
- BIND_VMETHOD(MethodInfo(Variant::TRANSFORM3D, "_get_subgizmo_transform", GIZMO_REF, PropertyInfo(Variant::INT, "id")));
- BIND_VMETHOD(MethodInfo("_set_subgizmo_transform", GIZMO_REF, PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::TRANSFORM3D, "transform")));
- MethodInfo cs = MethodInfo("_commit_subgizmos", GIZMO_REF, PropertyInfo(Variant::PACKED_INT32_ARRAY, "ids"), PropertyInfo(Variant::ARRAY, "restore"), PropertyInfo(Variant::BOOL, "cancel"));
- cs.default_arguments.push_back(false);
- BIND_VMETHOD(cs);
+ GDVIRTUAL_BIND(_set_handle, "gizmo", "handle_id", "camera", "screen_pos");
+ GDVIRTUAL_BIND(_commit_handle, "gizmo", "handle_id", "restore", "cancel");
-#undef GIZMO_REF
+ GDVIRTUAL_BIND(_subgizmos_intersect_ray, "gizmo", "camera", "screen_pos");
+ GDVIRTUAL_BIND(_subgizmos_intersect_frustum, "gizmo", "camera", "frustum_planes");
+ GDVIRTUAL_BIND(_get_subgizmo_transform, "gizmo", "subgizmo_id");
+ GDVIRTUAL_BIND(_set_subgizmo_transform, "gizmo", "subgizmo_id", "transform");
+ GDVIRTUAL_BIND(_commit_subgizmos, "gizmo", "ids", "restores", "cancel");
+ ;
}
bool EditorNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
- if (get_script_instance() && get_script_instance()->has_method("_has_gizmo")) {
- return get_script_instance()->call("_has_gizmo", p_spatial);
+ bool success;
+ if (GDVIRTUAL_CALL(_has_gizmo, p_spatial, success)) {
+ return success;
}
return false;
}
Ref<EditorNode3DGizmo> EditorNode3DGizmoPlugin::create_gizmo(Node3D *p_spatial) {
- if (get_script_instance() && get_script_instance()->has_method("_create_gizmo")) {
- return get_script_instance()->call("_create_gizmo", p_spatial);
+ Ref<EditorNode3DGizmo> ret;
+ if (GDVIRTUAL_CALL(_create_gizmo, p_spatial, ret)) {
+ return ret;
}
Ref<EditorNode3DGizmo> ref;
@@ -1103,106 +1088,100 @@ Ref<EditorNode3DGizmo> EditorNode3DGizmoPlugin::create_gizmo(Node3D *p_spatial)
}
bool EditorNode3DGizmoPlugin::can_be_hidden() const {
- if (get_script_instance() && get_script_instance()->has_method("_can_be_hidden")) {
- return get_script_instance()->call("_can_be_hidden");
+ bool ret;
+ if (GDVIRTUAL_CALL(_can_be_hidden, ret)) {
+ return ret;
}
return true;
}
bool EditorNode3DGizmoPlugin::is_selectable_when_hidden() const {
- if (get_script_instance() && get_script_instance()->has_method("_is_selectable_when_hidden")) {
- return get_script_instance()->call("_is_selectable_when_hidden");
+ bool ret;
+ if (GDVIRTUAL_CALL(_is_selectable_when_hidden, ret)) {
+ return ret;
}
return false;
}
void EditorNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
- if (get_script_instance() && get_script_instance()->has_method("_redraw")) {
- Ref<EditorNode3DGizmo> ref(p_gizmo);
- get_script_instance()->call("_redraw", ref);
- }
+ GDVIRTUAL_CALL(_redraw, p_gizmo);
}
bool EditorNode3DGizmoPlugin::is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_id) const {
- if (get_script_instance() && get_script_instance()->has_method("_is_handle_highlighted")) {
- return get_script_instance()->call("_is_handle_highlighted", p_gizmo, p_id);
+ bool ret;
+ if (GDVIRTUAL_CALL(_is_handle_highlighted, Ref<EditorNode3DGizmo>(p_gizmo), p_id, ret)) {
+ return ret;
}
return false;
}
String EditorNode3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const {
- if (get_script_instance() && get_script_instance()->has_method("_get_handle_name")) {
- return get_script_instance()->call("_get_handle_name", p_gizmo, p_id);
+ String ret;
+ if (GDVIRTUAL_CALL(_get_handle_name, Ref<EditorNode3DGizmo>(p_gizmo), p_id, ret)) {
+ return ret;
}
return "";
}
Variant EditorNode3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const {
- if (get_script_instance() && get_script_instance()->has_method("_get_handle_value")) {
- return get_script_instance()->call("_get_handle_value", p_gizmo, p_id);
+ Variant ret;
+ if (GDVIRTUAL_CALL(_get_handle_value, Ref<EditorNode3DGizmo>(p_gizmo), p_id, ret)) {
+ return ret;
}
return Variant();
}
void EditorNode3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) {
- if (get_script_instance() && get_script_instance()->has_method("_set_handle")) {
- get_script_instance()->call("_set_handle", p_gizmo, p_id, p_camera, p_point);
- }
+ GDVIRTUAL_CALL(_set_handle, Ref<EditorNode3DGizmo>(p_gizmo), p_id, p_camera, p_point);
}
void EditorNode3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) {
- if (get_script_instance() && get_script_instance()->has_method("_commit_handle")) {
- get_script_instance()->call("_commit_handle", p_gizmo, p_id, p_restore, p_cancel);
- }
+ GDVIRTUAL_CALL(_commit_handle, Ref<EditorNode3DGizmo>(p_gizmo), p_id, p_restore, p_cancel);
}
int EditorNode3DGizmoPlugin::subgizmos_intersect_ray(const EditorNode3DGizmo *p_gizmo, Camera3D *p_camera, const Vector2 &p_point) const {
- if (get_script_instance() && get_script_instance()->has_method("_subgizmos_intersect_ray")) {
- return get_script_instance()->call("_subgizmos_intersect_ray", p_camera, p_point);
+ int ret;
+ if (GDVIRTUAL_CALL(_subgizmos_intersect_ray, Ref<EditorNode3DGizmo>(p_gizmo), p_camera, p_point, ret)) {
+ return ret;
}
return -1;
}
Vector<int> EditorNode3DGizmoPlugin::subgizmos_intersect_frustum(const EditorNode3DGizmo *p_gizmo, const Camera3D *p_camera, const Vector<Plane> &p_frustum) const {
- if (get_script_instance() && get_script_instance()->has_method("_subgizmos_intersect_frustum")) {
- Array frustum;
- for (int i = 0; i < p_frustum.size(); i++) {
- frustum[i] = p_frustum[i];
- }
- return get_script_instance()->call("_subgizmos_intersect_frustum", p_camera, frustum);
+ TypedArray<Transform3D> frustum;
+ frustum.resize(p_frustum.size());
+ for (int i = 0; i < p_frustum.size(); i++) {
+ frustum[i] = p_frustum[i];
+ }
+ Vector<int> ret;
+ if (GDVIRTUAL_CALL(_subgizmos_intersect_frustum, Ref<EditorNode3DGizmo>(p_gizmo), p_camera, frustum, ret)) {
+ return ret;
}
return Vector<int>();
}
Transform3D EditorNode3DGizmoPlugin::get_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id) const {
- if (get_script_instance() && get_script_instance()->has_method("_get_subgizmo_transform")) {
- return get_script_instance()->call("_get_subgizmo_transform", p_id);
+ Transform3D ret;
+ if (GDVIRTUAL_CALL(_get_subgizmo_transform, Ref<EditorNode3DGizmo>(p_gizmo), p_id, ret)) {
+ return ret;
}
return Transform3D();
}
void EditorNode3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id, Transform3D p_transform) {
- if (get_script_instance() && get_script_instance()->has_method("_set_subgizmo_transform")) {
- get_script_instance()->call("_set_subgizmo_transform", p_id, p_transform);
- }
+ GDVIRTUAL_CALL(_set_subgizmo_transform, Ref<EditorNode3DGizmo>(p_gizmo), p_id, p_transform);
}
void EditorNode3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) {
- if (get_script_instance() && get_script_instance()->has_method("_commit_subgizmos")) {
- Array ids;
- for (int i = 0; i < p_ids.size(); i++) {
- ids[i] = p_ids[i];
- }
-
- Array restore;
- for (int i = 0; i < p_restore.size(); i++) {
- restore[i] = p_restore[i];
- }
-
- get_script_instance()->call("_commit_subgizmos", ids, restore, p_cancel);
+ TypedArray<Transform3D> restore;
+ restore.resize(p_restore.size());
+ for (int i = 0; i < p_restore.size(); i++) {
+ restore[i] = p_restore[i];
}
+
+ GDVIRTUAL_CALL(_commit_subgizmos, Ref<EditorNode3DGizmo>(p_gizmo), p_ids, restore, p_cancel);
}
void EditorNode3DGizmoPlugin::set_state(int p_state) {
@@ -1504,8 +1483,6 @@ void Light3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
}
}
-//////
-
//// player gizmo
AudioStreamPlayer3DGizmoPlugin::AudioStreamPlayer3DGizmoPlugin() {
Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/stream_player_3d", Color(0.4, 0.8, 1));
@@ -1644,6 +1621,29 @@ void AudioStreamPlayer3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
//////
+AudioListener3DGizmoPlugin::AudioListener3DGizmoPlugin() {
+ create_icon_material("audio_listener_3d_icon", Node3DEditor::get_singleton()->get_theme_icon("GizmoAudioListener3D", "EditorIcons"));
+}
+
+bool AudioListener3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
+ return Object::cast_to<AudioListener3D>(p_spatial) != nullptr;
+}
+
+String AudioListener3DGizmoPlugin::get_gizmo_name() const {
+ return "AudioListener3D";
+}
+
+int AudioListener3DGizmoPlugin::get_priority() const {
+ return -1;
+}
+
+void AudioListener3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
+ const Ref<Material> icon = get_material("audio_listener_3d_icon", p_gizmo);
+ p_gizmo->add_unscaled_billboard(icon, 0.05);
+}
+
+//////
+
Camera3DGizmoPlugin::Camera3DGizmoPlugin() {
Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/camera", Color(0.8, 0.4, 0.8));
@@ -1840,47 +1840,6 @@ void Camera3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->add_lines(lines, material);
p_gizmo->add_handles(handles, get_material("handles"));
-
- ClippedCamera3D *clipcam = Object::cast_to<ClippedCamera3D>(camera);
- if (clipcam) {
- Node3D *parent = Object::cast_to<Node3D>(camera->get_parent());
- if (!parent) {
- return;
- }
- Vector3 cam_normal = -camera->get_global_transform().basis.get_axis(Vector3::AXIS_Z).normalized();
- Vector3 cam_x = camera->get_global_transform().basis.get_axis(Vector3::AXIS_X).normalized();
- Vector3 cam_y = camera->get_global_transform().basis.get_axis(Vector3::AXIS_Y).normalized();
- Vector3 cam_pos = camera->get_global_transform().origin;
- Vector3 parent_pos = parent->get_global_transform().origin;
-
- Plane parent_plane(parent_pos, cam_normal);
- Vector3 ray_from = parent_plane.project(cam_pos);
-
- lines.clear();
- lines.push_back(ray_from + cam_x * 0.5 + cam_y * 0.5);
- lines.push_back(ray_from + cam_x * 0.5 + cam_y * -0.5);
-
- lines.push_back(ray_from + cam_x * 0.5 + cam_y * -0.5);
- lines.push_back(ray_from + cam_x * -0.5 + cam_y * -0.5);
-
- lines.push_back(ray_from + cam_x * -0.5 + cam_y * -0.5);
- lines.push_back(ray_from + cam_x * -0.5 + cam_y * 0.5);
-
- lines.push_back(ray_from + cam_x * -0.5 + cam_y * 0.5);
- lines.push_back(ray_from + cam_x * 0.5 + cam_y * 0.5);
-
- if (parent_plane.distance_to(cam_pos) < 0) {
- lines.push_back(ray_from);
- lines.push_back(cam_pos);
- }
-
- Transform3D local = camera->get_global_transform().affine_inverse();
- for (int i = 0; i < lines.size(); i++) {
- lines.write[i] = local.xform(lines[i]);
- }
-
- p_gizmo->add_lines(lines, material);
- }
}
//////
@@ -1889,7 +1848,7 @@ MeshInstance3DGizmoPlugin::MeshInstance3DGizmoPlugin() {
}
bool MeshInstance3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
- return Object::cast_to<MeshInstance3D>(p_spatial) != nullptr && Object::cast_to<SoftBody3D>(p_spatial) == nullptr;
+ return Object::cast_to<MeshInstance3D>(p_spatial) != nullptr && Object::cast_to<SoftDynamicBody3D>(p_spatial) == nullptr;
}
String MeshInstance3DGizmoPlugin::get_gizmo_name() const {
@@ -2070,171 +2029,6 @@ void Position3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->add_collision_segments(cursor_points);
}
-/////
-
-Skeleton3DGizmoPlugin::Skeleton3DGizmoPlugin() {
- Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/skeleton", Color(1, 0.8, 0.4));
- create_material("skeleton_material", gizmo_color);
-}
-
-bool Skeleton3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
- return Object::cast_to<Skeleton3D>(p_spatial) != nullptr;
-}
-
-String Skeleton3DGizmoPlugin::get_gizmo_name() const {
- return "Skeleton3D";
-}
-
-int Skeleton3DGizmoPlugin::get_priority() const {
- return -1;
-}
-
-void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
- Skeleton3D *skel = Object::cast_to<Skeleton3D>(p_gizmo->get_spatial_node());
-
- p_gizmo->clear();
-
- Ref<Material> material = get_material("skeleton_material", p_gizmo);
-
- Ref<SurfaceTool> surface_tool(memnew(SurfaceTool));
-
- surface_tool->begin(Mesh::PRIMITIVE_LINES);
- surface_tool->set_material(material);
- LocalVector<Transform3D> grests;
- grests.resize(skel->get_bone_count());
-
- LocalVector<int> bones;
- LocalVector<float> weights;
- bones.resize(4);
- weights.resize(4);
-
- for (int i = 0; i < 4; i++) {
- bones[i] = 0;
- weights[i] = 0;
- }
-
- weights[0] = 1;
-
- AABB aabb;
-
- Color bonecolor = Color(1.0, 0.4, 0.4, 0.3);
- Color rootcolor = Color(0.4, 1.0, 0.4, 0.1);
-
- //LocalVector<int> bones_to_process = skel->get_parentless_bones();
- LocalVector<int> bones_to_process;
- bones_to_process = skel->get_parentless_bones();
-
- while (bones_to_process.size() > 0) {
- int current_bone_idx = bones_to_process[0];
- bones_to_process.erase(current_bone_idx);
-
- LocalVector<int> child_bones_vector;
- child_bones_vector = skel->get_bone_children(current_bone_idx);
- int child_bones_size = child_bones_vector.size();
-
- // You have children but no parent, then you must be a root/parentless bone.
- if (child_bones_size >= 0 && skel->get_bone_parent(current_bone_idx) <= 0) {
- grests[current_bone_idx] = skel->global_pose_to_local_pose(current_bone_idx, skel->get_bone_global_pose(current_bone_idx));
- }
-
- for (int i = 0; i < child_bones_size; i++) {
- int child_bone_idx = child_bones_vector[i];
-
- grests[child_bone_idx] = skel->global_pose_to_local_pose(child_bone_idx, skel->get_bone_global_pose(child_bone_idx));
- Vector3 v0 = grests[current_bone_idx].origin;
- Vector3 v1 = grests[child_bone_idx].origin;
- Vector3 d = skel->get_bone_rest(child_bone_idx).origin.normalized();
- real_t dist = skel->get_bone_rest(child_bone_idx).origin.length();
-
- // Find closest axis.
- int closest = -1;
- real_t closest_d = 0.0;
- for (int j = 0; j < 3; j++) {
- real_t dp = Math::abs(grests[current_bone_idx].basis[j].normalized().dot(d));
- if (j == 0 || dp > closest_d) {
- closest = j;
- }
- }
-
- // Find closest other.
- Vector3 first;
- Vector3 points[4];
- int point_idx = 0;
- for (int j = 0; j < 3; j++) {
- bones[0] = current_bone_idx;
- surface_tool->set_bones(bones);
- surface_tool->set_weights(weights);
- surface_tool->set_color(rootcolor);
- surface_tool->add_vertex(v0 - grests[current_bone_idx].basis[j].normalized() * dist * 0.05);
- surface_tool->set_bones(bones);
- surface_tool->set_weights(weights);
- surface_tool->set_color(rootcolor);
- surface_tool->add_vertex(v0 + grests[current_bone_idx].basis[j].normalized() * dist * 0.05);
-
- if (j == closest) {
- continue;
- }
-
- Vector3 axis;
- if (first == Vector3()) {
- axis = d.cross(d.cross(grests[current_bone_idx].basis[j])).normalized();
- first = axis;
- } else {
- axis = d.cross(first).normalized();
- }
-
- for (int k = 0; k < 2; k++) {
- if (k == 1) {
- axis = -axis;
- }
- Vector3 point = v0 + d * dist * 0.2;
- point += axis * dist * 0.1;
-
- bones[0] = current_bone_idx;
- surface_tool->set_bones(bones);
- surface_tool->set_weights(weights);
- surface_tool->set_color(bonecolor);
- surface_tool->add_vertex(v0);
- surface_tool->set_bones(bones);
- surface_tool->set_weights(weights);
- surface_tool->set_color(bonecolor);
- surface_tool->add_vertex(point);
-
- bones[0] = current_bone_idx;
- surface_tool->set_bones(bones);
- surface_tool->set_weights(weights);
- surface_tool->set_color(bonecolor);
- surface_tool->add_vertex(point);
- bones[0] = child_bone_idx;
- surface_tool->set_bones(bones);
- surface_tool->set_weights(weights);
- surface_tool->set_color(bonecolor);
- surface_tool->add_vertex(v1);
- points[point_idx++] = point;
- }
- }
- SWAP(points[1], points[2]);
- for (int j = 0; j < 4; j++) {
- bones[0] = current_bone_idx;
- surface_tool->set_bones(bones);
- surface_tool->set_weights(weights);
- surface_tool->set_color(bonecolor);
- surface_tool->add_vertex(points[j]);
- surface_tool->set_bones(bones);
- surface_tool->set_weights(weights);
- surface_tool->set_color(bonecolor);
- surface_tool->add_vertex(points[(j + 1) % 4]);
- }
-
- // Add the bone's children to the list of bones to be processed.
- bones_to_process.push_back(child_bones_vector[i]);
- }
- }
-
- Ref<ArrayMesh> m = surface_tool->commit();
- p_gizmo->add_mesh(m, Ref<Material>(), Transform3D(), skel->register_skin(Ref<Skin>()));
-}
-
////
PhysicalBone3DGizmoPlugin::PhysicalBone3DGizmoPlugin() {
@@ -2514,30 +2308,30 @@ void VehicleWheel3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
///////////
-SoftBody3DGizmoPlugin::SoftBody3DGizmoPlugin() {
+SoftDynamicBody3DGizmoPlugin::SoftDynamicBody3DGizmoPlugin() {
Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1));
create_material("shape_material", gizmo_color);
create_handle_material("handles");
}
-bool SoftBody3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
- return Object::cast_to<SoftBody3D>(p_spatial) != nullptr;
+bool SoftDynamicBody3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
+ return Object::cast_to<SoftDynamicBody3D>(p_spatial) != nullptr;
}
-String SoftBody3DGizmoPlugin::get_gizmo_name() const {
- return "SoftBody3D";
+String SoftDynamicBody3DGizmoPlugin::get_gizmo_name() const {
+ return "SoftDynamicBody3D";
}
-int SoftBody3DGizmoPlugin::get_priority() const {
+int SoftDynamicBody3DGizmoPlugin::get_priority() const {
return -1;
}
-bool SoftBody3DGizmoPlugin::is_selectable_when_hidden() const {
+bool SoftDynamicBody3DGizmoPlugin::is_selectable_when_hidden() const {
return true;
}
-void SoftBody3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
- SoftBody3D *soft_body = Object::cast_to<SoftBody3D>(p_gizmo->get_spatial_node());
+void SoftDynamicBody3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
+ SoftDynamicBody3D *soft_body = Object::cast_to<SoftDynamicBody3D>(p_gizmo->get_spatial_node());
p_gizmo->clear();
@@ -2573,22 +2367,22 @@ void SoftBody3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->add_collision_triangles(tm);
}
-String SoftBody3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const {
- return "SoftBody3D pin point";
+String SoftDynamicBody3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const {
+ return "SoftDynamicBody3D pin point";
}
-Variant SoftBody3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const {
- SoftBody3D *soft_body = Object::cast_to<SoftBody3D>(p_gizmo->get_spatial_node());
+Variant SoftDynamicBody3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const {
+ SoftDynamicBody3D *soft_body = Object::cast_to<SoftDynamicBody3D>(p_gizmo->get_spatial_node());
return Variant(soft_body->is_point_pinned(p_id));
}
-void SoftBody3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) {
- SoftBody3D *soft_body = Object::cast_to<SoftBody3D>(p_gizmo->get_spatial_node());
+void SoftDynamicBody3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) {
+ SoftDynamicBody3D *soft_body = Object::cast_to<SoftDynamicBody3D>(p_gizmo->get_spatial_node());
soft_body->pin_point_toggle(p_id);
}
-bool SoftBody3DGizmoPlugin::is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_id) const {
- SoftBody3D *soft_body = Object::cast_to<SoftBody3D>(p_gizmo->get_spatial_node());
+bool SoftDynamicBody3DGizmoPlugin::is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_id) const {
+ SoftDynamicBody3D *soft_body = Object::cast_to<SoftDynamicBody3D>(p_gizmo->get_spatial_node());
return soft_body->is_point_pinned(p_id);
}
@@ -2654,7 +2448,7 @@ void VisibleOnScreenNotifier3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p
Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) };
- Vector3 ofs = aabb.position + aabb.size * 0.5;
+ Vector3 ofs = aabb.get_center();
Vector3 axis;
axis[p_id] = 1.0;
@@ -2730,7 +2524,7 @@ void VisibleOnScreenNotifier3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
handles.push_back(ax);
}
- Vector3 center = aabb.position + aabb.size * 0.5;
+ Vector3 center = aabb.get_center();
for (int i = 0; i < 3; i++) {
Vector3 ax;
ax[i] = 1.0;
@@ -2746,7 +2540,7 @@ void VisibleOnScreenNotifier3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
if (p_gizmo->is_selected()) {
Ref<Material> solid_material = get_material("visibility_notifier_solid_material", p_gizmo);
- p_gizmo->add_solid_box(solid_material, aabb.get_size(), aabb.get_position() + aabb.get_size() / 2.0);
+ p_gizmo->add_solid_box(solid_material, aabb.get_size(), aabb.get_center());
}
p_gizmo->add_handles(handles, get_material("handles"));
@@ -2845,7 +2639,7 @@ void GPUParticles3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int
Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) };
- Vector3 ofs = aabb.position + aabb.size * 0.5;
+ Vector3 ofs = aabb.get_center();
Vector3 axis;
axis[p_id] = 1.0;
@@ -2921,7 +2715,7 @@ void GPUParticles3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
handles.push_back(ax);
}
- Vector3 center = aabb.position + aabb.size * 0.5;
+ Vector3 center = aabb.get_center();
for (int i = 0; i < 3; i++) {
Vector3 ax;
ax[i] = 1.0;
@@ -2937,7 +2731,7 @@ void GPUParticles3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
if (p_gizmo->is_selected()) {
Ref<Material> solid_material = get_material("particles_solid_material", p_gizmo);
- p_gizmo->add_solid_box(solid_material, aabb.get_size(), aabb.get_position() + aabb.get_size() / 2.0);
+ p_gizmo->add_solid_box(solid_material, aabb.get_size(), aabb.get_center());
}
p_gizmo->add_handles(handles, get_material("handles"));
@@ -4091,6 +3885,10 @@ String CollisionShape3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_g
return p_id == 0 ? "Radius" : "Height";
}
+ if (Object::cast_to<SeparationRayShape3D>(*s)) {
+ return "Length";
+ }
+
return "";
}
@@ -4122,6 +3920,11 @@ Variant CollisionShape3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p
return p_id == 0 ? cs2->get_radius() : cs2->get_height();
}
+ if (Object::cast_to<SeparationRayShape3D>(*s)) {
+ Ref<SeparationRayShape3D> cs2 = s;
+ return cs2->get_length();
+ }
+
return Variant();
}
@@ -4157,6 +3960,22 @@ void CollisionShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, i
ss->set_radius(d);
}
+ if (Object::cast_to<SeparationRayShape3D>(*s)) {
+ Ref<SeparationRayShape3D> rs = s;
+ Vector3 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::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
+ }
+
+ if (d < 0.001) {
+ d = 0.001;
+ }
+
+ rs->set_length(d);
+ }
+
if (Object::cast_to<BoxShape3D>(*s)) {
Vector3 axis;
axis[p_id] = 1.0;
@@ -4311,6 +4130,20 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo
ur->commit_action();
}
+
+ if (Object::cast_to<SeparationRayShape3D>(*s)) {
+ Ref<SeparationRayShape3D> ss = s;
+ if (p_cancel) {
+ ss->set_length(p_restore);
+ return;
+ }
+
+ UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Separation Ray Shape Length"));
+ ur->add_do_method(ss.ptr(), "set_length", ss->get_length());
+ ur->add_undo_method(ss.ptr(), "set_length", p_restore);
+ ur->commit_action();
+ }
}
void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
@@ -4523,9 +4356,9 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->add_handles(handles, handles_material);
}
- if (Object::cast_to<WorldMarginShape3D>(*s)) {
- Ref<WorldMarginShape3D> ps = s;
- Plane p = ps->get_plane();
+ if (Object::cast_to<WorldBoundaryShape3D>(*s)) {
+ Ref<WorldBoundaryShape3D> wbs = s;
+ const Plane &p = wbs->get_plane();
Vector<Vector3> points;
Vector3 n1 = p.get_any_perpendicular_normal();
@@ -4581,6 +4414,19 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->add_collision_segments(cs2->get_debug_mesh_lines());
}
+ if (Object::cast_to<SeparationRayShape3D>(*s)) {
+ Ref<SeparationRayShape3D> rs = s;
+
+ Vector<Vector3> points;
+ points.push_back(Vector3());
+ points.push_back(Vector3(0, 0, rs->get_length()));
+ p_gizmo->add_lines(points, material);
+ p_gizmo->add_collision_segments(points);
+ Vector<Vector3> handles;
+ handles.push_back(Vector3(0, 0, rs->get_length()));
+ p_gizmo->add_handles(handles, handles_material);
+ }
+
if (Object::cast_to<HeightMapShape3D>(*s)) {
Ref<HeightMapShape3D> hms = s;
@@ -4723,10 +4569,10 @@ void NavigationRegion3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
}
Vector<Vector3> lines;
- for (Map<_EdgeKey, bool>::Element *E = edge_map.front(); E; E = E->next()) {
- if (E->get()) {
- lines.push_back(E->key().from);
- lines.push_back(E->key().to);
+ for (const KeyValue<_EdgeKey, bool> &E : edge_map) {
+ if (E.value) {
+ lines.push_back(E.key.from);
+ lines.push_back(E.key.to);
}
}
diff --git a/editor/plugins/node_3d_editor_gizmos.h b/editor/plugins/node_3d_editor_gizmos.h
index 2cc0951557..d1aca4d92e 100644
--- a/editor/plugins/node_3d_editor_gizmos.h
+++ b/editor/plugins/node_3d_editor_gizmos.h
@@ -33,10 +33,10 @@
#include "core/templates/local_vector.h"
#include "core/templates/ordered_hash_map.h"
+#include "scene/3d/camera_3d.h"
#include "scene/3d/node_3d.h"
#include "scene/3d/skeleton_3d.h"
-class Camera3D;
class Timer;
class EditorNode3DGizmoPlugin;
@@ -79,6 +79,19 @@ protected:
EditorNode3DGizmoPlugin *gizmo_plugin;
+ GDVIRTUAL0(_redraw)
+ GDVIRTUAL1RC(String, _get_handle_name, int)
+ GDVIRTUAL1RC(bool, _is_handle_highlighted, int)
+
+ GDVIRTUAL1RC(Variant, _get_handle_value, int)
+ GDVIRTUAL3(_set_handle, int, const Camera3D *, Vector2)
+ GDVIRTUAL3(_commit_handle, int, Variant, bool)
+
+ GDVIRTUAL2RC(int, _subgizmos_intersect_ray, const Camera3D *, Vector2)
+ GDVIRTUAL2RC(Vector<int>, _subgizmos_intersect_frustum, const Camera3D *, TypedArray<Plane>)
+ GDVIRTUAL1RC(Transform3D, _get_subgizmo_transform, int)
+ GDVIRTUAL2(_set_subgizmo_transform, int, Transform3D)
+ GDVIRTUAL3(_commit_subgizmos, Vector<int>, TypedArray<Transform3D>, bool)
public:
void add_lines(const Vector<Vector3> &p_lines, const Ref<Material> &p_material, bool p_billboard = false, const Color &p_modulate = Color(1, 1, 1));
void add_vertices(const Vector<Vector3> &p_vertices, const Ref<Material> &p_material, Mesh::PrimitiveType p_primitive_type, bool p_billboard = false, const Color &p_modulate = Color(1, 1, 1));
@@ -145,6 +158,28 @@ protected:
virtual bool has_gizmo(Node3D *p_spatial);
virtual Ref<EditorNode3DGizmo> create_gizmo(Node3D *p_spatial);
+ GDVIRTUAL1RC(bool, _has_gizmo, Node3D *)
+ GDVIRTUAL1RC(Ref<EditorNode3DGizmo>, _create_gizmo, Node3D *)
+
+ GDVIRTUAL0RC(String, _get_gizmo_name)
+ GDVIRTUAL0RC(int, _get_priority)
+ GDVIRTUAL0RC(bool, _can_be_hidden)
+ GDVIRTUAL0RC(bool, _is_selectable_when_hidden)
+
+ GDVIRTUAL1(_redraw, Ref<EditorNode3DGizmo>)
+ GDVIRTUAL2RC(String, _get_handle_name, Ref<EditorNode3DGizmo>, int)
+ GDVIRTUAL2RC(bool, _is_handle_highlighted, Ref<EditorNode3DGizmo>, int)
+ GDVIRTUAL2RC(Variant, _get_handle_value, Ref<EditorNode3DGizmo>, int)
+
+ GDVIRTUAL4(_set_handle, Ref<EditorNode3DGizmo>, int, const Camera3D *, Vector2)
+ GDVIRTUAL4(_commit_handle, Ref<EditorNode3DGizmo>, int, Variant, bool)
+
+ GDVIRTUAL3RC(int, _subgizmos_intersect_ray, Ref<EditorNode3DGizmo>, const Camera3D *, Vector2)
+ GDVIRTUAL3RC(Vector<int>, _subgizmos_intersect_frustum, Ref<EditorNode3DGizmo>, const Camera3D *, TypedArray<Plane>)
+ GDVIRTUAL2RC(Transform3D, _get_subgizmo_transform, Ref<EditorNode3DGizmo>, int)
+ GDVIRTUAL3(_set_subgizmo_transform, Ref<EditorNode3DGizmo>, int, Transform3D)
+ GDVIRTUAL4(_commit_subgizmos, Ref<EditorNode3DGizmo>, Vector<int>, TypedArray<Transform3D>, bool)
+
public:
void create_material(const String &p_name, const Color &p_color, bool p_billboard = false, bool p_on_top = false, bool p_use_vertex_color = false);
void create_icon_material(const String &p_name, const Ref<Texture2D> &p_texture, bool p_on_top = false, const Color &p_albedo = Color(1, 1, 1, 1));
@@ -214,6 +249,19 @@ public:
AudioStreamPlayer3DGizmoPlugin();
};
+class AudioListener3DGizmoPlugin : public EditorNode3DGizmoPlugin {
+ GDCLASS(AudioListener3DGizmoPlugin, EditorNode3DGizmoPlugin);
+
+public:
+ bool has_gizmo(Node3D *p_spatial) override;
+ String get_gizmo_name() const override;
+ int get_priority() const override;
+
+ void redraw(EditorNode3DGizmo *p_gizmo) override;
+
+ AudioListener3DGizmoPlugin();
+};
+
class Camera3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(Camera3DGizmoPlugin, EditorNode3DGizmoPlugin);
@@ -284,18 +332,6 @@ public:
Position3DGizmoPlugin();
};
-class Skeleton3DGizmoPlugin : public EditorNode3DGizmoPlugin {
- GDCLASS(Skeleton3DGizmoPlugin, EditorNode3DGizmoPlugin);
-
-public:
- bool has_gizmo(Node3D *p_spatial) override;
- String get_gizmo_name() const override;
- int get_priority() const override;
- void redraw(EditorNode3DGizmo *p_gizmo) override;
-
- Skeleton3DGizmoPlugin();
-};
-
class PhysicalBone3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(PhysicalBone3DGizmoPlugin, EditorNode3DGizmoPlugin);
@@ -344,8 +380,8 @@ public:
VehicleWheel3DGizmoPlugin();
};
-class SoftBody3DGizmoPlugin : public EditorNode3DGizmoPlugin {
- GDCLASS(SoftBody3DGizmoPlugin, EditorNode3DGizmoPlugin);
+class SoftDynamicBody3DGizmoPlugin : public EditorNode3DGizmoPlugin {
+ GDCLASS(SoftDynamicBody3DGizmoPlugin, EditorNode3DGizmoPlugin);
public:
bool has_gizmo(Node3D *p_spatial) override;
@@ -359,7 +395,7 @@ public:
void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) override;
bool is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_id) const override;
- SoftBody3DGizmoPlugin();
+ SoftDynamicBody3DGizmoPlugin();
};
class VisibleOnScreenNotifier3DGizmoPlugin : public EditorNode3DGizmoPlugin {
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 4300a56ef0..ea6ef8ab84 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -128,7 +128,7 @@ void ViewportRotationControl::_draw_axis(const Axis2D &p_axis) {
const Color axis_color = axis_colors[direction];
const double alpha = focused ? 1.0 : ((p_axis.z_axis + 1.0) / 2.0) * 0.5 + 0.5;
- const Color c = focused ? Color(0.9, 0.9, 0.9) : Color(axis_color.r, axis_color.g, axis_color.b, alpha);
+ const Color c = focused ? Color(0.9, 0.9, 0.9) : Color(axis_color, alpha);
if (positive) {
// Draw axis lines for the positive axes.
@@ -181,7 +181,7 @@ void ViewportRotationControl::_get_sorted_axis(Vector<Axis2D> &r_axis) {
r_axis.sort_custom<Axis2DCompare>();
}
-void ViewportRotationControl::_gui_input(Ref<InputEvent> p_event) {
+void ViewportRotationControl::gui_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
const Ref<InputEventMouseButton> mb = p_event;
@@ -252,10 +252,6 @@ void ViewportRotationControl::set_viewport(Node3DEditorViewport *p_viewport) {
viewport = p_viewport;
}
-void ViewportRotationControl::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_gui_input"), &ViewportRotationControl::_gui_input);
-}
-
void Node3DEditorViewport::_update_camera(real_t p_interp_delta) {
bool is_orthogonal = camera->get_projection() == Camera3D::PROJECTION_ORTHOGONAL;
@@ -269,15 +265,13 @@ void Node3DEditorViewport::_update_camera(real_t p_interp_delta) {
if (is_freelook_active()) {
// Higher inertia should increase "lag" (lerp with factor between 0 and 1)
// Inertia of zero should produce instant movement (lerp with factor of 1) in this case it returns a really high value and gets clamped to 1.
- real_t inertia = EDITOR_GET("editors/3d/freelook/freelook_inertia");
- inertia = MAX(0.001, inertia);
+ const real_t inertia = EDITOR_GET("editors/3d/freelook/freelook_inertia");
real_t factor = (1.0 / inertia) * p_interp_delta;
// We interpolate a different point here, because in freelook mode the focus point (cursor.pos) orbits around eye_pos
camera_cursor.eye_pos = old_camera_cursor.eye_pos.lerp(cursor.eye_pos, CLAMP(factor, 0, 1));
- real_t orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/orbit_inertia");
- orbit_inertia = MAX(0.0001, orbit_inertia);
+ const real_t orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/orbit_inertia");
camera_cursor.x_rot = Math::lerp(old_camera_cursor.x_rot, cursor.x_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia)));
camera_cursor.y_rot = Math::lerp(old_camera_cursor.y_rot, cursor.y_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia)));
@@ -293,24 +287,9 @@ void Node3DEditorViewport::_update_camera(real_t p_interp_delta) {
camera_cursor.pos = camera_cursor.eye_pos + forward * camera_cursor.distance;
} else {
- //when not being manipulated, move softly
- real_t free_orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/orbit_inertia");
- real_t free_translation_inertia = EDITOR_GET("editors/3d/navigation_feel/translation_inertia");
- //when being manipulated, move more quickly
- real_t manip_orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/manipulation_orbit_inertia");
- real_t manip_translation_inertia = EDITOR_GET("editors/3d/navigation_feel/manipulation_translation_inertia");
-
- real_t zoom_inertia = EDITOR_GET("editors/3d/navigation_feel/zoom_inertia");
-
- //determine if being manipulated
- bool manipulated = Input::get_singleton()->get_mouse_button_mask() & (2 | 4);
- manipulated |= Input::get_singleton()->is_key_pressed(KEY_SHIFT);
- manipulated |= Input::get_singleton()->is_key_pressed(KEY_ALT);
- manipulated |= Input::get_singleton()->is_key_pressed(KEY_CTRL);
-
- real_t orbit_inertia = MAX(0.00001, manipulated ? manip_orbit_inertia : free_orbit_inertia);
- real_t translation_inertia = MAX(0.0001, manipulated ? manip_translation_inertia : free_translation_inertia);
- zoom_inertia = MAX(0.0001, zoom_inertia);
+ const real_t orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/orbit_inertia");
+ const real_t translation_inertia = EDITOR_GET("editors/3d/navigation_feel/translation_inertia");
+ const real_t zoom_inertia = EDITOR_GET("editors/3d/navigation_feel/zoom_inertia");
camera_cursor.x_rot = Math::lerp(old_camera_cursor.x_rot, cursor.x_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia)));
camera_cursor.y_rot = Math::lerp(old_camera_cursor.y_rot, cursor.y_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia)));
@@ -378,8 +357,8 @@ int Node3DEditorViewport::get_selected_count() const {
int count = 0;
- for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) {
- Node3D *sp = Object::cast_to<Node3D>(E->key());
+ for (const KeyValue<Node *, Object *> &E : selection) {
+ Node3D *sp = Object::cast_to<Node3D>(E.key);
if (!sp) {
continue;
}
@@ -839,7 +818,7 @@ void Node3DEditorViewport::_update_name() {
if (orthogonal) {
name = TTR("Left Orthogonal");
} else {
- name = TTR("Right Perspective");
+ name = TTR("Left Perspective");
}
} break;
case VIEW_TYPE_RIGHT: {
@@ -875,8 +854,8 @@ void Node3DEditorViewport::_update_name() {
}
void Node3DEditorViewport::_compute_edit(const Point2 &p_point) {
- _edit.click_ray = _get_ray(Vector2(p_point.x, p_point.y));
- _edit.click_ray_pos = _get_ray_pos(Vector2(p_point.x, p_point.y));
+ _edit.click_ray = _get_ray(p_point);
+ _edit.click_ray_pos = _get_ray_pos(p_point);
_edit.plane = TRANSFORM_VIEW;
spatial_editor->update_transform_gizmo();
_edit.center = spatial_editor->get_gizmo_transform().origin;
@@ -885,8 +864,8 @@ void Node3DEditorViewport::_compute_edit(const Point2 &p_point) {
Node3DEditorSelectedItem *se = selected ? editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(selected) : nullptr;
if (se && se->gizmo.is_valid()) {
- for (Map<int, Transform3D>::Element *E = se->subgizmos.front(); E; E = E->next()) {
- int subgizmo_id = E->key();
+ for (const KeyValue<int, Transform3D> &E : se->subgizmos) {
+ int subgizmo_id = E.key;
se->subgizmos[subgizmo_id] = se->gizmo->get_subgizmo_transform(subgizmo_id);
}
se->original_local = selected->get_transform();
@@ -955,8 +934,8 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b
return false;
}
- Vector3 ray_pos = _get_ray_pos(Vector2(p_screenpos.x, p_screenpos.y));
- Vector3 ray = _get_ray(Vector2(p_screenpos.x, p_screenpos.y));
+ Vector3 ray_pos = _get_ray_pos(p_screenpos);
+ Vector3 ray = _get_ray(p_screenpos);
Transform3D gt = spatial_editor->get_gizmo_transform();
@@ -1019,7 +998,7 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b
} else {
//handle plane translate
_edit.mode = TRANSFORM_TRANSLATE;
- _compute_edit(Point2(p_screenpos.x, p_screenpos.y));
+ _compute_edit(p_screenpos);
_edit.plane = TransformPlane(TRANSFORM_X_AXIS + col_axis + (is_plane_translate ? 3 : 0));
}
return true;
@@ -1057,7 +1036,7 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b
} else {
//handle rotate
_edit.mode = TRANSFORM_ROTATE;
- _compute_edit(Point2(p_screenpos.x, p_screenpos.y));
+ _compute_edit(p_screenpos);
_edit.plane = TransformPlane(TRANSFORM_X_AXIS + col_axis);
}
return true;
@@ -1123,7 +1102,7 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b
} else {
//handle scale
_edit.mode = TRANSFORM_SCALE;
- _compute_edit(Point2(p_screenpos.x, p_screenpos.y));
+ _compute_edit(p_screenpos);
_edit.plane = TransformPlane(TRANSFORM_X_AXIS + col_axis + (is_plane_scale ? 3 : 0));
}
return true;
@@ -1312,24 +1291,31 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
return; //do NONE
}
+ EditorPlugin::AfterGUIInput after = EditorPlugin::AFTER_GUI_INPUT_PASS;
{
EditorNode *en = editor;
EditorPluginList *force_input_forwarding_list = en->get_editor_plugins_force_input_forwarding();
if (!force_input_forwarding_list->is_empty()) {
- bool discard = force_input_forwarding_list->forward_spatial_gui_input(camera, p_event, true);
- if (discard) {
+ EditorPlugin::AfterGUIInput discard = force_input_forwarding_list->forward_spatial_gui_input(camera, p_event, true);
+ if (discard == EditorPlugin::AFTER_GUI_INPUT_STOP) {
return;
}
+ if (discard == EditorPlugin::AFTER_GUI_INPUT_DESELECT) {
+ after = EditorPlugin::AFTER_GUI_INPUT_DESELECT;
+ }
}
}
{
EditorNode *en = editor;
EditorPluginList *over_plugin_list = en->get_editor_plugins_over();
if (!over_plugin_list->is_empty()) {
- bool discard = over_plugin_list->forward_spatial_gui_input(camera, p_event, false);
- if (discard) {
+ EditorPlugin::AfterGUIInput discard = over_plugin_list->forward_spatial_gui_input(camera, p_event, false);
+ if (discard == EditorPlugin::AFTER_GUI_INPUT_STOP) {
return;
}
+ if (discard == EditorPlugin::AFTER_GUI_INPUT_DESELECT) {
+ after = EditorPlugin::AFTER_GUI_INPUT_DESELECT;
+ }
}
}
@@ -1395,9 +1381,9 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
Vector<int> ids;
Vector<Transform3D> restore;
- for (Map<int, Transform3D>::Element *GE = se->subgizmos.front(); GE; GE = GE->next()) {
- ids.push_back(GE->key());
- restore.push_back(GE->value());
+ for (const KeyValue<int, Transform3D> &GE : se->subgizmos) {
+ ids.push_back(GE.key);
+ restore.push_back(GE.value);
}
se->gizmo->commit_subgizmos(ids, restore, true);
@@ -1594,17 +1580,19 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
break;
}
- clicked = _select_ray(b->get_position());
+ if (after != EditorPlugin::AFTER_GUI_INPUT_DESELECT) {
+ clicked = _select_ray(b->get_position());
- //clicking is always deferred to either move or release
+ //clicking is always deferred to either move or release
- clicked_wants_append = b->is_shift_pressed();
+ clicked_wants_append = b->is_shift_pressed();
- if (clicked.is_null()) {
- //default to regionselect
- cursor.region_select = true;
- cursor.region_begin = b->get_position();
- cursor.region_end = b->get_position();
+ if (clicked.is_null()) {
+ //default to regionselect
+ cursor.region_select = true;
+ cursor.region_begin = b->get_position();
+ cursor.region_end = b->get_position();
+ }
}
surface->update();
@@ -1615,14 +1603,16 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
break;
}
- if (clicked.is_valid()) {
- _select_clicked(false);
- }
+ if (after != EditorPlugin::AFTER_GUI_INPUT_DESELECT) {
+ if (clicked.is_valid()) {
+ _select_clicked(false);
+ }
- if (cursor.region_select) {
- _select_region();
- cursor.region_select = false;
- surface->update();
+ if (cursor.region_select) {
+ _select_region();
+ cursor.region_select = false;
+ surface->update();
+ }
}
if (_edit.mode != TRANSFORM_NONE) {
@@ -1633,9 +1623,9 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
Vector<int> ids;
Vector<Transform3D> restore;
- for (Map<int, Transform3D>::Element *GE = se->subgizmos.front(); GE; GE = GE->next()) {
- ids.push_back(GE->key());
- restore.push_back(GE->value());
+ for (const KeyValue<int, Transform3D> &GE : se->subgizmos) {
+ ids.push_back(GE.key);
+ restore.push_back(GE.value);
}
se->gizmo->commit_subgizmos(ids, restore, false);
@@ -1835,6 +1825,8 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
motion = Vector3(scale, scale, scale);
}
+ motion /= click.distance_to(_edit.center);
+
// Disable local transformation for TRANSFORM_VIEW
bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW);
@@ -1864,13 +1856,13 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
if (se->gizmo.is_valid()) {
- for (Map<int, Transform3D>::Element *GE = se->subgizmos.front(); GE; GE = GE->next()) {
- Transform3D xform = GE->get();
+ for (KeyValue<int, Transform3D> &GE : se->subgizmos) {
+ Transform3D xform = GE.value;
Transform3D new_xform = _compute_transform(TRANSFORM_SCALE, se->original * xform, xform, motion, snap, local_coords);
if (!local_coords) {
new_xform = se->original.affine_inverse() * new_xform;
}
- se->gizmo->set_subgizmo_transform(GE->key(), new_xform);
+ se->gizmo->set_subgizmo_transform(GE.key, new_xform);
}
} else {
Transform3D new_xform = _compute_transform(TRANSFORM_SCALE, se->original, se->original_local, motion, snap, local_coords);
@@ -1963,11 +1955,11 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
if (se->gizmo.is_valid()) {
- for (Map<int, Transform3D>::Element *GE = se->subgizmos.front(); GE; GE = GE->next()) {
- Transform3D xform = GE->get();
+ for (KeyValue<int, Transform3D> &GE : se->subgizmos) {
+ Transform3D xform = GE.value;
Transform3D new_xform = _compute_transform(TRANSFORM_TRANSLATE, se->original * xform, xform, motion, snap, local_coords);
new_xform = se->original.affine_inverse() * new_xform;
- se->gizmo->set_subgizmo_transform(GE->key(), new_xform);
+ se->gizmo->set_subgizmo_transform(GE.key, new_xform);
}
} else {
Transform3D new_xform = _compute_transform(TRANSFORM_TRANSLATE, se->original, se->original_local, motion, snap, local_coords);
@@ -2049,14 +2041,14 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
Vector3 compute_axis = local_coords ? axis : plane.normal;
if (se->gizmo.is_valid()) {
- for (Map<int, Transform3D>::Element *GE = se->subgizmos.front(); GE; GE = GE->next()) {
- Transform3D xform = GE->get();
+ for (KeyValue<int, Transform3D> &GE : se->subgizmos) {
+ Transform3D xform = GE.value;
Transform3D new_xform = _compute_transform(TRANSFORM_ROTATE, se->original * xform, xform, compute_axis, angle, local_coords);
if (!local_coords) {
new_xform = se->original.affine_inverse() * new_xform;
}
- se->gizmo->set_subgizmo_transform(GE->key(), new_xform);
+ se->gizmo->set_subgizmo_transform(GE.key, new_xform);
}
} else {
Transform3D new_xform = _compute_transform(TRANSFORM_ROTATE, se->original, se->original_local, compute_axis, angle, local_coords);
@@ -2256,7 +2248,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
return;
}
- if (!AnimationPlayerEditor::singleton->get_track_editor()->has_keying()) {
+ if (!AnimationPlayerEditor::get_singleton()->get_track_editor()->has_keying()) {
set_message(TTR("Keying is disabled (no key inserted)."));
return;
}
@@ -2502,8 +2494,14 @@ static bool is_shortcut_pressed(const String &p_path) {
if (shortcut.is_null()) {
return false;
}
- InputEventKey *k = Object::cast_to<InputEventKey>(shortcut->get_event().ptr());
- if (k == nullptr) {
+
+ const Array shortcuts = shortcut->get_events();
+ Ref<InputEventKey> k;
+ if (shortcuts.size() > 0) {
+ k = shortcuts.front();
+ }
+
+ if (k.is_null()) {
return false;
}
const Input &input = *Input::get_singleton();
@@ -2620,6 +2618,9 @@ void Node3DEditorViewport::_project_settings_changed() {
const bool use_occlusion_culling = GLOBAL_GET("rendering/occlusion_culling/use_occlusion_culling");
viewport->set_use_occlusion_culling(use_occlusion_culling);
+
+ const float lod_threshold = GLOBAL_GET("rendering/mesh_lod/lod_change/threshold_pixels");
+ viewport->set_lod_threshold(lod_threshold);
}
void Node3DEditorViewport::_notification(int p_what) {
@@ -2682,8 +2683,8 @@ void Node3DEditorViewport::_notification(int p_what) {
bool changed = false;
bool exist = false;
- for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) {
- Node3D *sp = Object::cast_to<Node3D>(E->key());
+ for (const KeyValue<Node *, Object *> &E : selection) {
+ Node3D *sp = Object::cast_to<Node3D>(E.key);
if (!sp) {
continue;
}
@@ -2707,15 +2708,28 @@ void Node3DEditorViewport::_notification(int p_what) {
se->aabb = new_aabb;
- t.translate(se->aabb.position);
+ Transform3D t_offset = t;
// apply AABB scaling before item's global transform
- Basis aabb_s;
- aabb_s.scale(se->aabb.size);
- t.basis = t.basis * aabb_s;
+ {
+ const Vector3 offset(0.005, 0.005, 0.005);
+ Basis aabb_s;
+ aabb_s.scale(se->aabb.size + offset);
+ t.translate(se->aabb.position - offset / 2);
+ t.basis = t.basis * aabb_s;
+ }
+ {
+ const Vector3 offset(0.01, 0.01, 0.01);
+ Basis aabb_s;
+ aabb_s.scale(se->aabb.size + offset);
+ t_offset.translate(se->aabb.position - offset / 2);
+ t_offset.basis = t_offset.basis * aabb_s;
+ }
RenderingServer::get_singleton()->instance_set_transform(se->sbox_instance, t);
+ RenderingServer::get_singleton()->instance_set_transform(se->sbox_instance_offset, t_offset);
RenderingServer::get_singleton()->instance_set_transform(se->sbox_instance_xray, t);
+ RenderingServer::get_singleton()->instance_set_transform(se->sbox_instance_xray_offset, t_offset);
}
if (changed || (spatial_editor->is_gizmo_visible() && !exist)) {
@@ -3825,8 +3839,8 @@ void Node3DEditorViewport::focus_selection() {
}
if (se->gizmo.is_valid()) {
- for (Map<int, Transform3D>::Element *GE = se->subgizmos.front(); GE; GE = GE->next()) {
- center += se->gizmo->get_subgizmo_transform(GE->key()).origin;
+ for (const KeyValue<int, Transform3D> &GE : se->subgizmos) {
+ center += se->gizmo->get_subgizmo_transform(GE.key).origin;
count++;
}
}
@@ -3936,7 +3950,7 @@ void Node3DEditorViewport::_remove_preview() {
}
bool Node3DEditorViewport::_cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node) {
- if (p_desired_node->get_filename() == p_target_scene_path) {
+ if (p_desired_node->get_scene_file_path() == p_target_scene_path) {
return true;
}
@@ -3978,15 +3992,15 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po
return false;
}
- if (editor->get_edited_scene()->get_filename() != "") { // cyclical instancing
- if (_cyclical_dependency_exists(editor->get_edited_scene()->get_filename(), instantiated_scene)) {
+ if (editor->get_edited_scene()->get_scene_file_path() != "") { // cyclical instancing
+ if (_cyclical_dependency_exists(editor->get_edited_scene()->get_scene_file_path(), instantiated_scene)) {
memdelete(instantiated_scene);
return false;
}
}
if (scene != nullptr) {
- instantiated_scene->set_filename(ProjectSettings::get_singleton()->localize_path(path));
+ instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(path));
}
editor_data->get_undo_redo().add_do_method(parent, "add_child", instantiated_scene);
@@ -4198,7 +4212,8 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, Edito
VBoxContainer *vbox = memnew(VBoxContainer);
surface->add_child(vbox);
- vbox->set_position(Point2(10, 10) * EDSCALE);
+ vbox->set_offset(SIDE_LEFT, 10 * EDSCALE);
+ vbox->set_offset(SIDE_TOP, 10 * EDSCALE);
view_menu = memnew(MenuButton);
view_menu->set_flat(false);
@@ -4428,7 +4443,7 @@ Node3DEditorViewport::~Node3DEditorViewport() {
//////////////////////////////////////////////////////////////
-void Node3DEditorViewportContainer::_gui_input(const Ref<InputEvent> &p_event) {
+void Node3DEditorViewportContainer::gui_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
Ref<InputEventMouseButton> mb = p_event;
@@ -4720,10 +4735,6 @@ Node3DEditorViewportContainer::View Node3DEditorViewportContainer::get_view() {
return view;
}
-void Node3DEditorViewportContainer::_bind_methods() {
- ClassDB::bind_method("_gui_input", &Node3DEditorViewportContainer::_gui_input);
-}
-
Node3DEditorViewportContainer::Node3DEditorViewportContainer() {
set_clip_contents(true);
view = VIEW_USE_1_VIEWPORT;
@@ -4744,9 +4755,15 @@ Node3DEditorSelectedItem::~Node3DEditorSelectedItem() {
if (sbox_instance.is_valid()) {
RenderingServer::get_singleton()->free(sbox_instance);
}
+ if (sbox_instance_offset.is_valid()) {
+ RenderingServer::get_singleton()->free(sbox_instance_offset);
+ }
if (sbox_instance_xray.is_valid()) {
RenderingServer::get_singleton()->free(sbox_instance_xray);
}
+ if (sbox_instance_xray_offset.is_valid()) {
+ RenderingServer::get_singleton()->free(sbox_instance_xray_offset);
+ }
}
void Node3DEditor::select_gizmo_highlight_axis(int p_axis) {
@@ -4769,8 +4786,8 @@ void Node3DEditor::update_transform_gizmo() {
Node3DEditorSelectedItem *se = selected ? editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(selected) : nullptr;
if (se && se->gizmo.is_valid()) {
- for (Map<int, Transform3D>::Element *E = se->subgizmos.front(); E; E = E->next()) {
- Transform3D xf = se->sp->get_global_transform() * se->gizmo->get_subgizmo_transform(E->key());
+ for (const KeyValue<int, Transform3D> &E : se->subgizmos) {
+ Transform3D xf = se->sp->get_global_transform() * se->gizmo->get_subgizmo_transform(E.key);
gizmo_center += xf.origin;
if (count == 0 && local_gizmo_coords) {
gizmo_basis = xf.basis;
@@ -4826,7 +4843,7 @@ void _update_all_gizmos(Node *p_node) {
}
void Node3DEditor::update_all_gizmos(Node *p_node) {
- if (!p_node && get_tree()) {
+ if (!p_node && is_inside_tree()) {
p_node = get_tree()->get_edited_scene_root();
}
@@ -4849,23 +4866,39 @@ Object *Node3DEditor::_get_editor_data(Object *p_what) {
si->sbox_instance = RenderingServer::get_singleton()->instance_create2(
selection_box->get_rid(),
sp->get_world_3d()->get_scenario());
+ si->sbox_instance_offset = RenderingServer::get_singleton()->instance_create2(
+ selection_box->get_rid(),
+ sp->get_world_3d()->get_scenario());
RS::get_singleton()->instance_geometry_set_cast_shadows_setting(
si->sbox_instance,
RS::SHADOW_CASTING_SETTING_OFF);
+ RS::get_singleton()->instance_geometry_set_cast_shadows_setting(
+ si->sbox_instance_offset,
+ RS::SHADOW_CASTING_SETTING_OFF);
// Use the Edit layer to hide the selection box when View Gizmos is disabled, since it is a bit distracting.
// It's still possible to approximately guess what is selected by looking at the manipulation gizmo position.
RS::get_singleton()->instance_set_layer_mask(si->sbox_instance, 1 << Node3DEditorViewport::GIZMO_EDIT_LAYER);
+ RS::get_singleton()->instance_set_layer_mask(si->sbox_instance_offset, 1 << Node3DEditorViewport::GIZMO_EDIT_LAYER);
RS::get_singleton()->instance_geometry_set_flag(si->sbox_instance, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
+ RS::get_singleton()->instance_geometry_set_flag(si->sbox_instance_offset, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
si->sbox_instance_xray = RenderingServer::get_singleton()->instance_create2(
selection_box_xray->get_rid(),
sp->get_world_3d()->get_scenario());
+ si->sbox_instance_xray_offset = RenderingServer::get_singleton()->instance_create2(
+ selection_box_xray->get_rid(),
+ sp->get_world_3d()->get_scenario());
RS::get_singleton()->instance_geometry_set_cast_shadows_setting(
si->sbox_instance_xray,
RS::SHADOW_CASTING_SETTING_OFF);
+ RS::get_singleton()->instance_geometry_set_cast_shadows_setting(
+ si->sbox_instance_xray_offset,
+ RS::SHADOW_CASTING_SETTING_OFF);
// Use the Edit layer to hide the selection box when View Gizmos is disabled, since it is a bit distracting.
// It's still possible to approximately guess what is selected by looking at the manipulation gizmo position.
RS::get_singleton()->instance_set_layer_mask(si->sbox_instance_xray, 1 << Node3DEditorViewport::GIZMO_EDIT_LAYER);
- RS::get_singleton()->instance_geometry_set_flag(si->sbox_instance, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
+ RS::get_singleton()->instance_set_layer_mask(si->sbox_instance_xray_offset, 1 << Node3DEditorViewport::GIZMO_EDIT_LAYER);
+ RS::get_singleton()->instance_geometry_set_flag(si->sbox_instance_xray, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
+ RS::get_singleton()->instance_geometry_set_flag(si->sbox_instance_xray_offset, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
return si;
}
@@ -4873,10 +4906,6 @@ Object *Node3DEditor::_get_editor_data(Object *p_what) {
void Node3DEditor::_generate_selection_boxes() {
// Use two AABBs to create the illusion of a slightly thicker line.
AABB aabb(Vector3(), Vector3(1, 1, 1));
- AABB aabb_offset(Vector3(), Vector3(1, 1, 1));
- // Grow the bounding boxes slightly to avoid Z-fighting with the mesh's edges.
- aabb.grow_by(0.005);
- aabb_offset.grow_by(0.01);
// Create a x-ray (visible through solid surfaces) and standard version of the selection box.
// Both will be drawn at the same position, but with different opacity.
@@ -4896,16 +4925,6 @@ void Node3DEditor::_generate_selection_boxes() {
st_xray->add_vertex(b);
}
- for (int i = 0; i < 12; i++) {
- Vector3 a, b;
- aabb_offset.get_edge(i, a, b);
-
- st->add_vertex(a);
- st->add_vertex(b);
- st_xray->add_vertex(a);
- st_xray->add_vertex(b);
- }
-
Ref<StandardMaterial3D> mat = memnew(StandardMaterial3D);
mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
const Color selection_box_color = EDITOR_GET("editors/3d/selection_box_color");
@@ -5606,6 +5625,8 @@ void Node3DEditor::_init_indicators() {
Ref<Shader> grid_shader = memnew(Shader);
grid_shader->set_code(R"(
+// 3D editor grid shader.
+
shader_type spatial;
render_mode unshaded;
@@ -5847,6 +5868,8 @@ void fragment() {
Ref<Shader> rotate_shader = memnew(Shader);
rotate_shader->set_code(R"(
+// 3D editor rotation manipulator gizmo shader.
+
shader_type spatial;
render_mode unshaded, depth_test_disabled;
@@ -5895,6 +5918,8 @@ void fragment() {
Ref<Shader> border_shader = memnew(Shader);
border_shader->set_code(R"(
+// 3D editor rotation manipulator gizmo shader (white outline).
+
shader_type spatial;
render_mode unshaded, depth_test_disabled;
@@ -6030,7 +6055,7 @@ void fragment() {
void Node3DEditor::_update_context_menu_stylebox() {
// This must be called when the theme changes to follow the new accent color.
Ref<StyleBoxFlat> context_menu_stylebox = memnew(StyleBoxFlat);
- const Color accent_color = EditorNode::get_singleton()->get_gui_base()->get_theme_color("accent_color", "Editor");
+ const Color accent_color = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("accent_color"), SNAME("Editor"));
context_menu_stylebox->set_bg_color(accent_color * Color(1, 1, 1, 0.1));
// Add an underline to the StyleBox, but prevent its minimum vertical size from changing.
context_menu_stylebox->set_border_color(accent_color);
@@ -6296,7 +6321,7 @@ void Node3DEditor::update_grid() {
// Gets a orthogonal or perspective position correctly (for the grid comparison)
const Vector3 camera_position = get_editor_viewport(0)->camera->get_position();
- if (!grid_init_draw || (camera_position - grid_camera_last_update_position).length() >= 10.0f) {
+ if (!grid_init_draw || grid_camera_last_update_position.distance_squared_to(camera_position) >= 100.0f) {
_finish_grid();
_init_grid();
grid_init_draw = true;
@@ -6512,7 +6537,7 @@ void Node3DEditor::snap_selected_nodes_to_floor() {
}
}
-void Node3DEditor::_unhandled_key_input(Ref<InputEvent> p_event) {
+void Node3DEditor::unhandled_key_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
if (!is_visible_in_tree()) {
@@ -6690,8 +6715,8 @@ Vector<int> Node3DEditor::get_subgizmo_selection() {
Vector<int> ret;
if (se) {
- for (Map<int, Transform3D>::Element *E = se->subgizmos.front(); E; E = E->next()) {
- ret.push_back(E->key());
+ for (const KeyValue<int, Transform3D> &E : se->subgizmos) {
+ ret.push_back(E.key);
}
}
return ret;
@@ -6743,6 +6768,33 @@ void Node3DEditor::_request_gizmo(Object *p_obj) {
}
}
+void Node3DEditor::_set_subgizmo_selection(Object *p_obj, Ref<Node3DGizmo> p_gizmo, int p_id, Transform3D p_transform) {
+ if (p_id == -1) {
+ _clear_subgizmo_selection(p_obj);
+ return;
+ }
+
+ Node3D *sp = nullptr;
+ if (p_obj) {
+ sp = Object::cast_to<Node3D>(p_obj);
+ } else {
+ sp = selected;
+ }
+
+ if (!sp) {
+ return;
+ }
+
+ Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp);
+ if (se) {
+ se->subgizmos.clear();
+ se->subgizmos.insert(p_id, p_transform);
+ se->gizmo = p_gizmo;
+ sp->update_gizmos();
+ update_transform_gizmo();
+ }
+}
+
void Node3DEditor::_clear_subgizmo_selection(Object *p_obj) {
Node3D *sp = nullptr;
if (p_obj) {
@@ -6863,11 +6915,11 @@ void Node3DEditor::_register_all_gizmos() {
add_gizmo_plugin(Ref<Camera3DGizmoPlugin>(memnew(Camera3DGizmoPlugin)));
add_gizmo_plugin(Ref<Light3DGizmoPlugin>(memnew(Light3DGizmoPlugin)));
add_gizmo_plugin(Ref<AudioStreamPlayer3DGizmoPlugin>(memnew(AudioStreamPlayer3DGizmoPlugin)));
+ add_gizmo_plugin(Ref<AudioListener3DGizmoPlugin>(memnew(AudioListener3DGizmoPlugin)));
add_gizmo_plugin(Ref<MeshInstance3DGizmoPlugin>(memnew(MeshInstance3DGizmoPlugin)));
add_gizmo_plugin(Ref<OccluderInstance3DGizmoPlugin>(memnew(OccluderInstance3DGizmoPlugin)));
- add_gizmo_plugin(Ref<SoftBody3DGizmoPlugin>(memnew(SoftBody3DGizmoPlugin)));
+ add_gizmo_plugin(Ref<SoftDynamicBody3DGizmoPlugin>(memnew(SoftDynamicBody3DGizmoPlugin)));
add_gizmo_plugin(Ref<Sprite3DGizmoPlugin>(memnew(Sprite3DGizmoPlugin)));
- add_gizmo_plugin(Ref<Skeleton3DGizmoPlugin>(memnew(Skeleton3DGizmoPlugin)));
add_gizmo_plugin(Ref<Position3DGizmoPlugin>(memnew(Position3DGizmoPlugin)));
add_gizmo_plugin(Ref<RayCast3DGizmoPlugin>(memnew(RayCast3DGizmoPlugin)));
add_gizmo_plugin(Ref<SpringArm3DGizmoPlugin>(memnew(SpringArm3DGizmoPlugin)));
@@ -6890,9 +6942,9 @@ void Node3DEditor::_register_all_gizmos() {
}
void Node3DEditor::_bind_methods() {
- ClassDB::bind_method("_unhandled_key_input", &Node3DEditor::_unhandled_key_input);
ClassDB::bind_method("_get_editor_data", &Node3DEditor::_get_editor_data);
ClassDB::bind_method("_request_gizmo", &Node3DEditor::_request_gizmo);
+ ClassDB::bind_method("_set_subgizmo_selection", &Node3DEditor::_set_subgizmo_selection);
ClassDB::bind_method("_clear_subgizmo_selection", &Node3DEditor::_clear_subgizmo_selection);
ClassDB::bind_method("_refresh_menu_icons", &Node3DEditor::_refresh_menu_icons);
@@ -7515,6 +7567,8 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) {
sun_direction_shader.instantiate();
sun_direction_shader->set_code(R"(
+// 3D editor Preview Sun direction shader.
+
shader_type canvas_item;
uniform vec3 sun_direction;
@@ -7717,6 +7771,13 @@ Vector3 Node3DEditor::snap_point(Vector3 p_target, Vector3 p_start) const {
return p_target;
}
+bool Node3DEditor::is_gizmo_visible() const {
+ if (selected) {
+ return gizmo.visible && selected->is_transform_gizmo_visible();
+ }
+ return gizmo.visible;
+}
+
double Node3DEditor::get_translate_snap() const {
double snap_value;
if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index 868b834993..2d5aeaa981 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -74,9 +74,8 @@ class ViewportRotationControl : public Control {
const float AXIS_CIRCLE_RADIUS = 8.0f * EDSCALE;
protected:
- static void _bind_methods();
void _notification(int p_what);
- void _gui_input(Ref<InputEvent> p_event);
+ virtual void gui_input(const Ref<InputEvent> &p_event) override;
void _draw();
void _draw_axis(const Axis2D &p_axis);
void _get_sorted_axis(Vector<Axis2D> &r_axis);
@@ -432,7 +431,9 @@ public:
bool last_xform_dirty;
Node3D *sp;
RID sbox_instance;
+ RID sbox_instance_offset;
RID sbox_instance_xray;
+ RID sbox_instance_xray_offset;
Ref<EditorNode3DGizmo> gizmo;
Map<int, Transform3D> subgizmos; // map ID -> initial transform
@@ -470,11 +471,10 @@ private:
Vector2 drag_begin_pos;
Vector2 drag_begin_ratio;
- void _gui_input(const Ref<InputEvent> &p_event);
+ virtual void gui_input(const Ref<InputEvent> &p_event) override;
protected:
void _notification(int p_what);
- static void _bind_methods();
public:
void set_view(View p_view);
@@ -665,6 +665,7 @@ private:
Node3D *selected;
void _request_gizmo(Object *p_obj);
+ void _set_subgizmo_selection(Object *p_obj, Ref<Node3DGizmo> p_gizmo, int p_id, Transform3D p_transform = Transform3D());
void _clear_subgizmo_selection(Object *p_obj = nullptr);
static Node3DEditor *singleton;
@@ -744,7 +745,7 @@ private:
protected:
void _notification(int p_what);
//void _gui_input(InputEvent p_event);
- void _unhandled_key_input(Ref<InputEvent> p_event);
+ virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override;
static void _bind_methods();
@@ -758,7 +759,7 @@ public:
float get_fov() const { return settings_fov->get_value(); }
Transform3D get_gizmo_transform() const { return gizmo.transform; }
- bool is_gizmo_visible() const { return gizmo.visible; }
+ bool is_gizmo_visible() const;
ToolMode get_tool_mode() const { return tool_mode; }
bool are_local_coords_enabled() const { return tool_option_button[Node3DEditor::TOOL_OPT_LOCAL_COORDS]->is_pressed(); }
diff --git a/editor/plugins/occluder_instance_3d_editor_plugin.cpp b/editor/plugins/occluder_instance_3d_editor_plugin.cpp
index ab88b9f00d..0328b1bea6 100644
--- a/editor/plugins/occluder_instance_3d_editor_plugin.cpp
+++ b/editor/plugins/occluder_instance_3d_editor_plugin.cpp
@@ -41,9 +41,9 @@ void OccluderInstance3DEditorPlugin::_bake_select_file(const String &p_file) {
switch (err) {
case OccluderInstance3D::BAKE_ERROR_NO_SAVE_PATH: {
- String scene_path = occluder_instance->get_filename();
+ String scene_path = occluder_instance->get_scene_file_path();
if (scene_path == String()) {
- scene_path = occluder_instance->get_owner()->get_filename();
+ scene_path = occluder_instance->get_owner()->get_scene_file_path();
}
if (scene_path == String()) {
EditorNode::get_singleton()->show_warning(TTR("Can't determine a save path for the occluder.\nSave your scene and try again."));
diff --git a/editor/plugins/packed_scene_translation_parser_plugin.cpp b/editor/plugins/packed_scene_translation_parser_plugin.cpp
index 0a949c8610..53c5b8dd70 100644
--- a/editor/plugins/packed_scene_translation_parser_plugin.cpp
+++ b/editor/plugins/packed_scene_translation_parser_plugin.cpp
@@ -31,6 +31,7 @@
#include "packed_scene_translation_parser_plugin.h"
#include "core/io/resource_loader.h"
+#include "scene/gui/option_button.h"
#include "scene/resources/packed_scene.h"
void PackedSceneEditorTranslationParserPlugin::get_recognized_extensions(List<String> *r_extensions) const {
@@ -50,21 +51,31 @@ Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path,
Ref<SceneState> state = Ref<PackedScene>(loaded_res)->get_state();
Vector<String> parsed_strings;
- String property_name;
- Variant property_value;
for (int i = 0; i < state->get_node_count(); i++) {
- if (!ClassDB::is_parent_class(state->get_node_type(i), "Control") && !ClassDB::is_parent_class(state->get_node_type(i), "Viewport")) {
+ String node_type = state->get_node_type(i);
+ if (!ClassDB::is_parent_class(node_type, "Control") && !ClassDB::is_parent_class(node_type, "Window")) {
continue;
}
+ // Find the `auto_translate` property, and abort the string parsing of the node if disabled.
+ bool auto_translating = true;
for (int j = 0; j < state->get_node_property_count(i); j++) {
- property_name = state->get_node_property_name(i, j);
- if (!lookup_properties.has(property_name)) {
- continue;
+ if (state->get_node_property_name(i, j) == "auto_translate" && (bool)state->get_node_property_value(i, j) == false) {
+ auto_translating = false;
+ break;
}
+ }
+ if (!auto_translating) {
+ continue;
+ }
- property_value = state->get_node_property_value(i, j);
+ for (int j = 0; j < state->get_node_property_count(i); j++) {
+ String property_name = state->get_node_property_name(i, j);
+ if (!lookup_properties.has(property_name) || (exception_list.has(node_type) && exception_list[node_type].has(property_name))) {
+ continue;
+ }
+ Variant property_value = state->get_node_property_value(i, j);
if (property_name == "script" && property_value.get_type() == Variant::OBJECT && !property_value.is_null()) {
// Parse built-in script.
Ref<Script> s = Object::cast_to<Script>(property_value);
@@ -76,7 +87,16 @@ Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path,
parsed_strings.append_array(temp);
r_ids_ctx_plural->append_array(ids_context_plural);
}
- } else if (property_name == "filters") {
+ } else if ((node_type == "MenuButton" || node_type == "OptionButton") && property_name == "items") {
+ Vector<String> str_values = property_value;
+ int incr_value = node_type == "MenuButton" ? PopupMenu::ITEM_PROPERTY_SIZE : OptionButton::ITEM_PROPERTY_SIZE;
+ for (int k = 0; k < str_values.size(); k += incr_value) {
+ String desc = str_values[k].get_slice(";", 1).strip_edges();
+ if (!desc.is_empty()) {
+ parsed_strings.push_back(desc);
+ }
+ }
+ } else if (node_type == "FileDialog" && property_name == "filters") {
// Extract FileDialog's filters property with values in format "*.png ; PNG Images","*.gd ; GDScript Files".
Vector<String> str_values = property_value;
for (int k = 0; k < str_values.size(); k++) {
@@ -105,12 +125,17 @@ PackedSceneEditorTranslationParserPlugin::PackedSceneEditorTranslationParserPlug
lookup_properties.insert("text");
lookup_properties.insert("hint_tooltip");
lookup_properties.insert("placeholder_text");
+ lookup_properties.insert("items");
+ lookup_properties.insert("title");
lookup_properties.insert("dialog_text");
lookup_properties.insert("filters");
lookup_properties.insert("script");
- //Add exception list (to prevent false positives)
- //line edit, text edit, richtextlabel
- //Set<String> exception_list;
- //exception_list.insert("RichTextLabel");
+ // Exception list (to prevent false positives).
+ exception_list.insert("LineEdit", Vector<StringName>());
+ exception_list["LineEdit"].append("text");
+ exception_list.insert("TextEdit", Vector<StringName>());
+ exception_list["TextEdit"].append("text");
+ exception_list.insert("CodeEdit", Vector<StringName>());
+ exception_list["CodeEdit"].append("text");
}
diff --git a/editor/plugins/packed_scene_translation_parser_plugin.h b/editor/plugins/packed_scene_translation_parser_plugin.h
index e51d65414e..af0291b69c 100644
--- a/editor/plugins/packed_scene_translation_parser_plugin.h
+++ b/editor/plugins/packed_scene_translation_parser_plugin.h
@@ -37,7 +37,9 @@ class PackedSceneEditorTranslationParserPlugin : public EditorTranslationParserP
GDCLASS(PackedSceneEditorTranslationParserPlugin, EditorTranslationParserPlugin);
// Scene Node's properties that contain translation strings.
- Set<String> lookup_properties;
+ Set<StringName> lookup_properties;
+ // Properties from specific Nodes that should be ignored.
+ Map<StringName, Vector<StringName>> exception_list;
public:
virtual Error parse_file(const String &p_path, Vector<String> *r_ids, Vector<Vector<String>> *r_ids_ctx_plural) override;
diff --git a/editor/plugins/path_2d_editor_plugin.cpp b/editor/plugins/path_2d_editor_plugin.cpp
index 584eb84ecd..119ecddf63 100644
--- a/editor/plugins/path_2d_editor_plugin.cpp
+++ b/editor/plugins/path_2d_editor_plugin.cpp
@@ -70,7 +70,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
return false;
}
- real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius");
+ real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp
index 13f7908170..0268b6e5ea 100644
--- a/editor/plugins/path_3d_editor_plugin.cpp
+++ b/editor/plugins/path_3d_editor_plugin.cpp
@@ -294,13 +294,13 @@ Path3DGizmo::Path3DGizmo(Path3D *p_path) {
orig_out_length = 0;
}
-bool Path3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
+EditorPlugin::AfterGUIInput Path3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
if (!path) {
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
Ref<Curve3D> c = path->get_curve();
if (c.is_null()) {
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
Transform3D gt = path->get_global_transform();
Transform3D it = gt.affine_inverse();
@@ -329,14 +329,14 @@ bool Path3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref
const Vector3 *r = v3a.ptr();
if (p_camera->unproject_position(gt.xform(c->get_point_position(0))).distance_to(mbpos) < click_dist) {
- return false; //nope, existing
+ return EditorPlugin::AFTER_GUI_INPUT_PASS; //nope, existing
}
for (int i = 0; i < c->get_point_count() - 1; i++) {
//find the offset and point index of the place to break up
int j = idx;
if (p_camera->unproject_position(gt.xform(c->get_point_position(i + 1))).distance_to(mbpos) < click_dist) {
- return false; //nope, existing
+ return EditorPlugin::AFTER_GUI_INPUT_PASS; //nope, existing
}
while (j < rc && c->get_point_position(i + 1) != r[j]) {
@@ -386,7 +386,7 @@ bool Path3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref
ur->add_do_method(c.ptr(), "add_point", closest_seg_point, Vector3(), Vector3(), closest_seg + 1);
ur->add_undo_method(c.ptr(), "remove_point", closest_seg + 1);
ur->commit_action();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else {
Vector3 org;
@@ -405,7 +405,7 @@ bool Path3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref
ur->add_do_method(c.ptr(), "add_point", it.xform(inters), Vector3(), Vector3(), -1);
ur->add_undo_method(c.ptr(), "remove_point", c->get_point_count());
ur->commit_action();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
//add new at pos
@@ -425,27 +425,27 @@ bool Path3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref
ur->add_do_method(c.ptr(), "remove_point", i);
ur->add_undo_method(c.ptr(), "add_point", c->get_point_position(i), c->get_point_in(i), c->get_point_out(i), i);
ur->commit_action();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else if (dist_to_p_out < click_dist) {
UndoRedo *ur = editor->get_undo_redo();
ur->create_action(TTR("Remove Out-Control Point"));
ur->add_do_method(c.ptr(), "set_point_out", i, Vector3());
ur->add_undo_method(c.ptr(), "set_point_out", i, c->get_point_out(i));
ur->commit_action();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else if (dist_to_p_in < click_dist) {
UndoRedo *ur = editor->get_undo_redo();
ur->create_action(TTR("Remove In-Control Point"));
ur->add_do_method(c.ptr(), "set_point_in", i, Vector3());
ur->add_undo_method(c.ptr(), "set_point_in", i, c->get_point_in(i));
ur->commit_action();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
}
}
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
void Path3DEditorPlugin::edit(Object *p_object) {
@@ -510,7 +510,14 @@ void Path3DEditorPlugin::_close_curve() {
if (c->get_point_count() < 2) {
return;
}
- c->add_point(c->get_point_position(0), c->get_point_in(0), c->get_point_out(0));
+ if (c->get_point_position(0) == c->get_point_position(c->get_point_count() - 1)) {
+ return;
+ }
+ UndoRedo *ur = editor->get_undo_redo();
+ ur->create_action(TTR("Close Curve"));
+ ur->add_do_method(c.ptr(), "add_point", c->get_point_position(0), c->get_point_in(0), c->get_point_out(0), -1);
+ ur->add_undo_method(c.ptr(), "remove_point", c->get_point_count());
+ ur->commit_action();
}
void Path3DEditorPlugin::_handle_option_pressed(int p_option) {
diff --git a/editor/plugins/path_3d_editor_plugin.h b/editor/plugins/path_3d_editor_plugin.h
index b74d7cc59e..974234ba8f 100644
--- a/editor/plugins/path_3d_editor_plugin.h
+++ b/editor/plugins/path_3d_editor_plugin.h
@@ -100,7 +100,7 @@ public:
Path3D *get_edited_path() { return path; }
static Path3DEditorPlugin *singleton;
- virtual bool forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override;
+ virtual EditorPlugin::AfterGUIInput forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override;
virtual String get_name() const override { return "Path3D"; }
bool has_main_screen() const override { return false; }
diff --git a/editor/plugins/physical_bone_3d_editor_plugin.cpp b/editor/plugins/physical_bone_3d_editor_plugin.cpp
index f92f50f826..b1e104e680 100644
--- a/editor/plugins/physical_bone_3d_editor_plugin.cpp
+++ b/editor/plugins/physical_bone_3d_editor_plugin.cpp
@@ -43,6 +43,7 @@ void PhysicalBone3DEditor::_on_toggle_button_transform_joint(bool p_is_pressed)
void PhysicalBone3DEditor::_set_move_joint() {
if (selected) {
selected->_set_gizmo_move_joint(button_transform_joint->is_pressed());
+ Node3DEditor::get_singleton()->update_transform_gizmo();
}
}
diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp
index 9377395418..5afe9ed60c 100644
--- a/editor/plugins/polygon_2d_editor_plugin.cpp
+++ b/editor/plugins/polygon_2d_editor_plugin.cpp
@@ -449,7 +449,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
if (mb.is_valid()) {
if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
if (mb->is_pressed()) {
- uv_drag_from = snap_point(Vector2(mb->get_position().x, mb->get_position().y));
+ uv_drag_from = snap_point(mb->get_position());
uv_drag = true;
points_prev = node->get_uv();
@@ -463,7 +463,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
if (uv_move_current == UV_MODE_CREATE) {
if (!uv_create) {
points_prev.resize(0);
- Vector2 tuv = mtx.affine_inverse().xform(snap_point(Vector2(mb->get_position().x, mb->get_position().y)));
+ Vector2 tuv = mtx.affine_inverse().xform(snap_point(mb->get_position()));
points_prev.push_back(tuv);
uv_create_to = tuv;
point_drag_index = 0;
@@ -483,7 +483,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
uv_edit_draw->update();
} else {
- Vector2 tuv = mtx.affine_inverse().xform(snap_point(Vector2(mb->get_position().x, mb->get_position().y)));
+ Vector2 tuv = mtx.affine_inverse().xform(snap_point(mb->get_position()));
// Close the polygon if selected point is near start. Threshold for closing scaled by zoom level
if (points_prev.size() > 2 && tuv.distance_to(points_prev[0]) < (8 / uv_draw_zoom)) {
@@ -527,7 +527,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
uv_create_bones_prev = node->call("_get_bones");
int internal_vertices = node->get_internal_vertex_count();
- Vector2 pos = mtx.affine_inverse().xform(snap_point(Vector2(mb->get_position().x, mb->get_position().y)));
+ Vector2 pos = mtx.affine_inverse().xform(snap_point(mb->get_position()));
uv_create_poly_prev.push_back(pos);
uv_create_uv_prev.push_back(pos);
@@ -573,7 +573,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
for (int i = points_prev.size() - internal_vertices; i < points_prev.size(); i++) {
Vector2 tuv = mtx.xform(uv_create_poly_prev[i]);
- real_t dist = tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y));
+ real_t dist = tuv.distance_to(mb->get_position());
if (dist < 8 && dist < closest_dist) {
closest = i;
closest_dist = dist;
@@ -626,7 +626,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
point_drag_index = -1;
for (int i = 0; i < points_prev.size(); i++) {
Vector2 tuv = mtx.xform(points_prev[i]);
- if (tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y)) < 8) {
+ if (tuv.distance_to(mb->get_position()) < 8) {
uv_drag_from = tuv;
point_drag_index = i;
}
@@ -643,7 +643,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
for (int i = 0; i < points_prev.size(); i++) {
Vector2 tuv = mtx.xform(points_prev[i]);
- real_t dist = tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y));
+ real_t dist = tuv.distance_to(mb->get_position());
if (dist < 8 && dist < closest_dist) {
closest = i;
closest_dist = dist;
@@ -695,7 +695,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
polys.write[j] = mtx.xform(points_prev[idx]);
}
- if (Geometry2D::is_point_in_polygon(Vector2(mb->get_position().x, mb->get_position().y), polys)) {
+ if (Geometry2D::is_point_in_polygon(mb->get_position(), polys)) {
erase_index = i;
break;
}
@@ -779,7 +779,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
if (mm.is_valid()) {
if ((mm->get_button_mask() & MOUSE_BUTTON_MASK_MIDDLE) || Input::get_singleton()->is_key_pressed(KEY_SPACE)) {
- Vector2 drag(mm->get_relative().x, mm->get_relative().y);
+ Vector2 drag = mm->get_relative();
uv_hscroll->set_value(uv_hscroll->get_value() - drag.x);
uv_vscroll->set_value(uv_vscroll->get_value() - drag.y);
@@ -791,7 +791,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
switch (uv_move_current) {
case UV_MODE_CREATE: {
if (uv_create) {
- uv_create_to = mtx.affine_inverse().xform(snap_point(Vector2(mm->get_position().x, mm->get_position().y)));
+ uv_create_to = mtx.affine_inverse().xform(snap_point(mm->get_position()));
}
} break;
case UV_MODE_EDIT_POINT: {
@@ -870,7 +870,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
} break;
case UV_MODE_PAINT_WEIGHT:
case UV_MODE_CLEAR_WEIGHT: {
- bone_paint_pos = Vector2(mm->get_position().x, mm->get_position().y);
+ bone_paint_pos = mm->get_position();
} break;
default: {
}
@@ -905,10 +905,10 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
uv_edit_draw->update();
CanvasItemEditor::get_singleton()->update_viewport();
} else if (polygon_create.size()) {
- uv_create_to = mtx.affine_inverse().xform(Vector2(mm->get_position().x, mm->get_position().y));
+ uv_create_to = mtx.affine_inverse().xform(mm->get_position());
uv_edit_draw->update();
} else if (uv_mode == UV_MODE_PAINT_WEIGHT || uv_mode == UV_MODE_CLEAR_WEIGHT) {
- bone_paint_pos = Vector2(mm->get_position().x, mm->get_position().y);
+ bone_paint_pos = mm->get_position();
uv_edit_draw->update();
}
}
@@ -1041,7 +1041,7 @@ void Polygon2DEditor::_uv_draw() {
for (int i = 0; i < uvs.size(); i++) {
int next = uv_draw_max > 0 ? (i + 1) % uv_draw_max : 0;
- if (i < uv_draw_max && uv_drag && uv_move_current == UV_MODE_EDIT_POINT && EDITOR_DEF("editors/poly_editor/show_previous_outline", true)) {
+ if (i < uv_draw_max && uv_drag && uv_move_current == UV_MODE_EDIT_POINT && EDITOR_DEF("editors/polygon_editor/show_previous_outline", true)) {
uv_edit_draw->draw_line(mtx.xform(points_prev[i]), mtx.xform(points_prev[next]), prev_color, Math::round(EDSCALE));
}
diff --git a/editor/plugins/resource_preloader_editor_plugin.cpp b/editor/plugins/resource_preloader_editor_plugin.cpp
index cbea2405b8..eae6916a92 100644
--- a/editor/plugins/resource_preloader_editor_plugin.cpp
+++ b/editor/plugins/resource_preloader_editor_plugin.cpp
@@ -35,9 +35,6 @@
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
-void ResourcePreloaderEditor::_gui_input(Ref<InputEvent> p_event) {
-}
-
void ResourcePreloaderEditor::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
load->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons")));
@@ -335,7 +332,6 @@ void ResourcePreloaderEditor::drop_data_fw(const Point2 &p_point, const Variant
}
void ResourcePreloaderEditor::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_gui_input"), &ResourcePreloaderEditor::_gui_input);
ClassDB::bind_method(D_METHOD("_update_library"), &ResourcePreloaderEditor::_update_library);
ClassDB::bind_method(D_METHOD("_remove_resource", "to_remove"), &ResourcePreloaderEditor::_remove_resource);
diff --git a/editor/plugins/resource_preloader_editor_plugin.h b/editor/plugins/resource_preloader_editor_plugin.h
index bc10b48a16..04ab458eb5 100644
--- a/editor/plugins/resource_preloader_editor_plugin.h
+++ b/editor/plugins/resource_preloader_editor_plugin.h
@@ -75,7 +75,7 @@ class ResourcePreloaderEditor : public PanelContainer {
protected:
void _notification(int p_what);
- void _gui_input(Ref<InputEvent> p_event);
+
static void _bind_methods();
public:
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 5c1e557286..677a5f1f2c 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -37,6 +37,7 @@
#include "core/os/keyboard.h"
#include "core/os/os.h"
#include "editor/debugger/editor_debugger_node.h"
+#include "editor/debugger/script_editor_debugger.h"
#include "editor/editor_node.h"
#include "editor/editor_run_script.h"
#include "editor/editor_scale.h"
@@ -45,6 +46,7 @@
#include "editor/find_in_files.h"
#include "editor/node_dock.h"
#include "editor/plugins/shader_editor_plugin.h"
+#include "modules/visual_script/visual_script_editor.h"
#include "scene/main/window.h"
#include "scene/scene_string_names.h"
#include "script_text_editor.h"
@@ -54,17 +56,17 @@
/*** SYNTAX HIGHLIGHTER ****/
String EditorSyntaxHighlighter::_get_name() const {
- ScriptInstance *si = get_script_instance();
- if (si && si->has_method("_get_name")) {
- return si->call("_get_name");
+ String ret;
+ if (GDVIRTUAL_CALL(_get_name, ret)) {
+ return ret;
}
return "Unnamed";
}
Array EditorSyntaxHighlighter::_get_supported_languages() const {
- ScriptInstance *si = get_script_instance();
- if (si && si->has_method("_get_supported_languages")) {
- return si->call("_get_supported_languages");
+ Array ret;
+ if (GDVIRTUAL_CALL(_get_supported_languages, ret)) {
+ return ret;
}
return Array();
}
@@ -81,9 +83,8 @@ Ref<EditorSyntaxHighlighter> EditorSyntaxHighlighter::_create() const {
void EditorSyntaxHighlighter::_bind_methods() {
ClassDB::bind_method(D_METHOD("_get_edited_resource"), &EditorSyntaxHighlighter::_get_edited_resource);
- BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_name"));
- BIND_VMETHOD(MethodInfo(Variant::ARRAY, "_get_supported_languages"));
- BIND_VMETHOD(MethodInfo(Variant::ARRAY, "_get_supported_extentions"));
+ GDVIRTUAL_BIND(_get_name)
+ GDVIRTUAL_BIND(_get_supported_languages)
}
////
@@ -116,9 +117,9 @@ void EditorStandardSyntaxHighlighter::_update_cache() {
}
/* Autoloads. */
- Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
- for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) {
- const ProjectSettings::AutoloadInfo &info = E->value();
+ OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+ for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
+ const ProjectSettings::AutoloadInfo &info = E.value();
if (info.is_singleton) {
highlighter->add_keyword_color(info.name, usertype_color);
}
@@ -213,6 +214,7 @@ Ref<EditorSyntaxHighlighter> EditorPlainTextSyntaxHighlighter::_create() const {
void ScriptEditorBase::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_base_editor"), &ScriptEditorBase::get_base_editor);
+ ClassDB::bind_method(D_METHOD("add_syntax_highlighter", "highlighter"), &ScriptEditorBase::add_syntax_highlighter);
ADD_SIGNAL(MethodInfo("name_changed"));
ADD_SIGNAL(MethodInfo("edited_script_changed"));
@@ -223,8 +225,6 @@ void ScriptEditorBase::_bind_methods() {
// TODO: This signal is no use for VisualScript.
ADD_SIGNAL(MethodInfo("search_in_files_requested", PropertyInfo(Variant::STRING, "text")));
ADD_SIGNAL(MethodInfo("replace_in_files_requested", PropertyInfo(Variant::STRING, "text")));
-
- BIND_VMETHOD(MethodInfo("_add_syntax_highlighter", PropertyInfo(Variant::OBJECT, "highlighter")));
}
static bool _is_built_in_script(Script *p_script) {
@@ -319,7 +319,7 @@ void ScriptEditorQuickOpen::_sbox_input(const Ref<InputEvent> &p_ie) {
k->get_keycode() == KEY_DOWN ||
k->get_keycode() == KEY_PAGEUP ||
k->get_keycode() == KEY_PAGEDOWN)) {
- search_options->call("_gui_input", k);
+ search_options->gui_input(k);
search_box->accept_event();
}
}
@@ -482,6 +482,75 @@ void ScriptEditor::_clear_execution(REF p_script) {
}
}
+void ScriptEditor::_set_breakpoint(REF p_script, int p_line, bool p_enabled) {
+ Ref<Script> script = Object::cast_to<Script>(*p_script);
+ if (script.is_valid() && (script->has_source_code() || script->get_path().is_resource_file())) {
+ // Update if open.
+ for (int i = 0; i < tab_container->get_child_count(); i++) {
+ ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
+ if (se && se->get_edited_resource()->get_path() == script->get_path()) {
+ se->set_breakpoint(p_line, p_enabled);
+ return;
+ }
+ }
+
+ // Handle closed.
+ Dictionary state = script_editor_cache->get_value(script->get_path(), "state");
+ Array breakpoints;
+ if (state.has("breakpoints")) {
+ breakpoints = state["breakpoints"];
+ }
+
+ if (breakpoints.has(p_line)) {
+ if (!p_enabled) {
+ breakpoints.erase(p_line);
+ }
+ } else if (p_enabled) {
+ breakpoints.push_back(p_line);
+ }
+ state["breakpoints"] = breakpoints;
+ script_editor_cache->set_value(script->get_path(), "state", state);
+ EditorDebuggerNode::get_singleton()->set_breakpoint(script->get_path(), p_line + 1, false);
+ }
+}
+
+void ScriptEditor::_clear_breakpoints() {
+ for (int i = 0; i < tab_container->get_child_count(); i++) {
+ ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
+ if (se) {
+ se->clear_breakpoints();
+ }
+ }
+
+ // Clear from closed scripts.
+ List<String> cached_editors;
+ script_editor_cache->get_sections(&cached_editors);
+ for (const String &E : cached_editors) {
+ Array breakpoints = _get_cached_breakpoints_for_script(E);
+ for (int i = 0; i < breakpoints.size(); i++) {
+ EditorDebuggerNode::get_singleton()->set_breakpoint(E, (int)breakpoints[i] + 1, false);
+ }
+
+ if (breakpoints.size() > 0) {
+ Dictionary state = script_editor_cache->get_value(E, "state");
+ state["breakpoints"] = Array();
+ script_editor_cache->set_value(E, "state", state);
+ }
+ }
+}
+
+Array ScriptEditor::_get_cached_breakpoints_for_script(const String &p_path) const {
+ if (!ResourceLoader::exists(p_path, "Script") || p_path.begins_with("local://") || !script_editor_cache->has_section_key(p_path, "state")) {
+ return Array();
+ }
+
+ Dictionary state = script_editor_cache->get_value(p_path, "state");
+ if (!state.has("breakpoints")) {
+ return Array();
+ }
+ return state["breakpoints"];
+}
+
ScriptEditorBase *ScriptEditor::_get_current_editor() const {
int selected = tab_container->get_current_tab();
if (selected < 0 || selected >= tab_container->get_child_count()) {
@@ -694,20 +763,24 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) {
ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tselected);
if (current) {
- Ref<Script> script = current->get_edited_resource();
- if (p_save && script.is_valid()) {
+ RES file = current->get_edited_resource();
+ if (p_save && file.is_valid()) {
// Do not try to save internal scripts, but prompt to save in-memory
// scripts which are not saved to disk yet (have empty path).
- if (script->get_path().find("local://") == -1 && script->get_path().find("::") == -1) {
+ if (file->get_path().find("local://") == -1 && file->get_path().find("::") == -1) {
save_current_script();
}
}
- if (script.is_valid()) {
- if (!script->get_path().is_empty()) {
+ if (file.is_valid()) {
+ if (!file->get_path().is_empty()) {
// Only saved scripts can be restored.
- previous_scripts.push_back(script->get_path());
+ previous_scripts.push_back(file->get_path());
+ }
+
+ Ref<Script> script = file;
+ if (script.is_valid()) {
+ notify_script_close(script);
}
- notify_script_close(script);
}
}
@@ -734,6 +807,7 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) {
int idx = tab_container->get_current_tab();
if (current) {
current->clear_edit_menu();
+ _save_editor_state(current);
}
memdelete(tselected);
if (idx >= tab_container->get_child_count()) {
@@ -786,39 +860,35 @@ void ScriptEditor::_copy_script_path() {
}
void ScriptEditor::_close_other_tabs() {
- int child_count = tab_container->get_child_count();
int current_idx = tab_container->get_current_tab();
- for (int i = child_count - 1; i >= 0; i--) {
- if (i == current_idx) {
- continue;
+ for (int i = tab_container->get_child_count() - 1; i >= 0; i--) {
+ if (i != current_idx) {
+ script_close_queue.push_back(i);
}
-
- tab_container->set_current_tab(i);
- ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
-
- if (se) {
- // Maybe there are unsaved changes
- if (se->is_unsaved()) {
- _ask_close_current_unsaved_tab(se);
- continue;
- }
- }
-
- _close_current_tab(false);
}
+ _queue_close_tabs();
}
void ScriptEditor::_close_all_tabs() {
- int child_count = tab_container->get_child_count();
- for (int i = child_count - 1; i >= 0; i--) {
- tab_container->set_current_tab(i);
- ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
+ for (int i = tab_container->get_child_count() - 1; i >= 0; i--) {
+ script_close_queue.push_back(i);
+ }
+ _queue_close_tabs();
+}
+
+void ScriptEditor::_queue_close_tabs() {
+ while (!script_close_queue.is_empty()) {
+ int idx = script_close_queue.front()->get();
+ script_close_queue.pop_front();
+ tab_container->set_current_tab(idx);
+ ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(idx));
if (se) {
- // Maybe there are unsaved changes
+ // Maybe there are unsaved changes.
if (se->is_unsaved()) {
_ask_close_current_unsaved_tab(se);
- continue;
+ erase_tab_confirm->connect(SceneStringNames::get_singleton()->visibility_changed, callable_mp(this, &ScriptEditor::_queue_close_tabs), varray(), CONNECT_ONESHOT);
+ break;
}
}
@@ -1012,35 +1082,21 @@ void ScriptEditor::_file_dialog_action(String p_file) {
}
file->close();
memdelete(file);
- [[fallthrough]];
- }
- case FILE_OPEN: {
- List<String> extensions;
- ResourceLoader::get_recognized_extensions_for_type("Script", &extensions);
- if (extensions.find(p_file.get_extension())) {
- Ref<Script> scr = ResourceLoader::load(p_file);
- if (!scr.is_valid()) {
- editor->show_warning(TTR("Could not load file at:") + "\n\n" + p_file, TTR("Error!"));
- file_dialog_option = -1;
- return;
- }
-
- edit(scr);
- file_dialog_option = -1;
- return;
- }
- Error error;
- Ref<TextFile> text_file = _load_text_file(p_file, &error);
- if (error != OK) {
- editor->show_warning(TTR("Could not load file at:") + "\n\n" + p_file, TTR("Error!"));
+ if (EditorFileSystem::get_singleton()) {
+ if (textfile_extensions.has(p_file.get_extension())) {
+ EditorFileSystem::get_singleton()->update_file(p_file);
+ }
}
- if (text_file.is_valid()) {
- edit(text_file);
- file_dialog_option = -1;
+ if (!open_textfile_after_create) {
return;
}
+ [[fallthrough]];
+ }
+ case FILE_OPEN: {
+ open_file(p_file);
+ file_dialog_option = -1;
} break;
case FILE_SAVE_AS: {
ScriptEditorBase *current = _get_current_editor();
@@ -1115,8 +1171,12 @@ void ScriptEditor::_menu_option(int p_option) {
file_dialog_option = FILE_NEW_TEXTFILE;
file_dialog->clear_filters();
+ for (const String &E : textfile_extensions) {
+ file_dialog->add_filter("*." + E + " ; " + E.to_upper());
+ }
file_dialog->popup_file_dialog();
file_dialog->set_title(TTR("New Text File..."));
+ open_textfile_after_create = true;
} break;
case FILE_OPEN: {
file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
@@ -1130,6 +1190,10 @@ void ScriptEditor::_menu_option(int p_option) {
file_dialog->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
}
+ for (const String &E : textfile_extensions) {
+ file_dialog->add_filter("*." + E + " ; " + E.to_upper());
+ }
+
file_dialog->popup_file_dialog();
file_dialog->set_title(TTR("Open File"));
return;
@@ -1215,14 +1279,15 @@ void ScriptEditor::_menu_option(int p_option) {
_update_script_names();
} break;
case TOGGLE_SCRIPTS_PANEL: {
+ toggle_scripts_panel();
if (current) {
- ScriptTextEditor *editor = Object::cast_to<ScriptTextEditor>(current);
- toggle_scripts_panel();
- if (editor) {
- editor->update_toggle_scripts_button();
- }
+ current->update_toggle_scripts_button();
} else {
- toggle_scripts_panel();
+ Control *tab = tab_container->get_current_tab_control();
+ EditorHelp *editor_help = Object::cast_to<EditorHelp>(tab);
+ if (editor_help) {
+ editor_help->update_toggle_scripts_button();
+ }
}
}
}
@@ -1469,6 +1534,7 @@ void ScriptEditor::_notification(int p_what) {
editor->connect("stop_pressed", callable_mp(this, &ScriptEditor::_editor_stop));
editor->connect("script_add_function_request", callable_mp(this, &ScriptEditor::_add_callback));
editor->connect("resource_saved", callable_mp(this, &ScriptEditor::_res_saved_callback));
+ editor->get_filesystem_dock()->connect("files_moved", callable_mp(this, &ScriptEditor::_files_moved));
editor->get_filesystem_dock()->connect("file_removed", callable_mp(this, &ScriptEditor::_file_removed));
script_list->connect("item_selected", callable_mp(this, &ScriptEditor::_script_selected));
@@ -1478,6 +1544,7 @@ void ScriptEditor::_notification(int p_what) {
EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &ScriptEditor::_editor_settings_changed));
EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &ScriptEditor::_filesystem_changed));
+ _editor_settings_changed();
[[fallthrough]];
}
case NOTIFICATION_TRANSLATION_CHANGED:
@@ -1581,6 +1648,7 @@ void ScriptEditor::notify_script_changed(const Ref<Script> &p_script) {
}
void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) {
+ Set<String> loaded_scripts;
for (int i = 0; i < tab_container->get_child_count(); i++) {
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
if (!se) {
@@ -1593,6 +1661,7 @@ void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) {
}
String base = script->get_path();
+ loaded_scripts.insert(base);
if (base.begins_with("local://") || base == "") {
continue;
}
@@ -1602,6 +1671,20 @@ void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) {
p_breakpoints->push_back(base + ":" + itos((int)bpoints[j] + 1));
}
}
+
+ // Load breakpoints that are in closed scripts.
+ List<String> cached_editors;
+ script_editor_cache->get_sections(&cached_editors);
+ for (const String &E : cached_editors) {
+ if (loaded_scripts.has(E)) {
+ continue;
+ }
+
+ Array breakpoints = _get_cached_breakpoints_for_script(E);
+ for (int i = 0; i < breakpoints.size(); i++) {
+ p_breakpoints->push_back(E + ":" + itos((int)breakpoints[i] + 1));
+ }
+ }
}
void ScriptEditor::_members_overview_selected(int p_idx) {
@@ -2037,7 +2120,7 @@ void ScriptEditor::_update_script_connections() {
}
}
-Ref<TextFile> ScriptEditor::_load_text_file(const String &p_path, Error *r_error) {
+Ref<TextFile> ScriptEditor::_load_text_file(const String &p_path, Error *r_error) const {
if (r_error) {
*r_error = ERR_FILE_CANT_OPEN;
}
@@ -2259,6 +2342,10 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
_add_recent_script(p_resource->get_path());
}
+ if (script_editor_cache->has_section(p_resource->get_path())) {
+ se->set_edit_state(script_editor_cache->get_value(p_resource->get_path(), "state"));
+ }
+
_sort_list_on_update = true;
_update_script_names();
_save_layout();
@@ -2417,6 +2504,41 @@ void ScriptEditor::open_script_create_dialog(const String &p_base_name, const St
script_create_dialog->config(p_base_name, p_base_path);
}
+void ScriptEditor::open_text_file_create_dialog(const String &p_base_path, const String &p_base_name) {
+ file_dialog->set_current_file(p_base_name);
+ file_dialog->set_current_dir(p_base_path);
+ _menu_option(FILE_NEW_TEXTFILE);
+ open_textfile_after_create = false;
+}
+
+RES ScriptEditor::open_file(const String &p_file) {
+ List<String> extensions;
+ ResourceLoader::get_recognized_extensions_for_type("Script", &extensions);
+ if (extensions.find(p_file.get_extension())) {
+ Ref<Script> scr = ResourceLoader::load(p_file);
+ if (!scr.is_valid()) {
+ editor->show_warning(TTR("Could not load file at:") + "\n\n" + p_file, TTR("Error!"));
+ return RES();
+ }
+
+ edit(scr);
+ return scr;
+ }
+
+ Error error;
+ Ref<TextFile> text_file = _load_text_file(p_file, &error);
+ if (error != OK) {
+ editor->show_warning(TTR("Could not load file at:") + "\n\n" + p_file, TTR("Error!"));
+ return RES();
+ }
+
+ if (text_file.is_valid()) {
+ edit(text_file);
+ return text_file;
+ }
+ return RES();
+}
+
void ScriptEditor::_editor_stop() {
for (int i = 0; i < tab_container->get_child_count(); i++) {
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
@@ -2459,6 +2581,20 @@ void ScriptEditor::_add_callback(Object *p_obj, const String &p_function, const
}
}
+void ScriptEditor::_save_editor_state(ScriptEditorBase *p_editor) {
+ if (restoring_layout) {
+ return;
+ }
+
+ const String &path = p_editor->get_edited_resource()->get_path();
+ if (!path.is_resource_file()) {
+ return;
+ }
+
+ script_editor_cache->set_value(path, "state", p_editor->get_edit_state());
+ // This is saved later when we save the editor layout.
+}
+
void ScriptEditor::_save_layout() {
if (restoring_layout) {
return;
@@ -2468,6 +2604,12 @@ void ScriptEditor::_save_layout() {
}
void ScriptEditor::_editor_settings_changed() {
+ textfile_extensions.clear();
+ const Vector<String> textfile_ext = ((String)(EditorSettings::get_singleton()->get("docks/filesystem/textfile_extensions"))).split(",", false);
+ for (const String &E : textfile_ext) {
+ textfile_extensions.insert(E);
+ }
+
trim_trailing_whitespace_on_save = EditorSettings::get_singleton()->get("text_editor/behavior/files/trim_trailing_whitespace_on_save");
convert_indent_on_save = EditorSettings::get_singleton()->get("text_editor/behavior/files/convert_indent_on_save");
use_space_indentation = EditorSettings::get_singleton()->get("text_editor/behavior/indent/type");
@@ -2504,6 +2646,26 @@ void ScriptEditor::_filesystem_changed() {
_update_script_names();
}
+void ScriptEditor::_files_moved(const String &p_old_file, const String &p_new_file) {
+ if (!script_editor_cache->has_section(p_old_file)) {
+ return;
+ }
+ Variant state = script_editor_cache->get_value(p_old_file, "state");
+ script_editor_cache->erase_section(p_old_file);
+ script_editor_cache->set_value(p_new_file, "state", state);
+
+ // If Script, update breakpoints with debugger.
+ Array breakpoints = _get_cached_breakpoints_for_script(p_new_file);
+ for (int i = 0; i < breakpoints.size(); i++) {
+ int line = (int)breakpoints[i] + 1;
+ EditorDebuggerNode::get_singleton()->set_breakpoint(p_old_file, line, false);
+ if (!p_new_file.begins_with("local://") && ResourceLoader::exists(p_new_file, "Script")) {
+ EditorDebuggerNode::get_singleton()->set_breakpoint(p_new_file, line, true);
+ }
+ }
+ // This is saved later when we save the editor layout.
+}
+
void ScriptEditor::_file_removed(const String &p_removed_file) {
for (int i = 0; i < tab_container->get_child_count(); i++) {
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
@@ -2515,6 +2677,15 @@ void ScriptEditor::_file_removed(const String &p_removed_file) {
_close_tab(i, false, false);
}
}
+
+ // Check closed.
+ if (script_editor_cache->has_section(p_removed_file)) {
+ Array breakpoints = _get_cached_breakpoints_for_script(p_removed_file);
+ for (int i = 0; i < breakpoints.size(); i++) {
+ EditorDebuggerNode::get_singleton()->set_breakpoint(p_removed_file, (int)breakpoints[i] + 1, false);
+ }
+ script_editor_cache->erase_section(p_removed_file);
+ }
}
void ScriptEditor::_update_find_replace_bar() {
@@ -2645,12 +2816,22 @@ bool ScriptEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data
if (file == "" || !FileAccess::exists(file)) {
continue;
}
- Ref<Script> scr = ResourceLoader::load(file);
- if (scr.is_valid()) {
- return true;
+ if (ResourceLoader::exists(file, "Script")) {
+ Ref<Script> scr = ResourceLoader::load(file);
+ if (scr.is_valid()) {
+ return true;
+ }
+ }
+
+ if (textfile_extensions.has(file.get_extension())) {
+ Error err;
+ Ref<TextFile> text_file = _load_text_file(file, &err);
+ if (text_file.is_valid() && err == OK) {
+ return true;
+ }
}
}
- return true;
+ return false;
}
return false;
@@ -2715,9 +2896,13 @@ void ScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Co
if (file == "" || !FileAccess::exists(file)) {
continue;
}
- Ref<Script> scr = ResourceLoader::load(file);
- if (scr.is_valid()) {
- edit(scr);
+
+ if (!ResourceLoader::exists(file, "Script") && !textfile_extensions.has(file.get_extension())) {
+ continue;
+ }
+
+ RES res = open_file(file);
+ if (res.is_valid()) {
if (tab_container->get_child_count() > num_tabs_before) {
tab_container->move_child(tab_container->get_child(tab_container->get_child_count() - 1), new_index);
num_tabs_before = tab_container->get_child_count();
@@ -2731,7 +2916,30 @@ void ScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Co
}
}
-void ScriptEditor::_unhandled_key_input(const Ref<InputEvent> &p_event) {
+void ScriptEditor::input(const Ref<InputEvent> &p_event) {
+ // This is implemented in `input()` rather than `unhandled_input()` to allow
+ // the shortcut to be used regardless of the click location.
+ // This feature can be disabled to avoid interfering with other uses of the additional
+ // mouse buttons, such as push-to-talk in a VoIP program.
+ if (EDITOR_GET("interface/editor/mouse_extra_buttons_navigate_history")) {
+ const Ref<InputEventMouseButton> mb = p_event;
+
+ // Navigate the script history using additional mouse buttons present on some mice.
+ // This must be hardcoded as the editor shortcuts dialog doesn't allow assigning
+ // more than one shortcut per action.
+ if (mb.is_valid() && mb->is_pressed() && is_visible_in_tree()) {
+ if (mb->get_button_index() == MOUSE_BUTTON_XBUTTON1) {
+ _history_back();
+ }
+
+ if (mb->get_button_index() == MOUSE_BUTTON_XBUTTON2) {
+ _history_forward();
+ }
+ }
+ }
+}
+
+void ScriptEditor::unhandled_key_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
if (!is_visible_in_tree() || !p_event->is_pressed() || p_event->is_echo()) {
@@ -2842,6 +3050,7 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) {
restoring_layout = true;
+ Set<String> loaded_scripts;
List<String> extensions;
ResourceLoader::get_recognized_extensions_for_type("Script", &extensions);
@@ -2854,8 +3063,12 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) {
}
if (!FileAccess::exists(path)) {
+ if (script_editor_cache->has_section(path)) {
+ script_editor_cache->erase_section(path);
+ }
continue;
}
+ loaded_scripts.insert(path);
if (extensions.find(path.get_extension())) {
Ref<Script> scr = ResourceLoader::load(path);
@@ -2900,6 +3113,26 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) {
script_split->set_split_offset(p_layout->get_value("ScriptEditor", "split_offset"));
}
+ // Remove any deleted editors that have been removed between launches.
+ // and if a Script, register breakpoints with the debugger.
+ List<String> cached_editors;
+ script_editor_cache->get_sections(&cached_editors);
+ for (const String &E : cached_editors) {
+ if (loaded_scripts.has(E)) {
+ continue;
+ }
+
+ if (!FileAccess::exists(E)) {
+ script_editor_cache->erase_section(E);
+ continue;
+ }
+
+ Array breakpoints = _get_cached_breakpoints_for_script(E);
+ for (int i = 0; i < breakpoints.size(); i++) {
+ EditorDebuggerNode::get_singleton()->set_breakpoint(E, (int)breakpoints[i] + 1, true);
+ }
+ }
+
restoring_layout = false;
_update_script_names();
@@ -2917,11 +3150,8 @@ void ScriptEditor::get_window_layout(Ref<ConfigFile> p_layout) {
continue;
}
- Dictionary script_info;
- script_info["path"] = path;
- script_info["state"] = se->get_edit_state();
-
- scripts.push_back(script_info);
+ _save_editor_state(se);
+ scripts.push_back(path);
}
EditorHelp *eh = Object::cast_to<EditorHelp>(tab_container->get_child(i));
@@ -2934,6 +3164,9 @@ void ScriptEditor::get_window_layout(Ref<ConfigFile> p_layout) {
p_layout->set_value("ScriptEditor", "open_scripts", scripts);
p_layout->set_value("ScriptEditor", "open_help", helps);
p_layout->set_value("ScriptEditor", "split_offset", script_split->get_split_offset());
+
+ // Save the cache.
+ script_editor_cache->save(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("script_editor_cache.cfg"));
}
void ScriptEditor::_help_class_open(const String &p_class) {
@@ -3275,7 +3508,6 @@ void ScriptEditor::_bind_methods() {
ClassDB::bind_method("_update_script_connections", &ScriptEditor::_update_script_connections);
ClassDB::bind_method("_help_class_open", &ScriptEditor::_help_class_open);
ClassDB::bind_method("_live_auto_reload_running_scripts", &ScriptEditor::_live_auto_reload_running_scripts);
- ClassDB::bind_method("_unhandled_key_input", &ScriptEditor::_unhandled_key_input);
ClassDB::bind_method("_update_members_overview", &ScriptEditor::_update_members_overview);
ClassDB::bind_method("_update_recent_scripts", &ScriptEditor::_update_recent_scripts);
@@ -3301,6 +3533,9 @@ void ScriptEditor::_bind_methods() {
ScriptEditor::ScriptEditor(EditorNode *p_editor) {
current_theme = "";
+ script_editor_cache.instantiate();
+ script_editor_cache->load(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("script_editor_cache.cfg"));
+
completion_cache = memnew(EditorScriptCodeCompletionCache);
restoring_layout = false;
waiting_update_names = false;
@@ -3408,9 +3643,11 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
ED_SHORTCUT("script_editor/window_sort", TTR("Sort"));
ED_SHORTCUT("script_editor/window_move_up", TTR("Move Up"), KEY_MASK_SHIFT | KEY_MASK_ALT | KEY_UP);
ED_SHORTCUT("script_editor/window_move_down", TTR("Move Down"), KEY_MASK_SHIFT | KEY_MASK_ALT | KEY_DOWN);
- ED_SHORTCUT("script_editor/next_script", TTR("Next Script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_PERIOD); // these should be KEY_GREATER and KEY_LESS but those don't work
+ // FIXME: These should be `KEY_GREATER` and `KEY_LESS` but those don't work.
+ ED_SHORTCUT("script_editor/next_script", TTR("Next Script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_PERIOD);
ED_SHORTCUT("script_editor/prev_script", TTR("Previous Script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_COMMA);
- set_process_unhandled_key_input(true);
+ set_process_input(true);
+ set_process_unhandled_input(true);
file_menu = memnew(MenuButton);
file_menu->set_text(TTR("File"));
@@ -3487,6 +3724,8 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
debugger->connect("set_execution", callable_mp(this, &ScriptEditor::_set_execution));
debugger->connect("clear_execution", callable_mp(this, &ScriptEditor::_clear_execution));
debugger->connect("breaked", callable_mp(this, &ScriptEditor::_breaked));
+ debugger->get_default_debugger()->connect("set_breakpoint", callable_mp(this, &ScriptEditor::_set_breakpoint));
+ debugger->get_default_debugger()->connect("clear_breakpoints", callable_mp(this, &ScriptEditor::_clear_breakpoints));
menu_hb->add_spacer();
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index e322828b6c..8caebc1c8c 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -56,6 +56,9 @@ private:
protected:
static void _bind_methods();
+ GDVIRTUAL0RC(String, _get_name)
+ GDVIRTUAL0RC(Array, _get_supported_languages)
+
public:
virtual String _get_name() const;
virtual Array _get_supported_languages() const;
@@ -74,7 +77,7 @@ private:
public:
virtual void _update_cache() override;
- virtual Dictionary _get_line_syntax_highlighting(int p_line) override { return highlighter->get_line_syntax_highlighting(p_line); }
+ virtual Dictionary _get_line_syntax_highlighting_impl(int p_line) override { return highlighter->get_line_syntax_highlighting(p_line); }
virtual String _get_name() const override { return TTR("Standard"); }
@@ -152,10 +155,13 @@ public:
virtual void tag_saved_version() = 0;
virtual void reload(bool p_soft) {}
virtual Array get_breakpoints() = 0;
+ virtual void set_breakpoint(int p_line, bool p_enabled) = 0;
+ virtual void clear_breakpoints() = 0;
virtual void add_callback(const String &p_function, PackedStringArray p_args) = 0;
virtual void update_settings() = 0;
virtual void set_debugger_active(bool p_active) = 0;
virtual bool can_lose_focus_on_node_selection() { return true; }
+ virtual void update_toggle_scripts_button() {}
virtual bool show_members_overview() = 0;
@@ -303,6 +309,7 @@ class ScriptEditor : public PanelContainer {
int history_pos;
List<String> previous_scripts;
+ List<int> script_close_queue;
void _tab_changed(int p_which);
void _menu_option(int p_option);
@@ -335,6 +342,7 @@ class ScriptEditor : public PanelContainer {
void _close_docs_tab();
void _close_other_tabs();
void _close_all_tabs();
+ void _queue_close_tabs();
void _copy_script_path();
@@ -358,6 +366,7 @@ class ScriptEditor : public PanelContainer {
void _add_callback(Object *p_obj, const String &p_function, const PackedStringArray &p_args);
void _res_saved_callback(const Ref<Resource> &p_res);
+ bool open_textfile_after_create = true;
bool trim_trailing_whitespace_on_save;
bool use_space_indentation;
bool convert_indent_on_save;
@@ -371,13 +380,19 @@ class ScriptEditor : public PanelContainer {
void _breaked(bool p_breaked, bool p_can_debug);
void _update_window_menu();
void _script_created(Ref<Script> p_script);
+ void _set_breakpoint(REF p_scrpt, int p_line, bool p_enabled);
+ void _clear_breakpoints();
+ Array _get_cached_breakpoints_for_script(const String &p_path) const;
ScriptEditorBase *_get_current_editor() const;
Array _get_open_script_editors() const;
+ Ref<ConfigFile> script_editor_cache;
+ void _save_editor_state(ScriptEditorBase *p_editor);
void _save_layout();
void _editor_settings_changed();
void _filesystem_changed();
+ void _files_moved(const String &p_old_file, const String &p_new_file);
void _file_removed(const String &p_file);
void _autosave_scripts();
void _update_autosave_timer();
@@ -408,7 +423,8 @@ class ScriptEditor : public PanelContainer {
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);
- void _unhandled_key_input(const Ref<InputEvent> &p_event);
+ virtual void input(const Ref<InputEvent> &p_event) override;
+ virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override;
void _script_list_gui_input(const Ref<InputEvent> &ev);
void _make_script_list_context_menu();
@@ -437,7 +453,8 @@ class ScriptEditor : public PanelContainer {
Ref<Script> _get_current_script();
Array _get_open_scripts() const;
- Ref<TextFile> _load_text_file(const String &p_path, Error *r_error);
+ Set<String> textfile_extensions;
+ Ref<TextFile> _load_text_file(const String &p_path, Error *r_error) const;
Error _save_text_file(Ref<TextFile> p_text_file, const String &p_path);
void _on_find_in_files_requested(String text);
@@ -461,6 +478,8 @@ public:
bool is_scripts_panel_toggled();
void apply_scripts() const;
void open_script_create_dialog(const String &p_base_name, const String &p_base_path);
+ void open_text_file_create_dialog(const String &p_base_path, const String &p_base_name = "");
+ RES open_file(const String &p_file);
void ensure_select_current();
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 5f48106afc..2c02389db2 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -158,7 +158,6 @@ void ScriptTextEditor::enable_editor() {
editor_enabled = true;
_enable_code_editor();
- _set_theme_for_script();
_validate_script();
}
@@ -434,10 +433,12 @@ void ScriptTextEditor::_validate_script() {
int warning_nb = warnings.size();
warnings_panel->clear();
+ bool has_connections_table = false;
// Add missing connections.
if (GLOBAL_GET("debug/gdscript/warnings/enable").booleanize()) {
Node *base = get_tree()->get_edited_scene_root();
if (base && missing_connections.size() > 0) {
+ has_connections_table = true;
warnings_panel->push_table(1);
for (const Connection &connection : missing_connections) {
String base_path = base->get_name();
@@ -459,6 +460,10 @@ void ScriptTextEditor::_validate_script() {
code_editor->set_error_count(errors.size());
code_editor->set_warning_count(warning_nb);
+ if (has_connections_table) {
+ warnings_panel->add_newline();
+ }
+
// Add script warnings.
warnings_panel->push_table(3);
for (const ScriptLanguage::Warning &w : warnings) {
@@ -831,7 +836,7 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c
if (info.is_singleton) {
EditorNode::get_singleton()->load_scene(info.path);
}
- } else if (p_symbol.is_rel_path()) {
+ } else if (p_symbol.is_relative_path()) {
// Every symbol other than absolute path is relative path so keep this condition at last.
String path = _get_absolute_path(p_symbol);
if (FileAccess::exists(path)) {
@@ -858,7 +863,7 @@ void ScriptTextEditor::_validate_symbol(const String &p_symbol) {
ScriptLanguage::LookupResult result;
if (ScriptServer::is_global_class(p_symbol) || p_symbol.is_resource_file() || script->get_language()->lookup_code(code_editor->get_text_editor()->get_text_for_symbol_lookup(), p_symbol, script->get_path(), base, result) == OK || (ProjectSettings::get_singleton()->has_autoload(p_symbol) && ProjectSettings::get_singleton()->get_autoload(p_symbol).is_singleton)) {
text_edit->set_symbol_lookup_word_as_valid(true);
- } else if (p_symbol.is_rel_path()) {
+ } else if (p_symbol.is_relative_path()) {
String path = _get_absolute_path(p_symbol);
if (FileAccess::exists(path)) {
text_edit->set_symbol_lookup_word_as_valid(true);
@@ -877,9 +882,7 @@ String ScriptTextEditor::_get_absolute_path(const String &rel_path) {
}
void ScriptTextEditor::update_toggle_scripts_button() {
- if (code_editor != nullptr) {
- code_editor->update_toggle_scripts_button();
- }
+ code_editor->update_toggle_scripts_button();
}
void ScriptTextEditor::_update_connected_methods() {
@@ -1243,7 +1246,7 @@ void ScriptTextEditor::_edit_option(int p_op) {
tx->set_caret_line(bpoints[bpoints.size() - 1]);
tx->center_viewport_to_caret();
} else {
- for (int i = bpoints.size(); i >= 0; i--) {
+ for (int i = bpoints.size() - 1; i >= 0; i--) {
int bline = bpoints[i];
if (bline < line) {
tx->unfold_line(bline);
@@ -1338,8 +1341,6 @@ void ScriptTextEditor::_bind_methods() {
ClassDB::bind_method("_get_drag_data_fw", &ScriptTextEditor::get_drag_data_fw);
ClassDB::bind_method("_can_drop_data_fw", &ScriptTextEditor::can_drop_data_fw);
ClassDB::bind_method("_drop_data_fw", &ScriptTextEditor::drop_data_fw);
-
- ClassDB::bind_method(D_METHOD("add_syntax_highlighter", "highlighter"), &ScriptTextEditor::add_syntax_highlighter);
}
Control *ScriptTextEditor::get_edit_menu() {
@@ -1370,6 +1371,14 @@ Array ScriptTextEditor::get_breakpoints() {
return code_editor->get_text_editor()->get_breakpointed_lines();
}
+void ScriptTextEditor::set_breakpoint(int p_line, bool p_enabled) {
+ code_editor->get_text_editor()->set_line_as_breakpoint(p_line, p_enabled);
+}
+
+void ScriptTextEditor::clear_breakpoints() {
+ code_editor->get_text_editor()->clear_breakpointed_lines();
+}
+
void ScriptTextEditor::set_tooltip_request_func(String p_method, Object *p_obj) {
code_editor->get_text_editor()->set_tooltip_request_func(p_obj, p_method, this);
}
@@ -1696,7 +1705,6 @@ void ScriptTextEditor::_enable_code_editor() {
code_editor->connect("show_warnings_panel", callable_mp(this, &ScriptTextEditor::_show_warnings_panel));
code_editor->connect("validate_script", callable_mp(this, &ScriptTextEditor::_validate_script));
code_editor->connect("load_theme_settings", callable_mp(this, &ScriptTextEditor::_load_theme_settings));
- code_editor->get_text_editor()->connect("breakpoint_toggled", callable_mp(this, &ScriptTextEditor::_breakpoint_toggled));
code_editor->get_text_editor()->connect("symbol_lookup", callable_mp(this, &ScriptTextEditor::_lookup_symbol));
code_editor->get_text_editor()->connect("symbol_validate", callable_mp(this, &ScriptTextEditor::_validate_symbol));
code_editor->get_text_editor()->connect("gutter_added", callable_mp(this, &ScriptTextEditor::_update_gutter_indexes));
@@ -1836,6 +1844,7 @@ ScriptTextEditor::ScriptTextEditor() {
code_editor->get_text_editor()->set_draw_breakpoints_gutter(true);
code_editor->get_text_editor()->set_draw_executing_lines_gutter(true);
+ code_editor->get_text_editor()->connect("breakpoint_toggled", callable_mp(this, &ScriptTextEditor::_breakpoint_toggled));
connection_gutter = 1;
code_editor->get_text_editor()->add_gutter(connection_gutter);
@@ -1956,11 +1965,8 @@ void ScriptTextEditor::register_editor() {
ED_SHORTCUT("script_text_editor/toggle_fold_line", TTR("Fold/Unfold Line"), KEY_MASK_ALT | KEY_F);
ED_SHORTCUT("script_text_editor/fold_all_lines", TTR("Fold All Lines"), KEY_NONE);
ED_SHORTCUT("script_text_editor/unfold_all_lines", TTR("Unfold All Lines"), KEY_NONE);
-#ifdef OSX_ENABLED
- ED_SHORTCUT("script_text_editor/duplicate_selection", TTR("Duplicate Selection"), KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_C);
-#else
ED_SHORTCUT("script_text_editor/duplicate_selection", TTR("Duplicate Selection"), KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_D);
-#endif
+ ED_SHORTCUT_OVERRIDE("script_text_editor/duplicate_selection", "macos", KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_C);
ED_SHORTCUT("script_text_editor/evaluate_selection", TTR("Evaluate Selection"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_E);
ED_SHORTCUT("script_text_editor/trim_trailing_whitespace", TTR("Trim Trailing Whitespace"), KEY_MASK_CMD | KEY_MASK_ALT | KEY_T);
ED_SHORTCUT("script_text_editor/convert_indent_to_spaces", TTR("Convert Indent to Spaces"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Y);
@@ -1968,42 +1974,35 @@ void ScriptTextEditor::register_editor() {
ED_SHORTCUT("script_text_editor/auto_indent", TTR("Auto Indent"), KEY_MASK_CMD | KEY_I);
ED_SHORTCUT_AND_COMMAND("script_text_editor/find", TTR("Find..."), KEY_MASK_CMD | KEY_F);
-#ifdef OSX_ENABLED
- ED_SHORTCUT("script_text_editor/find_next", TTR("Find Next"), KEY_MASK_CMD | KEY_G);
- ED_SHORTCUT("script_text_editor/find_previous", TTR("Find Previous"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_G);
- ED_SHORTCUT_AND_COMMAND("script_text_editor/replace", TTR("Replace..."), KEY_MASK_ALT | KEY_MASK_CMD | KEY_F);
-#else
+
ED_SHORTCUT("script_text_editor/find_next", TTR("Find Next"), KEY_F3);
+ ED_SHORTCUT_OVERRIDE("script_text_editor/find_next", "macos", KEY_MASK_CMD | KEY_G);
+
ED_SHORTCUT("script_text_editor/find_previous", TTR("Find Previous"), KEY_MASK_SHIFT | KEY_F3);
+ ED_SHORTCUT_OVERRIDE("script_text_editor/find_previous", "macos", KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_G);
+
ED_SHORTCUT_AND_COMMAND("script_text_editor/replace", TTR("Replace..."), KEY_MASK_CMD | KEY_R);
-#endif
+ ED_SHORTCUT_OVERRIDE("script_text_editor/replace", "macos", KEY_MASK_ALT | KEY_MASK_CMD | KEY_F);
ED_SHORTCUT("script_text_editor/find_in_files", TTR("Find in Files..."), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F);
ED_SHORTCUT("script_text_editor/replace_in_files", TTR("Replace in Files..."), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_R);
-#ifdef OSX_ENABLED
- ED_SHORTCUT("script_text_editor/contextual_help", TTR("Contextual Help"), KEY_MASK_ALT | KEY_MASK_SHIFT | KEY_SPACE);
-#else
ED_SHORTCUT("script_text_editor/contextual_help", TTR("Contextual Help"), KEY_MASK_ALT | KEY_F1);
-#endif
+ ED_SHORTCUT_OVERRIDE("script_text_editor/contextual_help", "macos", KEY_MASK_ALT | KEY_MASK_SHIFT | KEY_SPACE);
ED_SHORTCUT("script_text_editor/toggle_bookmark", TTR("Toggle Bookmark"), KEY_MASK_CMD | KEY_MASK_ALT | KEY_B);
ED_SHORTCUT("script_text_editor/goto_next_bookmark", TTR("Go to Next Bookmark"), KEY_MASK_CMD | KEY_B);
ED_SHORTCUT("script_text_editor/goto_previous_bookmark", TTR("Go to Previous Bookmark"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_B);
ED_SHORTCUT("script_text_editor/remove_all_bookmarks", TTR("Remove All Bookmarks"), KEY_NONE);
-#ifdef OSX_ENABLED
- ED_SHORTCUT("script_text_editor/goto_function", TTR("Go to Function..."), KEY_MASK_CTRL | KEY_MASK_CMD | KEY_J);
-#else
ED_SHORTCUT("script_text_editor/goto_function", TTR("Go to Function..."), KEY_MASK_ALT | KEY_MASK_CMD | KEY_F);
-#endif
+ ED_SHORTCUT_OVERRIDE("script_text_editor/goto_function", "macos", KEY_MASK_CTRL | KEY_MASK_CMD | KEY_J);
+
ED_SHORTCUT("script_text_editor/goto_line", TTR("Go to Line..."), KEY_MASK_CMD | KEY_L);
-#ifdef OSX_ENABLED
- ED_SHORTCUT("script_text_editor/toggle_breakpoint", TTR("Toggle Breakpoint"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_B);
-#else
ED_SHORTCUT("script_text_editor/toggle_breakpoint", TTR("Toggle Breakpoint"), KEY_F9);
-#endif
+ ED_SHORTCUT_OVERRIDE("script_text_editor/toggle_breakpoint", "macos", KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_B);
+
ED_SHORTCUT("script_text_editor/remove_all_breakpoints", TTR("Remove All Breakpoints"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F9);
ED_SHORTCUT("script_text_editor/goto_next_breakpoint", TTR("Go to Next Breakpoint"), KEY_MASK_CMD | KEY_PERIOD);
ED_SHORTCUT("script_text_editor/goto_previous_breakpoint", TTR("Go to Previous Breakpoint"), KEY_MASK_CMD | KEY_COMMA);
diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h
index 1ca6f56ea1..afe9a7453d 100644
--- a/editor/plugins/script_text_editor.h
+++ b/editor/plugins/script_text_editor.h
@@ -197,7 +197,7 @@ public:
virtual void add_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) override;
virtual void set_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) override;
- void update_toggle_scripts_button();
+ void update_toggle_scripts_button() override;
virtual void apply_code() override;
virtual RES get_edited_resource() const override;
@@ -225,6 +225,8 @@ public:
virtual void reload(bool p_soft) override;
virtual Array get_breakpoints() override;
+ virtual void set_breakpoint(int p_line, bool p_enabled) override;
+ virtual void clear_breakpoints() override;
virtual void add_callback(const String &p_function, PackedStringArray p_args) override;
virtual void update_settings() override;
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index 22ca5592bd..a88e24c0d0 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -131,9 +131,9 @@ void ShaderTextEditor::_load_theme_settings() {
List<String> built_ins;
if (shader.is_valid()) {
- for (const Map<StringName, ShaderLanguage::FunctionInfo>::Element *E = ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode())).front(); E; E = E->next()) {
- for (const Map<StringName, ShaderLanguage::BuiltInInfo>::Element *F = E->get().built_ins.front(); F; F = F->next()) {
- built_ins.push_back(F->key());
+ for (const KeyValue<StringName, ShaderLanguage::FunctionInfo> &E : ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode()))) {
+ for (const KeyValue<StringName, ShaderLanguage::BuiltInInfo> &F : E.value.built_ins) {
+ built_ins.push_back(F.key);
}
}
diff --git a/editor/plugins/skeleton_2d_editor_plugin.cpp b/editor/plugins/skeleton_2d_editor_plugin.cpp
index 7ef680d7ef..c350004f0f 100644
--- a/editor/plugins/skeleton_2d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_2d_editor_plugin.cpp
@@ -98,9 +98,10 @@ Skeleton2DEditor::Skeleton2DEditor() {
options->set_text(TTR("Skeleton2D"));
options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Skeleton2D"), SNAME("EditorIcons")));
- options->get_popup()->add_item(TTR("Make Rest Pose (From Bones)"), MENU_OPTION_MAKE_REST);
+ options->get_popup()->add_item(TTR("Reset to Rest Pose"), MENU_OPTION_MAKE_REST);
options->get_popup()->add_separator();
- options->get_popup()->add_item(TTR("Set Bones to Rest Pose"), MENU_OPTION_SET_REST);
+ // Use the "Overwrite" word to highlight that this is a destructive operation.
+ options->get_popup()->add_item(TTR("Overwrite Rest Pose"), MENU_OPTION_SET_REST);
options->set_switch_on_hover(true);
options->get_popup()->connect("id_pressed", callable_mp(this, &Skeleton2DEditor::_menu_option));
diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp
index 309821b3dc..531ffc6a73 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_3d_editor_plugin.cpp
@@ -37,11 +37,12 @@
#include "editor/plugins/animation_player_editor_plugin.h"
#include "node_3d_editor_plugin.h"
#include "scene/3d/collision_shape_3d.h"
+#include "scene/3d/joint_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"
+#include "scene/resources/surface_tool.h"
void BoneTransformEditor::create_editors() {
const Color section_color = get_theme_color(SNAME("prop_subsection"), SNAME("Editor"));
@@ -50,6 +51,11 @@ void BoneTransformEditor::create_editors() {
section->setup("trf_properties", label, this, section_color, true);
add_child(section);
+ 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);
+
key_button = memnew(Button);
key_button->set_text(TTR("Key Transform"));
key_button->set_visible(keyable);
@@ -57,49 +63,40 @@ void BoneTransformEditor::create_editors() {
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);
-
- // Translation property
+ // Translation property.
translation_property = memnew(EditorPropertyVector3());
translation_property->setup(-10000, 10000, 0.001f, true);
translation_property->set_label("Translation");
translation_property->set_use_folding(true);
- translation_property->set_read_only(false);
translation_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_vector3));
section->get_vbox()->add_child(translation_property);
- // Rotation property
+ // Rotation property.
rotation_property = memnew(EditorPropertyVector3());
rotation_property->setup(-10000, 10000, 0.001f, true);
rotation_property->set_label("Rotation Degrees");
rotation_property->set_use_folding(true);
- rotation_property->set_read_only(false);
rotation_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_vector3));
section->get_vbox()->add_child(rotation_property);
- // Scale property
+ // Scale property.
scale_property = memnew(EditorPropertyVector3());
scale_property->setup(-10000, 10000, 0.001f, true);
scale_property->set_label("Scale");
scale_property->set_use_folding(true);
- scale_property->set_read_only(false);
scale_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_vector3));
section->get_vbox()->add_child(scale_property);
- // Transform/Matrix section
+ // Transform/Matrix section.
transform_section = memnew(EditorInspectorSection);
transform_section->setup("trf_properties_transform", "Matrix", this, section_color, true);
section->get_vbox()->add_child(transform_section);
- // Transform/Matrix property
+ // Transform/Matrix property.
transform_property = memnew(EditorPropertyTransform3D());
transform_property->setup(-10000, 10000, 0.001f, true);
transform_property->set_label("Transform");
transform_property->set_use_folding(true);
- transform_property->set_read_only(false);
transform_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_transform));
transform_section->get_vbox()->add_child(transform_property);
}
@@ -109,7 +106,7 @@ void BoneTransformEditor::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
create_editors();
key_button->connect("pressed", callable_mp(this, &BoneTransformEditor::_key_button_pressed));
- enabled_checkbox->connect("toggled", callable_mp(this, &BoneTransformEditor::_checkbox_toggled));
+ enabled_checkbox->connect("pressed", callable_mp(this, &BoneTransformEditor::_checkbox_pressed));
[[fallthrough]];
}
case NOTIFICATION_SORT_CHILDREN: {
@@ -229,7 +226,7 @@ void BoneTransformEditor::_update_properties() {
return;
}
- if (skeleton == nullptr) {
+ if (!skeleton) {
return;
}
@@ -244,7 +241,7 @@ void BoneTransformEditor::_update_custom_pose_properties() {
return;
}
- if (skeleton == nullptr) {
+ if (!skeleton) {
return;
}
@@ -281,11 +278,32 @@ void BoneTransformEditor::set_target(const String &p_prop) {
void BoneTransformEditor::set_keyable(const bool p_keyable) {
keyable = p_keyable;
+}
+
+void BoneTransformEditor::_update_key_button(const bool p_keyable) {
+ bool is_keyable = keyable && p_keyable;
if (key_button) {
- key_button->set_visible(p_keyable);
+ key_button->set_visible(is_keyable);
}
}
+void BoneTransformEditor::set_properties_read_only(const bool p_readonly) {
+ enabled_checkbox->set_disabled(p_readonly);
+ enabled_checkbox->update();
+}
+
+void BoneTransformEditor::set_transform_read_only(const bool p_readonly) {
+ translation_property->set_read_only(p_readonly);
+ rotation_property->set_read_only(p_readonly);
+ scale_property->set_read_only(p_readonly);
+ transform_property->set_read_only(p_readonly);
+ translation_property->update();
+ rotation_property->update();
+ scale_property->update();
+ transform_property->update();
+ _update_key_button(!p_readonly);
+}
+
void BoneTransformEditor::set_toggle_enabled(const bool p_enabled) {
toggle_enabled = p_enabled;
if (enabled_checkbox) {
@@ -294,7 +312,7 @@ void BoneTransformEditor::set_toggle_enabled(const bool p_enabled) {
}
void BoneTransformEditor::_key_button_pressed() {
- if (skeleton == nullptr) {
+ if (!skeleton) {
return;
}
@@ -305,30 +323,152 @@ void BoneTransformEditor::_key_button_pressed() {
return;
}
- // Need to normalize the basis before you key it
Transform3D tform = compute_transform_from_vector3s();
- tform.orthonormalize();
- AnimationPlayerEditor::singleton->get_track_editor()->insert_transform_key(skeleton, name, tform);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_transform_key(skeleton, name, tform);
}
-void BoneTransformEditor::_checkbox_toggled(const bool p_toggled) {
+void BoneTransformEditor::_checkbox_pressed() {
+ if (!skeleton) {
+ return;
+ }
+
+ const BoneId bone_id = property.get_slicec('/', 1).to_int();
if (enabled_checkbox) {
- const String path = "bones/" + property.get_slicec('/', 1) + "/enabled";
- skeleton->set(path, p_toggled);
+ undo_redo->create_action(TTR("Set Pose Enabled"));
+ bool enabled = skeleton->is_bone_enabled(bone_id);
+ undo_redo->add_do_method(skeleton, "set_bone_enabled", bone_id, !enabled);
+ undo_redo->add_undo_method(skeleton, "set_bone_enabled", bone_id, enabled);
+ undo_redo->commit_action();
}
}
-void Skeleton3DEditor::_on_click_option(int p_option) {
+Skeleton3DEditor *Skeleton3DEditor::singleton = nullptr;
+
+void Skeleton3DEditor::set_keyable(const bool p_keyable) {
+ keyable = p_keyable;
+ skeleton_options->get_popup()->set_item_disabled(SKELETON_OPTION_INSERT_KEYS, !p_keyable);
+ skeleton_options->get_popup()->set_item_disabled(SKELETON_OPTION_INSERT_KEYS_EXISTED, !p_keyable);
+};
+
+void Skeleton3DEditor::set_rest_options_enabled(const bool p_rest_options_enabled) {
+ rest_options->get_popup()->set_item_disabled(REST_OPTION_POSE_TO_REST, !p_rest_options_enabled);
+};
+
+void Skeleton3DEditor::_update_show_rest_only() {
+ _update_pose_enabled(-1);
+}
+
+void Skeleton3DEditor::_update_pose_enabled(int p_bone) {
if (!skeleton) {
return;
}
+ if (pose_editor) {
+ pose_editor->set_properties_read_only(skeleton->is_show_rest_only());
- switch (p_option) {
- case MENU_OPTION_CREATE_PHYSICAL_SKELETON: {
+ if (selected_bone > 0) {
+ pose_editor->set_transform_read_only(skeleton->is_show_rest_only() || !(skeleton->is_bone_enabled(selected_bone)));
+ }
+ }
+ _update_gizmo_visible();
+}
+
+void Skeleton3DEditor::_on_click_skeleton_option(int p_skeleton_option) {
+ if (!skeleton) {
+ return;
+ }
+
+ switch (p_skeleton_option) {
+ case SKELETON_OPTION_CREATE_PHYSICAL_SKELETON: {
create_physical_skeleton();
break;
}
+ case SKELETON_OPTION_INIT_POSE: {
+ init_pose();
+ break;
+ }
+ case SKELETON_OPTION_INSERT_KEYS: {
+ insert_keys(true);
+ break;
+ }
+ case SKELETON_OPTION_INSERT_KEYS_EXISTED: {
+ insert_keys(false);
+ break;
+ }
+ }
+}
+
+void Skeleton3DEditor::_on_click_rest_option(int p_rest_option) {
+ if (!skeleton) {
+ return;
+ }
+
+ switch (p_rest_option) {
+ case REST_OPTION_POSE_TO_REST: {
+ pose_to_rest();
+ break;
+ }
+ }
+}
+
+void Skeleton3DEditor::init_pose() {
+ const int bone_len = skeleton->get_bone_count();
+ if (!bone_len) {
+ return;
+ }
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
+ for (int i = 0; i < bone_len; i++) {
+ ur->add_do_method(skeleton, "set_bone_pose", i, Transform3D());
+ ur->add_undo_method(skeleton, "set_bone_pose", i, skeleton->get_bone_pose(i));
+ }
+ ur->commit_action();
+}
+
+void Skeleton3DEditor::insert_keys(bool p_all_bones) {
+ if (!skeleton) {
+ return;
+ }
+
+ int bone_len = skeleton->get_bone_count();
+ Node *root = EditorNode::get_singleton()->get_tree()->get_root();
+ String path = root->get_path_to(skeleton);
+
+ AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor();
+ te->make_insert_queue();
+ for (int i = 0; i < bone_len; i++) {
+ const String name = skeleton->get_bone_name(i);
+
+ if (name.is_empty()) {
+ continue;
+ }
+
+ if (!p_all_bones && !te->has_transform_track(skeleton, name)) {
+ continue;
+ }
+
+ Transform3D tform = skeleton->get_bone_pose(i);
+ te->insert_transform_key(skeleton, name, tform);
+ }
+ te->commit_insert_queue();
+}
+
+void Skeleton3DEditor::pose_to_rest() {
+ if (!skeleton) {
+ return;
}
+
+ // Todo: Do method with multiple bone selection.
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
+
+ ur->add_do_method(skeleton, "set_bone_pose", selected_bone, Transform3D());
+ ur->add_undo_method(skeleton, "set_bone_pose", selected_bone, skeleton->get_bone_pose(selected_bone));
+ ur->add_do_method(skeleton, "set_bone_custom_pose", selected_bone, Transform3D());
+ ur->add_undo_method(skeleton, "set_bone_custom_pose", selected_bone, skeleton->get_bone_custom_pose(selected_bone));
+ ur->add_do_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_rest(selected_bone) * skeleton->get_bone_custom_pose(selected_bone) * skeleton->get_bone_pose(selected_bone));
+ ur->add_undo_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_rest(selected_bone));
+
+ ur->commit_action();
}
void Skeleton3DEditor::create_physical_skeleton() {
@@ -356,7 +496,7 @@ void Skeleton3DEditor::create_physical_skeleton() {
bones_infos.write[bone_id].relative_rest = bones_infos[parent].relative_rest * skeleton->get_bone_rest(bone_id);
- /// create physical bone on parent
+ // Create physical bone on parent.
if (!bones_infos[parent].physical_bone) {
bones_infos.write[parent].physical_bone = create_physical_bone(parent, bone_id, bones_infos);
@@ -370,7 +510,7 @@ void Skeleton3DEditor::create_physical_skeleton() {
bones_infos[parent].physical_bone->set_owner(owner);
bones_infos[parent].physical_bone->get_child(0)->set_owner(owner); // set shape owner
- /// Create joint between parent of parent
+ // Create joint between parent of parent.
if (-1 != parent_parent) {
bones_infos[parent].physical_bone->set_joint_type(PhysicalBone3D::JOINT_TYPE_PIN);
}
@@ -483,7 +623,7 @@ void Skeleton3DEditor::move_skeleton_bone(NodePath p_skeleton_path, int32_t p_se
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 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();
@@ -505,24 +645,30 @@ void Skeleton3DEditor::move_skeleton_bone(NodePath p_skeleton_path, int32_t p_se
void Skeleton3DEditor::_joint_tree_selection_changed() {
TreeItem *selected = joint_tree->get_selected();
- const String path = selected->get_metadata(0);
+ if (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) + "/";
+ 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_target(bone_path + "pose");
+ rest_editor->set_target(bone_path + "rest");
+ custom_pose_editor->set_target(bone_path + "custom_pose");
- _update_properties();
+ pose_editor->set_visible(true);
+ rest_editor->set_visible(true);
+ custom_pose_editor->set_visible(true);
- pose_editor->set_visible(true);
- rest_editor->set_visible(true);
- custom_pose_editor->set_visible(true);
+ selected_bone = b_idx;
+ }
}
+ set_rest_options_enabled(selected);
+ _update_properties();
+ _update_pose_enabled();
}
+// May be not used with single select mode.
void Skeleton3DEditor::_joint_tree_rmb_select(const Vector2 &p_pos) {
}
@@ -536,12 +682,13 @@ void Skeleton3DEditor::_update_properties() {
if (custom_pose_editor) {
custom_pose_editor->_update_custom_pose_properties();
}
+ _update_gizmo_transform();
}
void Skeleton3DEditor::update_joint_tree() {
joint_tree->clear();
- if (skeleton == nullptr) {
+ if (!skeleton) {
return;
}
@@ -569,7 +716,7 @@ void Skeleton3DEditor::update_joint_tree() {
joint_item->set_selectable(0, true);
joint_item->set_metadata(0, "bones/" + itos(current_bone_idx));
- // Add the bone's children to the list of bones to be processed
+ // Add the bone's children to the list of bones to be processed.
Vector<int> current_bone_child_bones = skeleton->get_bone_children(current_bone_idx);
int child_bone_size = current_bone_child_bones.size();
for (int i = 0; i < child_bone_size; i++) {
@@ -587,16 +734,56 @@ void Skeleton3DEditor::create_editors() {
set_focus_mode(FOCUS_ALL);
- // Create Top Menu Bar
- options = memnew(MenuButton);
- Node3DEditor::get_singleton()->add_control_to_menu_panel(options);
+ Node3DEditor *ne = Node3DEditor::get_singleton();
+ AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor();
- options->set_text(TTR("Skeleton3D"));
- options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Skeleton3D"), SNAME("EditorIcons")));
+ // Create Top Menu Bar.
+ separator = memnew(VSeparator);
+ ne->add_control_to_menu_panel(separator);
- options->get_popup()->add_item(TTR("Create physical skeleton"), MENU_OPTION_CREATE_PHYSICAL_SKELETON);
+ // Create Skeleton Option in Top Menu Bar.
+ skeleton_options = memnew(MenuButton);
+ ne->add_control_to_menu_panel(skeleton_options);
- options->get_popup()->connect("id_pressed", callable_mp(this, &Skeleton3DEditor::_on_click_option));
+ skeleton_options->set_text(TTR("Skeleton3D"));
+ skeleton_options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Skeleton3D"), SNAME("EditorIcons")));
+
+ skeleton_options->get_popup()->add_item(TTR("Init pose"), SKELETON_OPTION_INIT_POSE);
+ skeleton_options->get_popup()->add_item(TTR("Insert key of all bone poses"), SKELETON_OPTION_INSERT_KEYS);
+ skeleton_options->get_popup()->add_item(TTR("Insert key of bone poses already exist track"), SKELETON_OPTION_INSERT_KEYS_EXISTED);
+ skeleton_options->get_popup()->add_item(TTR("Create physical skeleton"), SKELETON_OPTION_CREATE_PHYSICAL_SKELETON);
+
+ skeleton_options->get_popup()->connect("id_pressed", callable_mp(this, &Skeleton3DEditor::_on_click_skeleton_option));
+
+ // Create Rest Option in Top Menu Bar.
+ rest_options = memnew(MenuButton);
+ ne->add_control_to_menu_panel(rest_options);
+
+ rest_options->set_text(TTR("Edit Rest"));
+ rest_options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("BoneAttachment3D"), SNAME("EditorIcons")));
+
+ rest_options->get_popup()->add_item(TTR("Apply current pose to rest"), REST_OPTION_POSE_TO_REST);
+ rest_options->get_popup()->connect("id_pressed", callable_mp(this, &Skeleton3DEditor::_on_click_rest_option));
+ set_rest_options_enabled(false);
+
+ Vector<Variant> button_binds;
+ button_binds.resize(1);
+
+ edit_mode_button = memnew(Button);
+ ne->add_control_to_menu_panel(edit_mode_button);
+ edit_mode_button->set_tooltip(TTR("Edit Mode\nShow buttons on joints."));
+ edit_mode_button->set_toggle_mode(true);
+ edit_mode_button->set_flat(true);
+ edit_mode_button->connect("toggled", callable_mp(this, &Skeleton3DEditor::edit_mode_toggled));
+
+ edit_mode = false;
+
+ set_keyable(te->has_keying());
+
+ if (skeleton) {
+ skeleton->add_child(handles_mesh_instance);
+ handles_mesh_instance->set_skeleton_path(NodePath(""));
+ }
const Color section_color = get_theme_color(SNAME("prop_subsection"), SNAME("Editor"));
@@ -612,7 +799,7 @@ void Skeleton3DEditor::create_editors() {
joint_tree = memnew(Tree);
joint_tree->set_columns(1);
- joint_tree->set_focus_mode(Control::FocusMode::FOCUS_NONE);
+ joint_tree->set_focus_mode(Control::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);
@@ -623,8 +810,8 @@ void Skeleton3DEditor::create_editors() {
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_keyable(te->has_keying());
pose_editor->set_visible(false);
add_child(pose_editor);
@@ -632,27 +819,34 @@ void Skeleton3DEditor::create_editors() {
rest_editor->set_label(TTR("Bone Rest"));
rest_editor->set_visible(false);
add_child(rest_editor);
+ rest_editor->set_transform_read_only(true);
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);
+ custom_pose_editor->set_transform_read_only(true);
}
void Skeleton3DEditor::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_READY: {
+ edit_mode_button->set_icon(get_theme_icon("ToolBoneSelect", "EditorIcons"));
+ get_tree()->connect("node_removed", callable_mp(this, &Skeleton3DEditor::_node_removed), Vector<Variant>(), Object::CONNECT_ONESHOT);
+ break;
+ }
case NOTIFICATION_ENTER_TREE: {
create_editors();
update_joint_tree();
update_editors();
-
- get_tree()->connect("node_removed", callable_mp(this, &Skeleton3DEditor::_node_removed), Vector<Variant>(), Object::CONNECT_ONESHOT);
joint_tree->connect("item_selected", callable_mp(this, &Skeleton3DEditor::_joint_tree_selection_changed));
joint_tree->connect("item_rmb_selected", callable_mp(this, &Skeleton3DEditor::_joint_tree_rmb_select));
#ifdef TOOLS_ENABLED
+ skeleton->connect("pose_updated", callable_mp(this, &Skeleton3DEditor::_draw_gizmo));
skeleton->connect("pose_updated", callable_mp(this, &Skeleton3DEditor::_update_properties));
-#endif // TOOLS_ENABLED
-
+ skeleton->connect("bone_enabled_changed", callable_mp(this, &Skeleton3DEditor::_update_pose_enabled));
+ skeleton->connect("show_rest_only_changed", callable_mp(this, &Skeleton3DEditor::_update_show_rest_only));
+#endif
break;
}
}
@@ -661,7 +855,8 @@ void Skeleton3DEditor::_notification(int p_what) {
void Skeleton3DEditor::_node_removed(Node *p_node) {
if (skeleton && p_node == skeleton) {
skeleton = nullptr;
- options->hide();
+ skeleton_options->hide();
+ rest_options->hide();
}
_update_properties();
@@ -671,24 +866,237 @@ 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_show_rest_only"), &Skeleton3DEditor::_update_show_rest_only);
+ ClassDB::bind_method(D_METHOD("_update_pose_enabled"), &Skeleton3DEditor::_update_pose_enabled);
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("_on_click_skeleton_option"), &Skeleton3DEditor::_on_click_skeleton_option);
+ ClassDB::bind_method(D_METHOD("_on_click_rest_option"), &Skeleton3DEditor::_on_click_rest_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("_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);
+
+ ClassDB::bind_method(D_METHOD("_draw_gizmo"), &Skeleton3DEditor::_draw_gizmo);
+}
+
+void Skeleton3DEditor::edit_mode_toggled(const bool pressed) {
+ edit_mode = pressed;
+ _update_gizmo_visible();
}
Skeleton3DEditor::Skeleton3DEditor(EditorInspectorPluginSkeleton *e_plugin, EditorNode *p_editor, Skeleton3D *p_skeleton) :
editor(p_editor),
editor_plugin(e_plugin),
skeleton(p_skeleton) {
+ singleton = this;
+
+ // Handle.
+ handle_material = Ref<ShaderMaterial>(memnew(ShaderMaterial));
+ handle_shader = Ref<Shader>(memnew(Shader));
+ handle_shader->set_code(R"(
+// Skeleton 3D gizmo handle shader.
+
+shader_type spatial;
+render_mode unshaded, shadows_disabled, depth_draw_always;
+uniform sampler2D texture_albedo : hint_albedo;
+uniform float point_size : hint_range(0,128) = 32;
+void vertex() {
+ if (!OUTPUT_IS_SRGB) {
+ COLOR.rgb = mix( pow((COLOR.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), COLOR.rgb* (1.0 / 12.92), lessThan(COLOR.rgb,vec3(0.04045)) );
+ }
+ VERTEX = VERTEX;
+ POSITION=PROJECTION_MATRIX*INV_CAMERA_MATRIX*WORLD_MATRIX*vec4(VERTEX.xyz,1.0);
+ POSITION.z = mix(POSITION.z, 0, 0.999);
+ POINT_SIZE = point_size;
+}
+void fragment() {
+ vec4 albedo_tex = texture(texture_albedo,POINT_COORD);
+ vec3 col = albedo_tex.rgb + COLOR.rgb;
+ col = vec3(min(col.r,1.0),min(col.g,1.0),min(col.b,1.0));
+ ALBEDO = col;
+ if (albedo_tex.a < 0.5) { discard; }
+ ALPHA = albedo_tex.a;
+}
+)");
+ handle_material->set_shader(handle_shader);
+ Ref<Texture2D> handle = editor->get_gui_base()->get_theme_icon("EditorBoneHandle", "EditorIcons");
+ handle_material->set_shader_param("point_size", handle->get_width());
+ handle_material->set_shader_param("texture_albedo", handle);
+
+ handles_mesh_instance = memnew(MeshInstance3D);
+ handles_mesh_instance->set_cast_shadows_setting(GeometryInstance3D::SHADOW_CASTING_SETTING_OFF);
+ handles_mesh.instantiate();
+ handles_mesh_instance->set_mesh(handles_mesh);
+}
+
+void Skeleton3DEditor::update_bone_original() {
+ if (!skeleton) {
+ return;
+ }
+ if (skeleton->get_bone_count() == 0 || selected_bone == -1) {
+ return;
+ }
+ bone_original = skeleton->get_bone_pose(selected_bone);
+}
+
+void Skeleton3DEditor::_hide_handles() {
+ handles_mesh_instance->hide();
+}
+
+void Skeleton3DEditor::_draw_gizmo() {
+ if (!skeleton) {
+ return;
+ }
+
+ // If you call get_bone_global_pose() while drawing the surface, such as toggle rest mode,
+ // the skeleton update will be done first and
+ // the drawing surface will be interrupted once and an error will occur.
+ skeleton->force_update_all_dirty_bones();
+
+ // Handles.
+ if (edit_mode) {
+ _draw_handles();
+ } else {
+ _hide_handles();
+ }
+}
+
+void Skeleton3DEditor::_draw_handles() {
+ handles_mesh_instance->show();
+
+ const int bone_len = skeleton->get_bone_count();
+ handles_mesh->clear_surfaces();
+ handles_mesh->surface_begin(Mesh::PRIMITIVE_POINTS);
+
+ for (int i = 0; i < bone_len; i++) {
+ Color c;
+ if (i == selected_bone) {
+ c = Color(1, 1, 0);
+ } else {
+ c = Color(0.1, 0.25, 0.8);
+ }
+ Vector3 point = skeleton->get_bone_global_pose(i).origin;
+ handles_mesh->surface_set_color(c);
+ handles_mesh->surface_add_vertex(point);
+ }
+ handles_mesh->surface_end();
+ handles_mesh->surface_set_material(0, handle_material);
+}
+
+TreeItem *Skeleton3DEditor::_find(TreeItem *p_node, const NodePath &p_path) {
+ if (!p_node) {
+ return nullptr;
+ }
+
+ NodePath np = p_node->get_metadata(0);
+ if (np == p_path) {
+ return p_node;
+ }
+
+ TreeItem *children = p_node->get_first_child();
+ while (children) {
+ TreeItem *n = _find(children, p_path);
+ if (n) {
+ return n;
+ }
+ children = children->get_next();
+ }
+
+ return nullptr;
+}
+
+void Skeleton3DEditor::_subgizmo_selection_change() {
+ if (!skeleton) {
+ return;
+ }
+
+ // Once validated by subgizmos_intersect_ray, but required if through inspector's bones tree.
+ if (!edit_mode) {
+ skeleton->clear_subgizmo_selection();
+ return;
+ }
+
+ int selected = -1;
+ Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();
+ if (se) {
+ selected = se->get_selected_bone();
+ }
+
+ if (selected >= 0) {
+ Vector<Ref<Node3DGizmo>> gizmos = skeleton->get_gizmos();
+ for (int i = 0; i < gizmos.size(); i++) {
+ Ref<EditorNode3DGizmo> gizmo = gizmos[i];
+ if (!gizmo.is_valid()) {
+ continue;
+ }
+ Ref<Skeleton3DGizmoPlugin> plugin = gizmo->get_plugin();
+ if (!plugin.is_valid()) {
+ continue;
+ }
+ skeleton->set_subgizmo_selection(gizmo, selected, skeleton->get_bone_global_pose(selected));
+ break;
+ }
+ } else {
+ skeleton->clear_subgizmo_selection();
+ }
+}
+
+void Skeleton3DEditor::select_bone(int p_idx) {
+ if (p_idx >= 0) {
+ TreeItem *ti = _find(joint_tree->get_root(), "bones/" + itos(p_idx));
+ if (ti) {
+ // Make visible when it's collapsed.
+ TreeItem *node = ti->get_parent();
+ while (node && node != joint_tree->get_root()) {
+ node->set_collapsed(false);
+ node = node->get_parent();
+ }
+ ti->select(0);
+ joint_tree->scroll_to_item(ti);
+ }
+ } else {
+ selected_bone = -1;
+ joint_tree->deselect_all();
+ _joint_tree_selection_changed();
+ }
}
Skeleton3DEditor::~Skeleton3DEditor() {
- if (options) {
- Node3DEditor::get_singleton()->remove_control_from_menu_panel(options);
+ if (skeleton) {
+#ifdef TOOLS_ENABLED
+ skeleton->disconnect("show_rest_only_changed", callable_mp(this, &Skeleton3DEditor::_update_show_rest_only));
+ skeleton->disconnect("bone_enabled_changed", callable_mp(this, &Skeleton3DEditor::_update_pose_enabled));
+ skeleton->disconnect("pose_updated", callable_mp(this, &Skeleton3DEditor::_draw_gizmo));
+ skeleton->disconnect("pose_updated", callable_mp(this, &Skeleton3DEditor::_update_properties));
+ skeleton->set_transform_gizmo_visible(true);
+#endif
+ handles_mesh_instance->get_parent()->remove_child(handles_mesh_instance);
+ }
+
+ handles_mesh_instance->queue_delete();
+
+ Node3DEditor *ne = Node3DEditor::get_singleton();
+
+ if (separator) {
+ ne->remove_control_from_menu_panel(separator);
+ memdelete(separator);
+ }
+
+ if (skeleton_options) {
+ ne->remove_control_from_menu_panel(skeleton_options);
+ memdelete(skeleton_options);
+ }
+
+ if (rest_options) {
+ ne->remove_control_from_menu_panel(rest_options);
+ memdelete(rest_options);
+ }
+
+ if (edit_mode_button) {
+ ne->remove_control_from_menu_panel(edit_mode_button);
+ memdelete(edit_mode_button);
}
}
@@ -700,16 +1108,413 @@ 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));
+ skel_editor = memnew(Skeleton3DEditor(this, editor, skeleton));
add_custom_control(skel_editor);
}
Skeleton3DEditorPlugin::Skeleton3DEditorPlugin(EditorNode *p_node) {
editor = p_node;
- Ref<EditorInspectorPluginSkeleton> skeleton_plugin;
- skeleton_plugin.instantiate();
+ skeleton_plugin = memnew(EditorInspectorPluginSkeleton);
skeleton_plugin->editor = editor;
EditorInspector::add_inspector_plugin(skeleton_plugin);
+
+ Ref<Skeleton3DGizmoPlugin> gizmo_plugin = Ref<Skeleton3DGizmoPlugin>(memnew(Skeleton3DGizmoPlugin));
+ Node3DEditor::get_singleton()->add_gizmo_plugin(gizmo_plugin);
+}
+
+EditorPlugin::AfterGUIInput Skeleton3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
+ Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();
+ Node3DEditor *ne = Node3DEditor::get_singleton();
+ if (se->is_edit_mode()) {
+ const Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (ne->get_tool_mode() != Node3DEditor::TOOL_MODE_SELECT) {
+ if (!ne->is_gizmo_visible()) {
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ }
+ if (mb->is_pressed()) {
+ se->update_bone_original();
+ }
+ }
+ return EditorPlugin::AFTER_GUI_INPUT_DESELECT;
+ }
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
+}
+
+bool Skeleton3DEditorPlugin::handles(Object *p_object) const {
+ return p_object->is_class("Skeleton3D");
+}
+
+void Skeleton3DEditor::_update_gizmo_transform() {
+ Node3DEditor::get_singleton()->update_transform_gizmo();
+};
+
+void Skeleton3DEditor::_update_gizmo_visible() {
+ _subgizmo_selection_change();
+ if (edit_mode) {
+ if (selected_bone == -1) {
+#ifdef TOOLS_ENABLED
+ skeleton->set_transform_gizmo_visible(false);
+#endif
+ } else {
+#ifdef TOOLS_ENABLED
+ if (skeleton->is_bone_enabled(selected_bone) && !skeleton->is_show_rest_only()) {
+ skeleton->set_transform_gizmo_visible(true);
+ } else {
+ skeleton->set_transform_gizmo_visible(false);
+ }
+#endif
+ }
+ } else {
+#ifdef TOOLS_ENABLED
+ skeleton->set_transform_gizmo_visible(true);
+#endif
+ }
+ _draw_gizmo();
+}
+
+int Skeleton3DEditor::get_selected_bone() const {
+ return selected_bone;
+}
+
+Skeleton3DGizmoPlugin::Skeleton3DGizmoPlugin() {
+ unselected_mat = Ref<StandardMaterial3D>(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);
+
+ selected_mat = Ref<ShaderMaterial>(memnew(ShaderMaterial));
+ selected_sh = Ref<Shader>(memnew(Shader));
+ selected_sh->set_code(R"(
+// Skeleton 3D gizmo bones shader.
+
+shader_type spatial;
+render_mode unshaded, shadows_disabled;
+void vertex() {
+ if (!OUTPUT_IS_SRGB) {
+ COLOR.rgb = mix( pow((COLOR.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), COLOR.rgb* (1.0 / 12.92), lessThan(COLOR.rgb,vec3(0.04045)) );
+ }
+ VERTEX = VERTEX;
+ POSITION=PROJECTION_MATRIX*INV_CAMERA_MATRIX*WORLD_MATRIX*vec4(VERTEX.xyz,1.0);
+ POSITION.z = mix(POSITION.z, 0, 0.998);
+}
+void fragment() {
+ ALBEDO = COLOR.rgb;
+ ALPHA = COLOR.a;
+}
+)");
+ selected_mat->set_shader(selected_sh);
+
+ // Regist properties in editor settings.
+ EDITOR_DEF("editors/3d_gizmos/gizmo_colors/skeleton", Color(1, 0.8, 0.4));
+ EDITOR_DEF("editors/3d_gizmos/gizmo_colors/selected_bone", Color(0.8, 0.3, 0.0));
+ EDITOR_DEF("editors/3d_gizmos/gizmo_settings/bone_axis_length", (float)0.1);
+ EDITOR_DEF("editors/3d_gizmos/gizmo_settings/bone_shape", 1);
+ EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "editors/3d_gizmos/gizmo_settings/bone_shape", PROPERTY_HINT_ENUM, "Wire,Octahedron"));
+}
+
+bool Skeleton3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
+ return Object::cast_to<Skeleton3D>(p_spatial) != nullptr;
+}
+
+String Skeleton3DGizmoPlugin::get_gizmo_name() const {
+ return "Skeleton3D";
+}
+
+int Skeleton3DGizmoPlugin::get_priority() const {
+ return -1;
+}
+
+int Skeleton3DGizmoPlugin::subgizmos_intersect_ray(const EditorNode3DGizmo *p_gizmo, Camera3D *p_camera, const Vector2 &p_point) const {
+ Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_gizmo->get_spatial_node());
+ ERR_FAIL_COND_V(!skeleton, -1);
+
+ Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();
+
+ if (!se->is_edit_mode()) {
+ return -1;
+ }
+
+ if (Node3DEditor::get_singleton()->get_tool_mode() != Node3DEditor::TOOL_MODE_SELECT) {
+ return -1;
+ }
+
+ // Select bone.
+ real_t grab_threshold = 4 * EDSCALE;
+ Vector3 ray_from = p_camera->get_global_transform().origin;
+ Transform3D gt = skeleton->get_global_transform();
+ int closest_idx = -1;
+ real_t closest_dist = 1e10;
+ const int bone_len = skeleton->get_bone_count();
+ for (int i = 0; i < bone_len; i++) {
+ Vector3 joint_pos_3d = gt.xform(skeleton->get_bone_global_pose(i).origin);
+ Vector2 joint_pos_2d = p_camera->unproject_position(joint_pos_3d);
+ real_t dist_3d = ray_from.distance_to(joint_pos_3d);
+ real_t dist_2d = p_point.distance_to(joint_pos_2d);
+ if (dist_2d < grab_threshold && dist_3d < closest_dist) {
+ closest_dist = dist_3d;
+ closest_idx = i;
+ }
+ }
+
+ if (closest_idx >= 0) {
+ WARN_PRINT("ray:");
+ WARN_PRINT(itos(closest_idx));
+ se->select_bone(closest_idx);
+ return closest_idx;
+ }
+
+ se->select_bone(-1);
+ return -1;
+}
+
+Transform3D Skeleton3DGizmoPlugin::get_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id) const {
+ Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_gizmo->get_spatial_node());
+ ERR_FAIL_COND_V(!skeleton, Transform3D());
+
+ return skeleton->get_bone_global_pose(p_id);
+}
+
+void Skeleton3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id, Transform3D p_transform) {
+ Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_gizmo->get_spatial_node());
+ ERR_FAIL_COND(!skeleton);
+
+ // Prepare for global to local.
+ Transform3D original_to_local = Transform3D();
+ int parent_idx = skeleton->get_bone_parent(p_id);
+ if (parent_idx >= 0) {
+ original_to_local = original_to_local * skeleton->get_bone_global_pose(parent_idx);
+ }
+ original_to_local = original_to_local * skeleton->get_bone_rest(p_id) * skeleton->get_bone_custom_pose(p_id);
+ Basis to_local = original_to_local.get_basis().inverse();
+
+ // Prepare transform.
+ Transform3D t = Transform3D();
+
+ // Basis.
+ t.basis = to_local * p_transform.get_basis();
+
+ // Origin.
+ Vector3 orig = Vector3();
+ orig = skeleton->get_bone_pose(p_id).origin;
+ Vector3 sub = p_transform.origin - skeleton->get_bone_global_pose(p_id).origin;
+ t.origin = orig + to_local.xform(sub);
+
+ // Apply transform.
+ skeleton->set_bone_pose(p_id, t);
+}
+
+void Skeleton3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) {
+ Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_gizmo->get_spatial_node());
+ ERR_FAIL_COND(!skeleton);
+
+ Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();
+
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ for (int i = 0; i < p_ids.size(); i++) {
+ ur->create_action(TTR("Set Bone Transform"));
+ ur->add_do_method(skeleton, "set_bone_pose", p_ids[i], skeleton->get_bone_pose(p_ids[i]));
+ ur->add_undo_method(skeleton, "set_bone_pose", p_ids[i], se->get_bone_original());
+ }
+ ur->commit_action();
+}
+
+void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
+ Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_gizmo->get_spatial_node());
+ p_gizmo->clear();
+
+ int selected = -1;
+ Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();
+ if (se) {
+ selected = se->get_selected_bone();
+ }
+
+ Color bone_color = EditorSettings::get_singleton()->get("editors/3d_gizmos/gizmo_colors/skeleton");
+ Color selected_bone_color = EditorSettings::get_singleton()->get("editors/3d_gizmos/gizmo_colors/selected_bone");
+ real_t bone_axis_length = EditorSettings::get_singleton()->get("editors/3d_gizmos/gizmo_settings/bone_axis_length");
+ int bone_shape = EditorSettings::get_singleton()->get("editors/3d_gizmos/gizmo_settings/bone_shape");
+
+ LocalVector<Color> axis_colors;
+ axis_colors.push_back(Node3DEditor::get_singleton()->get_theme_color(SNAME("axis_x_color"), SNAME("Editor")));
+ axis_colors.push_back(Node3DEditor::get_singleton()->get_theme_color(SNAME("axis_y_color"), SNAME("Editor")));
+ axis_colors.push_back(Node3DEditor::get_singleton()->get_theme_color(SNAME("axis_z_color"), SNAME("Editor")));
+
+ Ref<SurfaceTool> surface_tool(memnew(SurfaceTool));
+ surface_tool->begin(Mesh::PRIMITIVE_LINES);
+
+ if (p_gizmo->is_selected()) {
+ surface_tool->set_material(selected_mat);
+ } else {
+ unselected_mat->set_albedo(bone_color);
+ surface_tool->set_material(unselected_mat);
+ }
+
+ Vector<Transform3D> grests;
+ grests.resize(skeleton->get_bone_count());
+
+ LocalVector<int> bones;
+ LocalVector<float> weights;
+ bones.resize(4);
+ weights.resize(4);
+ for (int i = 0; i < 4; i++) {
+ bones[i] = 0;
+ weights[i] = 0;
+ }
+ weights[0] = 1;
+
+ int current_bone_index = 0;
+ Vector<int> bones_to_process = 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;
+
+ Vector<int> child_bones_vector;
+ child_bones_vector = skeleton->get_bone_children(current_bone_idx);
+ int child_bones_size = child_bones_vector.size();
+
+ // You have children but no parent, then you must be a root/parentless bone.
+ if (skeleton->get_bone_parent(current_bone_idx) < 0) {
+ grests.write[current_bone_idx] = skeleton->get_bone_rest(current_bone_idx);
+ }
+
+ for (int i = 0; i < child_bones_size; i++) {
+ // Something wrong.
+ if (child_bones_vector[i] < 0) {
+ continue;
+ }
+
+ int child_bone_idx = child_bones_vector[i];
+
+ grests.write[child_bone_idx] = grests[current_bone_idx] * skeleton->get_bone_rest(child_bone_idx);
+
+ Vector3 v0 = grests[current_bone_idx].origin;
+ Vector3 v1 = grests[child_bone_idx].origin;
+ Vector3 d = (v1 - v0).normalized();
+ real_t dist = v0.distance_to(v1);
+
+ // Find closest axis.
+ int closest = -1;
+ real_t closest_d = 0.0;
+ for (int j = 0; j < 3; j++) {
+ real_t dp = Math::abs(grests[current_bone_idx].basis[j].normalized().dot(d));
+ if (j == 0 || dp > closest_d) {
+ closest = j;
+ }
+ }
+
+ // Draw bone.
+ switch (bone_shape) {
+ case 0: { // Wire shape.
+ surface_tool->set_color(current_bone_color);
+ bones[0] = current_bone_idx;
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(v0);
+ bones[0] = child_bone_idx;
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(v1);
+ } break;
+
+ case 1: { // Octahedron shape.
+ Vector3 first;
+ Vector3 points[6];
+ int point_idx = 0;
+ for (int j = 0; j < 3; j++) {
+ Vector3 axis;
+ if (first == Vector3()) {
+ axis = d.cross(d.cross(grests[current_bone_idx].basis[j])).normalized();
+ first = axis;
+ } else {
+ axis = d.cross(first).normalized();
+ }
+
+ surface_tool->set_color(current_bone_color);
+ for (int k = 0; k < 2; k++) {
+ if (k == 1) {
+ axis = -axis;
+ }
+ Vector3 point = v0 + d * dist * 0.2;
+ point += axis * dist * 0.1;
+
+ bones[0] = current_bone_idx;
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(v0);
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(point);
+
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(point);
+ bones[0] = child_bone_idx;
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(v1);
+ points[point_idx++] = point;
+ }
+ }
+ surface_tool->set_color(current_bone_color);
+ SWAP(points[1], points[2]);
+ bones[0] = current_bone_idx;
+ for (int j = 0; j < 6; j++) {
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(points[j]);
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(points[(j + 1) % 6]);
+ }
+ } break;
+ }
+
+ // Axis as root of the bone.
+ for (int j = 0; j < 3; j++) {
+ bones[0] = current_bone_idx;
+ surface_tool->set_color(axis_colors[j]);
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(v0);
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(v0 + (grests[current_bone_idx].basis.inverse())[j].normalized() * dist * bone_axis_length);
+
+ if (j == closest) {
+ continue;
+ }
+ }
+
+ // Axis at the end of the bone children.
+ if (i == child_bones_size - 1) {
+ for (int j = 0; j < 3; j++) {
+ bones[0] = child_bone_idx;
+ surface_tool->set_color(axis_colors[j]);
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(v1);
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(v1 + (grests[child_bone_idx].basis.inverse())[j].normalized() * dist * bone_axis_length);
+
+ if (j == closest) {
+ continue;
+ }
+ }
+ }
+
+ // Add the bone's children to the list of bones to be processed.
+ bones_to_process.push_back(child_bones_vector[i]);
+ }
+ }
+
+ Ref<ArrayMesh> m = surface_tool->commit();
+ p_gizmo->add_mesh(m, Ref<Material>(), Transform3D(), skeleton->register_skin(Ref<Skin>()));
}
diff --git a/editor/plugins/skeleton_3d_editor_plugin.h b/editor/plugins/skeleton_3d_editor_plugin.h
index 9de52c6fa8..e2a1d9a628 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.h
+++ b/editor/plugins/skeleton_3d_editor_plugin.h
@@ -33,7 +33,12 @@
#include "editor/editor_node.h"
#include "editor/editor_plugin.h"
+#include "editor/editor_properties.h"
+#include "node_3d_editor_plugin.h"
+#include "scene/3d/camera_3d.h"
+#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/immediate_mesh.h"
class EditorInspectorPluginSkeleton;
class Joint;
@@ -41,8 +46,6 @@ class PhysicalBone3D;
class Skeleton3DEditorPlugin;
class Button;
class CheckBox;
-class EditorPropertyTransform3D;
-class EditorPropertyVector3;
class BoneTransformEditor : public VBoxContainer {
GDCLASS(BoneTransformEditor, VBoxContainer);
@@ -81,6 +84,8 @@ class BoneTransformEditor : public VBoxContainer {
void _value_changed_transform(const String p_property_name, const Transform3D p_transform, const StringName p_edited_property_name, const bool p_boolean);
// Changes the transform to the given transform and updates the UI accordingly.
void _change_transform(Transform3D p_new_transform);
+ // Update it is truely keyable then.
+ void _update_key_button(const bool p_keyable);
// Creates a Transform using the EditorPropertyVector3 properties.
Transform3D compute_transform_from_vector3s() const;
@@ -92,7 +97,7 @@ protected:
public:
BoneTransformEditor(Skeleton3D *p_skeleton);
- // Which transform target to modify
+ // Which transform target to modify.
void set_target(const String &p_prop);
void set_label(const String &p_label) { label = p_label; }
@@ -100,20 +105,21 @@ public:
void _update_custom_pose_properties();
void _update_transform_properties(Transform3D 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
+ // 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
+ // When rest mode, pose and custom_pose editor are diasbled.
+ void set_properties_read_only(const bool p_readonly);
+ void set_transform_read_only(const bool p_readonly);
+
+ // 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
+ // Key Transform Button pressed.
void _key_button_pressed();
- // Bone Enabled Checkbox toggled
- void _checkbox_toggled(const bool p_toggled);
+ // Bone Enabled Checkbox toggled.
+ void _checkbox_pressed();
};
class Skeleton3DEditor : public VBoxContainer {
@@ -121,13 +127,20 @@ class Skeleton3DEditor : public VBoxContainer {
friend class Skeleton3DEditorPlugin;
- enum Menu {
- MENU_OPTION_CREATE_PHYSICAL_SKELETON
+ enum SkeletonOption {
+ SKELETON_OPTION_INIT_POSE,
+ SKELETON_OPTION_INSERT_KEYS,
+ SKELETON_OPTION_INSERT_KEYS_EXISTED,
+ SKELETON_OPTION_CREATE_PHYSICAL_SKELETON
+ };
+
+ enum RestOption {
+ REST_OPTION_POSE_TO_REST
};
struct BoneInfo {
PhysicalBone3D *physical_bone = nullptr;
- Transform3D relative_rest; // Relative to skeleton node
+ Transform3D relative_rest; // Relative to skeleton node.
};
EditorNode *editor;
@@ -140,13 +153,24 @@ class Skeleton3DEditor : public VBoxContainer {
BoneTransformEditor *pose_editor = nullptr;
BoneTransformEditor *custom_pose_editor = nullptr;
- MenuButton *options = nullptr;
+ VSeparator *separator;
+ MenuButton *skeleton_options = nullptr;
+ MenuButton *rest_options = nullptr;
+ Button *edit_mode_button;
+
+ bool edit_mode = false;
+
EditorFileDialog *file_dialog = nullptr;
- UndoRedo *undo_redo = nullptr;
+ bool keyable;
+
+ static Skeleton3DEditor *singleton;
- void _on_click_option(int p_option);
+ void _on_click_skeleton_option(int p_skeleton_option);
+ void _on_click_rest_option(int p_rest_option);
void _file_selected(const String &p_file);
+ TreeItem *_find(TreeItem *p_node, const NodePath &p_path);
+ void edit_mode_toggled(const bool pressed);
EditorFileDialog *file_export_lib = nullptr;
@@ -155,6 +179,10 @@ class Skeleton3DEditor : public VBoxContainer {
void create_editors();
+ void init_pose();
+ void insert_keys(bool p_all_bones);
+ void pose_to_rest();
+
void create_physical_skeleton();
PhysicalBone3D *create_physical_bone(int bone_id, int bone_child_id, const Vector<BoneInfo> &bones_infos);
@@ -162,20 +190,56 @@ class Skeleton3DEditor : public VBoxContainer {
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);
+ void set_keyable(const bool p_keyable);
+ void set_rest_options_enabled(const bool p_rest_options_enabled);
+
+ // Handle.
+ MeshInstance3D *handles_mesh_instance;
+ Ref<ImmediateMesh> handles_mesh;
+ Ref<ShaderMaterial> handle_material;
+ Ref<Shader> handle_shader;
+
+ Transform3D bone_original;
+
+ void _update_pose_enabled(int p_bone = -1);
+ void _update_show_rest_only();
+
+ void _update_gizmo_transform();
+ void _update_gizmo_visible();
+
+ void _hide_handles();
+
+ void _draw_gizmo();
+ void _draw_handles();
+
+ void _joint_tree_selection_changed();
+ void _joint_tree_rmb_select(const Vector2 &p_pos);
+ void _update_properties();
+
+ void _subgizmo_selection_change();
+
+ int selected_bone = -1;
+
protected:
void _notification(int p_what);
void _node_removed(Node *p_node);
static void _bind_methods();
public:
+ static Skeleton3DEditor *get_singleton() { return singleton; }
+
+ void select_bone(int p_idx);
+
+ int get_selected_bone() const;
+
void move_skeleton_bone(NodePath p_skeleton_path, int32_t p_selected_boneidx, int32_t p_target_boneidx);
Skeleton3D *get_skeleton() const { return skeleton; };
- void _joint_tree_selection_changed();
- void _joint_tree_rmb_select(const Vector2 &p_pos);
+ bool is_edit_mode() const { return edit_mode; }
- void _update_properties();
+ void update_bone_original();
+ Transform3D get_bone_original() { return bone_original; };
Skeleton3DEditor(EditorInspectorPluginSkeleton *e_plugin, EditorNode *p_editor, Skeleton3D *skeleton);
~Skeleton3DEditor();
@@ -186,6 +250,7 @@ class EditorInspectorPluginSkeleton : public EditorInspectorPlugin {
friend class Skeleton3DEditorPlugin;
+ Skeleton3DEditor *skel_editor;
EditorNode *editor;
public:
@@ -196,12 +261,40 @@ public:
class Skeleton3DEditorPlugin : public EditorPlugin {
GDCLASS(Skeleton3DEditorPlugin, EditorPlugin);
+ EditorInspectorPluginSkeleton *skeleton_plugin;
EditorNode *editor;
public:
- Skeleton3DEditorPlugin(EditorNode *p_node);
+ virtual EditorPlugin::AfterGUIInput forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override;
+
+ bool has_main_screen() const override { return false; }
+ virtual bool handles(Object *p_object) const override;
virtual String get_name() const override { return "Skeleton3D"; }
+
+ Skeleton3DEditorPlugin(EditorNode *p_node);
+};
+
+class Skeleton3DGizmoPlugin : public EditorNode3DGizmoPlugin {
+ GDCLASS(Skeleton3DGizmoPlugin, EditorNode3DGizmoPlugin);
+
+ Ref<StandardMaterial3D> unselected_mat;
+ Ref<ShaderMaterial> selected_mat;
+ Ref<Shader> selected_sh;
+
+public:
+ bool has_gizmo(Node3D *p_spatial) override;
+ String get_gizmo_name() const override;
+ int get_priority() const override;
+
+ int subgizmos_intersect_ray(const EditorNode3DGizmo *p_gizmo, Camera3D *p_camera, const Vector2 &p_point) const override;
+ Transform3D get_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id) const override;
+ void set_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id, Transform3D p_transform) override;
+ void commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) override;
+
+ void redraw(EditorNode3DGizmo *p_gizmo) override;
+
+ Skeleton3DGizmoPlugin();
};
#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 0f889ce33d..eb5e527640 100644
--- a/editor/plugins/sprite_2d_editor_plugin.cpp
+++ b/editor/plugins/sprite_2d_editor_plugin.cpp
@@ -182,7 +182,7 @@ void Sprite2DEditor::_update_mesh_data() {
if (node->is_region_enabled()) {
rect = node->get_region_rect();
} else {
- rect.size = Size2(image->get_width(), image->get_height());
+ rect.size = image->get_size();
}
Ref<BitMap> bm;
@@ -209,7 +209,7 @@ void Sprite2DEditor::_update_mesh_data() {
computed_uv.clear();
computed_indices.clear();
- Size2 img_size = Vector2(image->get_width(), image->get_height());
+ Size2 img_size = image->get_size();
for (int i = 0; i < lines.size(); i++) {
lines.write[i] = expand(lines[i], rect, epsilon);
}
diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp
index 2883dbbc81..8a8d80891a 100644
--- a/editor/plugins/sprite_frames_editor_plugin.cpp
+++ b/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -40,7 +40,7 @@
#include "scene/gui/margin_container.h"
#include "scene/gui/panel_container.h"
-void SpriteFramesEditor::_gui_input(Ref<InputEvent> p_event) {
+void SpriteFramesEditor::gui_input(const Ref<InputEvent> &p_event) {
}
void SpriteFramesEditor::_open_sprite_sheet() {
@@ -54,22 +54,46 @@ void SpriteFramesEditor::_open_sprite_sheet() {
file_split_sheet->popup_file_dialog();
}
+int SpriteFramesEditor::_sheet_preview_position_to_frame_index(const Point2 &p_position) {
+ if (p_position.x < 0 || p_position.y < 0) {
+ return -1;
+ }
+
+ Size2i texture_size = split_sheet_preview->get_texture()->get_size();
+ int h = split_sheet_h->get_value();
+ int v = split_sheet_v->get_value();
+ if (h > texture_size.width || v > texture_size.height) {
+ return -1;
+ }
+
+ int x = int(p_position.x / sheet_zoom) / (texture_size.width / h);
+ int y = int(p_position.y / sheet_zoom) / (texture_size.height / v);
+ if (x >= h || y >= v) {
+ return -1;
+ }
+ return h * y + x;
+}
+
void SpriteFramesEditor::_sheet_preview_draw() {
- Size2i size = split_sheet_preview->get_size();
+ Size2i texture_size = split_sheet_preview->get_texture()->get_size();
int h = split_sheet_h->get_value();
int v = split_sheet_v->get_value();
- int width = size.width / h;
- int height = size.height / v;
+
+ real_t width = (texture_size.width / h) * sheet_zoom;
+ real_t height = (texture_size.height / v) * sheet_zoom;
const float a = 0.3;
- for (int i = 1; i < h; i++) {
- int x = i * width;
- split_sheet_preview->draw_line(Point2(x, 0), Point2(x, size.height), Color(1, 1, 1, a));
- split_sheet_preview->draw_line(Point2(x + 1, 0), Point2(x + 1, size.height), Color(0, 0, 0, a));
+
+ real_t y_end = v * height;
+ for (int i = 0; i <= h; i++) {
+ real_t x = i * width;
+ split_sheet_preview->draw_line(Point2(x, 0), Point2(x, y_end), Color(1, 1, 1, a));
+ split_sheet_preview->draw_line(Point2(x + 1, 0), Point2(x + 1, y_end), Color(0, 0, 0, a));
}
- for (int i = 1; i < v; i++) {
- int y = i * height;
- split_sheet_preview->draw_line(Point2(0, y), Point2(size.width, y), Color(1, 1, 1, a));
- split_sheet_preview->draw_line(Point2(0, y + 1), Point2(size.width, y + 1), Color(0, 0, 0, a));
+ real_t x_end = h * width;
+ for (int i = 0; i <= v; i++) {
+ real_t y = i * height;
+ split_sheet_preview->draw_line(Point2(0, y), Point2(x_end, y), Color(1, 1, 1, a));
+ split_sheet_preview->draw_line(Point2(0, y + 1), Point2(x_end, y + 1), Color(0, 0, 0, a));
}
if (frames_selected.size() == 0) {
@@ -83,9 +107,9 @@ void SpriteFramesEditor::_sheet_preview_draw() {
for (Set<int>::Element *E = frames_selected.front(); E; E = E->next()) {
int idx = E->get();
int xp = idx % h;
- int yp = (idx - xp) / h;
- int x = xp * width;
- int y = yp * height;
+ int yp = idx / h;
+ real_t x = xp * width;
+ real_t y = yp * height;
split_sheet_preview->draw_rect(Rect2(x + 5, y + 5, width - 10, height - 10), Color(0, 0, 0, 0.35), true);
split_sheet_preview->draw_rect(Rect2(x + 0, y + 0, width - 0, height - 0), Color(0, 0, 0, 1), false);
@@ -103,46 +127,43 @@ void SpriteFramesEditor::_sheet_preview_draw() {
void SpriteFramesEditor::_sheet_preview_input(const Ref<InputEvent> &p_event) {
const Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
- const Size2i size = split_sheet_preview->get_size();
- const int h = split_sheet_h->get_value();
- const int v = split_sheet_v->get_value();
-
- const int x = CLAMP(int(mb->get_position().x) * h / size.width, 0, h - 1);
- const int y = CLAMP(int(mb->get_position().y) * v / size.height, 0, v - 1);
-
- const int idx = h * y + x;
+ const int idx = _sheet_preview_position_to_frame_index(mb->get_position());
+
+ if (idx != -1) {
+ if (mb->is_shift_pressed() && last_frame_selected >= 0) {
+ //select multiple
+ int from = idx;
+ int to = last_frame_selected;
+ if (from > to) {
+ SWAP(from, to);
+ }
- if (mb->is_shift_pressed() && last_frame_selected >= 0) {
- //select multiple
- int from = idx;
- int to = last_frame_selected;
- if (from > to) {
- SWAP(from, to);
- }
+ for (int i = from; i <= to; i++) {
+ // Prevent double-toggling the same frame when moving the mouse when the mouse button is still held.
+ frames_toggled_by_mouse_hover.insert(idx);
- for (int i = from; i <= to; i++) {
+ if (mb->is_ctrl_pressed()) {
+ frames_selected.erase(i);
+ } else {
+ frames_selected.insert(i);
+ }
+ }
+ } else {
// Prevent double-toggling the same frame when moving the mouse when the mouse button is still held.
frames_toggled_by_mouse_hover.insert(idx);
- if (mb->is_ctrl_pressed()) {
- frames_selected.erase(i);
+ if (frames_selected.has(idx)) {
+ frames_selected.erase(idx);
} else {
- frames_selected.insert(i);
+ frames_selected.insert(idx);
}
}
- } else {
- // Prevent double-toggling the same frame when moving the mouse when the mouse button is still held.
- frames_toggled_by_mouse_hover.insert(idx);
-
- if (frames_selected.has(idx)) {
- frames_selected.erase(idx);
- } else {
- frames_selected.insert(idx);
- }
}
- last_frame_selected = idx;
- split_sheet_preview->update();
+ if (last_frame_selected != idx || idx != -1) {
+ last_frame_selected = idx;
+ split_sheet_preview->update();
+ }
}
if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
@@ -152,16 +173,9 @@ void SpriteFramesEditor::_sheet_preview_input(const Ref<InputEvent> &p_event) {
const Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid() && mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT) {
// Select by holding down the mouse button on frames.
- const Size2i size = split_sheet_preview->get_size();
- const int h = split_sheet_h->get_value();
- const int v = split_sheet_v->get_value();
-
- const int x = CLAMP(int(mm->get_position().x) * h / size.width, 0, h - 1);
- const int y = CLAMP(int(mm->get_position().y) * v / size.height, 0, v - 1);
-
- const int idx = h * y + x;
+ const int idx = _sheet_preview_position_to_frame_index(mm->get_position());
- if (!frames_toggled_by_mouse_hover.has(idx)) {
+ if (idx != -1 && !frames_toggled_by_mouse_hover.has(idx)) {
// Only allow toggling each tile once per mouse hold.
// Otherwise, the selection would constantly "flicker" in and out when moving the mouse cursor.
// The mouse button must be released before it can be toggled again.
@@ -199,17 +213,17 @@ void SpriteFramesEditor::_sheet_scroll_input(const Ref<InputEvent> &p_event) {
}
void SpriteFramesEditor::_sheet_add_frames() {
- Size2i size = split_sheet_preview->get_texture()->get_size();
+ Size2i texture_size = split_sheet_preview->get_texture()->get_size();
int frame_count_x = split_sheet_h->get_value();
int frame_count_y = split_sheet_v->get_value();
- Size2 frame_size(size.width / frame_count_x, size.height / frame_count_y);
+ Size2 frame_size(texture_size.width / frame_count_x, texture_size.height / frame_count_y);
undo_redo->create_action(TTR("Add Frame"));
int fc = frames->get_frame_count(edited_anim);
Point2 src_origin;
- Rect2 src_region(Point2(), size);
+ Rect2 src_region(Point2(), texture_size);
AtlasTexture *src_atlas = Object::cast_to<AtlasTexture>(*split_sheet_preview->get_texture());
if (src_atlas && src_atlas->get_atlas().is_valid()) {
diff --git a/editor/plugins/sprite_frames_editor_plugin.h b/editor/plugins/sprite_frames_editor_plugin.h
index e6c59e3533..5e3b2fb8c1 100644
--- a/editor/plugins/sprite_frames_editor_plugin.h
+++ b/editor/plugins/sprite_frames_editor_plugin.h
@@ -135,6 +135,7 @@ class SpriteFramesEditor : public HSplitContainer {
void _open_sprite_sheet();
void _prepare_sprite_sheet(const String &p_file);
+ int _sheet_preview_position_to_frame_index(const Vector2 &p_position);
void _sheet_preview_draw();
void _sheet_spin_changed(double);
void _sheet_preview_input(const Ref<InputEvent> &p_event);
@@ -147,7 +148,7 @@ class SpriteFramesEditor : public HSplitContainer {
protected:
void _notification(int p_what);
- void _gui_input(Ref<InputEvent> p_event);
+ virtual void gui_input(const Ref<InputEvent> &p_event) override;
static void _bind_methods();
public:
diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp
index 32bcc1a4e6..1fc7eb98e0 100644
--- a/editor/plugins/text_editor.cpp
+++ b/editor/plugins/text_editor.cpp
@@ -407,10 +407,6 @@ void TextEditor::_convert_case(CodeTextEditor::CaseStyle p_case) {
code_editor->convert_case(p_case);
}
-void TextEditor::_bind_methods() {
- ClassDB::bind_method(D_METHOD("add_syntax_highlighter", "highlighter"), &TextEditor::add_syntax_highlighter);
-}
-
static ScriptEditorBase *create_editor(const RES &p_resource) {
if (Object::cast_to<TextFile>(*p_resource)) {
return memnew(TextEditor);
@@ -513,6 +509,10 @@ void TextEditor::_make_context_menu(bool p_selection, bool p_can_fold, bool p_is
context_menu->popup();
}
+void TextEditor::update_toggle_scripts_button() {
+ code_editor->update_toggle_scripts_button();
+}
+
TextEditor::TextEditor() {
code_editor = memnew(CodeTextEditor);
add_child(code_editor);
@@ -521,6 +521,7 @@ TextEditor::TextEditor() {
code_editor->connect("validate_script", callable_mp(this, &TextEditor::_validate_script));
code_editor->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
code_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ code_editor->show_toggle_scripts_button();
update_settings();
diff --git a/editor/plugins/text_editor.h b/editor/plugins/text_editor.h
index 839e1c5f7a..7404557f46 100644
--- a/editor/plugins/text_editor.h
+++ b/editor/plugins/text_editor.h
@@ -87,8 +87,6 @@ private:
};
protected:
- static void _bind_methods();
-
void _edit_option(int p_op);
void _make_context_menu(bool p_selection, bool p_can_fold, bool p_is_folded, Vector2 p_position);
void _text_edit_gui_input(const Ref<InputEvent> &ev);
@@ -121,6 +119,8 @@ public:
virtual void set_edit_state(const Variant &p_state) override;
virtual Vector<String> get_functions() override;
virtual Array get_breakpoints() override;
+ virtual void set_breakpoint(int p_line, bool p_enabled) override{};
+ virtual void clear_breakpoints() override{};
virtual void goto_line(int p_line, bool p_with_error = false) override;
void goto_line_selection(int p_line, int p_begin, int p_end);
virtual void set_executing_line(int p_line) override;
@@ -137,6 +137,7 @@ public:
virtual void set_debugger_active(bool p_active) override;
virtual void set_tooltip_request_func(String p_method, Object *p_obj) override;
virtual void add_callback(const String &p_function, PackedStringArray p_args) override;
+ void update_toggle_scripts_button() override;
virtual Control *get_edit_menu() override;
virtual void clear_edit_menu() override;
diff --git a/editor/plugins/texture_3d_editor_plugin.cpp b/editor/plugins/texture_3d_editor_plugin.cpp
index 3987cdd6a0..bd1923f4ab 100644
--- a/editor/plugins/texture_3d_editor_plugin.cpp
+++ b/editor/plugins/texture_3d_editor_plugin.cpp
@@ -34,9 +34,6 @@
#include "core/io/resource_loader.h"
#include "editor/editor_settings.h"
-void Texture3DEditor::_gui_input(Ref<InputEvent> p_event) {
-}
-
void Texture3DEditor::_texture_rect_draw() {
texture_rect->draw_rect(Rect2(Point2(), texture_rect->get_size()), Color(1, 1, 1, 1));
}
@@ -79,6 +76,8 @@ void Texture3DEditor::_update_material() {
void Texture3DEditor::_make_shaders() {
shader.instantiate();
shader->set_code(R"(
+// Texture3DEditor preview shader.
+
shader_type canvas_item;
uniform sampler3D tex;
@@ -145,7 +144,6 @@ void Texture3DEditor::edit(Ref<Texture3D> p_texture) {
}
void Texture3DEditor::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_gui_input"), &Texture3DEditor::_gui_input);
ClassDB::bind_method(D_METHOD("_layer_changed"), &Texture3DEditor::_layer_changed);
}
diff --git a/editor/plugins/texture_3d_editor_plugin.h b/editor/plugins/texture_3d_editor_plugin.h
index 9d90d3653f..855194e644 100644
--- a/editor/plugins/texture_3d_editor_plugin.h
+++ b/editor/plugins/texture_3d_editor_plugin.h
@@ -65,7 +65,6 @@ class Texture3DEditor : public Control {
protected:
void _notification(int p_what);
- void _gui_input(Ref<InputEvent> p_event);
static void _bind_methods();
diff --git a/editor/plugins/texture_editor_plugin.cpp b/editor/plugins/texture_editor_plugin.cpp
index 44db06bcfd..b9ec6bf5ab 100644
--- a/editor/plugins/texture_editor_plugin.cpp
+++ b/editor/plugins/texture_editor_plugin.cpp
@@ -58,6 +58,21 @@ void TexturePreview::_notification(int p_what) {
}
}
+void TexturePreview::_update_metadata_label_text() {
+ Ref<Texture2D> texture = texture_display->get_texture();
+
+ String format;
+ if (Object::cast_to<ImageTexture>(*texture)) {
+ format = Image::get_format_name(Object::cast_to<ImageTexture>(*texture)->get_format());
+ } else if (Object::cast_to<StreamTexture2D>(*texture)) {
+ format = Image::get_format_name(Object::cast_to<StreamTexture2D>(*texture)->get_format());
+ } else {
+ format = texture->get_class();
+ }
+
+ metadata_label->set_text(itos(texture->get_width()) + "x" + itos(texture->get_height()) + " " + format);
+}
+
TexturePreview::TexturePreview(Ref<Texture2D> p_texture, bool p_show_metadata) {
checkerboard = memnew(TextureRect);
checkerboard->set_stretch_mode(TextureRect::STRETCH_TILE);
@@ -75,16 +90,8 @@ TexturePreview::TexturePreview(Ref<Texture2D> p_texture, bool p_show_metadata) {
if (p_show_metadata) {
metadata_label = memnew(Label);
- String format;
- if (Object::cast_to<ImageTexture>(*p_texture)) {
- format = Image::get_format_name(Object::cast_to<ImageTexture>(*p_texture)->get_format());
- } else if (Object::cast_to<StreamTexture2D>(*p_texture)) {
- format = Image::get_format_name(Object::cast_to<StreamTexture2D>(*p_texture)->get_format());
- } else {
- format = p_texture->get_class();
- }
-
- metadata_label->set_text(itos(p_texture->get_width()) + "x" + itos(p_texture->get_height()) + " " + format);
+ _update_metadata_label_text();
+ p_texture->connect("changed", callable_mp(this, &TexturePreview::_update_metadata_label_text));
// It's okay that these colors are static since the grid color is static too.
metadata_label->add_theme_color_override("font_color", Color::named("white"));
diff --git a/editor/plugins/texture_editor_plugin.h b/editor/plugins/texture_editor_plugin.h
index 36a5513ea6..60349febd7 100644
--- a/editor/plugins/texture_editor_plugin.h
+++ b/editor/plugins/texture_editor_plugin.h
@@ -44,6 +44,8 @@ private:
TextureRect *checkerboard = nullptr;
Label *metadata_label = nullptr;
+ void _update_metadata_label_text();
+
protected:
void _notification(int p_what);
diff --git a/editor/plugins/texture_layered_editor_plugin.cpp b/editor/plugins/texture_layered_editor_plugin.cpp
index 80359452ac..424e018a47 100644
--- a/editor/plugins/texture_layered_editor_plugin.cpp
+++ b/editor/plugins/texture_layered_editor_plugin.cpp
@@ -34,7 +34,7 @@
#include "core/io/resource_loader.h"
#include "editor/editor_settings.h"
-void TextureLayeredEditor::_gui_input(Ref<InputEvent> p_event) {
+void TextureLayeredEditor::gui_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
Ref<InputEventMouseMotion> mm = p_event;
@@ -106,6 +106,8 @@ void TextureLayeredEditor::_update_material() {
void TextureLayeredEditor::_make_shaders() {
shaders[0].instantiate();
shaders[0]->set_code(R"(
+// TextureLayeredEditor preview shader (2D array).
+
shader_type canvas_item;
uniform sampler2DArray tex;
@@ -118,6 +120,8 @@ void fragment() {
shaders[1].instantiate();
shaders[1]->set_code(R"(
+// TextureLayeredEditor preview shader (cubemap).
+
shader_type canvas_item;
uniform samplerCube tex;
@@ -132,6 +136,8 @@ void fragment() {
shaders[2].instantiate();
shaders[2]->set_code(R"(
+// TextureLayeredEditor preview shader (cubemap array).
+
shader_type canvas_item;
uniform samplerCubeArray tex;
@@ -214,7 +220,6 @@ void TextureLayeredEditor::edit(Ref<TextureLayered> p_texture) {
}
void TextureLayeredEditor::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_gui_input"), &TextureLayeredEditor::_gui_input);
ClassDB::bind_method(D_METHOD("_layer_changed"), &TextureLayeredEditor::_layer_changed);
}
diff --git a/editor/plugins/texture_layered_editor_plugin.h b/editor/plugins/texture_layered_editor_plugin.h
index c4ced62fb9..a7fe4b94e9 100644
--- a/editor/plugins/texture_layered_editor_plugin.h
+++ b/editor/plugins/texture_layered_editor_plugin.h
@@ -67,7 +67,7 @@ class TextureLayeredEditor : public Control {
protected:
void _notification(int p_what);
- void _gui_input(Ref<InputEvent> p_event);
+ virtual void gui_input(const Ref<InputEvent> &p_event) override;
static void _bind_methods();
public:
diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp
index 1a6eb7b63b..ce90d61616 100644
--- a/editor/plugins/texture_region_editor_plugin.cpp
+++ b/editor/plugins/texture_region_editor_plugin.cpp
@@ -50,7 +50,7 @@ void draw_margin_line(Control *edit_draw, Vector2 from, Vector2 to) {
EditorNode::get_singleton()->get_theme_base()->get_theme_color(SNAME("mono_color"), SNAME("Editor")).inverted() * Color(1, 1, 1, 0.5),
Math::round(2 * EDSCALE));
- while ((to - from).length_squared() > 200) {
+ while (from.distance_squared_to(to) > 200) {
edit_draw->draw_line(
from,
from + line,
@@ -63,16 +63,16 @@ void draw_margin_line(Control *edit_draw, Vector2 from, Vector2 to) {
void TextureRegionEditor::_region_draw() {
Ref<Texture2D> base_tex = nullptr;
- if (node_sprite) {
- base_tex = node_sprite->get_texture();
+ if (atlas_tex.is_valid()) {
+ base_tex = atlas_tex->get_atlas();
+ } else if (node_sprite_2d) {
+ base_tex = node_sprite_2d->get_texture();
} else if (node_sprite_3d) {
base_tex = node_sprite_3d->get_texture();
} else if (node_ninepatch) {
base_tex = node_ninepatch->get_texture();
} else if (obj_styleBox.is_valid()) {
base_tex = obj_styleBox->get_texture();
- } else if (atlas_tex.is_valid()) {
- base_tex = atlas_tex->get_atlas();
}
if (base_tex.is_null()) {
@@ -321,35 +321,38 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
prev_margin = margins[3];
}
if (edited_margin >= 0) {
- drag_from = Vector2(mb->get_position().x, mb->get_position().y);
+ drag_from = mb->get_position();
drag = true;
}
}
if (edited_margin < 0 && snap_mode == SNAP_AUTOSLICE) {
- Vector2 point = mtx.affine_inverse().xform(Vector2(mb->get_position().x, mb->get_position().y));
+ Vector2 point = mtx.affine_inverse().xform(mb->get_position());
for (const Rect2 &E : autoslice_cache) {
if (E.has_point(point)) {
rect = E;
if (Input::get_singleton()->is_key_pressed(KEY_CTRL) && !(Input::get_singleton()->is_key_pressed(Key(KEY_SHIFT | KEY_ALT)))) {
Rect2 r;
- if (node_sprite) {
- r = node_sprite->get_region_rect();
+ if (atlas_tex.is_valid()) {
+ r = atlas_tex->get_region();
+ } else if (node_sprite_2d) {
+ r = node_sprite_2d->get_region_rect();
} else if (node_sprite_3d) {
r = node_sprite_3d->get_region_rect();
} else if (node_ninepatch) {
r = node_ninepatch->get_region_rect();
} else if (obj_styleBox.is_valid()) {
r = obj_styleBox->get_region_rect();
- } else if (atlas_tex.is_valid()) {
- r = atlas_tex->get_region();
}
rect.expand_to(r.position);
- rect.expand_to(r.position + r.size);
+ rect.expand_to(r.get_end());
}
undo_redo->create_action(TTR("Set Region Rect"));
- if (node_sprite) {
- undo_redo->add_do_method(node_sprite, "set_region_rect", rect);
- undo_redo->add_undo_method(node_sprite, "set_region_rect", node_sprite->get_region_rect());
+ if (atlas_tex.is_valid()) {
+ undo_redo->add_do_method(atlas_tex.ptr(), "set_region", rect);
+ undo_redo->add_undo_method(atlas_tex.ptr(), "set_region", atlas_tex->get_region());
+ } else if (node_sprite_2d) {
+ undo_redo->add_do_method(node_sprite_2d, "set_region_rect", rect);
+ undo_redo->add_undo_method(node_sprite_2d, "set_region_rect", node_sprite_2d->get_region_rect());
} else if (node_sprite_3d) {
undo_redo->add_do_method(node_sprite_3d, "set_region_rect", rect);
undo_redo->add_undo_method(node_sprite_3d, "set_region_rect", node_sprite_3d->get_region_rect());
@@ -359,9 +362,6 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
} else if (obj_styleBox.is_valid()) {
undo_redo->add_do_method(obj_styleBox.ptr(), "set_region_rect", rect);
undo_redo->add_undo_method(obj_styleBox.ptr(), "set_region_rect", obj_styleBox->get_region_rect());
- } else if (atlas_tex.is_valid()) {
- undo_redo->add_do_method(atlas_tex.ptr(), "set_region", rect);
- undo_redo->add_undo_method(atlas_tex.ptr(), "set_region", atlas_tex->get_region());
}
undo_redo->add_do_method(this, "_update_rect");
undo_redo->add_undo_method(this, "_update_rect");
@@ -372,28 +372,28 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
}
}
} else if (edited_margin < 0) {
- drag_from = mtx.affine_inverse().xform(Vector2(mb->get_position().x, mb->get_position().y));
+ drag_from = mtx.affine_inverse().xform(mb->get_position());
if (snap_mode == SNAP_PIXEL) {
drag_from = drag_from.snapped(Vector2(1, 1));
} else if (snap_mode == SNAP_GRID) {
drag_from = snap_point(drag_from);
}
drag = true;
- if (node_sprite) {
- rect_prev = node_sprite->get_region_rect();
+ if (atlas_tex.is_valid()) {
+ rect_prev = atlas_tex->get_region();
+ } else if (node_sprite_2d) {
+ rect_prev = node_sprite_2d->get_region_rect();
} else if (node_sprite_3d) {
rect_prev = node_sprite_3d->get_region_rect();
} else if (node_ninepatch) {
rect_prev = node_ninepatch->get_region_rect();
} else if (obj_styleBox.is_valid()) {
rect_prev = obj_styleBox->get_region_rect();
- } else if (atlas_tex.is_valid()) {
- rect_prev = atlas_tex->get_region();
}
for (int i = 0; i < 8; i++) {
Vector2 tuv = endpoints[i];
- if (tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y)) < handle_radius) {
+ if (tuv.distance_to(mb->get_position()) < handle_radius) {
drag_index = i;
}
}
@@ -419,15 +419,15 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
edited_margin = -1;
} else {
undo_redo->create_action(TTR("Set Region Rect"));
- if (node_sprite) {
- undo_redo->add_do_method(node_sprite, "set_region_rect", node_sprite->get_region_rect());
- undo_redo->add_undo_method(node_sprite, "set_region_rect", rect_prev);
+ if (atlas_tex.is_valid()) {
+ undo_redo->add_do_method(atlas_tex.ptr(), "set_region", atlas_tex->get_region());
+ undo_redo->add_undo_method(atlas_tex.ptr(), "set_region", rect_prev);
+ } else if (node_sprite_2d) {
+ undo_redo->add_do_method(node_sprite_2d, "set_region_rect", node_sprite_2d->get_region_rect());
+ undo_redo->add_undo_method(node_sprite_2d, "set_region_rect", rect_prev);
} else if (node_sprite_3d) {
undo_redo->add_do_method(node_sprite_3d, "set_region_rect", node_sprite_3d->get_region_rect());
undo_redo->add_undo_method(node_sprite_3d, "set_region_rect", rect_prev);
- } else if (atlas_tex.is_valid()) {
- undo_redo->add_do_method(atlas_tex.ptr(), "set_region", atlas_tex->get_region());
- undo_redo->add_undo_method(atlas_tex.ptr(), "set_region", rect_prev);
} else if (node_ninepatch) {
undo_redo->add_do_method(node_ninepatch, "set_region_rect", node_ninepatch->get_region_rect());
undo_redo->add_undo_method(node_ninepatch, "set_region_rect", rect_prev);
@@ -544,7 +544,7 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
switch (drag_index) {
case 0: {
- Vector2 p = rect_prev.position + rect_prev.size;
+ Vector2 p = rect_prev.get_end();
rect = Rect2(p, Size2());
rect.expand_to(new_pos);
apply_rect(rect);
@@ -674,8 +674,7 @@ void TextureRegionEditor::_zoom_on_position(float p_zoom, Point2 p_position) {
draw_zoom = p_zoom;
Point2 ofs = p_position;
ofs = ofs / prev_zoom - ofs / draw_zoom;
- draw_ofs.x = Math::round(draw_ofs.x + ofs.x);
- draw_ofs.y = Math::round(draw_ofs.y + ofs.y);
+ draw_ofs = (draw_ofs + ofs).round();
edit_draw->update();
}
@@ -693,22 +692,24 @@ void TextureRegionEditor::_zoom_out() {
}
void TextureRegionEditor::apply_rect(const Rect2 &p_rect) {
- if (node_sprite) {
- node_sprite->set_region_rect(p_rect);
+ if (atlas_tex.is_valid()) {
+ atlas_tex->set_region(p_rect);
+ } else if (node_sprite_2d) {
+ node_sprite_2d->set_region_rect(p_rect);
} else if (node_sprite_3d) {
node_sprite_3d->set_region_rect(p_rect);
} else if (node_ninepatch) {
node_ninepatch->set_region_rect(p_rect);
} else if (obj_styleBox.is_valid()) {
obj_styleBox->set_region_rect(p_rect);
- } else if (atlas_tex.is_valid()) {
- atlas_tex->set_region(p_rect);
}
}
void TextureRegionEditor::_update_rect() {
- if (node_sprite) {
- rect = node_sprite->get_region_rect();
+ if (atlas_tex.is_valid()) {
+ rect = atlas_tex->get_region();
+ } else if (node_sprite_2d) {
+ rect = node_sprite_2d->get_region_rect();
} else if (node_sprite_3d) {
rect = node_sprite_3d->get_region_rect();
} else if (node_ninepatch) {
@@ -718,8 +719,6 @@ void TextureRegionEditor::_update_rect() {
}
} else if (obj_styleBox.is_valid()) {
rect = obj_styleBox->get_region_rect();
- } else if (atlas_tex.is_valid()) {
- rect = atlas_tex->get_region();
}
}
@@ -728,16 +727,16 @@ void TextureRegionEditor::_update_autoslice() {
autoslice_cache.clear();
Ref<Texture2D> texture = nullptr;
- if (node_sprite) {
- texture = node_sprite->get_texture();
+ if (atlas_tex.is_valid()) {
+ texture = atlas_tex->get_atlas();
+ } else if (node_sprite_2d) {
+ texture = node_sprite_2d->get_texture();
} else if (node_sprite_3d) {
texture = node_sprite_3d->get_texture();
} else if (node_ninepatch) {
texture = node_ninepatch->get_texture();
} else if (obj_styleBox.is_valid()) {
texture = obj_styleBox->get_texture();
- } else if (atlas_tex.is_valid()) {
- texture = atlas_tex->get_atlas();
}
if (texture.is_null()) {
@@ -823,8 +822,8 @@ void TextureRegionEditor::_notification(int p_what) {
}
void TextureRegionEditor::_node_removed(Object *p_obj) {
- if (p_obj == node_sprite || p_obj == node_sprite_3d || p_obj == node_ninepatch || p_obj == obj_styleBox.ptr() || p_obj == atlas_tex.ptr()) {
- node_sprite = nullptr;
+ if (p_obj == node_sprite_2d || p_obj == node_sprite_3d || p_obj == node_ninepatch || p_obj == obj_styleBox.ptr() || p_obj == atlas_tex.ptr()) {
+ node_sprite_2d = nullptr;
node_sprite_3d = nullptr;
node_ninepatch = nullptr;
obj_styleBox = Ref<StyleBox>(nullptr);
@@ -852,17 +851,17 @@ bool TextureRegionEditor::is_ninepatch() {
return node_ninepatch != nullptr;
}
-Sprite3D *TextureRegionEditor::get_sprite_3d() {
- return node_sprite_3d;
+Sprite2D *TextureRegionEditor::get_sprite_2d() {
+ return node_sprite_2d;
}
-Sprite2D *TextureRegionEditor::get_sprite() {
- return node_sprite;
+Sprite3D *TextureRegionEditor::get_sprite_3d() {
+ return node_sprite_3d;
}
void TextureRegionEditor::edit(Object *p_obj) {
- if (node_sprite) {
- node_sprite->disconnect("texture_changed", callable_mp(this, &TextureRegionEditor::_texture_changed));
+ if (node_sprite_2d) {
+ node_sprite_2d->disconnect("texture_changed", callable_mp(this, &TextureRegionEditor::_texture_changed));
}
if (node_sprite_3d) {
node_sprite_3d->disconnect("texture_changed", callable_mp(this, &TextureRegionEditor::_texture_changed));
@@ -877,7 +876,7 @@ void TextureRegionEditor::edit(Object *p_obj) {
atlas_tex->disconnect("changed", callable_mp(this, &TextureRegionEditor::_texture_changed));
}
if (p_obj) {
- node_sprite = Object::cast_to<Sprite2D>(p_obj);
+ node_sprite_2d = Object::cast_to<Sprite2D>(p_obj);
node_sprite_3d = Object::cast_to<Sprite3D>(p_obj);
node_ninepatch = Object::cast_to<NinePatchRect>(p_obj);
@@ -898,14 +897,14 @@ void TextureRegionEditor::edit(Object *p_obj) {
}
_edit_region();
} else {
- node_sprite = nullptr;
+ node_sprite_2d = nullptr;
node_sprite_3d = nullptr;
node_ninepatch = nullptr;
obj_styleBox = Ref<StyleBoxTexture>(nullptr);
atlas_tex = Ref<AtlasTexture>(nullptr);
}
edit_draw->update();
- if ((node_sprite && !node_sprite->is_region_enabled()) || (node_sprite_3d && !node_sprite_3d->is_region_enabled())) {
+ if ((node_sprite_2d && !node_sprite_2d->is_region_enabled()) || (node_sprite_3d && !node_sprite_3d->is_region_enabled())) {
set_process(true);
}
if (!p_obj) {
@@ -922,16 +921,16 @@ void TextureRegionEditor::_texture_changed() {
void TextureRegionEditor::_edit_region() {
Ref<Texture2D> texture = nullptr;
- if (node_sprite) {
- texture = node_sprite->get_texture();
+ if (atlas_tex.is_valid()) {
+ texture = atlas_tex->get_atlas();
+ } else if (node_sprite_2d) {
+ texture = node_sprite_2d->get_texture();
} else if (node_sprite_3d) {
texture = node_sprite_3d->get_texture();
} else if (node_ninepatch) {
texture = node_ninepatch->get_texture();
} else if (obj_styleBox.is_valid()) {
texture = obj_styleBox->get_texture();
- } else if (atlas_tex.is_valid()) {
- texture = atlas_tex->get_atlas();
}
if (texture.is_null()) {
@@ -967,7 +966,7 @@ Vector2 TextureRegionEditor::snap_point(Vector2 p_target) const {
}
TextureRegionEditor::TextureRegionEditor(EditorNode *p_editor) {
- node_sprite = nullptr;
+ node_sprite_2d = nullptr;
node_sprite_3d = nullptr;
node_ninepatch = nullptr;
obj_styleBox = Ref<StyleBoxTexture>(nullptr);
@@ -1122,7 +1121,9 @@ void TextureRegionEditorPlugin::_editor_visiblity_changed() {
void TextureRegionEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
texture_region_button->show();
- bool is_node_configured = region_editor->is_stylebox() || region_editor->is_atlas_texture() || region_editor->is_ninepatch() || (region_editor->get_sprite() && region_editor->get_sprite()->is_region_enabled()) || (region_editor->get_sprite_3d() && region_editor->get_sprite_3d()->is_region_enabled());
+ bool is_node_configured = region_editor->is_stylebox() || region_editor->is_atlas_texture() || region_editor->is_ninepatch();
+ is_node_configured |= region_editor->get_sprite_2d() && region_editor->get_sprite_2d()->is_region_enabled();
+ is_node_configured |= region_editor->get_sprite_3d() && region_editor->get_sprite_3d()->is_region_enabled();
if ((is_node_configured && !manually_hidden) || texture_region_button->is_pressed()) {
editor->make_bottom_panel_item_visible(region_editor);
}
diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h
index d3db0a08a9..c043d6ae33 100644
--- a/editor/plugins/texture_region_editor_plugin.h
+++ b/editor/plugins/texture_region_editor_plugin.h
@@ -83,7 +83,7 @@ class TextureRegionEditor : public VBoxContainer {
Vector2 snap_step;
Vector2 snap_separation;
- Sprite2D *node_sprite;
+ Sprite2D *node_sprite_2d;
Sprite3D *node_sprite_3d;
NinePatchRect *node_ninepatch;
Ref<StyleBoxTexture> obj_styleBox;
@@ -134,8 +134,8 @@ public:
bool is_stylebox();
bool is_atlas_texture();
bool is_ninepatch();
+ Sprite2D *get_sprite_2d();
Sprite3D *get_sprite_3d();
- Sprite2D *get_sprite();
void edit(Object *p_obj);
TextureRegionEditor(EditorNode *p_editor);
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
index 165a381407..19e1b40a0d 100644
--- a/editor/plugins/theme_editor_plugin.cpp
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -437,8 +437,8 @@ void ThemeItemImportTree::_update_total_selected(Theme::DataType p_data_type) {
}
int count = 0;
- for (Map<ThemeItem, ItemCheckedState>::Element *E = selected_items.front(); E; E = E->next()) {
- ThemeItem ti = E->key();
+ for (const KeyValue<ThemeItem, ItemCheckedState> &E : selected_items) {
+ ThemeItem ti = E.key;
if (ti.data_type == p_data_type) {
count++;
}
@@ -759,7 +759,7 @@ void ThemeItemImportTree::_import_selected() {
ProgressDialog::get_singleton()->add_task("import_theme_items", TTR("Importing Theme Items"), selected_items.size() + 2);
int idx = 0;
- for (Map<ThemeItem, ItemCheckedState>::Element *E = selected_items.front(); E; E = E->next()) {
+ for (KeyValue<ThemeItem, ItemCheckedState> &E : selected_items) {
// Arbitrary number of items to skip from reporting.
// Reduces the number of UI updates that this causes when copying large themes.
if (idx % 10 == 0) {
@@ -769,8 +769,8 @@ void ThemeItemImportTree::_import_selected() {
ProgressDialog::get_singleton()->task_step("import_theme_items", TTR("Importing items {n}/{n}").format(arr, "{n}"), idx);
}
- ItemCheckedState cs = E->get();
- ThemeItem ti = E->key();
+ ItemCheckedState cs = E.value;
+ ThemeItem ti = E.key;
if (cs == SELECT_IMPORT_DEFINITION || cs == SELECT_IMPORT_FULL) {
Variant item_value = Variant();
@@ -3333,7 +3333,7 @@ ThemeEditor::ThemeEditor() {
preview_tabs->set_h_size_flags(SIZE_EXPAND_FILL);
preview_tabbar_hb->add_child(preview_tabs);
preview_tabs->connect("tab_changed", callable_mp(this, &ThemeEditor::_change_preview_tab));
- preview_tabs->connect("right_button_pressed", callable_mp(this, &ThemeEditor::_remove_preview_tab));
+ preview_tabs->connect("tab_rmb_clicked", callable_mp(this, &ThemeEditor::_remove_preview_tab));
HBoxContainer *add_preview_button_hb = memnew(HBoxContainer);
preview_tabbar_hb->add_child(add_preview_button_hb);
diff --git a/editor/plugins/theme_editor_preview.cpp b/editor/plugins/theme_editor_preview.cpp
index 801ee0eac2..d26d0ec39d 100644
--- a/editor/plugins/theme_editor_preview.cpp
+++ b/editor/plugins/theme_editor_preview.cpp
@@ -126,8 +126,8 @@ void ThemeEditorPreview::_draw_picker_overlay() {
highlight_label_rect.size.x += margin_left + margin_right;
highlight_label_rect.size.y += margin_top + margin_bottom;
- highlight_label_rect.position.x = CLAMP(highlight_label_rect.position.x, 0.0, picker_overlay->get_size().width);
- highlight_label_rect.position.y = CLAMP(highlight_label_rect.position.y, 0.0, picker_overlay->get_size().height);
+ highlight_label_rect.position = highlight_label_rect.position.clamp(Vector2(), picker_overlay->get_size());
+
picker_overlay->draw_style_box(theme_cache.preview_picker_label, highlight_label_rect);
Point2 label_pos = highlight_label_rect.position;
diff --git a/editor/plugins/tiles/atlas_merging_dialog.cpp b/editor/plugins/tiles/atlas_merging_dialog.cpp
index d54906c98c..2a8a3216ed 100644
--- a/editor/plugins/tiles/atlas_merging_dialog.cpp
+++ b/editor/plugins/tiles/atlas_merging_dialog.cpp
@@ -94,12 +94,14 @@ void AtlasMergingDialog::_generate_merged(Vector<Ref<TileSetAtlasSource>> p_atla
}
// Copy the texture.
- Rect2i src_rect = atlas_source->get_tile_texture_region(tile_id);
- Rect2 dst_rect_wide = Rect2i(new_tile_rect_in_altas.position * new_texture_region_size, new_tile_rect_in_altas.size * new_texture_region_size);
- if (dst_rect_wide.get_end().x > output_image->get_width() || dst_rect_wide.get_end().y > output_image->get_height()) {
- output_image->crop(MAX(dst_rect_wide.get_end().x, output_image->get_width()), MAX(dst_rect_wide.get_end().y, output_image->get_height()));
+ for (int frame = 0; frame < atlas_source->get_tile_animation_frames_count(tile_id); frame++) {
+ Rect2i src_rect = atlas_source->get_tile_texture_region(tile_id, frame);
+ Rect2 dst_rect_wide = Rect2i(new_tile_rect_in_altas.position * new_texture_region_size, new_tile_rect_in_altas.size * new_texture_region_size);
+ if (dst_rect_wide.get_end().x > output_image->get_width() || dst_rect_wide.get_end().y > output_image->get_height()) {
+ output_image->crop(MAX(dst_rect_wide.get_end().x, output_image->get_width()), MAX(dst_rect_wide.get_end().y, output_image->get_height()));
+ }
+ output_image->blit_rect(atlas_source->get_texture()->get_image(), src_rect, dst_rect_wide.get_center() - src_rect.size / 2);
}
- output_image->blit_rect(atlas_source->get_texture()->get_image(), src_rect, (dst_rect_wide.get_position() + dst_rect_wide.get_end()) / 2 - src_rect.size / 2);
}
// Compute the atlas offset.
diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp
index 13f04cb804..f21d5098d3 100644
--- a/editor/plugins/tiles/tile_atlas_view.cpp
+++ b/editor/plugins/tiles/tile_atlas_view.cpp
@@ -41,7 +41,7 @@
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
-void TileAtlasView::_gui_input(const Ref<InputEvent> &p_event) {
+void TileAtlasView::gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
drag_type = DRAG_TYPE_NONE;
@@ -256,11 +256,15 @@ void TileAtlasView::_draw_base_tiles() {
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i);
- // Update the y to max value.
- Vector2i offset_pos = (margins + (atlas_coords * texture_region_size) + tile_set_atlas_source->get_tile_texture_region(atlas_coords).size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, 0));
+ for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(atlas_coords); frame++) {
+ // Update the y to max value.
+ int animation_columns = tile_set_atlas_source->get_tile_animation_columns(atlas_coords);
+ Vector2i frame_coords = atlas_coords + (tile_set_atlas_source->get_tile_size_in_atlas(atlas_coords) + tile_set_atlas_source->get_tile_animation_separation(atlas_coords)) * ((animation_columns > 0) ? Vector2i(frame % animation_columns, frame / animation_columns) : Vector2i(frame, 0));
+ Vector2i offset_pos = (margins + (frame_coords * texture_region_size) + tile_set_atlas_source->get_tile_texture_region(atlas_coords, frame).size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, 0));
- // Draw the tile.
- TileMap::draw_tile(base_tiles_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, 0);
+ // Draw the tile.
+ TileMap::draw_tile(base_tiles_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, 0, frame);
+ }
}
}
}
@@ -326,11 +330,18 @@ void TileAtlasView::_draw_base_tiles_shape_grid() {
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
Vector2 in_tile_base_offset = tile_set_atlas_source->get_tile_effective_texture_offset(tile_id, 0);
- Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(tile_id);
- Vector2 origin = texture_region.position + (texture_region.size - tile_shape_size) / 2 + in_tile_base_offset;
- // Draw only if the tile shape fits in the texture region
- tile_set->draw_tile_shape(base_tiles_shape_grid, Rect2(origin, tile_shape_size), grid_color);
+ for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(tile_id); frame++) {
+ Color color = grid_color;
+ if (frame > 0) {
+ color.a *= 0.3;
+ }
+ Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(tile_id);
+ Transform2D tile_xform;
+ tile_xform.set_origin(texture_region.get_center() + in_tile_base_offset);
+ tile_xform.set_scale(tile_shape_size);
+ tile_set->draw_tile_shape(base_tiles_shape_grid, tile_xform, color);
+ }
}
}
@@ -358,7 +369,7 @@ void TileAtlasView::_draw_alternatives() {
Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i);
current_pos.x = 0;
int y_increment = 0;
- Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(atlas_coords);
+ Size2i texture_region_size = tile_set_atlas_source->get_tile_texture_region(atlas_coords).size;
int alternatives_count = tile_set_atlas_source->get_alternative_tiles_count(atlas_coords);
for (int j = 1; j < alternatives_count; j++) {
int alternative_id = tile_set_atlas_source->get_alternative_tile_id(atlas_coords, j);
@@ -368,18 +379,18 @@ void TileAtlasView::_draw_alternatives() {
// Update the y to max value.
Vector2i offset_pos = current_pos;
if (transposed) {
- offset_pos = (current_pos + Vector2(texture_region.size.y, texture_region.size.x) / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, alternative_id));
- y_increment = MAX(y_increment, texture_region.size.x);
+ offset_pos = (current_pos + Vector2(texture_region_size.y, texture_region_size.x) / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, alternative_id));
+ y_increment = MAX(y_increment, texture_region_size.x);
} else {
- offset_pos = (current_pos + texture_region.size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, alternative_id));
- y_increment = MAX(y_increment, texture_region.size.y);
+ offset_pos = (current_pos + texture_region_size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, alternative_id));
+ y_increment = MAX(y_increment, texture_region_size.y);
}
// Draw the tile.
TileMap::draw_tile(alternatives_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, alternative_id);
// Increment the x position.
- current_pos.x += transposed ? texture_region.size.y : texture_region.size.x;
+ current_pos.x += transposed ? texture_region_size.y : texture_region_size.x;
}
if (alternatives_count > 1) {
current_pos.y += y_increment;
@@ -511,10 +522,10 @@ void TileAtlasView::_update_alternative_tiles_rect_cache() {
}
Vector3i TileAtlasView::get_alternative_tile_at_pos(const Vector2 p_pos) const {
- for (Map<Vector2, Map<int, Rect2i>>::Element *E_coords = alternative_tiles_rect_cache.front(); E_coords; E_coords = E_coords->next()) {
- for (Map<int, Rect2i>::Element *E_alternative = E_coords->value().front(); E_alternative; E_alternative = E_alternative->next()) {
- if (E_alternative->value().has_point(p_pos)) {
- return Vector3i(E_coords->key().x, E_coords->key().y, E_alternative->key());
+ for (const KeyValue<Vector2, Map<int, Rect2i>> &E_coords : alternative_tiles_rect_cache) {
+ for (const KeyValue<int, Rect2i> &E_alternative : E_coords.value) {
+ if (E_alternative.value.has_point(p_pos)) {
+ return Vector3i(E_coords.key.x, E_coords.key.y, E_alternative.key);
}
}
}
@@ -548,8 +559,6 @@ void TileAtlasView::_notification(int p_what) {
}
void TileAtlasView::_bind_methods() {
- ClassDB::bind_method("_gui_input", &TileAtlasView::_gui_input);
-
ADD_SIGNAL(MethodInfo("transform_changed", PropertyInfo(Variant::FLOAT, "zoom"), PropertyInfo(Variant::VECTOR2, "scroll")));
}
@@ -582,7 +591,7 @@ TileAtlasView::TileAtlasView() {
center_container = memnew(CenterContainer);
center_container->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
center_container->set_anchors_preset(Control::PRESET_CENTER);
- center_container->connect("gui_input", callable_mp(this, &TileAtlasView::_gui_input));
+ center_container->connect("gui_input", callable_mp(this, &TileAtlasView::gui_input));
panel->add_child(center_container);
missing_source_label = memnew(Label);
diff --git a/editor/plugins/tiles/tile_atlas_view.h b/editor/plugins/tiles/tile_atlas_view.h
index b2046f4322..5b0df366ae 100644
--- a/editor/plugins/tiles/tile_atlas_view.h
+++ b/editor/plugins/tiles/tile_atlas_view.h
@@ -62,7 +62,7 @@ private:
void _update_zoom_and_panning(bool p_zoom_on_mouse_pos = false);
void _zoom_widget_changed();
void _center_view();
- void _gui_input(const Ref<InputEvent> &p_event);
+ virtual void gui_input(const Ref<InputEvent> &p_event) override;
Map<Vector2, Map<int, Rect2i>> alternative_tiles_rect_cache;
void _update_alternative_tiles_rect_cache();
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
index bab55df65a..1a69d19d3c 100644
--- a/editor/plugins/tiles/tile_data_editors.cpp
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -110,7 +110,7 @@ void DummyObject::clear_dummy_properties() {
void GenericTilePolygonEditor::_base_control_draw() {
ERR_FAIL_COND(!tile_set.is_valid());
- real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius");
+ real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color");
const Ref<Texture2D> handle = get_theme_icon(SNAME("EditorPathSharpHandle"), SNAME("EditorIcons"));
@@ -124,7 +124,9 @@ void GenericTilePolygonEditor::_base_control_draw() {
base_control->draw_set_transform_matrix(xform);
// Draw the tile shape filled.
- tile_set->draw_tile_shape(base_control, Rect2(-tile_size / 2, tile_size), Color(1.0, 1.0, 1.0, 0.3), true);
+ Transform2D tile_xform;
+ tile_xform.set_scale(tile_size);
+ tile_set->draw_tile_shape(base_control, tile_xform, Color(1.0, 1.0, 1.0, 0.3), true);
// Draw the background.
if (background_texture.is_valid()) {
@@ -213,7 +215,7 @@ void GenericTilePolygonEditor::_base_control_draw() {
// Draw the tile shape line.
base_control->draw_set_transform_matrix(xform);
- tile_set->draw_tile_shape(base_control, Rect2(-tile_size / 2, tile_size), grid_color, false);
+ tile_set->draw_tile_shape(base_control, tile_xform, grid_color, false);
base_control->draw_set_transform_matrix(Transform2D());
}
@@ -229,10 +231,14 @@ void GenericTilePolygonEditor::_zoom_changed() {
void GenericTilePolygonEditor::_advanced_menu_item_pressed(int p_item_pressed) {
switch (p_item_pressed) {
- case RESET_TO_DEFAULT_TILE:
+ case RESET_TO_DEFAULT_TILE: {
undo_redo->create_action(TTR("Edit Polygons"));
undo_redo->add_do_method(this, "clear_polygons");
- undo_redo->add_do_method(this, "add_polygon", tile_set->get_tile_shape_polygon());
+ Vector<Vector2> polygon = tile_set->get_tile_shape_polygon();
+ for (int i = 0; i < polygon.size(); i++) {
+ polygon.write[i] = polygon[i] * tile_set->get_tile_size();
+ }
+ undo_redo->add_do_method(this, "add_polygon", polygon);
undo_redo->add_do_method(base_control, "update");
undo_redo->add_do_method(this, "emit_signal", "polygons_changed");
undo_redo->add_undo_method(this, "clear_polygons");
@@ -242,8 +248,8 @@ void GenericTilePolygonEditor::_advanced_menu_item_pressed(int p_item_pressed) {
undo_redo->add_undo_method(base_control, "update");
undo_redo->add_undo_method(this, "emit_signal", "polygons_changed");
undo_redo->commit_action(true);
- break;
- case CLEAR_TILE:
+ } break;
+ case CLEAR_TILE: {
undo_redo->create_action(TTR("Edit Polygons"));
undo_redo->add_do_method(this, "clear_polygons");
undo_redo->add_do_method(base_control, "update");
@@ -255,14 +261,14 @@ void GenericTilePolygonEditor::_advanced_menu_item_pressed(int p_item_pressed) {
undo_redo->add_undo_method(base_control, "update");
undo_redo->add_undo_method(this, "emit_signal", "polygons_changed");
undo_redo->commit_action(true);
- break;
+ } break;
default:
break;
}
}
void GenericTilePolygonEditor::_grab_polygon_point(Vector2 p_pos, const Transform2D &p_polygon_xform, int &r_polygon_index, int &r_point_index) {
- const real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius");
+ const real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
r_polygon_index = -1;
r_point_index = -1;
float closest_distance = grab_threshold + 1.0;
@@ -280,7 +286,7 @@ void GenericTilePolygonEditor::_grab_polygon_point(Vector2 p_pos, const Transfor
}
void GenericTilePolygonEditor::_grab_polygon_segment_point(Vector2 p_pos, const Transform2D &p_polygon_xform, int &r_polygon_index, int &r_segment_index, Vector2 &r_point) {
- const real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius");
+ const real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
Point2 point = p_polygon_xform.affine_inverse().xform(p_pos);
r_polygon_index = -1;
@@ -306,6 +312,9 @@ void GenericTilePolygonEditor::_snap_to_tile_shape(Point2 &r_point, float &r_cur
ERR_FAIL_COND(!tile_set.is_valid());
Vector<Point2> polygon = tile_set->get_tile_shape_polygon();
+ for (int i = 0; i < polygon.size(); i++) {
+ polygon.write[i] = polygon[i] * tile_set->get_tile_size();
+ }
Point2 snapped_point = r_point;
// Snap to polygon vertices.
@@ -340,7 +349,7 @@ void GenericTilePolygonEditor::_snap_to_half_pixel(Point2 &r_point) {
}
void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event) {
- real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius");
+ real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
hovered_polygon_index = -1;
hovered_point_index = -1;
@@ -537,7 +546,11 @@ void GenericTilePolygonEditor::set_tile_set(Ref<TileSet> p_tile_set) {
// Set the default tile shape
clear_polygons();
if (p_tile_set.is_valid()) {
- add_polygon(p_tile_set->get_tile_shape_polygon());
+ Vector<Vector2> polygon = p_tile_set->get_tile_shape_polygon();
+ for (int i = 0; i < polygon.size(); i++) {
+ polygon.write[i] = polygon[i] * p_tile_set->get_tile_size();
+ }
+ add_polygon(polygon);
}
}
tile_set = p_tile_set;
@@ -620,7 +633,7 @@ void GenericTilePolygonEditor::_notification(int p_what) {
button_delete->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("CurveDelete"), SNAME("EditorIcons")));
button_center_view->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("CenterView"), SNAME("EditorIcons")));
button_pixel_snap->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Snap"), SNAME("EditorIcons")));
- button_advanced_menu->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("GuiTabMenu"), SNAME("EditorIcons")));
+ button_advanced_menu->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
break;
}
}
@@ -743,10 +756,10 @@ Variant TileDataDefaultEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_s
}
void TileDataDefaultEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map<TileMapCell, Variant> p_previous_values, Variant p_new_value) {
- for (Map<TileMapCell, Variant>::Element *E = p_previous_values.front(); E; E = E->next()) {
- Vector2i coords = E->key().get_atlas_coords();
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/%s", coords.x, coords.y, E->key().alternative_tile, property), E->get());
- undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/%s", coords.x, coords.y, E->key().alternative_tile, property), p_new_value);
+ for (const KeyValue<TileMapCell, Variant> &E : p_previous_values) {
+ Vector2i coords = E.key.get_atlas_coords();
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/%s", coords.x, coords.y, E.key.alternative_tile, property), E.value);
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/%s", coords.x, coords.y, E.key.alternative_tile, property), p_new_value);
}
}
@@ -1072,14 +1085,15 @@ void TileDataTextureOffsetEditor::draw_over_tile(CanvasItem *p_canvas_item, Tran
ERR_FAIL_COND(!tile_data);
Vector2i tile_set_tile_size = tile_set->get_tile_size();
- Rect2i rect = Rect2i(-tile_set_tile_size / 2, tile_set_tile_size);
Color color = Color(1.0, 0.0, 0.0);
if (p_selected) {
Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color");
Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0);
color = selection_color;
}
- tile_set->draw_tile_shape(p_canvas_item, p_transform.xform(rect), color);
+ Transform2D tile_xform;
+ tile_xform.set_scale(tile_set_tile_size);
+ tile_set->draw_tile_shape(p_canvas_item, p_transform * tile_xform, color);
}
void TileDataPositionEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) {
@@ -1177,10 +1191,10 @@ Variant TileDataOcclusionShapeEditor::_get_value(TileSetAtlasSource *p_tile_set_
}
void TileDataOcclusionShapeEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map<TileMapCell, Variant> p_previous_values, Variant p_new_value) {
- for (Map<TileMapCell, Variant>::Element *E = p_previous_values.front(); E; E = E->next()) {
- Vector2i coords = E->key().get_atlas_coords();
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/occlusion_layer_%d/polygon", coords.x, coords.y, E->key().alternative_tile, occlusion_layer), E->get());
- undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/occlusion_layer_%d/polygon", coords.x, coords.y, E->key().alternative_tile, occlusion_layer), p_new_value);
+ for (const KeyValue<TileMapCell, Variant> &E : p_previous_values) {
+ Vector2i coords = E.key.get_atlas_coords();
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/occlusion_layer_%d/polygon", coords.x, coords.y, E.key.alternative_tile, occlusion_layer), E.value);
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/occlusion_layer_%d/polygon", coords.x, coords.y, E.key.alternative_tile, occlusion_layer), p_new_value);
}
}
@@ -1262,17 +1276,21 @@ void TileDataCollisionEditor::_polygons_changed() {
}
Variant TileDataCollisionEditor::_get_painted_value() {
+ Dictionary dict;
+ dict["linear_velocity"] = dummy_object->get("linear_velocity");
+ dict["angular_velocity"] = dummy_object->get("angular_velocity");
Array array;
for (int i = 0; i < polygon_editor->get_polygon_count(); i++) {
ERR_FAIL_COND_V(polygon_editor->get_polygon(i).size() < 3, Variant());
- Dictionary dict;
- dict["points"] = polygon_editor->get_polygon(i);
- dict["one_way"] = dummy_object->get(vformat("polygon_%d_one_way", i));
- dict["one_way_margin"] = dummy_object->get(vformat("polygon_%d_one_way_margin", i));
- array.push_back(dict);
+ Dictionary polygon_dict;
+ polygon_dict["points"] = polygon_editor->get_polygon(i);
+ polygon_dict["one_way"] = dummy_object->get(vformat("polygon_%d_one_way", i));
+ polygon_dict["one_way_margin"] = dummy_object->get(vformat("polygon_%d_one_way_margin", i));
+ array.push_back(polygon_dict);
}
+ dict["polygons"] = array;
- return array;
+ return dict;
}
void TileDataCollisionEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) {
@@ -1288,12 +1306,14 @@ void TileDataCollisionEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_
}
_polygons_changed();
+ dummy_object->set("linear_velocity", tile_data->get_constant_linear_velocity(physics_layer));
+ dummy_object->set("angular_velocity", tile_data->get_constant_angular_velocity(physics_layer));
for (int i = 0; i < tile_data->get_collision_polygons_count(physics_layer); i++) {
dummy_object->set(vformat("polygon_%d_one_way", i), tile_data->is_collision_polygon_one_way(physics_layer, i));
dummy_object->set(vformat("polygon_%d_one_way_margin", i), tile_data->get_collision_polygon_one_way_margin(physics_layer, i));
}
- for (Map<StringName, EditorProperty *>::Element *E = property_editors.front(); E; E = E->next()) {
- E->get()->update_property();
+ for (const KeyValue<StringName, EditorProperty *> &E : property_editors) {
+ E.value->update_property();
}
polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), p_tile_set_atlas_source->get_tile_effective_texture_offset(p_coords, p_alternative_tile), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate());
@@ -1303,13 +1323,16 @@ void TileDataCollisionEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_so
TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile));
ERR_FAIL_COND(!tile_data);
- Array array = p_value;
+ Dictionary dict = p_value;
+ tile_data->set_constant_linear_velocity(physics_layer, dict["linear_velocity"]);
+ tile_data->set_constant_angular_velocity(physics_layer, dict["angular_velocity"]);
+ Array array = dict["polygons"];
tile_data->set_collision_polygons_count(physics_layer, array.size());
for (int i = 0; i < array.size(); i++) {
- Dictionary dict = array[i];
- tile_data->set_collision_polygon_points(physics_layer, i, dict["points"]);
- tile_data->set_collision_polygon_one_way(physics_layer, i, dict["one_way"]);
- tile_data->set_collision_polygon_one_way_margin(physics_layer, i, dict["one_way_margin"]);
+ Dictionary polygon_dict = array[i];
+ tile_data->set_collision_polygon_points(physics_layer, i, polygon_dict["points"]);
+ tile_data->set_collision_polygon_one_way(physics_layer, i, polygon_dict["one_way"]);
+ tile_data->set_collision_polygon_one_way_margin(physics_layer, i, polygon_dict["one_way_margin"]);
}
polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), p_tile_set_atlas_source->get_tile_effective_texture_offset(p_coords, p_alternative_tile), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate());
@@ -1319,37 +1342,41 @@ Variant TileDataCollisionEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas
TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile));
ERR_FAIL_COND_V(!tile_data, Variant());
+ Dictionary dict;
+ dict["linear_velocity"] = tile_data->get_constant_linear_velocity(physics_layer);
+ dict["angular_velocity"] = tile_data->get_constant_angular_velocity(physics_layer);
Array array;
for (int i = 0; i < tile_data->get_collision_polygons_count(physics_layer); i++) {
- Dictionary dict;
- dict["points"] = tile_data->get_collision_polygon_points(physics_layer, i);
- dict["one_way"] = tile_data->is_collision_polygon_one_way(physics_layer, i);
- dict["one_way_margin"] = tile_data->get_collision_polygon_one_way_margin(physics_layer, i);
- array.push_back(dict);
+ Dictionary polygon_dict;
+ polygon_dict["points"] = tile_data->get_collision_polygon_points(physics_layer, i);
+ polygon_dict["one_way"] = tile_data->is_collision_polygon_one_way(physics_layer, i);
+ polygon_dict["one_way_margin"] = tile_data->get_collision_polygon_one_way_margin(physics_layer, i);
+ array.push_back(polygon_dict);
}
- return array;
+ dict["polygons"] = array;
+ return dict;
}
void TileDataCollisionEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map<TileMapCell, Variant> p_previous_values, Variant p_new_value) {
Array new_array = p_new_value;
- for (Map<TileMapCell, Variant>::Element *E = p_previous_values.front(); E; E = E->next()) {
- Array old_array = E->get();
+ for (KeyValue<TileMapCell, Variant> &E : p_previous_values) {
+ Array old_array = E.value;
- Vector2i coords = E->key().get_atlas_coords();
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygons_count", coords.x, coords.y, E->key().alternative_tile, physics_layer), old_array.size());
+ Vector2i coords = E.key.get_atlas_coords();
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygons_count", coords.x, coords.y, E.key.alternative_tile, physics_layer), old_array.size());
for (int i = 0; i < old_array.size(); i++) {
Dictionary dict = old_array[i];
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/points", coords.x, coords.y, E->key().alternative_tile, physics_layer, i), dict["points"]);
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way", coords.x, coords.y, E->key().alternative_tile, physics_layer, i), dict["one_way"]);
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way_margin", coords.x, coords.y, E->key().alternative_tile, physics_layer, i), dict["one_way_margin"]);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/points", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), dict["points"]);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), dict["one_way"]);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way_margin", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), dict["one_way_margin"]);
}
- undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygons_count", coords.x, coords.y, E->key().alternative_tile, physics_layer), new_array.size());
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygons_count", coords.x, coords.y, E.key.alternative_tile, physics_layer), new_array.size());
for (int i = 0; i < new_array.size(); i++) {
Dictionary dict = new_array[i];
- undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/points", coords.x, coords.y, E->key().alternative_tile, physics_layer, i), dict["points"]);
- undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way", coords.x, coords.y, E->key().alternative_tile, physics_layer, i), dict["one_way"]);
- undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way_margin", coords.x, coords.y, E->key().alternative_tile, physics_layer, i), dict["one_way_margin"]);
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/points", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), dict["points"]);
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), dict["one_way"]);
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way_margin", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), dict["one_way_margin"]);
}
}
}
@@ -1375,6 +1402,27 @@ TileDataCollisionEditor::TileDataCollisionEditor() {
polygon_editor->connect("polygons_changed", callable_mp(this, &TileDataCollisionEditor::_polygons_changed));
add_child(polygon_editor);
+ dummy_object->add_dummy_property("linear_velocity");
+ dummy_object->set("linear_velocity", Vector2());
+ dummy_object->add_dummy_property("angular_velocity");
+ dummy_object->set("angular_velocity", 0.0);
+
+ EditorProperty *linear_velocity_editor = EditorInspectorDefaultPlugin::get_editor_for_property(dummy_object, Variant::VECTOR2, "linear_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT);
+ linear_velocity_editor->set_object_and_property(dummy_object, "linear_velocity");
+ linear_velocity_editor->set_label("linear_velocity");
+ linear_velocity_editor->connect("property_changed", callable_mp(this, &TileDataCollisionEditor::_property_value_changed).unbind(1));
+ linear_velocity_editor->update_property();
+ add_child(linear_velocity_editor);
+ property_editors["linear_velocity"] = linear_velocity_editor;
+
+ EditorProperty *angular_velocity_editor = EditorInspectorDefaultPlugin::get_editor_for_property(dummy_object, Variant::FLOAT, "angular_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT);
+ angular_velocity_editor->set_object_and_property(dummy_object, "angular_velocity");
+ angular_velocity_editor->set_label("angular_velocity");
+ angular_velocity_editor->connect("property_changed", callable_mp(this, &TileDataCollisionEditor::_property_value_changed).unbind(1));
+ angular_velocity_editor->update_property();
+ add_child(angular_velocity_editor);
+ property_editors["angular_velocity"] = linear_velocity_editor;
+
_polygons_changed();
}
@@ -1465,12 +1513,13 @@ void TileDataTerrainsEditor::_tile_set_changed() {
ERR_FAIL_COND(!tile_set.is_valid());
// Fix if wrong values are selected.
- if (int(dummy_object->get("terrain_set")) > tile_set->get_terrain_sets_count()) {
+ int terrain_set = int(dummy_object->get("terrain_set"));
+ if (terrain_set >= tile_set->get_terrain_sets_count()) {
+ terrain_set = -1;
dummy_object->set("terrain_set", -1);
}
- int terrain_set = int(dummy_object->get("terrain"));
if (terrain_set >= 0) {
- if (int(dummy_object->get("terrain")) > tile_set->get_terrains_count(terrain_set)) {
+ if (int(dummy_object->get("terrain")) >= tile_set->get_terrains_count(terrain_set)) {
dummy_object->set("terrain", -1);
}
}
@@ -1491,7 +1540,7 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas
TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(hovered_coords, 0));
int terrain_set = tile_data->get_terrain_set();
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(hovered_coords);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(hovered_coords, 0);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(hovered_coords, 0);
if (terrain_set >= 0 && terrain_set == int(dummy_object->get("terrain_set"))) {
// Draw hovered bit.
@@ -1513,9 +1562,10 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas
}
} else {
// Draw hovered tile.
- Vector2i tile_size = tile_set->get_tile_size();
- Rect2i rect = p_transform.xform(Rect2i(position - tile_size / 2, tile_size));
- tile_set->draw_tile_shape(p_canvas_item, rect, Color(1.0, 1.0, 1.0, 0.5), true);
+ Transform2D tile_xform;
+ tile_xform.set_origin(position);
+ tile_xform.set_scale(tile_set->get_tile_size());
+ tile_set->draw_tile_shape(p_canvas_item, p_transform * tile_xform, Color(1.0, 1.0, 1.0, 0.5), true);
}
}
}
@@ -1535,7 +1585,7 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas
// Text
p_canvas_item->draw_set_transform_matrix(Transform2D());
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
Color color = Color(1, 1, 1);
String text;
@@ -1627,7 +1677,7 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas
Vector2i coords = E->get().get_atlas_coords();
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
@@ -1663,7 +1713,7 @@ void TileDataTerrainsEditor::forward_draw_over_alternatives(TileAtlasView *p_til
TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(hovered_coords, hovered_alternative));
int terrain_set = tile_data->get_terrain_set();
Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(hovered_coords, hovered_alternative);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(hovered_coords, hovered_alternative);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(hovered_coords, hovered_alternative);
if (terrain_set == int(dummy_object->get("terrain_set"))) {
// Draw hovered bit.
@@ -1685,9 +1735,10 @@ void TileDataTerrainsEditor::forward_draw_over_alternatives(TileAtlasView *p_til
}
} else {
// Draw hovered tile.
- Vector2i tile_size = tile_set->get_tile_size();
- Rect2i rect = p_transform.xform(Rect2i(position - tile_size / 2, tile_size));
- tile_set->draw_tile_shape(p_canvas_item, rect, Color(1.0, 1.0, 1.0, 0.5), true);
+ Transform2D tile_xform;
+ tile_xform.set_origin(position);
+ tile_xform.set_scale(tile_set->get_tile_size());
+ tile_set->draw_tile_shape(p_canvas_item, p_transform * tile_xform, Color(1.0, 1.0, 1.0, 0.5), true);
}
}
}
@@ -1709,7 +1760,7 @@ void TileDataTerrainsEditor::forward_draw_over_alternatives(TileAtlasView *p_til
// Text
p_canvas_item->draw_set_transform_matrix(Transform2D());
Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
Color color = Color(1, 1, 1);
String text;
@@ -1790,7 +1841,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
// Set the terrains bits.
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
if (tile_data->is_valid_peering_bit_terrain(bit)) {
@@ -1818,7 +1869,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, 0));
int terrain_set = tile_data->get_terrain_set();
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
dummy_object->set("terrain_set", terrain_set);
dummy_object->set("terrain", -1);
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
@@ -1916,7 +1967,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
// Set the terrain bit.
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
@@ -1970,16 +2021,16 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
drag_type = DRAG_TYPE_NONE;
} else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET) {
undo_redo->create_action(TTR("Painting Terrain Set"));
- for (Map<TileMapCell, Variant>::Element *E = drag_modified.front(); E; E = E->next()) {
- Dictionary dict = E->get();
- Vector2i coords = E->key().get_atlas_coords();
- undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E->key().alternative_tile), drag_painted_value);
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E->key().alternative_tile), dict["terrain_set"]);
+ for (KeyValue<TileMapCell, Variant> &E : drag_modified) {
+ Dictionary dict = E.value;
+ Vector2i coords = E.key.get_atlas_coords();
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), drag_painted_value);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), dict["terrain_set"]);
Array array = dict["terrain_peering_bits"];
for (int i = 0; i < array.size(); i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
if (tile_set->is_valid_peering_bit_terrain(dict["terrain_set"], bit)) {
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->key().alternative_tile), array[i]);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
}
}
}
@@ -1990,17 +2041,17 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
int terrain_set = int(painted["terrain_set"]);
int terrain = int(painted["terrain"]);
undo_redo->create_action(TTR("Painting Terrain"));
- for (Map<TileMapCell, Variant>::Element *E = drag_modified.front(); E; E = E->next()) {
- Dictionary dict = E->get();
- Vector2i coords = E->key().get_atlas_coords();
+ for (KeyValue<TileMapCell, Variant> &E : drag_modified) {
+ Dictionary dict = E.value;
+ Vector2i coords = E.key.get_atlas_coords();
Array array = dict["terrain_peering_bits"];
for (int i = 0; i < array.size(); i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
- undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->key().alternative_tile), terrain);
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), terrain);
}
if (tile_set->is_valid_peering_bit_terrain(dict["terrain_set"], bit)) {
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->key().alternative_tile), array[i]);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
}
}
}
@@ -2049,7 +2100,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit);
for (int j = 0; j < polygon.size(); j++) {
@@ -2132,7 +2183,7 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
// Set the terrains bits.
Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
if (tile_data->is_valid_peering_bit_terrain(bit)) {
@@ -2161,7 +2212,7 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, alternative_tile));
int terrain_set = tile_data->get_terrain_set();
Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
dummy_object->set("terrain_set", terrain_set);
dummy_object->set("terrain", -1);
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
@@ -2236,7 +2287,7 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
// Set the terrain bit.
Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
@@ -2253,14 +2304,14 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
} else {
if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET) {
undo_redo->create_action(TTR("Painting Tiles Property"));
- for (Map<TileMapCell, Variant>::Element *E = drag_modified.front(); E; E = E->next()) {
- Dictionary dict = E->get();
- Vector2i coords = E->key().get_atlas_coords();
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E->key().alternative_tile), dict["terrain_set"]);
- undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E->key().alternative_tile), drag_painted_value);
+ for (KeyValue<TileMapCell, Variant> &E : drag_modified) {
+ Dictionary dict = E.value;
+ Vector2i coords = E.key.get_atlas_coords();
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), dict["terrain_set"]);
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), drag_painted_value);
Array array = dict["terrain_peering_bits"];
for (int i = 0; i < array.size(); i++) {
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->key().alternative_tile), array[i]);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
}
}
undo_redo->commit_action(false);
@@ -2270,17 +2321,17 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
int terrain_set = int(painted["terrain_set"]);
int terrain = int(painted["terrain"]);
undo_redo->create_action(TTR("Painting Terrain"));
- for (Map<TileMapCell, Variant>::Element *E = drag_modified.front(); E; E = E->next()) {
- Dictionary dict = E->get();
- Vector2i coords = E->key().get_atlas_coords();
+ for (KeyValue<TileMapCell, Variant> &E : drag_modified) {
+ Dictionary dict = E.value;
+ Vector2i coords = E.key.get_atlas_coords();
Array array = dict["terrain_peering_bits"];
for (int i = 0; i < array.size(); i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
- undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->key().alternative_tile), terrain);
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), terrain);
}
if (tile_set->is_valid_peering_bit_terrain(dict["terrain_set"], bit)) {
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->key().alternative_tile), array[i]);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
}
}
}
@@ -2392,10 +2443,10 @@ Variant TileDataNavigationEditor::_get_value(TileSetAtlasSource *p_tile_set_atla
}
void TileDataNavigationEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map<TileMapCell, Variant> p_previous_values, Variant p_new_value) {
- for (Map<TileMapCell, Variant>::Element *E = p_previous_values.front(); E; E = E->next()) {
- Vector2i coords = E->key().get_atlas_coords();
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/navigation_layer_%d/polygon", coords.x, coords.y, E->key().alternative_tile, navigation_layer), E->get());
- undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/navigation_layer_%d/polygon", coords.x, coords.y, E->key().alternative_tile, navigation_layer), p_new_value);
+ for (const KeyValue<TileMapCell, Variant> &E : p_previous_values) {
+ Vector2i coords = E.key.get_atlas_coords();
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/navigation_layer_%d/polygon", coords.x, coords.y, E.key.alternative_tile, navigation_layer), E.value);
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/navigation_layer_%d/polygon", coords.x, coords.y, E.key.alternative_tile, navigation_layer), p_new_value);
}
}
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index 77084f551a..c2e86f8b43 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -455,34 +455,36 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
switch (drag_type) {
case DRAG_TYPE_PAINT: {
Map<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, drag_last_mouse_pos, mpos);
- for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) {
- if (!erase_button->is_pressed() && E->get().source_id == TileSet::INVALID_SOURCE) {
+ for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
+ if (!erase_button->is_pressed() && E.value.source_id == TileSet::INVALID_SOURCE) {
continue;
}
- Vector2i coords = E->key();
+ Vector2i coords = E.key;
if (!drag_modified.has(coords)) {
drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords));
}
- tile_map->set_cell(tile_map_layer, coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ tile_map->set_cell(tile_map_layer, coords, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
}
+ _fix_invalid_tiles_in_tile_map_selection();
} break;
case DRAG_TYPE_BUCKET: {
Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->world_to_map(drag_last_mouse_pos), tile_map->world_to_map(mpos));
for (int i = 0; i < line.size(); i++) {
if (!drag_modified.has(line[i])) {
Map<Vector2i, TileMapCell> to_draw = _draw_bucket_fill(line[i], bucket_continuous_checkbox->is_pressed());
- for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) {
- if (!erase_button->is_pressed() && E->get().source_id == TileSet::INVALID_SOURCE) {
+ for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
+ if (!erase_button->is_pressed() && E.value.source_id == TileSet::INVALID_SOURCE) {
continue;
}
- Vector2i coords = E->key();
+ Vector2i coords = E.key;
if (!drag_modified.has(coords)) {
drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords));
}
- tile_map->set_cell(tile_map_layer, coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ tile_map->set_cell(tile_map_layer, coords, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
}
}
}
+ _fix_invalid_tiles_in_tile_map_selection();
} break;
default:
break;
@@ -508,6 +510,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
drag_start_mouse_pos = mpos;
if (tile_map_selection.has(tile_map->world_to_map(drag_start_mouse_pos)) && !mb->is_shift_pressed()) {
// Move the selection
+ _update_selection_pattern_from_tilemap_selection(); // Make sure the pattern is up to date before moving.
drag_type = DRAG_TYPE_MOVE;
drag_modified.clear();
for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
@@ -531,16 +534,17 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
drag_start_mouse_pos = mpos;
drag_modified.clear();
Map<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, mpos, mpos);
- for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) {
- if (!erase_button->is_pressed() && E->get().source_id == TileSet::INVALID_SOURCE) {
+ for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
+ if (!erase_button->is_pressed() && E.value.source_id == TileSet::INVALID_SOURCE) {
continue;
}
- Vector2i coords = E->key();
+ Vector2i coords = E.key;
if (!drag_modified.has(coords)) {
drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords));
}
- tile_map->set_cell(tile_map_layer, coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ tile_map->set_cell(tile_map_layer, coords, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
}
+ _fix_invalid_tiles_in_tile_map_selection();
} else if (tool_buttons_group->get_pressed_button() == line_tool_button) {
drag_type = DRAG_TYPE_LINE;
drag_start_mouse_pos = mpos;
@@ -557,18 +561,19 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
for (int i = 0; i < line.size(); i++) {
if (!drag_modified.has(line[i])) {
Map<Vector2i, TileMapCell> to_draw = _draw_bucket_fill(line[i], bucket_continuous_checkbox->is_pressed());
- for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) {
- if (!erase_button->is_pressed() && E->get().source_id == TileSet::INVALID_SOURCE) {
+ for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
+ if (!erase_button->is_pressed() && E.value.source_id == TileSet::INVALID_SOURCE) {
continue;
}
- Vector2i coords = E->key();
+ Vector2i coords = E.key;
if (!drag_modified.has(coords)) {
drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords));
}
- tile_map->set_cell(tile_map_layer, coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ tile_map->set_cell(tile_map_layer, coords, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
}
}
}
+ _fix_invalid_tiles_in_tile_map_selection();
}
}
}
@@ -636,8 +641,10 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
for (int y = rect.position.y; y < rect.get_end().y; y++) {
Vector2i coords = Vector2i(x, y);
if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) {
- Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(coords) - tile_shape_size / 2, tile_shape_size));
- tile_set->draw_tile_shape(p_overlay, cell_region, Color(1.0, 1.0, 1.0), false);
+ Transform2D tile_xform;
+ tile_xform.set_origin(tile_map->map_to_world(coords));
+ tile_xform.set_scale(tile_shape_size);
+ tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(1.0, 1.0, 1.0), false);
}
}
}
@@ -708,8 +715,8 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
// Expand the grid if needed
if (expand_grid && !preview.is_empty()) {
drawn_grid_rect = Rect2i(preview.front()->key(), Vector2i(1, 1));
- for (Map<Vector2i, TileMapCell>::Element *E = preview.front(); E; E = E->next()) {
- drawn_grid_rect.expand_to(E->key());
+ for (const KeyValue<Vector2i, TileMapCell> &E : preview) {
+ drawn_grid_rect.expand_to(E.key);
}
}
}
@@ -734,33 +741,35 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
float bottom_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.y, (float)(drawn_grid_rect.size.y - fading), (float)pos_in_rect.y), 0.0f, 1.0f);
float opacity = CLAMP(MIN(left_opacity, MIN(right_opacity, MIN(top_opacity, bottom_opacity))) + 0.1, 0.0f, 1.0f);
- Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(Vector2(x, y)) - tile_shape_size / 2, tile_shape_size));
+ Transform2D tile_xform;
+ tile_xform.set_origin(tile_map->map_to_world(Vector2(x, y)));
+ tile_xform.set_scale(tile_shape_size);
Color color = grid_color;
color.a = color.a * opacity;
- tile_set->draw_tile_shape(p_overlay, cell_region, color, false);
+ tile_set->draw_tile_shape(p_overlay, xform * tile_xform, color, false);
}
}
}
}
// Draw the preview.
- for (Map<Vector2i, TileMapCell>::Element *E = preview.front(); E; E = E->next()) {
- Vector2i size = tile_set->get_tile_size();
- Vector2 position = tile_map->map_to_world(E->key()) - size / 2;
- Rect2 cell_region = xform.xform(Rect2(position, size));
+ for (const KeyValue<Vector2i, TileMapCell> &E : preview) {
+ Transform2D tile_xform;
+ tile_xform.set_origin(tile_map->map_to_world(E.key));
+ tile_xform.set_scale(tile_set->get_tile_size());
if (!erase_button->is_pressed() && random_tile_checkbox->is_pressed()) {
- tile_set->draw_tile_shape(p_overlay, cell_region, Color(1.0, 1.0, 1.0, 0.5), true);
+ tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(1.0, 1.0, 1.0, 0.5), true);
} else {
- if (tile_set->has_source(E->get().source_id)) {
- TileSetSource *source = *tile_set->get_source(E->get().source_id);
+ if (tile_set->has_source(E.value.source_id)) {
+ TileSetSource *source = *tile_set->get_source(E.value.source_id);
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
// Get tile data.
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile));
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E.value.get_atlas_coords(), E.value.alternative_tile));
// Compute the offset
- Rect2i source_rect = atlas_source->get_tile_texture_region(E->get().get_atlas_coords());
- Vector2i tile_offset = atlas_source->get_tile_effective_texture_offset(E->get().get_atlas_coords(), E->get().alternative_tile);
+ Rect2i source_rect = atlas_source->get_tile_texture_region(E.value.get_atlas_coords());
+ Vector2i tile_offset = atlas_source->get_tile_effective_texture_offset(E.value.get_atlas_coords(), E.value.alternative_tile);
// Compute the destination rectangle in the CanvasItem.
Rect2 dest_rect;
@@ -768,9 +777,9 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
bool transpose = tile_data->get_transpose();
if (transpose) {
- dest_rect.position = (tile_map->map_to_world(E->key()) - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset);
+ dest_rect.position = (tile_map->map_to_world(E.key) - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset);
} else {
- dest_rect.position = (tile_map->map_to_world(E->key()) - dest_rect.size / 2 - tile_offset);
+ dest_rect.position = (tile_map->map_to_world(E.key) - dest_rect.size / 2 - tile_offset);
}
dest_rect = xform.xform(dest_rect);
@@ -786,15 +795,15 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
// Get the tile modulation.
Color modulate = tile_data->get_modulate();
Color self_modulate = tile_map->get_self_modulate();
- modulate = Color(modulate.r * self_modulate.r, modulate.g * self_modulate.g, modulate.b * self_modulate.b, modulate.a * self_modulate.a);
+ modulate *= self_modulate;
// Draw the tile.
p_overlay->draw_texture_rect_region(atlas_source->get_texture(), dest_rect, source_rect, modulate * Color(1.0, 1.0, 1.0, 0.5), transpose, tile_set->is_uv_clipping());
} else {
- tile_set->draw_tile_shape(p_overlay, cell_region, Color(1.0, 1.0, 1.0, 0.5), true);
+ tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(1.0, 1.0, 1.0, 0.5), true);
}
} else {
- tile_set->draw_tile_shape(p_overlay, cell_region, Color(0.0, 0.0, 0.0, 0.5), true);
+ tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(0.0, 0.0, 0.0, 0.5), true);
}
}
}
@@ -1037,7 +1046,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i
TypedArray<Vector2i> to_check;
if (source.source_id == TileSet::INVALID_SOURCE) {
Rect2i rect = tile_map->get_used_rect();
- if (rect.size.x <= 0 || rect.size.y <= 0) {
+ if (rect.has_no_area()) {
rect = Rect2i(p_coords, Vector2i(1, 1));
}
for (int x = boundaries.position.x; x < boundaries.get_end().x; x++) {
@@ -1161,11 +1170,11 @@ void TileMapEditorTilesPlugin::_stop_dragging() {
}
undo_redo->create_action(TTR("Move tiles"));
// Move the tiles.
- for (Map<Vector2i, TileMapCell>::Element *E = cells_do.front(); E; E = E->next()) {
- undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ for (const KeyValue<Vector2i, TileMapCell> &E : cells_do) {
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
}
- for (Map<Vector2i, TileMapCell>::Element *E = cells_undo.front(); E; E = E->next()) {
- undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ for (const KeyValue<Vector2i, TileMapCell> &E : cells_undo) {
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
}
// Update the selection.
@@ -1201,41 +1210,41 @@ void TileMapEditorTilesPlugin::_stop_dragging() {
} break;
case DRAG_TYPE_PAINT: {
undo_redo->create_action(TTR("Paint tiles"));
- for (Map<Vector2i, TileMapCell>::Element *E = drag_modified.front(); E; E = E->next()) {
- undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), tile_map->get_cell_source_id(tile_map_layer, E->key()), tile_map->get_cell_atlas_coords(tile_map_layer, E->key()), tile_map->get_cell_alternative_tile(tile_map_layer, E->key()));
- undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ for (const KeyValue<Vector2i, TileMapCell> &E : drag_modified) {
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E.key, tile_map->get_cell_source_id(tile_map_layer, E.key), tile_map->get_cell_atlas_coords(tile_map_layer, E.key), tile_map->get_cell_alternative_tile(tile_map_layer, E.key));
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
}
undo_redo->commit_action(false);
} break;
case DRAG_TYPE_LINE: {
Map<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, drag_start_mouse_pos, mpos);
undo_redo->create_action(TTR("Paint tiles"));
- for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) {
- if (!erase_button->is_pressed() && E->get().source_id == TileSet::INVALID_SOURCE) {
+ for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
+ if (!erase_button->is_pressed() && E.value.source_id == TileSet::INVALID_SOURCE) {
continue;
}
- undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
- undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), tile_map->get_cell_source_id(tile_map_layer, E->key()), tile_map->get_cell_atlas_coords(tile_map_layer, E->key()), tile_map->get_cell_alternative_tile(tile_map_layer, E->key()));
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E.key, tile_map->get_cell_source_id(tile_map_layer, E.key), tile_map->get_cell_atlas_coords(tile_map_layer, E.key), tile_map->get_cell_alternative_tile(tile_map_layer, E.key));
}
undo_redo->commit_action();
} break;
case DRAG_TYPE_RECT: {
Map<Vector2i, TileMapCell> to_draw = _draw_rect(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(mpos));
undo_redo->create_action(TTR("Paint tiles"));
- for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) {
- if (!erase_button->is_pressed() && E->get().source_id == TileSet::INVALID_SOURCE) {
+ for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
+ if (!erase_button->is_pressed() && E.value.source_id == TileSet::INVALID_SOURCE) {
continue;
}
- undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
- undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), tile_map->get_cell_source_id(tile_map_layer, E->key()), tile_map->get_cell_atlas_coords(tile_map_layer, E->key()), tile_map->get_cell_alternative_tile(tile_map_layer, E->key()));
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E.key, tile_map->get_cell_source_id(tile_map_layer, E.key), tile_map->get_cell_atlas_coords(tile_map_layer, E.key), tile_map->get_cell_alternative_tile(tile_map_layer, E.key));
}
undo_redo->commit_action();
} break;
case DRAG_TYPE_BUCKET: {
undo_redo->create_action(TTR("Paint tiles"));
- for (Map<Vector2i, TileMapCell>::Element *E = drag_modified.front(); E; E = E->next()) {
- undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), tile_map->get_cell_source_id(tile_map_layer, E->key()), tile_map->get_cell_atlas_coords(tile_map_layer, E->key()), tile_map->get_cell_alternative_tile(tile_map_layer, E->key()));
- undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ for (const KeyValue<Vector2i, TileMapCell> &E : drag_modified) {
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E.key, tile_map->get_cell_source_id(tile_map_layer, E.key), tile_map->get_cell_atlas_coords(tile_map_layer, E.key), tile_map->get_cell_alternative_tile(tile_map_layer, E.key));
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
}
undo_redo->commit_action(false);
} break;
@@ -1319,6 +1328,25 @@ void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() {
}
}
+void TileMapEditorTilesPlugin::_fix_invalid_tiles_in_tile_map_selection() {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ Set<Vector2i> to_remove;
+ for (Vector2i selected : tile_map_selection) {
+ TileMapCell cell = tile_map->get_cell(tile_map_layer, selected);
+ if (cell.source_id == TileSet::INVALID_SOURCE && cell.get_atlas_coords() == TileSetSource::INVALID_ATLAS_COORDS && cell.alternative_tile == TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) {
+ to_remove.insert(selected);
+ }
+ }
+
+ for (Vector2i cell : to_remove) {
+ tile_map_selection.erase(cell);
+ }
+}
+
void TileMapEditorTilesPlugin::_update_selection_pattern_from_tilemap_selection() {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
@@ -1360,17 +1388,17 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection(
}
int vertical_offset = 0;
- for (Map<int, List<const TileMapCell *>>::Element *E_source = per_source.front(); E_source; E_source = E_source->next()) {
+ for (const KeyValue<int, List<const TileMapCell *>> &E_source : per_source) {
// Per source.
List<const TileMapCell *> unorganized;
Rect2i encompassing_rect_coords;
Map<Vector2i, const TileMapCell *> organized_pattern;
- TileSetSource *source = *tile_set->get_source(E_source->key());
+ TileSetSource *source = *tile_set->get_source(E_source.key);
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
// Organize using coordinates.
- for (const TileMapCell *current : E_source->get()) {
+ for (const TileMapCell *current : E_source.value) {
if (current->alternative_tile == 0) {
organized_pattern[current->get_atlas_coords()] = current;
} else {
@@ -1389,14 +1417,14 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection(
}
} else {
// Add everything unorganized.
- for (const TileMapCell *cell : E_source->get()) {
+ for (const TileMapCell *cell : E_source.value) {
unorganized.push_back(cell);
}
}
// Now add everything to the output pattern.
- for (Map<Vector2i, const TileMapCell *>::Element *E_cell = organized_pattern.front(); E_cell; E_cell = E_cell->next()) {
- selection_pattern->set_cell(E_cell->key() - encompassing_rect_coords.position + Vector2i(0, vertical_offset), E_cell->get()->source_id, E_cell->get()->get_atlas_coords(), E_cell->get()->alternative_tile);
+ for (const KeyValue<Vector2i, const TileMapCell *> &E_cell : organized_pattern) {
+ selection_pattern->set_cell(E_cell.key - encompassing_rect_coords.position + Vector2i(0, vertical_offset), E_cell.value->source_id, E_cell.value->get_atlas_coords(), E_cell.value->alternative_tile);
}
Vector2i organized_size = selection_pattern->get_size();
int unorganized_index = 0;
@@ -1419,6 +1447,8 @@ void TileMapEditorTilesPlugin::_update_tileset_selection_from_selection_pattern(
}
}
_update_bottom_panel();
+ tile_atlas_control->update();
+ alternative_tiles_control->update();
}
void TileMapEditorTilesPlugin::_tile_atlas_control_draw() {
@@ -1452,13 +1482,25 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_draw() {
Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0);
for (Set<TileMapCell>::Element *E = tile_set_selection.front(); E; E = E->next()) {
if (E->get().source_id == source_id && E->get().alternative_tile == 0) {
- tile_atlas_control->draw_rect(atlas->get_tile_texture_region(E->get().get_atlas_coords()), selection_color, false);
+ for (int frame = 0; frame < atlas->get_tile_animation_frames_count(E->get().get_atlas_coords()); frame++) {
+ Color color = selection_color;
+ if (frame > 0) {
+ color.a *= 0.3;
+ }
+ tile_atlas_control->draw_rect(atlas->get_tile_texture_region(E->get().get_atlas_coords(), frame), color, false);
+ }
}
}
// Draw the hovered tile.
if (hovered_tile.get_atlas_coords() != TileSetSource::INVALID_ATLAS_COORDS && hovered_tile.alternative_tile == 0 && !tile_set_dragging_selection) {
- tile_atlas_control->draw_rect(atlas->get_tile_texture_region(hovered_tile.get_atlas_coords()), Color(1.0, 1.0, 1.0), false);
+ for (int frame = 0; frame < atlas->get_tile_animation_frames_count(hovered_tile.get_atlas_coords()); frame++) {
+ Color color = Color(1.0, 1.0, 1.0);
+ if (frame > 0) {
+ color.a *= 0.3;
+ }
+ tile_atlas_control->draw_rect(atlas->get_tile_texture_region(hovered_tile.get_atlas_coords(), frame), color, false);
+ }
}
// Draw the selection rect.
@@ -2292,14 +2334,14 @@ Set<TileMapEditorTerrainsPlugin::TerrainsTilePattern> TileMapEditorTerrainsPlugi
// Returns all tiles compatible with the given constraints.
Set<TerrainsTilePattern> compatible_terrain_tile_patterns;
- for (Map<TerrainsTilePattern, Set<TileMapCell>>::Element *E = per_terrain_terrains_tile_patterns_tiles[p_terrain_set].front(); E; E = E->next()) {
+ for (const KeyValue<TerrainsTilePattern, Set<TileMapCell>> &E : per_terrain_terrains_tile_patterns_tiles[p_terrain_set]) {
int valid = true;
int in_pattern_count = 0;
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, bit)) {
// Check if the bit is compatible with the constraints.
- Constraint terrain_bit_constraint = Constraint(tile_map, p_position, bit, E->key()[in_pattern_count]);
+ Constraint terrain_bit_constraint = Constraint(tile_map, p_position, bit, E.key[in_pattern_count]);
Set<Constraint>::Element *in_set_constraint_element = p_constraints.find(terrain_bit_constraint);
if (in_set_constraint_element && in_set_constraint_element->get().get_terrain() != terrain_bit_constraint.get_terrain()) {
@@ -2311,7 +2353,7 @@ Set<TileMapEditorTerrainsPlugin::TerrainsTilePattern> TileMapEditorTerrainsPlugi
}
if (valid) {
- compatible_terrain_tile_patterns.insert(E->key());
+ compatible_terrain_tile_patterns.insert(E.key);
}
}
@@ -2352,15 +2394,15 @@ Set<TileMapEditorTerrainsPlugin::Constraint> TileMapEditorTerrainsPlugin::_get_c
// Count the number of occurrences per terrain.
Map<Vector2i, TileSet::CellNeighbor> overlapping_terrain_bits = c.get_overlapping_coords_and_peering_bits();
- for (Map<Vector2i, TileSet::CellNeighbor>::Element *E_overlapping = overlapping_terrain_bits.front(); E_overlapping; E_overlapping = E_overlapping->next()) {
- if (!p_to_replace.has(E_overlapping->key())) {
- TileMapCell neighbor_cell = tile_map->get_cell(tile_map_layer, E_overlapping->key());
+ for (const KeyValue<Vector2i, TileSet::CellNeighbor> &E_overlapping : overlapping_terrain_bits) {
+ if (!p_to_replace.has(E_overlapping.key)) {
+ TileMapCell neighbor_cell = tile_map->get_cell(tile_map_layer, E_overlapping.key);
TileData *neighbor_tile_data = nullptr;
if (terrain_tiles.has(neighbor_cell) && terrain_tiles[neighbor_cell]->get_terrain_set() == p_terrain_set) {
neighbor_tile_data = terrain_tiles[neighbor_cell];
}
- int terrain = neighbor_tile_data ? neighbor_tile_data->get_peering_bit_terrain(TileSet::CellNeighbor(E_overlapping->get())) : -1;
+ int terrain = neighbor_tile_data ? neighbor_tile_data->get_peering_bit_terrain(TileSet::CellNeighbor(E_overlapping.value)) : -1;
if (terrain_count.has(terrain)) {
terrain_count[terrain] = 0;
}
@@ -2371,10 +2413,10 @@ Set<TileMapEditorTerrainsPlugin::Constraint> TileMapEditorTerrainsPlugin::_get_c
// Get the terrain with the max number of occurrences.
int max = 0;
int max_terrain = -1;
- for (Map<int, int>::Element *E_terrain_count = terrain_count.front(); E_terrain_count; E_terrain_count = E_terrain_count->next()) {
- if (E_terrain_count->get() > max) {
- max = E_terrain_count->get();
- max_terrain = E_terrain_count->key();
+ for (const KeyValue<int, int> &E_terrain_count : terrain_count) {
+ if (E_terrain_count.value > max) {
+ max = E_terrain_count.value;
+ max_terrain = E_terrain_count.key;
}
}
@@ -2442,15 +2484,15 @@ Map<Vector2i, TileMapEditorTerrainsPlugin::TerrainsTilePattern> TileMapEditorTer
while (!to_replace.is_empty()) {
// Compute the minimum number of tile possibilities for each cell.
int min_nb_possibilities = 100000000;
- for (Map<Vector2i, Set<TerrainsTilePattern>>::Element *E = per_cell_acceptable_tiles.front(); E; E = E->next()) {
- min_nb_possibilities = MIN(min_nb_possibilities, E->get().size());
+ for (const KeyValue<Vector2i, Set<TerrainsTilePattern>> &E : per_cell_acceptable_tiles) {
+ min_nb_possibilities = MIN(min_nb_possibilities, E.value.size());
}
// Get the set of possible cells to fill.
LocalVector<Vector2i> to_choose_from;
- for (Map<Vector2i, Set<TerrainsTilePattern>>::Element *E = per_cell_acceptable_tiles.front(); E; E = E->next()) {
- if (E->get().size() == min_nb_possibilities) {
- to_choose_from.push_back(E->key());
+ for (const KeyValue<Vector2i, Set<TerrainsTilePattern>> &E : per_cell_acceptable_tiles) {
+ if (E.value.size() == min_nb_possibilities) {
+ to_choose_from.push_back(E.key);
}
}
@@ -2568,9 +2610,9 @@ Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const Map
// Add the constraints from the added tiles.
Set<TileMapEditorTerrainsPlugin::Constraint> added_tiles_constraints_set;
- for (Map<Vector2i, TerrainsTilePattern>::Element *E_to_paint = p_to_paint.front(); E_to_paint; E_to_paint = E_to_paint->next()) {
- Vector2i coords = E_to_paint->key();
- TerrainsTilePattern terrains_tile_pattern = E_to_paint->get();
+ for (const KeyValue<Vector2i, TerrainsTilePattern> &E_to_paint : p_to_paint) {
+ Vector2i coords = E_to_paint.key;
+ TerrainsTilePattern terrains_tile_pattern = E_to_paint.value;
Set<TileMapEditorTerrainsPlugin::Constraint> cell_constraints = _get_constraints_from_added_tile(coords, p_terrain_set, terrains_tile_pattern);
for (Set<TileMapEditorTerrainsPlugin::Constraint>::Element *E = cell_constraints.front(); E; E = E->next()) {
@@ -2580,8 +2622,8 @@ Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const Map
// Build the list of potential tiles to replace.
Set<Vector2i> potential_to_replace;
- for (Map<Vector2i, TerrainsTilePattern>::Element *E_to_paint = p_to_paint.front(); E_to_paint; E_to_paint = E_to_paint->next()) {
- Vector2i coords = E_to_paint->key();
+ for (const KeyValue<Vector2i, TerrainsTilePattern> &E_to_paint : p_to_paint) {
+ Vector2i coords = E_to_paint.key;
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
if (tile_map->is_existing_neighbor(TileSet::CellNeighbor(i))) {
Vector2i neighbor = tile_map->get_neighbor_cell(coords, TileSet::CellNeighbor(i));
@@ -2596,8 +2638,8 @@ Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const Map
Set<Vector2i> to_replace;
// Add the central tiles to the one to replace. TODO: maybe change that.
- for (Map<Vector2i, TerrainsTilePattern>::Element *E_to_paint = p_to_paint.front(); E_to_paint; E_to_paint = E_to_paint->next()) {
- to_replace.insert(E_to_paint->key());
+ for (const KeyValue<Vector2i, TerrainsTilePattern> &E_to_paint : p_to_paint) {
+ to_replace.insert(E_to_paint.key);
}
// Add the constraints from the surroundings of the modified areas.
@@ -2611,9 +2653,9 @@ Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const Map
Map<Constraint, Set<Vector2i>> source_tiles_of_constraint;
for (Set<Constraint>::Element *E = removed_cells_constraints_set.front(); E; E = E->next()) {
Map<Vector2i, TileSet::CellNeighbor> sources_of_constraint = E->get().get_overlapping_coords_and_peering_bits();
- for (Map<Vector2i, TileSet::CellNeighbor>::Element *E_source_tile_of_constraint = sources_of_constraint.front(); E_source_tile_of_constraint; E_source_tile_of_constraint = E_source_tile_of_constraint->next()) {
- if (potential_to_replace.has(E_source_tile_of_constraint->key())) {
- source_tiles_of_constraint[E->get()].insert(E_source_tile_of_constraint->key());
+ for (const KeyValue<Vector2i, TileSet::CellNeighbor> &E_source_tile_of_constraint : sources_of_constraint) {
+ if (potential_to_replace.has(E_source_tile_of_constraint.key)) {
+ source_tiles_of_constraint[E->get()].insert(E_source_tile_of_constraint.key);
}
}
}
@@ -2630,8 +2672,8 @@ Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const Map
potential_to_replace.erase(to_add_to_remove);
to_replace.insert(to_add_to_remove);
to_replace_modified = true;
- for (Map<Constraint, Set<Vector2i>>::Element *E_source_tiles_of_constraint = source_tiles_of_constraint.front(); E_source_tiles_of_constraint; E_source_tiles_of_constraint = E_source_tiles_of_constraint->next()) {
- E_source_tiles_of_constraint->get().erase(to_add_to_remove);
+ for (KeyValue<Constraint, Set<Vector2i>> &E_source_tiles_of_constraint : source_tiles_of_constraint) {
+ E_source_tiles_of_constraint.value.erase(to_add_to_remove);
}
break;
}
@@ -2649,13 +2691,13 @@ Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const Map
Map<Vector2i, TerrainsTilePattern> wfc_output = _wave_function_collapse(to_replace, p_terrain_set, constraints);
// Use the WFC run for the output.
- for (Map<Vector2i, TerrainsTilePattern>::Element *E = wfc_output.front(); E; E = E->next()) {
- output[E->key()] = _get_random_tile_from_pattern(p_terrain_set, E->get());
+ for (const KeyValue<Vector2i, TerrainsTilePattern> &E : wfc_output) {
+ output[E.key] = _get_random_tile_from_pattern(p_terrain_set, E.value);
}
// Override the WFC results to make sure at least the painted tiles are actually painted.
- for (Map<Vector2i, TerrainsTilePattern>::Element *E_to_paint = p_to_paint.front(); E_to_paint; E_to_paint = E_to_paint->next()) {
- output[E_to_paint->key()] = _get_random_tile_from_pattern(p_terrain_set, E_to_paint->get());
+ for (const KeyValue<Vector2i, TerrainsTilePattern> &E_to_paint : p_to_paint) {
+ output[E_to_paint.key] = _get_random_tile_from_pattern(p_terrain_set, E_to_paint.value);
}
return output;
@@ -2733,9 +2775,9 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() {
} break;
case DRAG_TYPE_PAINT: {
undo_redo->create_action(TTR("Paint terrain"));
- for (Map<Vector2i, TileMapCell>::Element *E = drag_modified.front(); E; E = E->next()) {
- undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), tile_map->get_cell_source_id(tile_map_layer, E->key()), tile_map->get_cell_atlas_coords(tile_map_layer, E->key()), tile_map->get_cell_alternative_tile(tile_map_layer, E->key()));
- undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ for (const KeyValue<Vector2i, TileMapCell> &E : drag_modified) {
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E.key, tile_map->get_cell_source_id(tile_map_layer, E.key), tile_map->get_cell_atlas_coords(tile_map_layer, E.key), tile_map->get_cell_alternative_tile(tile_map_layer, E.key));
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
}
undo_redo->commit_action(false);
} break;
@@ -2809,11 +2851,11 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
to_draw[line[i]] = selected_terrains_tile_pattern;
}
Map<Vector2i, TileMapCell> modified = _draw_terrains(to_draw, selected_terrain_set);
- for (Map<Vector2i, TileMapCell>::Element *E = modified.front(); E; E = E->next()) {
- if (!drag_modified.has(E->key())) {
- drag_modified[E->key()] = tile_map->get_cell(tile_map_layer, E->key());
+ for (const KeyValue<Vector2i, TileMapCell> &E : modified) {
+ if (!drag_modified.has(E.key)) {
+ drag_modified[E.key] = tile_map->get_cell(tile_map_layer, E.key);
}
- tile_map->set_cell(tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ tile_map->set_cell(tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
}
}
} break;
@@ -2848,9 +2890,9 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
terrains_to_draw[tile_map->world_to_map(mpos)] = selected_terrains_tile_pattern;
Map<Vector2i, TileMapCell> to_draw = _draw_terrains(terrains_to_draw, selected_terrain_set);
- for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) {
- drag_modified[E->key()] = tile_map->get_cell(tile_map_layer, E->key());
- tile_map->set_cell(tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
+ drag_modified[E.key] = tile_map->get_cell(tile_map_layer, E.key);
+ tile_map->set_cell(tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
}
}
}
@@ -3177,7 +3219,7 @@ TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
paint_tool_button->set_toggle_mode(true);
paint_tool_button->set_button_group(tool_buttons_group);
paint_tool_button->set_pressed(true);
- paint_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/paint_tool", "Paint", KEY_E));
+ paint_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/paint_tool", "Paint", KEY_D));
paint_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(paint_tool_button);
@@ -3549,30 +3591,76 @@ void TileMapEditor::_update_layers_selection() {
tile_map_editor_plugins[tabs->get_current_tab()]->edit(tile_map_id, tile_map_layer);
}
-void TileMapEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value) {
+void TileMapEditor::_move_tile_map_array_element(Object *p_undo_redo, Object *p_edited, String p_array_prefix, int p_from_index, int p_to_pos) {
UndoRedo *undo_redo = Object::cast_to<UndoRedo>(p_undo_redo);
ERR_FAIL_COND(!undo_redo);
TileMap *tile_map = Object::cast_to<TileMap>(p_edited);
- if (tile_map) {
- if (p_property == "layers_count") {
- int new_layers_count = (int)p_new_value;
- if (new_layers_count < tile_map->get_layers_count()) {
- List<PropertyInfo> property_list;
- tile_map->get_property_list(&property_list);
-
- for (PropertyInfo property_info : property_list) {
- Vector<String> components = String(property_info.name).split("/", true, 2);
- if (components.size() == 2 && components[0].begins_with("layer_") && components[0].trim_prefix("layer_").is_valid_int()) {
- int index = components[0].trim_prefix("layer_").to_int();
- if (index >= new_layers_count) {
- undo_redo->add_undo_property(tile_map, property_info.name, tile_map->get(property_info.name));
- }
- }
+ if (!tile_map) {
+ return;
+ }
+
+ // Compute the array indices to save.
+ int begin = 0;
+ int end;
+ if (p_array_prefix == "layer_") {
+ end = tile_map->get_layers_count();
+ } else {
+ ERR_FAIL_MSG("Invalid array prefix for TileSet.");
+ }
+ if (p_from_index < 0) {
+ // Adding new.
+ if (p_to_pos >= 0) {
+ begin = p_to_pos;
+ } else {
+ end = 0; // Nothing to save when adding at the end.
+ }
+ } else if (p_to_pos < 0) {
+ // Removing.
+ begin = p_from_index;
+ } else {
+ // Moving.
+ begin = MIN(p_from_index, p_to_pos);
+ end = MIN(MAX(p_from_index, p_to_pos) + 1, end);
+ }
+
+#define ADD_UNDO(obj, property) undo_redo->add_undo_property(obj, property, obj->get(property));
+ // Save layers' properties.
+ if (p_from_index < 0) {
+ undo_redo->add_undo_method(tile_map, "remove_layer", p_to_pos < 0 ? tile_map->get_layers_count() : p_to_pos);
+ } else if (p_to_pos < 0) {
+ undo_redo->add_undo_method(tile_map, "add_layer", p_from_index);
+ }
+
+ List<PropertyInfo> properties;
+ tile_map->get_property_list(&properties);
+ for (PropertyInfo pi : properties) {
+ if (pi.name.begins_with(p_array_prefix)) {
+ String str = pi.name.trim_prefix(p_array_prefix);
+ int to_char_index = 0;
+ while (to_char_index < str.length()) {
+ if (str[to_char_index] < '0' || str[to_char_index] > '9') {
+ break;
+ }
+ to_char_index++;
+ }
+ if (to_char_index > 0) {
+ int array_index = str.left(to_char_index).to_int();
+ if (array_index >= begin && array_index < end) {
+ ADD_UNDO(tile_map, pi.name);
}
}
}
}
+#undef ADD_UNDO
+
+ if (p_from_index < 0) {
+ undo_redo->add_do_method(tile_map, "add_layer", p_to_pos);
+ } else if (p_to_pos < 0) {
+ undo_redo->add_do_method(tile_map, "remove_layer", p_from_index);
+ } else {
+ undo_redo->add_do_method(tile_map, "move_layer", p_from_index, p_to_pos);
+ }
}
bool TileMapEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
@@ -3643,8 +3731,10 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
0.8);
// Draw the scaled tile.
- Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(coords) - Vector2(tile_shape_size) / 2, Vector2(tile_shape_size)));
- tile_set->draw_tile_shape(p_overlay, cell_region, color, true, warning_pattern_texture);
+ Transform2D tile_xform;
+ tile_xform.set_origin(tile_map->map_to_world(coords));
+ tile_xform.set_scale(tile_shape_size);
+ tile_set->draw_tile_shape(p_overlay, xform * tile_xform, color, true, warning_pattern_texture);
}
// Draw the warning icon.
@@ -3700,10 +3790,12 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
float bottom_opacity = CLAMP(Math::inverse_lerp((float)displayed_rect.size.y, (float)(displayed_rect.size.y - fading), (float)pos_in_rect.y), 0.0f, 1.0f);
float opacity = CLAMP(MIN(left_opacity, MIN(right_opacity, MIN(top_opacity, bottom_opacity))) + 0.1, 0.0f, 1.0f);
- Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(Vector2(x, y)) - tile_shape_size / 2, tile_shape_size));
+ Transform2D tile_xform;
+ tile_xform.set_origin(tile_map->map_to_world(Vector2(x, y)));
+ tile_xform.set_scale(tile_shape_size);
Color color = grid_color;
color.a = color.a * opacity;
- tile_set->draw_tile_shape(p_overlay, cell_region, color, false);
+ tile_set->draw_tile_shape(p_overlay, xform * tile_xform, color, false);
}
}
}
@@ -3851,7 +3943,7 @@ TileMapEditor::TileMapEditor() {
_tab_changed(0);
// Registers UndoRedo inspector callback.
- EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileMapEditor::_undo_redo_inspector_callback));
+ EditorNode::get_singleton()->get_editor_data().add_move_array_element_function(SNAME("TileMap"), callable_mp(this, &TileMapEditor::_move_tile_map_array_element));
}
TileMapEditor::~TileMapEditor() {
diff --git a/editor/plugins/tiles/tile_map_editor.h b/editor/plugins/tiles/tile_map_editor.h
index 6e2f2ce2ba..a1ab3db318 100644
--- a/editor/plugins/tiles/tile_map_editor.h
+++ b/editor/plugins/tiles/tile_map_editor.h
@@ -124,6 +124,7 @@ private:
void _update_selection_pattern_from_tileset_selection();
void _update_tileset_selection_from_selection_pattern();
void _update_fix_selected_and_hovered();
+ void _fix_invalid_tiles_in_tile_map_selection();
///// Bottom panel. ////.
Label *missing_source_label;
@@ -341,7 +342,7 @@ private:
void _update_layers_selection();
// Inspector undo/redo callback.
- void _undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value);
+ void _move_tile_map_array_element(Object *p_undo_redo, Object *p_edited, String p_array_prefix, int p_from_index, int p_to_pos);
protected:
void _notification(int p_what);
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index 432f48fa85..3fbd315aec 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -87,7 +87,7 @@ void TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::_get_property_list
p_list->push_back(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"));
p_list->push_back(PropertyInfo(Variant::VECTOR2I, "margins", PROPERTY_HINT_NONE, ""));
p_list->push_back(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE, ""));
- p_list->push_back(PropertyInfo(Variant::VECTOR2I, "tile_size", PROPERTY_HINT_NONE, ""));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2I, "texture_region_size", PROPERTY_HINT_NONE, ""));
}
void TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::_bind_methods() {
@@ -131,52 +131,127 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_set(const StringName &p_na
return false;
}
+ // ID and size related properties.
if (tiles.size() == 1) {
const Vector2i &coords = tiles.front()->get().tile;
const int &alternative = tiles.front()->get().alternative;
- if (alternative == 0 && p_name == "atlas_coords") {
- Vector2i as_vector2i = Vector2i(p_value);
- ERR_FAIL_COND_V(!tile_set_atlas_source->can_move_tile_in_atlas(coords, as_vector2i), false);
+ if (alternative == 0) {
+ Vector<String> components = String(p_name).split("/", true, 2);
+ if (p_name == "atlas_coords") {
+ Vector2i as_vector2i = Vector2i(p_value);
+ bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(as_vector2i, tile_set_atlas_source->get_tile_size_in_atlas(coords), tile_set_atlas_source->get_tile_animation_columns(coords), tile_set_atlas_source->get_tile_animation_separation(coords), tile_set_atlas_source->get_tile_animation_frames_count(coords), coords);
+ ERR_FAIL_COND_V(!has_room_for_tile, false);
+
+ if (tiles_set_atlas_source_editor->selection.front()->get().tile == coords) {
+ tiles_set_atlas_source_editor->selection.clear();
+ tiles_set_atlas_source_editor->selection.insert({ as_vector2i, 0 });
+ tiles_set_atlas_source_editor->_update_tile_id_label();
+ }
- if (tiles_set_atlas_source_editor->selection.front()->get().tile == coords) {
- tiles_set_atlas_source_editor->selection.clear();
- tiles_set_atlas_source_editor->selection.insert({ as_vector2i, 0 });
- tiles_set_atlas_source_editor->_update_tile_id_label();
+ tile_set_atlas_source->move_tile_in_atlas(coords, as_vector2i);
+ tiles.clear();
+ tiles.insert({ as_vector2i, 0 });
+ emit_signal(SNAME("changed"), "atlas_coords");
+ return true;
+ } else if (p_name == "size_in_atlas") {
+ Vector2i as_vector2i = Vector2i(p_value);
+ bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(coords, as_vector2i, tile_set_atlas_source->get_tile_animation_columns(coords), tile_set_atlas_source->get_tile_animation_separation(coords), tile_set_atlas_source->get_tile_animation_frames_count(coords), coords);
+ ERR_FAIL_COND_V(!has_room_for_tile, false);
+ tile_set_atlas_source->move_tile_in_atlas(coords, TileSetSource::INVALID_ATLAS_COORDS, as_vector2i);
+ emit_signal(SNAME("changed"), "size_in_atlas");
+ return true;
}
+ } else if (alternative > 0) {
+ if (p_name == "alternative_id") {
+ int as_int = int(p_value);
+ ERR_FAIL_COND_V(as_int < 0, false);
+ ERR_FAIL_COND_V_MSG(tile_set_atlas_source->has_alternative_tile(coords, as_int), false, vformat("Cannot change alternative tile ID. Another alternative exists with id %d for tile at coords %s.", as_int, coords));
+
+ if (tiles_set_atlas_source_editor->selection.front()->get().alternative == alternative) {
+ tiles_set_atlas_source_editor->selection.clear();
+ tiles_set_atlas_source_editor->selection.insert({ coords, as_int });
+ }
- tile_set_atlas_source->move_tile_in_atlas(coords, as_vector2i);
- tiles.clear();
- tiles.insert({ as_vector2i, 0 });
- emit_signal(SNAME("changed"), "atlas_coords");
- return true;
- } else if (alternative == 0 && p_name == "size_in_atlas") {
- Vector2i as_vector2i = Vector2i(p_value);
- ERR_FAIL_COND_V(!tile_set_atlas_source->can_move_tile_in_atlas(coords, TileSetSource::INVALID_ATLAS_COORDS, as_vector2i), false);
+ int previous_alternative_tile = alternative;
+ tiles.clear();
+ tiles.insert({ coords, as_int }); // tiles must be updated before.
+ tile_set_atlas_source->set_alternative_tile_id(coords, previous_alternative_tile, as_int);
- tile_set_atlas_source->move_tile_in_atlas(coords, TileSetSource::INVALID_ATLAS_COORDS, as_vector2i);
- emit_signal(SNAME("changed"), "size_in_atlas");
- return true;
- } else if (alternative > 0 && p_name == "alternative_id") {
- int as_int = int(p_value);
- ERR_FAIL_COND_V(as_int < 0, false);
- ERR_FAIL_COND_V_MSG(tile_set_atlas_source->has_alternative_tile(coords, as_int), false, vformat("Cannot change alternative tile ID. Another alternative exists with id %d for tile at coords %s.", as_int, coords));
-
- if (tiles_set_atlas_source_editor->selection.front()->get().alternative == alternative) {
- tiles_set_atlas_source_editor->selection.clear();
- tiles_set_atlas_source_editor->selection.insert({ coords, as_int });
+ emit_signal(SNAME("changed"), "alternative_id");
+ return true;
}
+ }
+ }
- int previous_alternative_tile = alternative;
- tiles.clear();
- tiles.insert({ coords, as_int }); // tiles must be updated before.
- tile_set_atlas_source->set_alternative_tile_id(coords, previous_alternative_tile, as_int);
+ // Animation.
+ // Check if all tiles have an alternative_id of 0.
+ bool all_alternatve_id_zero = true;
+ for (TileSelection tile : tiles) {
+ if (tile.alternative != 0) {
+ all_alternatve_id_zero = false;
+ break;
+ }
+ }
- emit_signal(SNAME("changed"), "alternative_id");
+ if (all_alternatve_id_zero) {
+ Vector<String> components = String(p_name).split("/", true, 2);
+ if (p_name == "animation_columns") {
+ for (TileSelection tile : tiles) {
+ bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(tile.tile, tile_set_atlas_source->get_tile_size_in_atlas(tile.tile), p_value, tile_set_atlas_source->get_tile_animation_separation(tile.tile), tile_set_atlas_source->get_tile_animation_frames_count(tile.tile), tile.tile);
+ if (!has_room_for_tile) {
+ ERR_PRINT("No room for tile");
+ } else {
+ tile_set_atlas_source->set_tile_animation_columns(tile.tile, p_value);
+ }
+ }
+ emit_signal(SNAME("changed"), "animation_columns");
+ return true;
+ } else if (p_name == "animation_separation") {
+ for (TileSelection tile : tiles) {
+ bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(tile.tile, tile_set_atlas_source->get_tile_size_in_atlas(tile.tile), tile_set_atlas_source->get_tile_animation_columns(tile.tile), p_value, tile_set_atlas_source->get_tile_animation_frames_count(tile.tile), tile.tile);
+ if (!has_room_for_tile) {
+ ERR_PRINT("No room for tile");
+ } else {
+ tile_set_atlas_source->set_tile_animation_separation(tile.tile, p_value);
+ }
+ }
+ emit_signal(SNAME("changed"), "animation_separation");
return true;
+ } else if (p_name == "animation_speed") {
+ for (TileSelection tile : tiles) {
+ tile_set_atlas_source->set_tile_animation_speed(tile.tile, p_value);
+ }
+ emit_signal(SNAME("changed"), "animation_speed");
+ return true;
+ } else if (p_name == "animation_frames_count") {
+ for (TileSelection tile : tiles) {
+ bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(tile.tile, tile_set_atlas_source->get_tile_size_in_atlas(tile.tile), tile_set_atlas_source->get_tile_animation_columns(tile.tile), tile_set_atlas_source->get_tile_animation_separation(tile.tile), p_value, tile.tile);
+ if (!has_room_for_tile) {
+ ERR_PRINT("No room for tile");
+ } else {
+ tile_set_atlas_source->set_tile_animation_frames_count(tile.tile, p_value);
+ }
+ }
+ notify_property_list_changed();
+ emit_signal(SNAME("changed"), "animation_separation");
+ return true;
+ } else if (components.size() == 2 && components[0].begins_with("animation_frame_") && components[0].trim_prefix("animation_frame_").is_valid_int()) {
+ for (TileSelection tile : tiles) {
+ int frame = components[0].trim_prefix("animation_frame_").to_int();
+ if (frame < 0 || frame >= tile_set_atlas_source->get_tile_animation_frames_count(tile.tile)) {
+ ERR_PRINT(vformat("No tile animation frame with index %d", frame));
+ } else {
+ if (components[1] == "duration") {
+ tile_set_atlas_source->set_tile_animation_frame_duration(tile.tile, frame, p_value);
+ return true;
+ }
+ }
+ }
}
}
+ // Other properties.
bool any_valid = false;
for (Set<TileSelection>::Element *E = tiles.front(); E; E = E->next()) {
const Vector2i &coords = E->get().tile;
@@ -202,19 +277,63 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_get(const StringName &p_na
return false;
}
+ // ID and size related properties.s
if (tiles.size() == 1) {
const Vector2i &coords = tiles.front()->get().tile;
const int &alternative = tiles.front()->get().alternative;
- if (alternative == 0 && p_name == "atlas_coords") {
- r_ret = coords;
+ if (alternative == 0) {
+ Vector<String> components = String(p_name).split("/", true, 2);
+ if (p_name == "atlas_coords") {
+ r_ret = coords;
+ return true;
+ } else if (p_name == "size_in_atlas") {
+ r_ret = tile_set_atlas_source->get_tile_size_in_atlas(coords);
+ return true;
+ }
+ } else if (alternative > 0) {
+ if (p_name == "alternative_id") {
+ r_ret = alternative;
+ return true;
+ }
+ }
+ }
+
+ // Animation.
+ // Check if all tiles have an alternative_id of 0.
+ bool all_alternatve_id_zero = true;
+ for (TileSelection tile : tiles) {
+ if (tile.alternative != 0) {
+ all_alternatve_id_zero = false;
+ break;
+ }
+ }
+
+ if (all_alternatve_id_zero) {
+ const Vector2i &coords = tiles.front()->get().tile;
+
+ Vector<String> components = String(p_name).split("/", true, 2);
+ if (p_name == "animation_columns") {
+ r_ret = tile_set_atlas_source->get_tile_animation_columns(coords);
return true;
- } else if (alternative == 0 && p_name == "size_in_atlas") {
- r_ret = tile_set_atlas_source->get_tile_size_in_atlas(coords);
+ } else if (p_name == "animation_separation") {
+ r_ret = tile_set_atlas_source->get_tile_animation_separation(coords);
return true;
- } else if (alternative > 0 && p_name == "alternative_id") {
- r_ret = alternative;
+ } else if (p_name == "animation_speed") {
+ r_ret = tile_set_atlas_source->get_tile_animation_speed(coords);
return true;
+ } else if (p_name == "animation_frames_count") {
+ r_ret = tile_set_atlas_source->get_tile_animation_frames_count(coords);
+ return true;
+ } else if (components.size() == 2 && components[0].begins_with("animation_frame_") && components[0].trim_prefix("animation_frame_").is_valid_int()) {
+ int frame = components[0].trim_prefix("animation_frame_").to_int();
+ if (components[1] == "duration") {
+ if (frame < 0 || frame >= tile_set_atlas_source->get_tile_animation_frames_count(coords)) {
+ return false;
+ }
+ r_ret = tile_set_atlas_source->get_tile_animation_frame_duration(coords, frame);
+ return true;
+ }
}
}
@@ -242,6 +361,7 @@ void TileSetAtlasSourceEditor::AtlasTileProxyObject::_get_property_list(List<Pro
return;
}
+ // ID and size related properties.
if (tiles.size() == 1) {
if (tiles.front()->get().alternative == 0) {
p_list->push_back(PropertyInfo(Variant::VECTOR2I, "atlas_coords", PROPERTY_HINT_NONE, ""));
@@ -251,6 +371,32 @@ void TileSetAtlasSourceEditor::AtlasTileProxyObject::_get_property_list(List<Pro
}
}
+ // Animation.
+ // Check if all tiles have an alternative_id of 0.
+ bool all_alternatve_id_zero = true;
+ for (TileSelection tile : tiles) {
+ if (tile.alternative != 0) {
+ all_alternatve_id_zero = false;
+ break;
+ }
+ }
+
+ if (all_alternatve_id_zero) {
+ p_list->push_back(PropertyInfo(Variant::NIL, "Animation", PROPERTY_HINT_NONE, "animation_", PROPERTY_USAGE_GROUP));
+ p_list->push_back(PropertyInfo(Variant::INT, "animation_columns", PROPERTY_HINT_NONE, ""));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2I, "animation_separation", PROPERTY_HINT_NONE, ""));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "animation_speed", PROPERTY_HINT_NONE, ""));
+ p_list->push_back(PropertyInfo(Variant::INT, "animation_frames_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, "Frames,animation_frame_"));
+ // Not optimal, but returns value for the first tile. This is similar to what MultiNodeEdit does.
+ if (tile_set_atlas_source->get_tile_animation_frames_count(tiles.front()->get().tile) == 1) {
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "animation_frame_0/duration", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY));
+ } else {
+ for (int i = 0; i < tile_set_atlas_source->get_tile_animation_frames_count(tiles.front()->get().tile); i++) {
+ p_list->push_back(PropertyInfo(Variant::FLOAT, vformat("animation_frame_%d/duration", i), PROPERTY_HINT_NONE, ""));
+ }
+ }
+ }
+
// Get the list of properties common to all tiles (similar to what's done in MultiNodeEdit).
struct PropertyId {
int occurence_id = 0;
@@ -598,9 +744,9 @@ void TileSetAtlasSourceEditor::_update_tile_data_editors() {
#undef ADD_TILE_DATA_EDITOR
// Add tile data editors as children.
- for (Map<String, TileDataEditor *>::Element *E = tile_data_editors.front(); E; E = E->next()) {
+ for (KeyValue<String, TileDataEditor *> &E : tile_data_editors) {
// Tile Data Editor.
- TileDataEditor *tile_data_editor = E->get();
+ TileDataEditor *tile_data_editor = E.value;
if (!tile_data_editor->is_inside_tree()) {
tile_data_painting_editor_container->add_child(tile_data_editor);
}
@@ -640,9 +786,9 @@ void TileSetAtlasSourceEditor::_update_current_tile_data_editor() {
}
// Hide all editors but the current one.
- for (Map<String, TileDataEditor *>::Element *E = tile_data_editors.front(); E; E = E->next()) {
- E->get()->hide();
- E->get()->get_toolbar()->hide();
+ for (const KeyValue<String, TileDataEditor *> &E : tile_data_editors) {
+ E.value->hide();
+ E.value->get_toolbar()->hide();
}
if (tile_data_editors.has(property)) {
current_tile_data_editor = tile_data_editors[property];
@@ -846,15 +992,15 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
CursorShape cursor_shape = CURSOR_ARROW;
bool can_grow[4];
for (int i = 0; i < 4; i++) {
- can_grow[i] = tile_set_atlas_source->can_move_tile_in_atlas(selected.tile, selected.tile + directions[i]);
+ can_grow[i] = tile_set_atlas_source->has_room_for_tile(selected.tile + directions[i], tile_set_atlas_source->get_tile_size_in_atlas(selected.tile), tile_set_atlas_source->get_tile_animation_columns(selected.tile), tile_set_atlas_source->get_tile_animation_separation(selected.tile), tile_set_atlas_source->get_tile_animation_frames_count(selected.tile), selected.tile);
can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1;
}
for (int i = 0; i < 4; i++) {
- Vector2 pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[i];
+ Vector2 pos = rect.position + rect.size * coords[i];
if (can_grow[i] && can_grow[(i + 3) % 4] && Rect2(pos, zoomed_size).has_point(mouse_local_pos)) {
cursor_shape = (i % 2) ? CURSOR_BDIAGSIZE : CURSOR_FDIAGSIZE;
}
- Vector2 next_pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[(i + 1) % 4];
+ Vector2 next_pos = rect.position + rect.size * coords[(i + 1) % 4];
if (can_grow[i] && Rect2((pos + next_pos) / 2.0, zoomed_size).has_point(mouse_local_pos)) {
cursor_shape = (i % 2) ? CURSOR_HSIZE : CURSOR_VSIZE;
}
@@ -869,7 +1015,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
Rect2i new_rect = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs();
new_rect.size += Vector2i(1, 1);
// Check if the new tile can fit in the new rect.
- if (tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size)) {
+ if (tile_set_atlas_source->has_room_for_tile(new_rect.position, new_rect.size, tile_set_atlas_source->get_tile_animation_columns(drag_current_tile), tile_set_atlas_source->get_tile_animation_separation(drag_current_tile), tile_set_atlas_source->get_tile_animation_frames_count(drag_current_tile), drag_current_tile)) {
// Move and resize the tile.
tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size);
drag_current_tile = new_rect.position;
@@ -908,7 +1054,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
Vector2 mouse_offset = (Vector2(tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile)) / 2.0 - Vector2(0.5, 0.5)) * tile_set->get_tile_size();
Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position() - mouse_offset);
coords = coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1));
- if (drag_current_tile != coords && tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, coords)) {
+ if (drag_current_tile != coords && tile_set_atlas_source->has_room_for_tile(coords, tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile), tile_set_atlas_source->get_tile_animation_columns(drag_current_tile), tile_set_atlas_source->get_tile_animation_separation(drag_current_tile), tile_set_atlas_source->get_tile_animation_frames_count(drag_current_tile), drag_current_tile)) {
tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, coords);
selection.clear();
selection.insert({ coords, 0 });
@@ -948,7 +1094,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
new_rect.set_end(Vector2i(new_rect.get_end().x, MAX(new_base_tiles_coords.y, old_rect.position.y + 1)));
}
- if (tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size)) {
+ if (tile_set_atlas_source->has_room_for_tile(new_rect.position, new_rect.size, tile_set_atlas_source->get_tile_animation_columns(drag_current_tile), tile_set_atlas_source->get_tile_animation_separation(drag_current_tile), tile_set_atlas_source->get_tile_animation_frames_count(drag_current_tile), drag_current_tile)) {
tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size);
selection.clear();
selection.insert({ new_rect.position, 0 });
@@ -1056,11 +1202,11 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
CursorShape cursor_shape = CURSOR_ARROW;
bool can_grow[4];
for (int i = 0; i < 4; i++) {
- can_grow[i] = tile_set_atlas_source->can_move_tile_in_atlas(selected.tile, selected.tile + directions[i]);
+ can_grow[i] = tile_set_atlas_source->has_room_for_tile(selected.tile + directions[i], tile_set_atlas_source->get_tile_size_in_atlas(selected.tile), tile_set_atlas_source->get_tile_animation_columns(selected.tile), tile_set_atlas_source->get_tile_animation_separation(selected.tile), tile_set_atlas_source->get_tile_animation_frames_count(selected.tile), selected.tile);
can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1;
}
for (int i = 0; i < 4; i++) {
- Vector2 pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[i];
+ Vector2 pos = rect.position + rect.size * coords[i];
if (can_grow[i] && can_grow[(i + 3) % 4] && Rect2(pos, zoomed_size).has_point(mouse_local_pos)) {
drag_type = (DragType)((int)DRAG_TYPE_RESIZE_TOP_LEFT + i * 2);
drag_start_mouse_pos = mouse_local_pos;
@@ -1069,7 +1215,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
drag_start_tile_shape = Rect2i(selected.tile, tile_set_atlas_source->get_tile_size_in_atlas(selected.tile));
cursor_shape = (i % 2) ? CURSOR_BDIAGSIZE : CURSOR_FDIAGSIZE;
}
- Vector2 next_pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[(i + 1) % 4];
+ Vector2 next_pos = rect.position + rect.size * coords[(i + 1) % 4];
if (can_grow[i] && Rect2((pos + next_pos) / 2.0, zoomed_size).has_point(mouse_local_pos)) {
drag_type = (DragType)((int)DRAG_TYPE_RESIZE_TOP + i * 2);
drag_start_mouse_pos = mouse_local_pos;
@@ -1511,37 +1657,45 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
TileSelection selected = E->get();
if (selected.alternative == 0) {
// Draw the rect.
- Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile);
- tile_atlas_control->draw_rect(region, selection_color, false);
+ for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(selected.tile); frame++) {
+ Color color = selection_color;
+ if (frame > 0) {
+ color.a *= 0.3;
+ }
+ Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile, frame);
+ tile_atlas_control->draw_rect(region, color, false);
+ }
}
}
if (selection.size() == 1) {
// Draw the resize handles (only when it's possible to expand).
TileSelection selected = selection.front()->get();
- Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(selected.tile);
- Size2 zoomed_size = resize_handle->get_size() / tile_atlas_view->get_zoom();
- Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile);
- Rect2 rect = region.grow_individual(zoomed_size.x, zoomed_size.y, 0, 0);
- Vector2i coords[] = { Vector2i(0, 0), Vector2i(1, 0), Vector2i(1, 1), Vector2i(0, 1) };
- Vector2i directions[] = { Vector2i(0, -1), Vector2i(1, 0), Vector2i(0, 1), Vector2i(-1, 0) };
- bool can_grow[4];
- for (int i = 0; i < 4; i++) {
- can_grow[i] = tile_set_atlas_source->can_move_tile_in_atlas(selected.tile, selected.tile + directions[i]);
- can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1;
- }
- for (int i = 0; i < 4; i++) {
- Vector2 pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[i];
- if (can_grow[i] && can_grow[(i + 3) % 4]) {
- tile_atlas_control->draw_texture_rect(resize_handle, Rect2(pos, zoomed_size), false);
- } else {
- tile_atlas_control->draw_texture_rect(resize_handle_disabled, Rect2(pos, zoomed_size), false);
+ if (selected.alternative == 0) {
+ Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(selected.tile);
+ Size2 zoomed_size = resize_handle->get_size() / tile_atlas_view->get_zoom();
+ Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile);
+ Rect2 rect = region.grow_individual(zoomed_size.x, zoomed_size.y, 0, 0);
+ Vector2i coords[] = { Vector2i(0, 0), Vector2i(1, 0), Vector2i(1, 1), Vector2i(0, 1) };
+ Vector2i directions[] = { Vector2i(0, -1), Vector2i(1, 0), Vector2i(0, 1), Vector2i(-1, 0) };
+ bool can_grow[4];
+ for (int i = 0; i < 4; i++) {
+ can_grow[i] = tile_set_atlas_source->has_room_for_tile(selected.tile + directions[i], tile_set_atlas_source->get_tile_size_in_atlas(selected.tile), tile_set_atlas_source->get_tile_animation_columns(selected.tile), tile_set_atlas_source->get_tile_animation_separation(selected.tile), tile_set_atlas_source->get_tile_animation_frames_count(selected.tile), selected.tile);
+ can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1;
}
- Vector2 next_pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[(i + 1) % 4];
- if (can_grow[i]) {
- tile_atlas_control->draw_texture_rect(resize_handle, Rect2((pos + next_pos) / 2.0, zoomed_size), false);
- } else {
- tile_atlas_control->draw_texture_rect(resize_handle_disabled, Rect2((pos + next_pos) / 2.0, zoomed_size), false);
+ for (int i = 0; i < 4; i++) {
+ Vector2 pos = rect.position + rect.size * coords[i];
+ if (can_grow[i] && can_grow[(i + 3) % 4]) {
+ tile_atlas_control->draw_texture_rect(resize_handle, Rect2(pos, zoomed_size), false);
+ } else {
+ tile_atlas_control->draw_texture_rect(resize_handle_disabled, Rect2(pos, zoomed_size), false);
+ }
+ Vector2 next_pos = rect.position + rect.size * coords[(i + 1) % 4];
+ if (can_grow[i]) {
+ tile_atlas_control->draw_texture_rect(resize_handle, Rect2((pos + next_pos) / 2.0, zoomed_size), false);
+ } else {
+ tile_atlas_control->draw_texture_rect(resize_handle_disabled, Rect2((pos + next_pos) / 2.0, zoomed_size), false);
+ }
}
}
}
@@ -1550,7 +1704,9 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
if (drag_type == DRAG_TYPE_REMOVE_TILES) {
// Draw the tiles to be removed.
for (Set<Vector2i>::Element *E = drag_modified_tiles.front(); E; E = E->next()) {
- tile_atlas_control->draw_rect(tile_set_atlas_source->get_tile_texture_region(E->get()), Color(0.0, 0.0, 0.0), false);
+ for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(E->get()); frame++) {
+ tile_atlas_control->draw_rect(tile_set_atlas_source->get_tile_texture_region(E->get(), frame), Color(0.0, 0.0, 0.0), false);
+ }
}
} else if (drag_type == DRAG_TYPE_RECT_SELECT || drag_type == DRAG_TYPE_REMOVE_TILES_USING_RECT) {
// Draw tiles to be removed.
@@ -1617,7 +1773,13 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
Vector2i hovered_tile = tile_set_atlas_source->get_tile_at_coords(hovered_base_tile_coords);
if (hovered_tile != TileSetSource::INVALID_ATLAS_COORDS) {
// Draw existing hovered tile.
- tile_atlas_control->draw_rect(tile_set_atlas_source->get_tile_texture_region(hovered_tile), Color(1.0, 1.0, 1.0), false);
+ for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(hovered_tile); frame++) {
+ Color color = Color(1.0, 1.0, 1.0);
+ if (frame > 0) {
+ color.a *= 0.3;
+ }
+ tile_atlas_control->draw_rect(tile_set_atlas_source->get_tile_texture_region(hovered_tile, frame), color, false);
+ }
} else {
// Draw empty tile, only in add/remove tiles mode.
if (tools_button_group->get_pressed_button() == tool_setup_atlas_source_button) {
@@ -1638,7 +1800,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_unscaled_draw() {
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i coords = tile_set_atlas_source->get_tile_id(i);
Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(coords);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
+ Vector2i position = texture_region.get_center() + tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
Transform2D xform = tile_atlas_control->get_parent_control()->get_transform();
xform.translate(position);
@@ -1661,7 +1823,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_unscaled_draw() {
continue;
}
Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(E->get().tile);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(E->get().tile, 0);
+ Vector2i position = texture_region.get_center() + tile_set_atlas_source->get_tile_effective_texture_offset(E->get().tile, 0);
Transform2D xform = tile_atlas_control->get_parent_control()->get_transform();
xform.translate(position);
@@ -1805,7 +1967,7 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_unscaled_draw() {
continue;
}
Rect2i rect = tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile);
- Vector2 position = (rect.get_position() + rect.get_end()) / 2;
+ Vector2 position = rect.get_center();
Transform2D xform = alternative_tiles_control->get_parent_control()->get_transform();
xform.translate(position);
@@ -1829,7 +1991,7 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_unscaled_draw() {
continue;
}
Rect2i rect = tile_atlas_view->get_alternative_tile_rect(E->get().tile, E->get().alternative);
- Vector2 position = (rect.get_position() + rect.get_end()) / 2;
+ Vector2 position = rect.get_center();
Transform2D xform = alternative_tiles_control->get_parent_control()->get_transform();
xform.translate(position);
@@ -1854,6 +2016,11 @@ void TileSetAtlasSourceEditor::_tile_set_atlas_source_changed() {
tile_set_atlas_source_changed_needs_update = true;
}
+void TileSetAtlasSourceEditor::_tile_proxy_object_changed(String p_what) {
+ tile_set_atlas_source_changed_needs_update = false; // Avoid updating too many things.
+ _update_atlas_view();
+}
+
void TileSetAtlasSourceEditor::_atlas_source_proxy_object_changed(String p_what) {
if (p_what == "texture" && !atlas_source_proxy_object->get("texture").is_null()) {
confirm_auto_create_tiles->popup_centered();
@@ -1866,7 +2033,7 @@ void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo
UndoRedo *undo_redo = Object::cast_to<UndoRedo>(p_undo_redo);
ERR_FAIL_COND(!undo_redo);
-#define ADD_UNDO(obj, property) undo_redo->add_undo_property(obj, property, tile_data->get(property));
+#define ADD_UNDO(obj, property) undo_redo->add_undo_property(obj, property, obj->get(property));
AtlasTileProxyObject *tile_data = Object::cast_to<AtlasTileProxyObject>(p_edited);
if (tile_data) {
@@ -2114,7 +2281,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
middle_vbox_container->add_child(tile_inspector_label);
tile_proxy_object = memnew(AtlasTileProxyObject(this));
- tile_proxy_object->connect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_update_atlas_view).unbind(1));
+ tile_proxy_object->connect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_proxy_object_changed));
tile_inspector = memnew(EditorInspector);
tile_inspector->set_undo_redo(undo_redo);
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h
index 501416c340..6448b55feb 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.h
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h
@@ -264,6 +264,7 @@ private:
AcceptDialog *confirm_auto_create_tiles;
void _tile_set_atlas_source_changed();
+ void _tile_proxy_object_changed(String p_what);
void _atlas_source_proxy_object_changed(String p_what);
void _undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value);
diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp
index ba98a7d6b3..48d0d9b333 100644
--- a/editor/plugins/tiles/tile_set_editor.cpp
+++ b/editor/plugins/tiles/tile_set_editor.cpp
@@ -330,11 +330,192 @@ void TileSetEditor::_tile_set_changed() {
tile_set_changed_needs_update = true;
}
+void TileSetEditor::_move_tile_set_array_element(Object *p_undo_redo, Object *p_edited, String p_array_prefix, int p_from_index, int p_to_pos) {
+ UndoRedo *undo_redo = Object::cast_to<UndoRedo>(p_undo_redo);
+ ERR_FAIL_COND(!undo_redo);
+
+ TileSet *tile_set = Object::cast_to<TileSet>(p_edited);
+ if (!tile_set) {
+ return;
+ }
+
+ Vector<String> components = String(p_array_prefix).split("/", true, 2);
+
+ // Compute the array indices to save.
+ int begin = 0;
+ int end;
+ if (p_array_prefix == "occlusion_layer_") {
+ end = tile_set->get_occlusion_layers_count();
+ } else if (p_array_prefix == "physics_layer_") {
+ end = tile_set->get_physics_layers_count();
+ } else if (p_array_prefix == "terrain_set_") {
+ end = tile_set->get_terrain_sets_count();
+ } else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "terrain_") {
+ int terrain_set = components[0].trim_prefix("terrain_set_").to_int();
+ end = tile_set->get_terrains_count(terrain_set);
+ } else if (p_array_prefix == "navigation_layer_") {
+ end = tile_set->get_navigation_layers_count();
+ } else if (p_array_prefix == "custom_data_layer_") {
+ end = tile_set->get_custom_data_layers_count();
+ } else {
+ ERR_FAIL_MSG("Invalid array prefix for TileSet.");
+ }
+ if (p_from_index < 0) {
+ // Adding new.
+ if (p_to_pos >= 0) {
+ begin = p_to_pos;
+ } else {
+ end = 0; // Nothing to save when adding at the end.
+ }
+ } else if (p_to_pos < 0) {
+ // Removing.
+ begin = p_from_index;
+ } else {
+ // Moving.
+ begin = MIN(p_from_index, p_to_pos);
+ end = MIN(MAX(p_from_index, p_to_pos) + 1, end);
+ }
+
+#define ADD_UNDO(obj, property) undo_redo->add_undo_property(obj, property, obj->get(property));
+ // Save layers' properties.
+ List<PropertyInfo> properties;
+ tile_set->get_property_list(&properties);
+ for (PropertyInfo pi : properties) {
+ if (pi.name.begins_with(p_array_prefix)) {
+ String str = pi.name.trim_prefix(p_array_prefix);
+ int to_char_index = 0;
+ while (to_char_index < str.length()) {
+ if (str[to_char_index] < '0' || str[to_char_index] > '9') {
+ break;
+ }
+ to_char_index++;
+ }
+ if (to_char_index > 0) {
+ int array_index = str.left(to_char_index).to_int();
+ if (array_index >= begin && array_index < end) {
+ ADD_UNDO(tile_set, pi.name);
+ }
+ }
+ }
+ }
+
+ // Save properties for TileSetAtlasSources tile data
+ for (int i = 0; i < tile_set->get_source_count(); i++) {
+ int source_id = tile_set->get_source_id(i);
+
+ Ref<TileSetAtlasSource> tas = tile_set->get_source(source_id);
+ if (tas.is_valid()) {
+ for (int j = 0; j < tas->get_tiles_count(); j++) {
+ Vector2i tile_id = tas->get_tile_id(j);
+ for (int k = 0; k < tas->get_alternative_tiles_count(tile_id); k++) {
+ int alternative_id = tas->get_alternative_tile_id(tile_id, k);
+ TileData *tile_data = Object::cast_to<TileData>(tas->get_tile_data(tile_id, alternative_id));
+ ERR_FAIL_COND(!tile_data);
+
+ // Actually saving stuff.
+ if (p_array_prefix == "occlusion_layer_") {
+ for (int layer_index = begin; layer_index < end; layer_index++) {
+ ADD_UNDO(tile_data, vformat("occlusion_layer_%d/polygon", layer_index));
+ }
+ } else if (p_array_prefix == "physics_layer_") {
+ for (int layer_index = begin; layer_index < end; layer_index++) {
+ ADD_UNDO(tile_data, vformat("physics_layer_%d/polygons_count", layer_index));
+ for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(layer_index); polygon_index++) {
+ ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/points", layer_index, polygon_index));
+ ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way", layer_index, polygon_index));
+ ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way_margin", layer_index, polygon_index));
+ }
+ }
+ } else if (p_array_prefix == "terrain_set_") {
+ ADD_UNDO(tile_data, "terrain_set");
+ for (int terrain_set_index = begin; terrain_set_index < end; terrain_set_index++) {
+ for (int l = 0; l < TileSet::CELL_NEIGHBOR_MAX; l++) {
+ TileSet::CellNeighbor bit = TileSet::CellNeighbor(l);
+ if (tile_data->is_valid_peering_bit_terrain(bit)) {
+ ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[l]));
+ }
+ }
+ }
+ } else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "terrain_") {
+ for (int terrain_index = 0; terrain_index < TileSet::CELL_NEIGHBOR_MAX; terrain_index++) {
+ TileSet::CellNeighbor bit = TileSet::CellNeighbor(terrain_index);
+ if (tile_data->is_valid_peering_bit_terrain(bit)) {
+ ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[terrain_index]));
+ }
+ }
+ } else if (p_array_prefix == "navigation_layer_") {
+ for (int layer_index = begin; layer_index < end; layer_index++) {
+ ADD_UNDO(tile_data, vformat("navigation_layer_%d/polygon", layer_index));
+ }
+ } else if (p_array_prefix == "custom_data_layer_") {
+ for (int layer_index = begin; layer_index < end; layer_index++) {
+ ADD_UNDO(tile_data, vformat("custom_data_%d", layer_index));
+ }
+ }
+ }
+ }
+ }
+ }
+#undef ADD_UNDO
+
+ // Add do method.
+ if (p_array_prefix == "occlusion_layer_") {
+ if (p_from_index < 0) {
+ undo_redo->add_do_method(tile_set, "add_occlusion_layer", p_to_pos);
+ } else if (p_to_pos < 0) {
+ undo_redo->add_do_method(tile_set, "remove_occlusion_layer", p_from_index);
+ } else {
+ undo_redo->add_do_method(tile_set, "move_occlusion_layer", p_from_index, p_to_pos);
+ }
+ } else if (p_array_prefix == "physics_layer_") {
+ if (p_from_index < 0) {
+ undo_redo->add_do_method(tile_set, "add_physics_layer", p_to_pos);
+ } else if (p_to_pos < 0) {
+ undo_redo->add_do_method(tile_set, "remove_physics_layer", p_from_index);
+ } else {
+ undo_redo->add_do_method(tile_set, "move_physics_layer", p_from_index, p_to_pos);
+ }
+ } else if (p_array_prefix == "terrain_set_") {
+ if (p_from_index < 0) {
+ undo_redo->add_do_method(tile_set, "add_terrain_set", p_to_pos);
+ } else if (p_to_pos < 0) {
+ undo_redo->add_do_method(tile_set, "remove_terrain_set", p_from_index);
+ } else {
+ undo_redo->add_do_method(tile_set, "move_terrain_set", p_from_index, p_to_pos);
+ }
+ } else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "terrain_") {
+ int terrain_set = components[0].trim_prefix("terrain_set_").to_int();
+ if (p_from_index < 0) {
+ undo_redo->add_do_method(tile_set, "add_terrain", terrain_set, p_to_pos);
+ } else if (p_to_pos < 0) {
+ undo_redo->add_do_method(tile_set, "remove_terrain", terrain_set, p_from_index);
+ } else {
+ undo_redo->add_do_method(tile_set, "move_terrain", terrain_set, p_from_index, p_to_pos);
+ }
+ } else if (p_array_prefix == "navigation_layer_") {
+ if (p_from_index < 0) {
+ undo_redo->add_do_method(tile_set, "add_navigation_layer", p_to_pos);
+ } else if (p_to_pos < 0) {
+ undo_redo->add_do_method(tile_set, "remove_navigation_layer", p_from_index);
+ } else {
+ undo_redo->add_do_method(tile_set, "move_navigation_layer", p_from_index, p_to_pos);
+ }
+ } else if (p_array_prefix == "custom_data_layer_") {
+ if (p_from_index < 0) {
+ undo_redo->add_do_method(tile_set, "add_custom_data_layer", p_to_pos);
+ } else if (p_to_pos < 0) {
+ undo_redo->add_do_method(tile_set, "remove_custom_data_layer", p_from_index);
+ } else {
+ undo_redo->add_do_method(tile_set, "move_custom_data_layer", p_from_index, p_to_pos);
+ }
+ }
+}
+
void TileSetEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value) {
UndoRedo *undo_redo = Object::cast_to<UndoRedo>(p_undo_redo);
ERR_FAIL_COND(!undo_redo);
-#define ADD_UNDO(obj, property) undo_redo->add_undo_property(obj, property, tile_data->get(property));
+#define ADD_UNDO(obj, property) undo_redo->add_undo_property(obj, property, obj->get(property));
TileSet *tile_set = Object::cast_to<TileSet>(p_edited);
if (tile_set) {
Vector<String> components = p_property.split("/", true, 3);
@@ -350,30 +531,7 @@ void TileSetEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p
TileData *tile_data = Object::cast_to<TileData>(tas->get_tile_data(tile_id, alternative_id));
ERR_FAIL_COND(!tile_data);
- if (p_property == "occlusion_layers_count") {
- int new_layer_count = p_new_value;
- int old_layer_count = tile_set->get_occlusion_layers_count();
- if (new_layer_count < old_layer_count) {
- for (int occclusion_layer_index = new_layer_count - 1; occclusion_layer_index < old_layer_count; occclusion_layer_index++) {
- ADD_UNDO(tile_data, vformat("occlusion_layer_%d/polygon", occclusion_layer_index));
- }
- }
- } else if (p_property == "physics_layers_count") {
- int new_layer_count = p_new_value;
- int old_layer_count = tile_set->get_physics_layers_count();
- if (new_layer_count < old_layer_count) {
- for (int physics_layer_index = new_layer_count - 1; physics_layer_index < old_layer_count; physics_layer_index++) {
- ADD_UNDO(tile_data, vformat("physics_layer_%d/polygons_count", physics_layer_index));
- for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(physics_layer_index); polygon_index++) {
- ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/points", physics_layer_index, polygon_index));
- ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way", physics_layer_index, polygon_index));
- ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way_margin", physics_layer_index, polygon_index));
- }
- }
- }
- } else if ((p_property == "terrains_sets_count" && tile_data->get_terrain_set() >= (int)p_new_value) ||
- (components.size() == 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "mode") ||
- (components.size() == 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "terrains_count" && tile_data->get_terrain_set() == components[0].trim_prefix("terrain_set_").to_int() && (int)p_new_value < tile_set->get_terrains_count(tile_data->get_terrain_set()))) {
+ if (components.size() == 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "mode") {
ADD_UNDO(tile_data, "terrain_set");
for (int l = 0; l < TileSet::CELL_NEIGHBOR_MAX; l++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(l);
@@ -381,22 +539,6 @@ void TileSetEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p
ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[l]));
}
}
- } else if (p_property == "navigation_layers_count") {
- int new_layer_count = p_new_value;
- int old_layer_count = tile_set->get_navigation_layers_count();
- if (new_layer_count < old_layer_count) {
- for (int navigation_layer_index = new_layer_count - 1; navigation_layer_index < old_layer_count; navigation_layer_index++) {
- ADD_UNDO(tile_data, vformat("navigation_layer_%d/polygon", navigation_layer_index));
- }
- }
- } else if (p_property == "custom_data_layers_count") {
- int new_layer_count = p_new_value;
- int old_layer_count = tile_set->get_custom_data_layers_count();
- if (new_layer_count < old_layer_count) {
- for (int custom_data_layer_index = new_layer_count - 1; custom_data_layer_index < old_layer_count; custom_data_layer_index++) {
- ADD_UNDO(tile_data, vformat("custom_data_%d", custom_data_layer_index));
- }
- }
} else if (components.size() == 2 && components[0].begins_with("custom_data_layer_") && components[0].trim_prefix("custom_data_layer_").is_valid_int() && components[1] == "type") {
int custom_data_layer = components[0].trim_prefix("custom_data_layer_").is_valid_int();
ADD_UNDO(tile_data, vformat("custom_data_%d", custom_data_layer));
@@ -531,6 +673,7 @@ TileSetEditor::TileSetEditor() {
tile_set_scenes_collection_source_editor->hide();
// Registers UndoRedo inspector callback.
+ EditorNode::get_singleton()->get_editor_data().add_move_array_element_function(SNAME("TileSet"), callable_mp(this, &TileSetEditor::_move_tile_set_array_element));
EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileSetEditor::_undo_redo_inspector_callback));
}
diff --git a/editor/plugins/tiles/tile_set_editor.h b/editor/plugins/tiles/tile_set_editor.h
index 970e3fabb6..fe854b2281 100644
--- a/editor/plugins/tiles/tile_set_editor.h
+++ b/editor/plugins/tiles/tile_set_editor.h
@@ -71,6 +71,7 @@ private:
void _tile_set_changed();
+ void _move_tile_set_array_element(Object *p_undo_redo, Object *p_edited, String p_array_prefix, int p_from_index, int p_to_pos);
void _undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value);
protected:
diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp
index 79b869b511..d0d01a8d49 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.cpp
+++ b/editor/plugins/tiles/tiles_editor_plugin.cpp
@@ -146,7 +146,7 @@ void TilesEditor::synchronize_atlas_view(Object *p_current) {
ERR_FAIL_COND(!tile_atlas_view);
if (tile_atlas_view->is_visible_in_tree()) {
- tile_atlas_view->set_transform(atlas_view_zoom, Vector2(atlas_view_scroll.x, atlas_view_scroll.y));
+ tile_atlas_view->set_transform(atlas_view_zoom, atlas_view_scroll);
}
}
@@ -246,11 +246,9 @@ void TilesEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
tiles_editor_button->show();
editor_node->make_bottom_panel_item_visible(tiles_editor);
- //get_tree()->connect_compat("idle_frame", tileset_editor, "_on_workspace_process");
} else {
editor_node->hide_bottom_panel();
tiles_editor_button->hide();
- //get_tree()->disconnect_compat("idle_frame", tileset_editor, "_on_workspace_process");
}
}
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index d84ed4828c..b1b64564bb 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -70,14 +70,15 @@ const int MAX_FLOAT_CONST_DEFS = sizeof(float_constant_defs) / sizeof(FloatConst
///////////////////
Control *VisualShaderNodePlugin::create_editor(const Ref<Resource> &p_parent_resource, const Ref<VisualShaderNode> &p_node) {
- if (get_script_instance()) {
- return get_script_instance()->call("_create_editor", p_parent_resource, p_node);
+ Object *ret;
+ if (GDVIRTUAL_CALL(_create_editor, p_parent_resource, p_node, ret)) {
+ return Object::cast_to<Control>(ret);
}
return nullptr;
}
void VisualShaderNodePlugin::_bind_methods() {
- BIND_VMETHOD(MethodInfo(Variant::OBJECT, "_create_editor", PropertyInfo(Variant::OBJECT, "parent_resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"), PropertyInfo(Variant::OBJECT, "for_node", PROPERTY_HINT_RESOURCE_TYPE, "VisualShaderNode")));
+ GDVIRTUAL_BIND(_create_editor, "parent_resource", "visual_shader_node");
}
///////////////////
@@ -122,9 +123,9 @@ void VisualShaderGraphPlugin::set_connections(List<VisualShader::Connection> &p_
void VisualShaderGraphPlugin::show_port_preview(VisualShader::Type p_type, int p_node_id, int p_port_id) {
if (visual_shader->get_shader_type() == p_type && links.has(p_node_id) && links[p_node_id].output_ports.has(p_port_id)) {
- for (Map<int, Port>::Element *E = links[p_node_id].output_ports.front(); E; E = E->next()) {
- if (E->value().preview_button != nullptr) {
- E->value().preview_button->set_pressed(false);
+ for (const KeyValue<int, Port> &E : links[p_node_id].output_ports) {
+ if (E.value.preview_button != nullptr) {
+ E.value.preview_button->set_pressed(false);
}
}
@@ -263,11 +264,11 @@ void VisualShaderGraphPlugin::register_curve_editor(int p_node_id, int p_index,
}
void VisualShaderGraphPlugin::update_uniform_refs() {
- for (Map<int, Link>::Element *E = links.front(); E; E = E->next()) {
- VisualShaderNodeUniformRef *ref = Object::cast_to<VisualShaderNodeUniformRef>(E->get().visual_node);
+ for (KeyValue<int, Link> &E : links) {
+ VisualShaderNodeUniformRef *ref = Object::cast_to<VisualShaderNodeUniformRef>(E.value.visual_node);
if (ref) {
- remove_node(E->get().type, E->key());
- add_node(E->get().type, E->key());
+ remove_node(E.value.type, E.key);
+ add_node(E.value.type, E.key);
}
}
}
@@ -1944,7 +1945,7 @@ void VisualShaderEditor::_set_node_size(int p_type, int p_node, const Vector2 &p
box_size.x -= 28 * EDSCALE;
box_size.y -= text_box->get_offset(SIDE_TOP);
box_size.y -= 28 * EDSCALE;
- text_box->set_custom_minimum_size(Size2(box_size.x, box_size.y));
+ text_box->set_custom_minimum_size(box_size);
text_box->set_size(Size2(1, 1));
}
}
@@ -2546,6 +2547,7 @@ void VisualShaderEditor::_add_node(int p_idx, int p_op_idx, String p_resource_pa
}
}
}
+ _member_cancel();
VisualShaderNodeUniform *uniform = Object::cast_to<VisualShaderNodeUniform>(vsnode.ptr());
if (uniform) {
@@ -3170,7 +3172,7 @@ void VisualShaderEditor::_sbox_input(const Ref<InputEvent> &p_ie) {
ie->get_keycode() == KEY_DOWN ||
ie->get_keycode() == KEY_ENTER ||
ie->get_keycode() == KEY_KP_ENTER)) {
- members->call("_gui_input", ie);
+ members->gui_input(ie);
node_filter->accept_event();
}
}
@@ -4040,7 +4042,7 @@ VisualShaderEditor::VisualShaderEditor() {
graph->get_zoom_hbox()->add_child(add_node);
add_node->set_text(TTR("Add Node..."));
graph->get_zoom_hbox()->move_child(add_node, 0);
- add_node->connect("pressed", callable_mp(this, &VisualShaderEditor::_show_members_dialog), varray(false));
+ add_node->connect("pressed", callable_mp(this, &VisualShaderEditor::_show_members_dialog), varray(false, VisualShaderNode::PORT_TYPE_MAX, VisualShaderNode::PORT_TYPE_MAX));
preview_shader = memnew(Button);
preview_shader->set_flat(true);
diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h
index fbfe1197a3..9f24c5af72 100644
--- a/editor/plugins/visual_shader_editor_plugin.h
+++ b/editor/plugins/visual_shader_editor_plugin.h
@@ -47,6 +47,8 @@ class VisualShaderNodePlugin : public RefCounted {
protected:
static void _bind_methods();
+ GDVIRTUAL2RC(Object *, _create_editor, RES, Ref<VisualShaderNode>)
+
public:
virtual Control *create_editor(const Ref<Resource> &p_parent_resource, const Ref<VisualShaderNode> &p_node);
};
diff --git a/editor/plugins/voxel_gi_editor_plugin.cpp b/editor/plugins/voxel_gi_editor_plugin.cpp
index 5bbc0c9dd5..9a44d40dcb 100644
--- a/editor/plugins/voxel_gi_editor_plugin.cpp
+++ b/editor/plugins/voxel_gi_editor_plugin.cpp
@@ -33,7 +33,7 @@
void VoxelGIEditorPlugin::_bake() {
if (voxel_gi) {
if (voxel_gi->get_probe_data().is_null()) {
- String path = get_tree()->get_edited_scene_root()->get_filename();
+ String path = get_tree()->get_edited_scene_root()->get_scene_file_path();
if (path == String()) {
path = "res://" + voxel_gi->get_name() + "_data.res";
} else {