summaryrefslogtreecommitdiffstats
path: root/editor
diff options
context:
space:
mode:
Diffstat (limited to 'editor')
-rw-r--r--editor/animation_bezier_editor.cpp2
-rw-r--r--editor/animation_track_editor.cpp9
-rw-r--r--editor/editor_settings.cpp1
-rw-r--r--editor/editor_settings_dialog.cpp4
-rw-r--r--editor/filesystem_dock.cpp126
-rw-r--r--editor/filesystem_dock.h5
-rw-r--r--editor/gui/editor_spin_slider.cpp4
-rw-r--r--editor/icons/FlipWinding.svg1
-rw-r--r--editor/icons/LookAtModifier3D.svg1
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.cpp33
-rw-r--r--editor/plugins/navigation_obstacle_3d_editor_plugin.cpp882
-rw-r--r--editor/plugins/navigation_obstacle_3d_editor_plugin.h110
-rw-r--r--editor/plugins/script_editor_plugin.cpp6
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp4
14 files changed, 773 insertions, 415 deletions
diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp
index 8d7c6a1f16..b923dc0732 100644
--- a/editor/animation_bezier_editor.cpp
+++ b/editor/animation_bezier_editor.cpp
@@ -1650,7 +1650,7 @@ void AnimationBezierTrackEdit::_zoom_callback(float p_zoom_factor, Vector2 p_ori
Ref<InputEventWithModifiers> iewm = p_event;
if (iewm.is_valid() && iewm->is_alt_pressed()) {
// Alternate zoom (doesn't affect timeline).
- timeline_v_zoom = CLAMP(timeline_v_zoom * p_zoom_factor, 0.000001, 100000);
+ timeline_v_zoom = CLAMP(timeline_v_zoom / p_zoom_factor, 0.000001, 100000);
} else {
float zoom_factor = p_zoom_factor > 1.0 ? AnimationTimelineEdit::SCROLL_ZOOM_FACTOR_IN : AnimationTimelineEdit::SCROLL_ZOOM_FACTOR_OUT;
timeline->_zoom_callback(zoom_factor, p_origin, p_event);
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index 076ba6d905..55ea7c0082 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -7383,16 +7383,17 @@ void AnimationTrackEditor::_update_snap_unit() {
float AnimationTrackEditor::snap_time(float p_value, bool p_relative) {
if (is_snap_keys_enabled()) {
+ double current_snap = snap_unit;
if (Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
// Use more precise snapping when holding Shift.
- snap_unit *= 0.25;
+ current_snap *= 0.25;
}
if (p_relative) {
- double rel = Math::fmod(timeline->get_value(), snap_unit);
- p_value = Math::snapped(p_value + rel, snap_unit) - rel;
+ double rel = Math::fmod(timeline->get_value(), current_snap);
+ p_value = Math::snapped(p_value + rel, current_snap) - rel;
} else {
- p_value = Math::snapped(p_value, snap_unit);
+ p_value = Math::snapped(p_value, current_snap);
}
}
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 44690f81f0..b0d1c3e6bb 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -715,6 +715,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("text_editor/script_list/sort_members_outline_alphabetically", false, true);
_initial_set("text_editor/script_list/script_temperature_enabled", true);
_initial_set("text_editor/script_list/script_temperature_history_size", 15);
+ _initial_set("text_editor/script_list/highlight_scene_scripts", true);
_initial_set("text_editor/script_list/group_help_pages", true);
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "text_editor/script_list/sort_scripts_by", 0, "Name,Path,None");
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "text_editor/script_list/list_script_names_as", 0, "Name,Parent Directory And Name,Full Path");
diff --git a/editor/editor_settings_dialog.cpp b/editor/editor_settings_dialog.cpp
index d6742c9b55..8989b9cf9b 100644
--- a/editor/editor_settings_dialog.cpp
+++ b/editor/editor_settings_dialog.cpp
@@ -106,8 +106,8 @@ void EditorSettingsDialog::update_navigation_preset() {
orbit_mod_key_2 = InputEventKey::create_reference(Key::NONE);
pan_mod_key_1 = InputEventKey::create_reference(Key::SHIFT);
pan_mod_key_2 = InputEventKey::create_reference(Key::NONE);
- zoom_mod_key_1 = InputEventKey::create_reference(Key::SHIFT);
- zoom_mod_key_2 = InputEventKey::create_reference(Key::CTRL);
+ zoom_mod_key_1 = InputEventKey::create_reference(Key::CTRL);
+ zoom_mod_key_2 = InputEventKey::create_reference(Key::NONE);
} else if (nav_scheme == Node3DEditorViewport::NAVIGATION_MAYA) {
set_preset = true;
set_orbit_mouse_button = Node3DEditorViewport::NAVIGATION_LEFT_MOUSE;
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index fcd5a572b4..3921cde71e 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -203,9 +203,7 @@ Ref<Texture2D> FileSystemDock::_get_tree_item_icon(bool p_is_valid, const String
}
}
-bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory *p_dir, Vector<String> &uncollapsed_paths, bool p_select_in_favorites, bool p_unfold_path) {
- bool parent_should_expand = false;
-
+void FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory *p_dir, Vector<String> &uncollapsed_paths, bool p_select_in_favorites, bool p_unfold_path) {
// Create a tree item for the subdirectory.
TreeItem *subdirectory_item = tree->create_item(p_parent);
String dname = p_dir->get_name();
@@ -213,6 +211,7 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory
if (dname.is_empty()) {
dname = "res://";
+ resources_item = subdirectory_item;
}
// Set custom folder color (if applicable).
@@ -258,16 +257,13 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory
} else {
subdirectory_item->set_collapsed(!uncollapsed_paths.has(lpath));
}
- if (!searched_tokens.is_empty() && _matches_all_search_tokens(dname)) {
- parent_should_expand = true;
- }
// Create items for all subdirectories.
bool reversed = file_sort == FileSortOption::FILE_SORT_NAME_REVERSE;
for (int i = reversed ? p_dir->get_subdir_count() - 1 : 0;
reversed ? i >= 0 : i < p_dir->get_subdir_count();
reversed ? i-- : i++) {
- parent_should_expand = (_create_tree(subdirectory_item, p_dir->get_subdir(i), uncollapsed_paths, p_select_in_favorites, p_unfold_path) || parent_should_expand);
+ _create_tree(subdirectory_item, p_dir->get_subdir(i), uncollapsed_paths, p_select_in_favorites, p_unfold_path);
}
// Create all items for the files in the subdirectory.
@@ -283,17 +279,6 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory
continue;
}
- String file_name = p_dir->get_file(i);
- if (!searched_tokens.is_empty()) {
- if (!_matches_all_search_tokens(file_name)) {
- // The searched string is not in the file name, we skip it.
- continue;
- } else {
- // We expand all parents.
- parent_should_expand = true;
- }
- }
-
FileInfo file_info;
file_info.name = p_dir->get_file(i);
file_info.type = p_dir->get_file_type(i);
@@ -346,24 +331,12 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory
subdirectory_item->set_as_cursor(0);
}
}
-
- if (!searched_tokens.is_empty()) {
- if (parent_should_expand) {
- subdirectory_item->set_collapsed(false);
- } else if (dname != "res://") {
- subdirectory_item->get_parent()->remove_child(subdirectory_item);
- memdelete(subdirectory_item);
- }
- }
-
- return parent_should_expand;
}
Vector<String> FileSystemDock::get_uncollapsed_paths() const {
Vector<String> uncollapsed_paths;
TreeItem *root = tree->get_root();
if (root) {
- TreeItem *favorites_item = root->get_first_child();
if (!favorites_item->is_collapsed()) {
uncollapsed_paths.push_back(favorites_item->get_metadata(0));
}
@@ -400,7 +373,7 @@ void FileSystemDock::_update_tree(const Vector<String> &p_uncollapsed_paths, boo
TreeItem *root = tree->create_item();
// Handles the favorites.
- TreeItem *favorites_item = tree->create_item(root);
+ favorites_item = tree->create_item(root);
favorites_item->set_icon(0, get_editor_theme_icon(SNAME("Favorites")));
favorites_item->set_text(0, TTR("Favorites:"));
favorites_item->set_metadata(0, "Favorites");
@@ -453,24 +426,22 @@ void FileSystemDock::_update_tree(const Vector<String> &p_uncollapsed_paths, boo
color = Color(1, 1, 1);
}
- if (searched_tokens.is_empty() || _matches_all_search_tokens(text)) {
- TreeItem *ti = tree->create_item(favorites_item);
- ti->set_text(0, text);
- ti->set_icon(0, icon);
- ti->set_icon_modulate(0, color);
- ti->set_tooltip_text(0, favorite);
- ti->set_selectable(0, true);
- ti->set_metadata(0, favorite);
- if (p_select_in_favorites && favorite == current_path) {
- ti->select(0);
- ti->set_as_cursor(0);
- }
- if (!favorite.ends_with("/")) {
- Array udata;
- udata.push_back(tree_update_id);
- udata.push_back(ti);
- EditorResourcePreview::get_singleton()->queue_resource_preview(favorite, this, "_tree_thumbnail_done", udata);
- }
+ TreeItem *ti = tree->create_item(favorites_item);
+ ti->set_text(0, text);
+ ti->set_icon(0, icon);
+ ti->set_icon_modulate(0, color);
+ ti->set_tooltip_text(0, favorite);
+ ti->set_selectable(0, true);
+ ti->set_metadata(0, favorite);
+ if (p_select_in_favorites && favorite == current_path) {
+ ti->select(0);
+ ti->set_as_cursor(0);
+ }
+ if (!favorite.ends_with("/")) {
+ Array udata;
+ udata.push_back(tree_update_id);
+ udata.push_back(ti);
+ EditorResourcePreview::get_singleton()->queue_resource_preview(favorite, this, "_tree_thumbnail_done", udata);
}
}
@@ -676,7 +647,6 @@ void FileSystemDock::_tree_multi_selected(Object *p_item, int p_column, bool p_s
return;
}
- TreeItem *favorites_item = tree->get_root()->get_first_child();
if (selected->get_parent() == favorites_item && !String(selected->get_metadata(0)).ends_with("/")) {
// Go to the favorites if we click in the favorites and the path has changed.
current_path = "Favorites";
@@ -771,6 +741,36 @@ void FileSystemDock::_navigate_to_path(const String &p_path, bool p_select_in_fa
}
}
+bool FileSystemDock::_update_filtered_items(TreeItem *p_tree_item) {
+ TreeItem *item = p_tree_item;
+ if (!item) {
+ item = tree->get_root();
+ }
+ ERR_FAIL_NULL_V(item, false);
+
+ bool keep_visible = false;
+ for (TreeItem *child = item->get_first_child(); child; child = child->get_next()) {
+ keep_visible = _update_filtered_items(child) || keep_visible;
+ }
+
+ if (searched_tokens.is_empty()) {
+ item->set_visible(true);
+ // Always uncollapse root (the hidden item above res:// and favorites).
+ item->set_collapsed(item != tree->get_root() && !uncollapsed_paths_before_search.has(item->get_metadata(0)));
+ return true;
+ }
+
+ if (keep_visible) {
+ item->set_collapsed(false);
+ } else {
+ // res:// and favorites are always visible.
+ keep_visible = item == resources_item || item == favorites_item;
+ keep_visible = keep_visible || _matches_all_search_tokens(item->get_text(0));
+ }
+ item->set_visible(keep_visible);
+ return keep_visible;
+}
+
void FileSystemDock::navigate_to_path(const String &p_path) {
file_list_search_box->clear();
_navigate_to_path(p_path);
@@ -2028,7 +2028,6 @@ Vector<String> FileSystemDock::_tree_get_selected(bool remove_self_inclusion, bo
// Build a list of selected items with the active one at the first position.
Vector<String> selected_strings;
- TreeItem *favorites_item = tree->get_root()->get_first_child();
TreeItem *cursor_item = tree->get_selected();
if (cursor_item && (p_include_unselected_cursor || cursor_item->is_selected(0)) && cursor_item != favorites_item) {
selected_strings.push_back(cursor_item->get_metadata(0));
@@ -2637,16 +2636,12 @@ void FileSystemDock::_search_changed(const String &p_text, const Control *p_from
tree_search_box->set_text(searched_string);
}
- bool unfold_path = (p_text.is_empty() && !current_path.is_empty());
- switch (display_mode) {
- case DISPLAY_MODE_TREE_ONLY: {
- _update_tree(searched_tokens.is_empty() ? uncollapsed_paths_before_search : Vector<String>(), false, false, unfold_path);
- } break;
- case DISPLAY_MODE_HSPLIT:
- case DISPLAY_MODE_VSPLIT: {
- _update_file_list(false);
- _update_tree(searched_tokens.is_empty() ? uncollapsed_paths_before_search : Vector<String>(), false, false, unfold_path);
- } break;
+ _update_filtered_items();
+ if (display_mode == DISPLAY_MODE_HSPLIT || display_mode == DISPLAY_MODE_VSPLIT) {
+ _update_file_list(false);
+ }
+ if (searched_tokens.is_empty()) {
+ _navigate_to_path(current_path);
}
}
@@ -2786,7 +2781,6 @@ Variant FileSystemDock::get_drag_data_fw(const Point2 &p_point, Control *p_from)
// Check if the first selected is in favorite.
TreeItem *selected = tree->get_next_selected(tree->get_root());
while (selected) {
- TreeItem *favorites_item = tree->get_root()->get_first_child();
if (selected == favorites_item) {
// The "Favorites" item is not draggable.
return Variant();
@@ -2838,10 +2832,6 @@ bool FileSystemDock::can_drop_data_fw(const Point2 &p_point, const Variant &p_da
}
int drop_section = tree->get_drop_section_at_position(p_point);
- TreeItem *favorites_item = tree->get_root()->get_first_child();
-
- TreeItem *resources_item = favorites_item->get_next();
-
if (ti == favorites_item) {
return (drop_section == 1); // The parent, first fav.
}
@@ -2922,9 +2912,6 @@ void FileSystemDock::drop_data_fw(const Point2 &p_point, const Variant &p_data,
int drop_position;
Vector<String> drag_files = drag_data["files"];
- TreeItem *favorites_item = tree->get_root()->get_first_child();
- TreeItem *resources_item = favorites_item->get_next();
-
if (ti == favorites_item) {
// Drop on the favorite folder.
drop_position = 0;
@@ -3352,7 +3339,6 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, const Vect
[[maybe_unused]] bool added_separator = false;
if (favorites_list.has(fpath)) {
- TreeItem *favorites_item = tree->get_root()->get_first_child();
TreeItem *cursor_item = tree->get_selected();
bool is_item_in_favorites = false;
while (cursor_item != nullptr) {
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index fe83129c07..d2e403a8af 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -232,6 +232,8 @@ private:
FileSystemTree *tree = nullptr;
FileSystemList *files = nullptr;
bool import_dock_needs_update = false;
+ TreeItem *resources_item = nullptr;
+ TreeItem *favorites_item = nullptr;
bool holding_branch = false;
Vector<TreeItem *> tree_items_selected_on_drag_begin;
@@ -245,9 +247,10 @@ private:
void _reselect_items_selected_on_drag_begin(bool reset = false);
Ref<Texture2D> _get_tree_item_icon(bool p_is_valid, const String &p_file_type, const String &p_icon_path);
- bool _create_tree(TreeItem *p_parent, EditorFileSystemDirectory *p_dir, Vector<String> &uncollapsed_paths, bool p_select_in_favorites, bool p_unfold_path = false);
+ void _create_tree(TreeItem *p_parent, EditorFileSystemDirectory *p_dir, Vector<String> &uncollapsed_paths, bool p_select_in_favorites, bool p_unfold_path = false);
void _update_tree(const Vector<String> &p_uncollapsed_paths = Vector<String>(), bool p_uncollapse_root = false, bool p_select_in_favorites = false, bool p_unfold_path = false);
void _navigate_to_path(const String &p_path, bool p_select_in_favorites = false);
+ bool _update_filtered_items(TreeItem *p_tree_item = nullptr);
void _file_list_gui_input(Ref<InputEvent> p_event);
void _tree_gui_input(Ref<InputEvent> p_event);
diff --git a/editor/gui/editor_spin_slider.cpp b/editor/gui/editor_spin_slider.cpp
index 27b6bbafb7..712e91faca 100644
--- a/editor/gui/editor_spin_slider.cpp
+++ b/editor/gui/editor_spin_slider.cpp
@@ -437,7 +437,7 @@ void EditorSpinSlider::_draw_spin_slider() {
Vector2 scale = get_global_transform_with_canvas().get_scale();
grabber->set_scale(scale);
grabber->reset_size();
- grabber->set_position(get_global_position() + (grabber_rect.get_center() - grabber->get_size() * 0.5) * scale);
+ grabber->set_position((grabber_rect.get_center() - grabber->get_size() * 0.5) * scale);
if (mousewheel_over_grabber) {
Input::get_singleton()->warp_mouse(grabber->get_position() + grabber_rect.size);
@@ -731,7 +731,7 @@ EditorSpinSlider::EditorSpinSlider() {
grabber = memnew(TextureRect);
add_child(grabber);
grabber->hide();
- grabber->set_as_top_level(true);
+ grabber->set_z_index(1);
grabber->set_mouse_filter(MOUSE_FILTER_STOP);
grabber->connect(SceneStringName(mouse_entered), callable_mp(this, &EditorSpinSlider::_grabber_mouse_entered));
grabber->connect(SceneStringName(mouse_exited), callable_mp(this, &EditorSpinSlider::_grabber_mouse_exited));
diff --git a/editor/icons/FlipWinding.svg b/editor/icons/FlipWinding.svg
new file mode 100644
index 0000000000..8964ca8d5d
--- /dev/null
+++ b/editor/icons/FlipWinding.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><rect width="4.596" height="4.596" x="5.7" y="5.7" fill="#e0e0e0" fill-opacity=".6" rx="1" ry="1" transform="rotate(45 8 8)"/><path fill="none" stroke="#e0e0e0" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 2a6 6 0 00-2.5 11m.5-3L6 14H2M9 14a6 6 0 002.5-11M11 6 10 2h4"/></svg> \ No newline at end of file
diff --git a/editor/icons/LookAtModifier3D.svg b/editor/icons/LookAtModifier3D.svg
new file mode 100644
index 0000000000..9315b297ef
--- /dev/null
+++ b/editor/icons/LookAtModifier3D.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="#fc7f7f"><path d="m5.742 11.508c.916-2.959 3.507-4.508 5.592-4.508.803 0 1.673.223 2.492.658.297-.182.563-.423.768-.731.754-1.134.446-2.665-.688-3.419-.309-.205-.66-.338-1.026-.389-.188-1.349-1.433-2.291-2.782-2.103s-2.29 1.433-2.103 2.782c.051.367.184.717.389 1.026l-3.56 3.56c-1.134-.754-2.665-.446-3.419.688s-.446 2.664.688 3.419c.308.205.659.338 1.026.389.188 1.349 1.433 2.29 2.782 2.103.342-.048.658-.164.936-.333-.467-.612-.856-1.337-1.102-2.206-.085-.3-.085-.617.007-.936z"/><path d="m11.334 8c-1.704 0-3.861 1.299-4.637 3.804-.034.119-.034.246 0 .366.745 2.638 2.97 3.83 4.637 3.83s3.891-1.192 4.641-3.816c.034-.12.034-.247 0-.367-.734-2.526-2.938-3.817-4.641-3.817zm0 6.667c-1.473 0-2.667-1.194-2.667-2.667s1.194-2.666 2.667-2.666 2.667 1.193 2.667 2.666-1.194 2.667-2.667 2.667z"/><circle cx="11.334" cy="12" r="1.333"/></g></svg> \ No newline at end of file
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp
index eef890d013..096e92e235 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp
@@ -45,6 +45,7 @@
#include "scene/3d/skeleton_3d.h"
#include "scene/animation/animation_player.h"
#include "scene/gui/check_box.h"
+#include "scene/gui/grid_container.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/option_button.h"
#include "scene/gui/panel.h"
@@ -1397,32 +1398,30 @@ bool EditorInspectorPluginAnimationNodeAnimation::parse_property(Object *p_objec
}
AnimationNodeAnimationEditorDialog::AnimationNodeAnimationEditorDialog() {
- set_title(TTR("Select Markers..."));
- VBoxContainer *vbox = memnew(VBoxContainer);
- add_child(vbox);
- vbox->set_offsets_preset(Control::PRESET_FULL_RECT);
-
- HBoxContainer *container_start = memnew(HBoxContainer);
- vbox->add_child(container_start);
- Label *label_start = memnew(Label);
- container_start->add_child(label_start);
+ set_title(TTR("Select Markers"));
+
+ GridContainer *grid = memnew(GridContainer);
+ grid->set_columns(2);
+ grid->set_offsets_preset(Control::PRESET_FULL_RECT);
+ add_child(grid);
+
+ Label *label_start = memnew(Label(TTR("Start Marker")));
+ grid->add_child(label_start);
label_start->set_h_size_flags(Control::SIZE_EXPAND_FILL);
label_start->set_stretch_ratio(1);
- label_start->set_text(TTR("Start Marker"));
select_start = memnew(OptionButton);
- container_start->add_child(select_start);
+ select_start->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
+ grid->add_child(select_start);
select_start->set_h_size_flags(Control::SIZE_EXPAND_FILL);
select_start->set_stretch_ratio(2);
- HBoxContainer *container_end = memnew(HBoxContainer);
- vbox->add_child(container_end);
- Label *label_end = memnew(Label);
- container_end->add_child(label_end);
+ Label *label_end = memnew(Label(TTR("End Marker")));
+ grid->add_child(label_end);
label_end->set_h_size_flags(Control::SIZE_EXPAND_FILL);
label_end->set_stretch_ratio(1);
- label_end->set_text(TTR("End Marker"));
select_end = memnew(OptionButton);
- container_end->add_child(select_end);
+ select_end->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
+ grid->add_child(select_end);
select_end->set_h_size_flags(Control::SIZE_EXPAND_FILL);
select_end->set_stretch_ratio(2);
}
diff --git a/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp b/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp
index 360e6b00da..94ad20f05a 100644
--- a/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp
+++ b/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp
@@ -30,149 +30,502 @@
#include "navigation_obstacle_3d_editor_plugin.h"
-#include "canvas_item_editor_plugin.h"
-#include "core/input/input.h"
-#include "core/io/file_access.h"
+#include "core/config/project_settings.h"
#include "core/math/geometry_2d.h"
-#include "core/os/keyboard.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
-#include "node_3d_editor_plugin.h"
-#include "scene/3d/camera_3d.h"
-#include "scene/gui/separator.h"
+#include "editor/plugins/node_3d_editor_plugin.h"
+#include "scene/3d/navigation_obstacle_3d.h"
+#include "scene/gui/button.h"
+#include "scene/gui/dialogs.h"
+#include "servers/navigation_server_3d.h"
+
+bool NavigationObstacle3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
+ return Object::cast_to<NavigationObstacle3D>(p_spatial) != nullptr;
+}
+
+String NavigationObstacle3DGizmoPlugin::get_gizmo_name() const {
+ return "NavigationObstacle3D";
+}
+
+void NavigationObstacle3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
+ p_gizmo->clear();
+
+ if (!p_gizmo->is_selected() && get_state() == HIDDEN) {
+ return;
+ }
+
+ NavigationObstacle3D *obstacle = Object::cast_to<NavigationObstacle3D>(p_gizmo->get_node_3d());
+
+ if (!obstacle) {
+ return;
+ }
+
+ const Vector<Vector3> &vertices = obstacle->get_vertices();
+ if (vertices.is_empty()) {
+ return;
+ }
+
+ float height = obstacle->get_height();
+ Basis gbi = obstacle->get_global_basis().inverse();
+
+ const int vertex_count = vertices.size();
+
+ Vector<Vector3> lines_mesh_vertices;
+ lines_mesh_vertices.resize(vertex_count * 8);
+ Vector3 *lines_mesh_vertices_ptrw = lines_mesh_vertices.ptrw();
+
+ int vertex_index = 0;
+
+ for (int i = 0; i < vertex_count; i++) {
+ Vector3 point = vertices[i];
+ Vector3 next_point = vertices[(i + 1) % vertex_count];
+
+ Vector3 direction = next_point.direction_to(point);
+ Vector3 arrow_dir = direction.cross(Vector3(0.0, 1.0, 0.0));
+ Vector3 edge_middle = point + ((next_point - point) * 0.5);
+
+ lines_mesh_vertices_ptrw[vertex_index++] = gbi.xform(edge_middle);
+ lines_mesh_vertices_ptrw[vertex_index++] = gbi.xform(edge_middle + (arrow_dir * 0.5));
+
+ lines_mesh_vertices_ptrw[vertex_index++] = gbi.xform(point);
+ lines_mesh_vertices_ptrw[vertex_index++] = gbi.xform(next_point);
+
+ lines_mesh_vertices_ptrw[vertex_index++] = gbi.xform(Vector3(point.x, height, point.z));
+ lines_mesh_vertices_ptrw[vertex_index++] = gbi.xform(Vector3(next_point.x, height, next_point.z));
+
+ lines_mesh_vertices_ptrw[vertex_index++] = gbi.xform(point);
+ lines_mesh_vertices_ptrw[vertex_index++] = gbi.xform(Vector3(point.x, height, point.z));
+ }
+
+ Vector<Vector2> polygon_2d_vertices;
+ polygon_2d_vertices.resize(vertex_count);
+ for (int i = 0; i < vertex_count; i++) {
+ const Vector3 &vert = vertices[i];
+ polygon_2d_vertices.write[i] = Vector2(vert.x, vert.z);
+ }
+ Vector<int> triangulated_polygon_2d_indices = Geometry2D::triangulate_polygon(polygon_2d_vertices);
+
+ NavigationServer3D *ns3d = NavigationServer3D::get_singleton();
+
+ if (triangulated_polygon_2d_indices.is_empty()) {
+ p_gizmo->add_lines(lines_mesh_vertices, ns3d->get_debug_navigation_avoidance_static_obstacle_pushin_edge_material());
+ } else {
+ p_gizmo->add_lines(lines_mesh_vertices, ns3d->get_debug_navigation_avoidance_static_obstacle_pushout_edge_material());
+ }
+ p_gizmo->add_collision_segments(lines_mesh_vertices);
+
+ if (p_gizmo->is_selected()) {
+ NavigationObstacle3DEditorPlugin::singleton->redraw();
+ }
+}
+
+bool NavigationObstacle3DGizmoPlugin::can_be_hidden() const {
+ return true;
+}
+
+int NavigationObstacle3DGizmoPlugin::get_priority() const {
+ return -1;
+}
+
+int NavigationObstacle3DGizmoPlugin::subgizmos_intersect_ray(const EditorNode3DGizmo *p_gizmo, Camera3D *p_camera, const Vector2 &p_point) const {
+ if (NavigationObstacle3DEditorPlugin::singleton->get_mode() != 1) { // MODE_EDIT
+ return -1;
+ }
+
+ NavigationObstacle3D *obstacle_node = Object::cast_to<NavigationObstacle3D>(p_gizmo->get_node_3d());
+ ERR_FAIL_NULL_V(obstacle_node, -1);
+
+ Transform3D gt = Transform3D(Basis(), obstacle_node->get_global_position());
+ const Vector<Vector3> &vertices = obstacle_node->get_vertices();
+
+ for (int idx = 0; idx < vertices.size(); ++idx) {
+ Vector3 pos = gt.xform(vertices[idx]);
+ if (p_camera->unproject_position(pos).distance_to(p_point) < 20) {
+ return idx;
+ }
+ }
+
+ return -1;
+}
+
+Vector<int> NavigationObstacle3DGizmoPlugin::subgizmos_intersect_frustum(const EditorNode3DGizmo *p_gizmo, const Camera3D *p_camera, const Vector<Plane> &p_frustum) const {
+ Vector<int> contained_points;
+ if (NavigationObstacle3DEditorPlugin::singleton->get_mode() != 1) { // MODE_EDIT
+ return contained_points;
+ }
+
+ NavigationObstacle3D *obstacle_node = Object::cast_to<NavigationObstacle3D>(p_gizmo->get_node_3d());
+ ERR_FAIL_NULL_V(obstacle_node, contained_points);
+
+ Transform3D gt = Transform3D(Basis(), obstacle_node->get_global_position());
+ const Vector<Vector3> &vertices = obstacle_node->get_vertices();
+
+ for (int idx = 0; idx < vertices.size(); ++idx) {
+ Vector3 pos = gt.xform(vertices[idx]);
+ bool is_contained_in_frustum = true;
+ for (int i = 0; i < p_frustum.size(); ++i) {
+ if (p_frustum[i].distance_to(pos) > 0) {
+ is_contained_in_frustum = false;
+ break;
+ }
+ }
+
+ if (is_contained_in_frustum) {
+ contained_points.push_back(idx);
+ }
+ }
+
+ return contained_points;
+}
+
+Transform3D NavigationObstacle3DGizmoPlugin::get_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id) const {
+ NavigationObstacle3D *obstacle_node = Object::cast_to<NavigationObstacle3D>(p_gizmo->get_node_3d());
+ ERR_FAIL_NULL_V(obstacle_node, Transform3D());
+
+ const Vector<Vector3> &vertices = obstacle_node->get_vertices();
+ ERR_FAIL_INDEX_V(p_id, vertices.size(), Transform3D());
+
+ Basis gbi = obstacle_node->get_global_basis().inverse();
+
+ Transform3D subgizmo_transform = Transform3D(Basis(), gbi.xform(vertices[p_id]));
+ return subgizmo_transform;
+}
+
+void NavigationObstacle3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id, Transform3D p_transform) {
+ NavigationObstacle3D *obstacle_node = Object::cast_to<NavigationObstacle3D>(p_gizmo->get_node_3d());
+ ERR_FAIL_NULL(obstacle_node);
+
+ Basis gb = obstacle_node->get_global_basis();
+
+ Vector3 new_vertex_pos = p_transform.origin;
-void NavigationObstacle3DEditor::_notification(int p_what) {
+ Vector<Vector3> vertices = obstacle_node->get_vertices();
+ ERR_FAIL_INDEX(p_id, vertices.size());
+
+ Vector3 vertex = gb.xform(new_vertex_pos);
+ vertex.y = 0.0;
+ vertices.write[p_id] = vertex;
+
+ obstacle_node->set_vertices(vertices);
+}
+
+void NavigationObstacle3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) {
+ NavigationObstacle3D *obstacle_node = Object::cast_to<NavigationObstacle3D>(p_gizmo->get_node_3d());
+ ERR_FAIL_NULL(obstacle_node);
+
+ Basis gb = obstacle_node->get_global_basis();
+
+ Vector<Vector3> vertices = obstacle_node->get_vertices();
+ Vector<Vector3> restore_vertices = vertices;
+
+ for (int i = 0; i < p_ids.size(); ++i) {
+ const int idx = p_ids[i];
+ Vector3 vertex = gb.xform(p_restore[i].origin);
+ vertex.y = 0.0;
+ restore_vertices.write[idx] = vertex;
+ }
+
+ if (p_cancel) {
+ obstacle_node->set_vertices(restore_vertices);
+ return;
+ }
+
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->create_action(TTR("Set Obstacle Vertices"));
+ undo_redo->add_do_method(obstacle_node, "set_vertices", vertices);
+ undo_redo->add_undo_method(obstacle_node, "set_vertices", restore_vertices);
+ undo_redo->commit_action();
+}
+
+NavigationObstacle3DGizmoPlugin::NavigationObstacle3DGizmoPlugin() {
+ current_state = VISIBLE;
+}
+
+void NavigationObstacle3DEditorPlugin::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ _update_theme();
+ } break;
+
case NOTIFICATION_READY: {
- button_create->set_button_icon(get_editor_theme_icon(SNAME("Edit")));
- button_edit->set_button_icon(get_editor_theme_icon(SNAME("MovePoint")));
+ _update_theme();
button_edit->set_pressed(true);
- get_tree()->connect("node_removed", callable_mp(this, &NavigationObstacle3DEditor::_node_removed));
+ get_tree()->connect("node_removed", callable_mp(this, &NavigationObstacle3DEditorPlugin::_node_removed));
+ EditorNode::get_singleton()->get_gui_base()->connect(SceneStringName(theme_changed), callable_mp(this, &NavigationObstacle3DEditorPlugin::_update_theme));
+ } break;
+ case NOTIFICATION_EXIT_TREE: {
+ get_tree()->disconnect("node_removed", callable_mp(this, &NavigationObstacle3DEditorPlugin::_node_removed));
+ EditorNode::get_singleton()->get_gui_base()->disconnect(SceneStringName(theme_changed), callable_mp(this, &NavigationObstacle3DEditorPlugin::_update_theme));
} break;
}
}
-void NavigationObstacle3DEditor::_node_removed(Node *p_node) {
- if (p_node == obstacle_node) {
- obstacle_node = nullptr;
- if (point_lines_meshinstance->get_parent() == p_node) {
- p_node->remove_child(point_lines_meshinstance);
+void NavigationObstacle3DEditorPlugin::edit(Object *p_object) {
+ obstacle_node = Object::cast_to<NavigationObstacle3D>(p_object);
+
+ RenderingServer *rs = RenderingServer::get_singleton();
+
+ if (obstacle_node) {
+ if (obstacle_node->get_vertices().is_empty()) {
+ set_mode(MODE_CREATE);
+ } else {
+ set_mode(MODE_EDIT);
}
- hide();
+ wip_vertices.clear();
+ wip_active = false;
+ edited_point = -1;
+
+ rs->instance_set_scenario(point_lines_instance_rid, obstacle_node->get_world_3d()->get_scenario());
+ rs->instance_set_scenario(point_handles_instance_rid, obstacle_node->get_world_3d()->get_scenario());
+
+ redraw();
+
+ } else {
+ obstacle_node = nullptr;
+
+ rs->mesh_clear(point_lines_mesh_rid);
+ rs->mesh_clear(point_handle_mesh_rid);
+ rs->instance_set_scenario(point_lines_instance_rid, RID());
+ rs->instance_set_scenario(point_handles_instance_rid, RID());
}
}
-void NavigationObstacle3DEditor::_menu_option(int p_option) {
- switch (p_option) {
- case MODE_CREATE: {
- mode = MODE_CREATE;
- button_create->set_pressed(true);
- button_edit->set_pressed(false);
- } break;
- case MODE_EDIT: {
- mode = MODE_EDIT;
- button_create->set_pressed(false);
- button_edit->set_pressed(true);
- } break;
+bool NavigationObstacle3DEditorPlugin::handles(Object *p_object) const {
+ return Object::cast_to<NavigationObstacle3D>(p_object);
+}
+
+void NavigationObstacle3DEditorPlugin::make_visible(bool p_visible) {
+ if (p_visible) {
+ obstacle_editor->show();
+ } else {
+ obstacle_editor->hide();
+ edit(nullptr);
}
}
-void NavigationObstacle3DEditor::_wip_close() {
- ERR_FAIL_NULL_MSG(obstacle_node, "Edited NavigationObstacle3D is not valid.");
+void NavigationObstacle3DEditorPlugin::action_flip_vertices() {
+ if (!obstacle_node) {
+ return;
+ }
+
+ Vector<Vector3> flipped_vertices = obstacle_node->get_vertices();
+ flipped_vertices.reverse();
+
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Set NavigationObstacle3D Vertices"));
+ undo_redo->create_action(TTR("Edit Obstacle (Flip Winding)"));
+ undo_redo->add_do_method(obstacle_node, "set_vertices", flipped_vertices);
undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_node->get_vertices());
+ undo_redo->commit_action();
- PackedVector3Array polygon_3d_vertices;
- Vector<int> triangulated_polygon_2d_indices = Geometry2D::triangulate_polygon(wip);
+ obstacle_node->update_gizmos();
+}
- if (!triangulated_polygon_2d_indices.is_empty()) {
- polygon_3d_vertices.resize(wip.size());
- Vector3 *polygon_3d_vertices_ptr = polygon_3d_vertices.ptrw();
- for (int i = 0; i < wip.size(); i++) {
- const Vector2 &vert = wip[i];
- polygon_3d_vertices_ptr[i] = Vector3(vert.x, 0.0, vert.y);
- }
+void NavigationObstacle3DEditorPlugin::action_clear_vertices() {
+ if (!obstacle_node) {
+ return;
}
- undo_redo->add_do_method(obstacle_node, "set_vertices", polygon_3d_vertices);
- undo_redo->add_do_method(this, "_polygon_draw");
- undo_redo->add_undo_method(this, "_polygon_draw");
- wip.clear();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->create_action(TTR("Edit Obstacle (Clear Vertices)"));
+ undo_redo->add_do_method(obstacle_node, "set_vertices", Vector<Vector3>());
+ undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_node->get_vertices());
+ undo_redo->commit_action();
+
+ obstacle_node->update_gizmos();
+ edit(obstacle_node);
+}
+
+void NavigationObstacle3DEditorPlugin::_update_theme() {
+ button_create->set_tooltip_text(TTR("Add Vertex"));
+ button_edit->set_tooltip_text(TTR("Edit Vertex"));
+ button_delete->set_tooltip_text(TTR("Delete Vertex"));
+ button_flip->set_tooltip_text(TTR("Flip Winding"));
+ button_clear->set_tooltip_text(TTR("Clear Vertices"));
+ button_create->set_button_icon(button_create->get_editor_theme_icon(SNAME("CurveCreate")));
+ button_edit->set_button_icon(button_edit->get_editor_theme_icon(SNAME("CurveEdit")));
+ button_delete->set_button_icon(button_delete->get_editor_theme_icon(SNAME("CurveDelete")));
+ button_flip->set_button_icon(button_flip->get_editor_theme_icon(SNAME("FlipWinding")));
+ button_clear->set_button_icon(button_clear->get_editor_theme_icon(SNAME("Clear")));
+}
+
+void NavigationObstacle3DEditorPlugin::_node_removed(Node *p_node) {
+ if (obstacle_node == p_node) {
+ obstacle_node = nullptr;
+
+ RenderingServer *rs = RenderingServer::get_singleton();
+ rs->mesh_clear(point_lines_mesh_rid);
+ rs->mesh_clear(point_handle_mesh_rid);
+
+ obstacle_editor->hide();
+ }
+}
+
+void NavigationObstacle3DEditorPlugin::set_mode(int p_option) {
+ if (p_option == NavigationObstacle3DEditorPlugin::ACTION_FLIP) {
+ button_flip->set_pressed(false);
+ action_flip_vertices();
+ return;
+ }
+
+ if (p_option == NavigationObstacle3DEditorPlugin::ACTION_CLEAR) {
+ button_clear->set_pressed(false);
+ button_clear_dialog->reset_size();
+ button_clear_dialog->popup_centered();
+ return;
+ }
+
+ mode = p_option;
+
+ button_create->set_pressed(p_option == NavigationObstacle3DEditorPlugin::MODE_CREATE);
+ button_edit->set_pressed(p_option == NavigationObstacle3DEditorPlugin::MODE_EDIT);
+ button_delete->set_pressed(p_option == NavigationObstacle3DEditorPlugin::MODE_DELETE);
+ button_flip->set_pressed(false);
+ button_clear->set_pressed(false);
+}
+
+void NavigationObstacle3DEditorPlugin::_wip_cancel() {
+ wip_vertices.clear();
wip_active = false;
- mode = MODE_EDIT;
- button_edit->set_pressed(true);
- button_create->set_pressed(false);
+
edited_point = -1;
- undo_redo->commit_action();
+
+ redraw();
}
-EditorPlugin::AfterGUIInput NavigationObstacle3DEditor::forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
+void NavigationObstacle3DEditorPlugin::_wip_close() {
+ ERR_FAIL_NULL_MSG(obstacle_node, "Edited NavigationObstacle3D is not valid.");
+
+ Vector<Vector2> wip_2d_vertices;
+ wip_2d_vertices.resize(wip_vertices.size());
+ for (int i = 0; i < wip_vertices.size(); i++) {
+ const Vector3 &vert = wip_vertices[i];
+ wip_2d_vertices.write[i] = Vector2(vert.x, vert.z);
+ }
+ Vector<int> triangulated_polygon_2d_indices = Geometry2D::triangulate_polygon(wip_2d_vertices);
+
+ if (!triangulated_polygon_2d_indices.is_empty()) {
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->create_action(TTR("Set Obstacle Vertices"));
+ undo_redo->add_do_method(obstacle_node, "set_vertices", wip_vertices);
+ undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_node->get_vertices());
+ undo_redo->commit_action();
+
+ wip_vertices.clear();
+ wip_active = false;
+ //mode = MODE_EDIT;
+ NavigationObstacle3DEditorPlugin::singleton->set_mode(NavigationObstacle3DEditorPlugin::MODE_EDIT);
+ button_edit->set_pressed(true);
+ button_create->set_pressed(false);
+ edited_point = -1;
+ }
+}
+
+EditorPlugin::AfterGUIInput NavigationObstacle3DEditorPlugin::forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
if (!obstacle_node) {
return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
- // Use special transformation rules for NavigationObstacle3D: Only take global y-rotation into account and limit scaling to positive values.
- Transform3D gt;
- gt.origin = obstacle_node->get_global_position();
- gt.scale_basis(obstacle_node->get_global_basis().get_scale().abs().maxf(0.001));
- gt.rotate_basis(Vector3(0.0, 1.0, 0.0), obstacle_node->get_global_rotation().y);
- Transform3D gi = gt.affine_inverse();
- Plane p(Vector3(0.0, 1.0, 0.0), gt.origin);
- point_lines_meshinstance->set_transform(gt.translated(Vector3(0.0, 0.0, 0.00001)));
+ if (!obstacle_node->is_visible_in_tree()) {
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
+ }
+
+ Ref<InputEventMouse> mouse_event = p_event;
+
+ if (mouse_event.is_null()) {
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
+ }
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
- Vector2 gpoint = mb->get_position();
- Vector3 ray_from = p_camera->project_ray_origin(gpoint);
- Vector3 ray_dir = p_camera->project_ray_normal(gpoint);
+ Vector2 mouse_position = mb->get_position();
+ Vector3 ray_from = p_camera->project_ray_origin(mouse_position);
+ Vector3 ray_dir = p_camera->project_ray_normal(mouse_position);
+
+ Transform3D gt = Transform3D(Basis(), obstacle_node->get_global_position());
+ Transform3D gi = gt.affine_inverse();
+ Plane projection_plane(Vector3(0.0, 1.0, 0.0), gt.origin);
Vector3 spoint;
- if (!p.intersects_ray(ray_from, ray_dir, &spoint)) {
+ if (!projection_plane.intersects_ray(ray_from, ray_dir, &spoint)) {
return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
spoint = gi.xform(spoint);
- Vector2 cpoint(spoint.x, spoint.z);
-
- //DO NOT snap here, it's confusing in 3D for adding points.
- //Let the snap happen when the point is being moved, instead.
- //cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint);
-
- PackedVector2Array poly = _get_polygon();
+ Vector3 cpoint = Vector3(spoint.x, 0.0, spoint.z);
+ Vector<Vector3> obstacle_vertices = obstacle_node->get_vertices();
- //first check if a point is to be added (segment split)
real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
switch (mode) {
case MODE_CREATE: {
if (mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
+ if (obstacle_vertices.size() >= 3) {
+ int closest_idx = -1;
+ Vector2 closest_edge_point;
+ real_t closest_dist = 1e10;
+ for (int i = 0; i < obstacle_vertices.size(); i++) {
+ Vector2 points[2] = {
+ p_camera->unproject_position(gt.xform(obstacle_vertices[i])),
+ p_camera->unproject_position(gt.xform(obstacle_vertices[(i + 1) % obstacle_vertices.size()]))
+ };
+
+ Vector2 cp = Geometry2D::get_closest_point_to_segment(mouse_position, points);
+ if (cp.distance_squared_to(points[0]) < grab_threshold || cp.distance_squared_to(points[1]) < grab_threshold) {
+ continue; // Skip edge as clicked point is too close to existing vertex.
+ }
+
+ real_t d = cp.distance_to(mouse_position);
+ if (d < closest_dist && d < grab_threshold) {
+ closest_dist = d;
+ closest_edge_point = cp;
+ closest_idx = i;
+ }
+ }
+ if (closest_idx >= 0) {
+ edited_point = -1;
+ Vector3 _ray_from = p_camera->project_ray_origin(closest_edge_point);
+ Vector3 _ray_dir = p_camera->project_ray_normal(closest_edge_point);
+ Vector3 edge_intersection_point;
+ if (projection_plane.intersects_ray(_ray_from, _ray_dir, &edge_intersection_point)) {
+ edge_intersection_point = gi.xform(edge_intersection_point);
+
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->create_action(TTR("Edit Obstacle (Add Vertex)"));
+ undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_vertices);
+ obstacle_vertices.insert(closest_idx + 1, edge_intersection_point);
+ undo_redo->add_do_method(obstacle_node, "set_vertices", obstacle_vertices);
+ undo_redo->commit_action();
+ redraw();
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ }
+ }
if (!wip_active) {
- wip.clear();
- wip.push_back(cpoint);
+ wip_vertices.clear();
+ wip_vertices.push_back(cpoint);
wip_active = true;
edited_point_pos = cpoint;
snap_ignore = false;
- _polygon_draw();
+ redraw();
edited_point = 1;
return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else {
- if (wip.size() > 1 && p_camera->unproject_position(gt.xform(Vector3(wip[0].x, 0.0, wip[0].y))).distance_to(gpoint) < grab_threshold) {
- //wip closed
+ if (wip_vertices.size() > 1 && p_camera->unproject_position(gt.xform(wip_vertices[0])).distance_to(mouse_position) < grab_threshold) {
_wip_close();
return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else {
- wip.push_back(cpoint);
- edited_point = wip.size();
+ wip_vertices.push_back(cpoint);
+ edited_point = wip_vertices.size();
snap_ignore = false;
- _polygon_draw();
+ redraw();
return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
@@ -186,13 +539,11 @@ EditorPlugin::AfterGUIInput NavigationObstacle3DEditor::forward_3d_gui_input(Cam
if (mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
if (mb->is_ctrl_pressed()) {
- if (poly.size() < 3) {
+ if (obstacle_vertices.size() < 3) {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Edit Vertices"));
+ undo_redo->create_action(TTR("Edit Obstacle (Add Vertex)"));
undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_node->get_vertices());
- poly.push_back(cpoint);
- undo_redo->add_do_method(this, "_polygon_draw");
- undo_redo->add_undo_method(this, "_polygon_draw");
+ obstacle_vertices.push_back(cpoint);
undo_redo->commit_action();
return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
@@ -201,18 +552,18 @@ EditorPlugin::AfterGUIInput NavigationObstacle3DEditor::forward_3d_gui_input(Cam
int closest_idx = -1;
Vector2 closest_pos;
real_t closest_dist = 1e10;
- for (int i = 0; i < poly.size(); i++) {
+ for (int i = 0; i < obstacle_vertices.size(); i++) {
Vector2 points[2] = {
- p_camera->unproject_position(gt.xform(Vector3(poly[i].x, 0.0, poly[i].y))),
- p_camera->unproject_position(gt.xform(Vector3(poly[(i + 1) % poly.size()].x, 0.0, poly[(i + 1) % poly.size()].y)))
+ p_camera->unproject_position(gt.xform(obstacle_vertices[i])),
+ p_camera->unproject_position(gt.xform(obstacle_vertices[(i + 1) % obstacle_vertices.size()]))
};
- Vector2 cp = Geometry2D::get_closest_point_to_segment(gpoint, points);
+ Vector2 cp = Geometry2D::get_closest_point_to_segment(mouse_position, points);
if (cp.distance_squared_to(points[0]) < CMP_EPSILON2 || cp.distance_squared_to(points[1]) < CMP_EPSILON2) {
continue; //not valid to reuse point
}
- real_t d = cp.distance_to(gpoint);
+ real_t d = cp.distance_to(mouse_position);
if (d < closest_dist && d < grab_threshold) {
closest_dist = d;
closest_pos = cp;
@@ -221,26 +572,24 @@ EditorPlugin::AfterGUIInput NavigationObstacle3DEditor::forward_3d_gui_input(Cam
}
if (closest_idx >= 0) {
- pre_move_edit = poly;
- poly.insert(closest_idx + 1, cpoint);
+ pre_move_edit = obstacle_vertices;
+ obstacle_vertices.insert(closest_idx + 1, cpoint);
edited_point = closest_idx + 1;
edited_point_pos = cpoint;
- _set_polygon(poly);
- _polygon_draw();
+ obstacle_node->set_vertices(obstacle_vertices);
+ redraw();
snap_ignore = true;
return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
} else {
- //look for points to move
-
int closest_idx = -1;
Vector2 closest_pos;
real_t closest_dist = 1e10;
- for (int i = 0; i < poly.size(); i++) {
- Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, 0.0, poly[i].y)));
+ for (int i = 0; i < obstacle_vertices.size(); i++) {
+ Vector2 cp = p_camera->unproject_position(gt.xform(obstacle_vertices[i]));
- real_t d = cp.distance_to(gpoint);
+ real_t d = cp.distance_to(mouse_position);
if (d < closest_dist && d < grab_threshold) {
closest_dist = d;
closest_pos = cp;
@@ -249,10 +598,10 @@ EditorPlugin::AfterGUIInput NavigationObstacle3DEditor::forward_3d_gui_input(Cam
}
if (closest_idx >= 0) {
- pre_move_edit = poly;
+ pre_move_edit = obstacle_vertices;
edited_point = closest_idx;
- edited_point_pos = poly[closest_idx];
- _polygon_draw();
+ edited_point_pos = obstacle_vertices[closest_idx];
+ redraw();
snap_ignore = false;
return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
@@ -261,16 +610,13 @@ EditorPlugin::AfterGUIInput NavigationObstacle3DEditor::forward_3d_gui_input(Cam
snap_ignore = false;
if (edited_point != -1) {
- //apply
+ ERR_FAIL_INDEX_V(edited_point, obstacle_vertices.size(), EditorPlugin::AFTER_GUI_INPUT_PASS);
+ obstacle_vertices.write[edited_point] = edited_point_pos;
- ERR_FAIL_INDEX_V(edited_point, poly.size(), EditorPlugin::AFTER_GUI_INPUT_PASS);
- poly.write[edited_point] = edited_point_pos;
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Edit Poly"));
- //undo_redo->add_do_method(obj, "set_polygon", poly);
- //undo_redo->add_undo_method(obj, "set_polygon", pre_move_edit);
- undo_redo->add_do_method(this, "_polygon_draw");
- undo_redo->add_undo_method(this, "_polygon_draw");
+ undo_redo->create_action(TTR("Edit Obstacle (Move Vertex)"));
+ undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_node->get_vertices());
+ undo_redo->add_do_method(obstacle_node, "set_vertices", obstacle_vertices);
undo_redo->commit_action();
edited_point = -1;
@@ -278,30 +624,31 @@ EditorPlugin::AfterGUIInput NavigationObstacle3DEditor::forward_3d_gui_input(Cam
}
}
}
- if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed() && edited_point == -1) {
+
+ } break;
+
+ case MODE_DELETE: {
+ if (mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
int closest_idx = -1;
- Vector2 closest_pos;
real_t closest_dist = 1e10;
- for (int i = 0; i < poly.size(); i++) {
- Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, 0.0, poly[i].y)));
-
- real_t d = cp.distance_to(gpoint);
+ for (int i = 0; i < obstacle_vertices.size(); i++) {
+ Vector2 point = p_camera->unproject_position(gt.xform(obstacle_vertices[i]));
+ real_t d = point.distance_to(mouse_position);
if (d < closest_dist && d < grab_threshold) {
closest_dist = d;
- closest_pos = cp;
closest_idx = i;
}
}
if (closest_idx >= 0) {
+ edited_point = -1;
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Edit Poly (Remove Point)"));
- //undo_redo->add_undo_method(obj, "set_polygon", poly);
- poly.remove_at(closest_idx);
- //undo_redo->add_do_method(obj, "set_polygon", poly);
- undo_redo->add_do_method(this, "_polygon_draw");
- undo_redo->add_undo_method(this, "_polygon_draw");
+ undo_redo->create_action(TTR("Edit Obstacle (Remove Vertex)"));
+ undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_vertices);
+ obstacle_vertices.remove_at(closest_idx);
+ undo_redo->add_do_method(obstacle_node, "set_vertices", obstacle_vertices);
undo_redo->commit_action();
+ redraw();
return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
@@ -314,20 +661,24 @@ EditorPlugin::AfterGUIInput NavigationObstacle3DEditor::forward_3d_gui_input(Cam
if (mm.is_valid()) {
if (edited_point != -1 && (wip_active || mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
- Vector2 gpoint = mm->get_position();
+ Vector2 mouse_position = mm->get_position();
- Vector3 ray_from = p_camera->project_ray_origin(gpoint);
- Vector3 ray_dir = p_camera->project_ray_normal(gpoint);
+ Vector3 ray_from = p_camera->project_ray_origin(mouse_position);
+ Vector3 ray_dir = p_camera->project_ray_normal(mouse_position);
- Vector3 spoint;
+ Transform3D gt = Transform3D(Basis(), obstacle_node->get_global_position());
+ Transform3D gi = gt.affine_inverse();
+ Plane projection_plane(Vector3(0.0, 1.0, 0.0), gt.origin);
- if (!p.intersects_ray(ray_from, ray_dir, &spoint)) {
+ Vector3 intersection_point;
+
+ if (!projection_plane.intersects_ray(ray_from, ray_dir, &intersection_point)) {
return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
- spoint = gi.xform(spoint);
+ intersection_point = gi.xform(intersection_point);
- Vector2 cpoint(spoint.x, spoint.z);
+ Vector2 cpoint(intersection_point.x, intersection_point.z);
if (snap_ignore && !Input::get_singleton()->is_key_pressed(Key::CTRL)) {
snap_ignore = false;
@@ -336,220 +687,217 @@ EditorPlugin::AfterGUIInput NavigationObstacle3DEditor::forward_3d_gui_input(Cam
if (!snap_ignore && Node3DEditor::get_singleton()->is_snap_enabled()) {
cpoint = cpoint.snappedf(Node3DEditor::get_singleton()->get_translate_snap());
}
- edited_point_pos = cpoint;
+ edited_point_pos = Vector3(cpoint.x, 0.0, cpoint.y);
- _polygon_draw();
+ redraw();
}
}
- return EditorPlugin::AFTER_GUI_INPUT_PASS;
-}
+ Ref<InputEventKey> k = p_event;
-PackedVector2Array NavigationObstacle3DEditor::_get_polygon() {
- ERR_FAIL_NULL_V_MSG(obstacle_node, PackedVector2Array(), "Edited object is not valid.");
- return PackedVector2Array(obstacle_node->call("get_polygon"));
-}
+ if (k.is_valid() && k->is_pressed()) {
+ if (wip_active && k->get_keycode() == Key::ENTER) {
+ _wip_close();
+ } else if (wip_active && k->get_keycode() == Key::ESCAPE) {
+ _wip_cancel();
+ }
+ }
-void NavigationObstacle3DEditor::_set_polygon(const PackedVector2Array &p_poly) {
- ERR_FAIL_NULL_MSG(obstacle_node, "Edited object is not valid.");
- obstacle_node->call("set_polygon", p_poly);
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
-void NavigationObstacle3DEditor::_polygon_draw() {
+void NavigationObstacle3DEditorPlugin::redraw() {
if (!obstacle_node) {
return;
}
+ RenderingServer *rs = RenderingServer::get_singleton();
+
+ rs->mesh_clear(point_lines_mesh_rid);
+ rs->mesh_clear(point_handle_mesh_rid);
- PackedVector2Array poly;
- PackedVector3Array polygon_3d_vertices;
+ if (!obstacle_node->is_visible_in_tree()) {
+ return;
+ }
+
+ Vector<Vector3> edited_vertices;
if (wip_active) {
- poly = wip;
+ edited_vertices = wip_vertices;
} else {
- poly = _get_polygon();
+ edited_vertices = obstacle_node->get_vertices();
}
- polygon_3d_vertices.resize(poly.size());
- Vector3 *polygon_3d_vertices_ptr = polygon_3d_vertices.ptrw();
- for (int i = 0; i < poly.size(); i++) {
- const Vector2 &vert = poly[i];
- polygon_3d_vertices_ptr[i] = Vector3(vert.x, 0.0, vert.y);
+ if (edited_vertices.is_empty()) {
+ return;
}
- point_handle_mesh->clear_surfaces();
- point_lines_mesh->clear_surfaces();
- point_lines_meshinstance->set_material_override(line_material);
+ Array point_lines_mesh_array;
+ point_lines_mesh_array.resize(Mesh::ARRAY_MAX);
- if (poly.is_empty()) {
- return;
- }
+ Vector<Vector3> point_lines_mesh_vertices;
+ point_lines_mesh_vertices.resize(edited_vertices.size() * 2);
+ Vector3 *point_lines_mesh_vertices_ptr = point_lines_mesh_vertices.ptrw();
- point_lines_mesh->surface_begin(Mesh::PRIMITIVE_LINES);
+ int vertex_index = 0;
- for (int i = 0; i < poly.size(); i++) {
- Vector2 p, p2;
+ for (int i = 0; i < edited_vertices.size(); i++) {
+ Vector3 point, next_point;
if (i == edited_point) {
- p = edited_point_pos;
+ point = edited_point_pos;
} else {
- p = poly[i];
+ point = edited_vertices[i];
}
- if ((wip_active && i == poly.size() - 1) || (((i + 1) % poly.size()) == edited_point)) {
- p2 = edited_point_pos;
+ if ((wip_active && i == edited_vertices.size() - 1) || (((i + 1) % edited_vertices.size()) == edited_point)) {
+ next_point = edited_point_pos;
} else {
- p2 = poly[(i + 1) % poly.size()];
+ next_point = edited_vertices[(i + 1) % edited_vertices.size()];
}
- Vector3 point = Vector3(p.x, 0.0, p.y);
- Vector3 next_point = Vector3(p2.x, 0.0, p2.y);
-
- point_lines_mesh->surface_set_color(Color(1, 0.3, 0.1, 0.8));
- point_lines_mesh->surface_add_vertex(point);
- point_lines_mesh->surface_set_color(Color(1, 0.3, 0.1, 0.8));
- point_lines_mesh->surface_add_vertex(next_point);
-
- //Color col=Color(1,0.3,0.1,0.8);
- //vpc->draw_line(point,next_point,col,2);
- //vpc->draw_texture(handle,point-handle->get_size()*0.5);
+ point_lines_mesh_vertices_ptr[vertex_index++] = point;
+ point_lines_mesh_vertices_ptr[vertex_index++] = next_point;
}
- point_lines_mesh->surface_end();
+ point_lines_mesh_array[Mesh::ARRAY_VERTEX] = point_lines_mesh_vertices;
+
+ rs->mesh_add_surface_from_arrays(point_lines_mesh_rid, RS::PRIMITIVE_LINES, point_lines_mesh_array);
+ rs->instance_set_surface_override_material(point_lines_instance_rid, 0, line_material->get_rid());
+ rs->instance_set_transform(point_lines_instance_rid, Transform3D(Basis(), obstacle_node->get_global_position()));
Array point_handle_mesh_array;
point_handle_mesh_array.resize(Mesh::ARRAY_MAX);
Vector<Vector3> point_handle_mesh_vertices;
- point_handle_mesh_vertices.resize(poly.size());
+ point_handle_mesh_vertices.resize(edited_vertices.size());
Vector3 *point_handle_mesh_vertices_ptr = point_handle_mesh_vertices.ptrw();
- for (int i = 0; i < poly.size(); i++) {
- Vector2 point_2d;
- Vector2 p2;
+ for (int i = 0; i < edited_vertices.size(); i++) {
+ Vector3 point_handle_3d;
if (i == edited_point) {
- point_2d = edited_point_pos;
+ point_handle_3d = edited_point_pos;
} else {
- point_2d = poly[i];
+ point_handle_3d = edited_vertices[i];
}
- Vector3 point_handle_3d = Vector3(point_2d.x, 0.0, point_2d.y);
point_handle_mesh_vertices_ptr[i] = point_handle_3d;
}
point_handle_mesh_array[Mesh::ARRAY_VERTEX] = point_handle_mesh_vertices;
- point_handle_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_POINTS, point_handle_mesh_array);
- point_handle_mesh->surface_set_material(0, handle_material);
-}
-
-void NavigationObstacle3DEditor::edit(Node *p_node) {
- obstacle_node = Object::cast_to<NavigationObstacle3D>(p_node);
- if (obstacle_node) {
- //Enable the pencil tool if the polygon is empty
- if (_get_polygon().is_empty()) {
- _menu_option(MODE_CREATE);
- }
- wip.clear();
- wip_active = false;
- edited_point = -1;
- if (point_lines_meshinstance->get_parent()) {
- point_lines_meshinstance->reparent(p_node, false);
- } else {
- p_node->add_child(point_lines_meshinstance);
- }
- _polygon_draw();
-
- } else {
- obstacle_node = nullptr;
-
- if (point_lines_meshinstance->get_parent()) {
- point_lines_meshinstance->get_parent()->remove_child(point_lines_meshinstance);
- }
- }
+ rs->mesh_add_surface_from_arrays(point_handle_mesh_rid, RS::PRIMITIVE_POINTS, point_handle_mesh_array);
+ rs->instance_set_surface_override_material(point_handles_instance_rid, 0, handle_material->get_rid());
+ rs->instance_set_transform(point_handles_instance_rid, Transform3D(Basis(), obstacle_node->get_global_position()));
}
-void NavigationObstacle3DEditor::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_polygon_draw"), &NavigationObstacle3DEditor::_polygon_draw);
-}
-
-NavigationObstacle3DEditor::NavigationObstacle3DEditor() {
- obstacle_node = nullptr;
-
- button_create = memnew(Button);
- button_create->set_theme_type_variation("FlatButton");
- add_child(button_create);
- button_create->connect(SceneStringName(pressed), callable_mp(this, &NavigationObstacle3DEditor::_menu_option).bind(MODE_CREATE));
- button_create->set_toggle_mode(true);
-
- button_edit = memnew(Button);
- button_edit->set_theme_type_variation("FlatButton");
- add_child(button_edit);
- button_edit->connect(SceneStringName(pressed), callable_mp(this, &NavigationObstacle3DEditor::_menu_option).bind(MODE_EDIT));
- button_edit->set_toggle_mode(true);
+NavigationObstacle3DEditorPlugin *NavigationObstacle3DEditorPlugin::singleton = nullptr;
- mode = MODE_EDIT;
- wip_active = false;
- point_lines_meshinstance = memnew(MeshInstance3D);
- point_lines_mesh.instantiate();
- point_lines_meshinstance->set_mesh(point_lines_mesh);
- point_lines_meshinstance->set_transform(Transform3D(Basis(), Vector3(0, 0, 0.00001)));
- point_lines_meshinstance->set_as_top_level(true);
+NavigationObstacle3DEditorPlugin::NavigationObstacle3DEditorPlugin() {
+ singleton = this;
- line_material.instantiate();
+ line_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
line_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
- line_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
line_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
line_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
- line_material->set_albedo(Color(1, 1, 1));
+ line_material->set_albedo(Color(1, 0.3, 0.1, 0.8));
+ line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
- handle_material.instantiate();
+ handle_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
handle_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
- handle_material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true);
handle_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
+ handle_material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true);
handle_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
handle_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
handle_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
Ref<Texture2D> handle = EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Editor3DHandle"), EditorStringName(EditorIcons));
handle_material->set_point_size(handle->get_width());
handle_material->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, handle);
+ handle_material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
- point_handles_meshinstance = memnew(MeshInstance3D);
- point_lines_meshinstance->add_child(point_handles_meshinstance);
- point_handle_mesh.instantiate();
- point_handles_meshinstance->set_mesh(point_handle_mesh);
- point_handles_meshinstance->set_transform(Transform3D(Basis(), Vector3(0, 0, 0.00001)));
+ RenderingServer *rs = RenderingServer::get_singleton();
- snap_ignore = false;
-}
+ point_lines_mesh_rid = rs->mesh_create();
+ point_handle_mesh_rid = rs->mesh_create();
-NavigationObstacle3DEditor::~NavigationObstacle3DEditor() {
- memdelete(point_lines_meshinstance);
-}
+ point_lines_instance_rid = rs->instance_create();
+ point_handles_instance_rid = rs->instance_create();
-void NavigationObstacle3DEditorPlugin::edit(Object *p_object) {
- obstacle_editor->edit(Object::cast_to<Node>(p_object));
-}
+ rs->instance_set_base(point_lines_instance_rid, point_lines_mesh_rid);
+ rs->instance_set_base(point_handles_instance_rid, point_handle_mesh_rid);
-bool NavigationObstacle3DEditorPlugin::handles(Object *p_object) const {
- return Object::cast_to<NavigationObstacle3D>(p_object);
-}
+ obstacle_editor = memnew(HBoxContainer);
+ obstacle_editor->hide();
-void NavigationObstacle3DEditorPlugin::make_visible(bool p_visible) {
- if (p_visible) {
- obstacle_editor->show();
- } else {
- obstacle_editor->hide();
- obstacle_editor->edit(nullptr);
- }
-}
+ Ref<ButtonGroup> bg;
+ bg.instantiate();
+
+ button_create = memnew(Button);
+ button_create->set_theme_type_variation("FlatButton");
+ obstacle_editor->add_child(button_create);
+ button_create->set_tooltip_text(TTR("Add Vertex"));
+ button_create->connect(SceneStringName(pressed), callable_mp(this, &NavigationObstacle3DEditorPlugin::set_mode).bind(NavigationObstacle3DEditorPlugin::MODE_CREATE));
+ button_create->set_toggle_mode(true);
+ button_create->set_button_group(bg);
+
+ button_edit = memnew(Button);
+ button_edit->set_theme_type_variation("FlatButton");
+ obstacle_editor->add_child(button_edit);
+ button_edit->connect(SceneStringName(pressed), callable_mp(this, &NavigationObstacle3DEditorPlugin::set_mode).bind(NavigationObstacle3DEditorPlugin::MODE_EDIT));
+ button_edit->set_toggle_mode(true);
+ button_edit->set_button_group(bg);
+
+ button_delete = memnew(Button);
+ button_delete->set_theme_type_variation("FlatButton");
+ obstacle_editor->add_child(button_delete);
+ button_delete->connect(SceneStringName(pressed), callable_mp(this, &NavigationObstacle3DEditorPlugin::set_mode).bind(NavigationObstacle3DEditorPlugin::MODE_DELETE));
+ button_delete->set_toggle_mode(true);
+ button_delete->set_button_group(bg);
+
+ button_flip = memnew(Button);
+ button_flip->set_theme_type_variation("FlatButton");
+ obstacle_editor->add_child(button_flip);
+ button_flip->connect(SceneStringName(pressed), callable_mp(this, &NavigationObstacle3DEditorPlugin::set_mode).bind(NavigationObstacle3DEditorPlugin::ACTION_FLIP));
+ button_flip->set_toggle_mode(true);
+
+ button_clear = memnew(Button);
+ button_clear->set_theme_type_variation("FlatButton");
+ obstacle_editor->add_child(button_clear);
+ button_clear->connect(SceneStringName(pressed), callable_mp(this, &NavigationObstacle3DEditorPlugin::set_mode).bind(NavigationObstacle3DEditorPlugin::ACTION_CLEAR));
+ button_clear->set_toggle_mode(true);
+
+ button_clear_dialog = memnew(ConfirmationDialog);
+ button_clear_dialog->set_title(TTR("Please Confirm..."));
+ button_clear_dialog->set_text(TTR("Remove all vertices?"));
+ button_clear_dialog->connect(SceneStringName(confirmed), callable_mp(NavigationObstacle3DEditorPlugin::singleton, &NavigationObstacle3DEditorPlugin::action_clear_vertices));
+ obstacle_editor->add_child(button_clear_dialog);
-NavigationObstacle3DEditorPlugin::NavigationObstacle3DEditorPlugin() {
- obstacle_editor = memnew(NavigationObstacle3DEditor);
Node3DEditor::get_singleton()->add_control_to_menu_panel(obstacle_editor);
- obstacle_editor->hide();
+ Ref<NavigationObstacle3DGizmoPlugin> gizmo_plugin = memnew(NavigationObstacle3DGizmoPlugin());
+ obstacle_3d_gizmo_plugin = gizmo_plugin;
+ Node3DEditor::get_singleton()->add_gizmo_plugin(gizmo_plugin);
}
NavigationObstacle3DEditorPlugin::~NavigationObstacle3DEditorPlugin() {
+ RenderingServer *rs = RenderingServer::get_singleton();
+ ERR_FAIL_NULL(rs);
+
+ if (point_lines_instance_rid.is_valid()) {
+ rs->free(point_lines_instance_rid);
+ point_lines_instance_rid = RID();
+ }
+ if (point_lines_mesh_rid.is_valid()) {
+ rs->free(point_lines_mesh_rid);
+ point_lines_mesh_rid = RID();
+ }
+
+ if (point_handles_instance_rid.is_valid()) {
+ rs->free(point_handles_instance_rid);
+ point_handles_instance_rid = RID();
+ }
+ if (point_handle_mesh_rid.is_valid()) {
+ rs->free(point_handle_mesh_rid);
+ point_handle_mesh_rid = RID();
+ }
}
diff --git a/editor/plugins/navigation_obstacle_3d_editor_plugin.h b/editor/plugins/navigation_obstacle_3d_editor_plugin.h
index c62a5a281b..b6f3a11cf6 100644
--- a/editor/plugins/navigation_obstacle_3d_editor_plugin.h
+++ b/editor/plugins/navigation_obstacle_3d_editor_plugin.h
@@ -32,79 +32,99 @@
#define NAVIGATION_OBSTACLE_3D_EDITOR_PLUGIN_H
#include "editor/plugins/editor_plugin.h"
-#include "scene/3d/mesh_instance_3d.h"
-#include "scene/3d/physics/collision_polygon_3d.h"
+#include "editor/plugins/node_3d_editor_gizmos.h"
#include "scene/gui/box_container.h"
-#include "scene/resources/immediate_mesh.h"
-#include "scene/3d/navigation_obstacle_3d.h"
+class Button;
+class ConfirmationDialog;
+class NavigationObstacle3D;
-class CanvasItemEditor;
-class MenuButton;
+class NavigationObstacle3DGizmoPlugin : public EditorNode3DGizmoPlugin {
+ GDCLASS(NavigationObstacle3DGizmoPlugin, EditorNode3DGizmoPlugin);
-class NavigationObstacle3DEditor : public HBoxContainer {
- GDCLASS(NavigationObstacle3DEditor, HBoxContainer);
+public:
+ virtual bool has_gizmo(Node3D *p_spatial) override;
+ virtual String get_gizmo_name() const override;
- enum Mode {
- MODE_CREATE,
- MODE_EDIT,
+ virtual void redraw(EditorNode3DGizmo *p_gizmo) override;
- };
+ bool can_be_hidden() const override;
+ int get_priority() const override;
- Mode mode;
+ virtual int subgizmos_intersect_ray(const EditorNode3DGizmo *p_gizmo, Camera3D *p_camera, const Vector2 &p_point) const override;
+ virtual Vector<int> subgizmos_intersect_frustum(const EditorNode3DGizmo *p_gizmo, const Camera3D *p_camera, const Vector<Plane> &p_frustum) const override;
+ virtual Transform3D get_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id) const override;
+ virtual void set_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id, Transform3D p_transform) override;
+ virtual void commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel = false) override;
- Button *button_create = nullptr;
- Button *button_edit = nullptr;
+ NavigationObstacle3DGizmoPlugin();
+};
+
+class NavigationObstacle3DEditorPlugin : public EditorPlugin {
+ GDCLASS(NavigationObstacle3DEditorPlugin, EditorPlugin);
+
+ Ref<NavigationObstacle3DGizmoPlugin> obstacle_3d_gizmo_plugin;
+
+ NavigationObstacle3D *obstacle_node = nullptr;
Ref<StandardMaterial3D> line_material;
Ref<StandardMaterial3D> handle_material;
- Panel *panel = nullptr;
- NavigationObstacle3D *obstacle_node = nullptr;
- Ref<ImmediateMesh> point_lines_mesh;
- MeshInstance3D *point_lines_meshinstance = nullptr;
- MeshInstance3D *point_handles_meshinstance = nullptr;
- Ref<ArrayMesh> point_handle_mesh;
+ RID point_lines_mesh_rid;
+ RID point_lines_instance_rid;
+ RID point_handle_mesh_rid;
+ RID point_handles_instance_rid;
- MenuButton *options = nullptr;
+public:
+ enum Mode {
+ MODE_CREATE = 0,
+ MODE_EDIT,
+ MODE_DELETE,
+ ACTION_FLIP,
+ ACTION_CLEAR,
+ };
- int edited_point = 0;
- Vector2 edited_point_pos;
- PackedVector2Array pre_move_edit;
- PackedVector2Array wip;
- bool wip_active;
- bool snap_ignore;
+private:
+ int mode = MODE_EDIT;
- float prev_depth = 0.0f;
+ int edited_point = 0;
+ Vector3 edited_point_pos;
+ Vector<Vector3> pre_move_edit;
+ Vector<Vector3> wip_vertices;
+ bool wip_active = false;
+ bool snap_ignore = false;
void _wip_close();
- void _polygon_draw();
- void _menu_option(int p_option);
+ void _wip_cancel();
+ void _update_theme();
+
+ Button *button_create = nullptr;
+ Button *button_edit = nullptr;
+ Button *button_delete = nullptr;
+ Button *button_flip = nullptr;
+ Button *button_clear = nullptr;
- PackedVector2Array _get_polygon();
- void _set_polygon(const PackedVector2Array &p_poly);
+ ConfirmationDialog *button_clear_dialog = nullptr;
protected:
void _notification(int p_what);
void _node_removed(Node *p_node);
- static void _bind_methods();
public:
- virtual EditorPlugin::AfterGUIInput forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event);
- void edit(Node *p_node);
- NavigationObstacle3DEditor();
- ~NavigationObstacle3DEditor();
-};
+ HBoxContainer *obstacle_editor = nullptr;
+ static NavigationObstacle3DEditorPlugin *singleton;
-class NavigationObstacle3DEditorPlugin : public EditorPlugin {
- GDCLASS(NavigationObstacle3DEditorPlugin, EditorPlugin);
+ void redraw();
- NavigationObstacle3DEditor *obstacle_editor = nullptr;
+ void set_mode(int p_mode);
+ int get_mode() { return mode; }
-public:
- virtual EditorPlugin::AfterGUIInput forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override { return obstacle_editor->forward_3d_gui_input(p_camera, p_event); }
+ void action_flip_vertices();
+ void action_clear_vertices();
+
+ virtual EditorPlugin::AfterGUIInput forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override;
- virtual String get_name() const override { return "NavigationObstacle3DEditor"; }
+ virtual String get_name() const override { return "NavigationObstacle3D"; }
bool has_main_screen() const override { return false; }
virtual void edit(Object *p_object) override;
virtual bool handles(Object *p_object) const override;
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 2edc096382..8c3979918d 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -2185,8 +2185,6 @@ void ScriptEditor::_update_script_colors() {
continue;
}
- script_list->set_item_custom_bg_color(i, Color(0, 0, 0, 0));
-
if (script_temperature_enabled) {
int pass = n->get_meta("__editor_pass", -1);
if (pass < 0) {
@@ -2212,7 +2210,7 @@ void ScriptEditor::_update_script_names() {
HashSet<Ref<Script>> used;
Node *edited = EditorNode::get_singleton()->get_edited_scene();
- if (edited) {
+ if (edited && EDITOR_GET("text_editor/script_list/highlight_scene_scripts")) {
_find_scripts(edited, edited, used);
}
@@ -2382,7 +2380,7 @@ void ScriptEditor::_update_script_names() {
script_list->set_item_tooltip(index, sedata_filtered[i].tooltip);
script_list->set_item_metadata(index, sedata_filtered[i].index); /* Saving as metadata the script's index in the tab container and not the filtered one */
if (sedata_filtered[i].used) {
- script_list->set_item_custom_bg_color(index, Color(88 / 255.0, 88 / 255.0, 60 / 255.0));
+ script_list->set_item_custom_bg_color(index, Color(.5, .5, .5, .125));
}
if (tab_container->get_current_tab() == sedata_filtered[i].index) {
script_list->select(index);
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index 34e24c1424..941d44c85e 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -1699,7 +1699,7 @@ void TileSetAtlasSourceEditor::_menu_option(int p_option) {
void TileSetAtlasSourceEditor::shortcut_input(const Ref<InputEvent> &p_event) {
// Check for shortcuts.
- if (ED_IS_SHORTCUT("tiles_editor/delete_tile", p_event)) {
+ if (ED_IS_SHORTCUT("tiles_editor/delete", p_event)) {
if (tools_button_group->get_pressed_button() == tool_select_button && !selection.is_empty()) {
_menu_option(TILE_DELETE);
accept_event();
@@ -2711,7 +2711,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tile_atlas_control_unscaled->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
alternative_tile_popup_menu = memnew(PopupMenu);
- alternative_tile_popup_menu->add_shortcut(ED_SHORTCUT("tiles_editor/delete_tile", TTR("Delete"), Key::KEY_DELETE), TILE_DELETE);
+ alternative_tile_popup_menu->add_shortcut(ED_GET_SHORTCUT("tiles_editor/delete"), TILE_DELETE);
alternative_tile_popup_menu->connect(SceneStringName(id_pressed), callable_mp(this, &TileSetAtlasSourceEditor::_menu_option));
tile_atlas_view->add_child(alternative_tile_popup_menu);