diff options
Diffstat (limited to 'editor/plugins')
98 files changed, 4440 insertions, 1976 deletions
diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp index 22e0a3dabb..ad6d8e6379 100644 --- a/editor/plugins/abstract_polygon_2d_editor.cpp +++ b/editor/plugins/abstract_polygon_2d_editor.cpp @@ -703,10 +703,8 @@ AbstractPolygon2DEditor::PosVertex AbstractPolygon2DEditor::closest_edge_point(c } AbstractPolygon2DEditor::AbstractPolygon2DEditor(bool p_wip_destructive) { - canvas_item_editor = nullptr; undo_redo = EditorNode::get_undo_redo(); - wip_active = false; edited_point = PosVertex(); wip_destructive = p_wip_destructive; @@ -736,8 +734,6 @@ AbstractPolygon2DEditor::AbstractPolygon2DEditor(bool p_wip_destructive) { create_resource = memnew(ConfirmationDialog); add_child(create_resource); create_resource->get_ok_button()->set_text(TTR("Create")); - - mode = MODE_EDIT; } void AbstractPolygon2DEditorPlugin::edit(Object *p_object) { diff --git a/editor/plugins/abstract_polygon_2d_editor.h b/editor/plugins/abstract_polygon_2d_editor.h index b0483cbb62..696fd7b637 100644 --- a/editor/plugins/abstract_polygon_2d_editor.h +++ b/editor/plugins/abstract_polygon_2d_editor.h @@ -80,10 +80,10 @@ class AbstractPolygon2DEditor : public HBoxContainer { Vector<Vector2> pre_move_edit; Vector<Vector2> wip; - bool wip_active; - bool wip_destructive; + bool wip_active = false; + bool wip_destructive = false; - bool _polygon_editing_enabled; + bool _polygon_editing_enabled = false; CanvasItemEditor *canvas_item_editor = nullptr; Panel *panel = nullptr; @@ -97,7 +97,7 @@ protected: MODE_CONT, }; - int mode; + int mode = MODE_EDIT; UndoRedo *undo_redo = nullptr; diff --git a/editor/plugins/animation_blend_space_1d_editor.cpp b/editor/plugins/animation_blend_space_1d_editor.cpp index 94882b3464..2e4dcab203 100644 --- a/editor/plugins/animation_blend_space_1d_editor.cpp +++ b/editor/plugins/animation_blend_space_1d_editor.cpp @@ -83,7 +83,7 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven for (const StringName &E : classes) { String name = String(E).replace_first("AnimationNode", ""); - if (name == "Animation") { + if (name == "Animation" || name == "StartState" || name == "EndState") { continue; } @@ -594,7 +594,6 @@ AnimationNodeBlendSpace1DEditor *AnimationNodeBlendSpace1DEditor::singleton = nu AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() { singleton = this; - updating = false; HBoxContainer *top_hb = memnew(HBoxContainer); add_child(top_hb); @@ -745,9 +744,5 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() { open_file->connect("file_selected", callable_mp(this, &AnimationNodeBlendSpace1DEditor::_file_opened)); undo_redo = EditorNode::get_undo_redo(); - selected_point = -1; - dragging_selected = false; - dragging_selected_attempt = false; - set_custom_minimum_size(Size2(0, 150 * EDSCALE)); } diff --git a/editor/plugins/animation_blend_space_1d_editor.h b/editor/plugins/animation_blend_space_1d_editor.h index 816c2555ca..2f7dee65fc 100644 --- a/editor/plugins/animation_blend_space_1d_editor.h +++ b/editor/plugins/animation_blend_space_1d_editor.h @@ -65,14 +65,14 @@ class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin { SpinBox *edit_value = nullptr; Button *open_editor = nullptr; - int selected_point; + int selected_point = -1; Control *blend_space_draw = nullptr; PanelContainer *error_panel = nullptr; Label *error_label = nullptr; - bool updating; + bool updating = false; UndoRedo *undo_redo = nullptr; @@ -90,11 +90,11 @@ class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin { PopupMenu *menu = nullptr; PopupMenu *animations_menu = nullptr; Vector<String> animations_to_add; - float add_point_pos; + float add_point_pos = 0.0f; Vector<real_t> points; - bool dragging_selected_attempt; - bool dragging_selected; + bool dragging_selected_attempt = false; + bool dragging_selected = false; Vector2 drag_from; Vector2 drag_ofs; diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp index 4b7df75aec..86addde87b 100644 --- a/editor/plugins/animation_blend_space_2d_editor.cpp +++ b/editor/plugins/animation_blend_space_2d_editor.cpp @@ -107,7 +107,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven for (const StringName &E : classes) { String name = String(E).replace_first("AnimationNode", ""); - if (name == "Animation") { + if (name == "Animation" || name == "StartState" || name == "EndState") { continue; // nope } int idx = menu->get_item_count(); diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index 0b3164aada..5e703cf814 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -264,6 +264,8 @@ void AnimationNodeBlendTreeEditor::_update_graph() { float graph_minimap_opacity = EditorSettings::get_singleton()->get("editors/visual_editors/minimap_opacity"); graph->set_minimap_opacity(graph_minimap_opacity); + float graph_lines_curvature = EditorSettings::get_singleton()->get("editors/visual_editors/lines_curvature"); + graph->set_connection_lines_curvature(graph_lines_curvature); } void AnimationNodeBlendTreeEditor::_file_opened(const String &p_file) { @@ -459,16 +461,22 @@ void AnimationNodeBlendTreeEditor::_delete_request(const String &p_which) { undo_redo->commit_action(); } -void AnimationNodeBlendTreeEditor::_delete_nodes_request() { +void AnimationNodeBlendTreeEditor::_delete_nodes_request(const TypedArray<StringName> &p_nodes) { List<StringName> to_erase; - for (int i = 0; i < graph->get_child_count(); i++) { - GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i)); - if (gn) { - if (gn->is_selected() && gn->is_close_button_visible()) { - to_erase.push_back(gn->get_name()); + if (p_nodes.is_empty()) { + for (int i = 0; i < graph->get_child_count(); i++) { + GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i)); + if (gn) { + if (gn->is_selected() && gn->is_close_button_visible()) { + to_erase.push_back(gn->get_name()); + } } } + } else { + for (int i = 0; i < p_nodes.size(); i++) { + to_erase.push_back(p_nodes[i]); + } } if (to_erase.is_empty()) { @@ -557,8 +565,8 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref<AnimationNode> &ano updating = true; - Set<String> paths; - HashMap<String, Set<String>> types; + HashSet<String> paths; + HashMap<String, RBSet<String>> types; { List<StringName> animations; player->get_animation_list(&animations); @@ -595,10 +603,10 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref<AnimationNode> &ano filters->clear(); TreeItem *root = filters->create_item(); - Map<String, TreeItem *> parenthood; + HashMap<String, TreeItem *> parenthood; - for (Set<String>::Element *E = paths.front(); E; E = E->next()) { - NodePath path = E->get(); + for (const String &E : paths) { + NodePath path = E; TreeItem *ti = nullptr; String accum; for (int i = 0; i < path.get_name_count(); i++) { @@ -692,11 +700,12 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref<AnimationNode> &ano //just a node, not a property track String types_text = "["; if (types.has(path)) { - Set<String>::Element *F = types[path].front(); - types_text += F->get(); - while (F->next()) { - F = F->next(); - types_text += " / " + F->get(); + RBSet<String>::Iterator F = types[path].begin(); + types_text += *F; + while (F) { + types_text += " / " + *F; + ; + ++F; } } types_text += "]"; @@ -903,8 +912,8 @@ void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<Anima } //update animations - for (Map<StringName, ProgressBar *>::Element *E = animations.front(); E; E = E->next()) { - if (E->key() == prev_name) { + for (const KeyValue<StringName, ProgressBar *> &E : animations) { + if (E.key == prev_name) { animations[new_name] = animations[prev_name]; animations.erase(prev_name); break; @@ -962,6 +971,8 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { 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); + float graph_lines_curvature = EditorSettings::get_singleton()->get("editors/visual_editors/lines_curvature"); + graph->set_connection_lines_curvature(graph_lines_curvature); VSeparator *vs = memnew(VSeparator); graph->get_zoom_hbox()->add_child(vs); diff --git a/editor/plugins/animation_blend_tree_editor_plugin.h b/editor/plugins/animation_blend_tree_editor_plugin.h index cacf8379f9..b5bf91a1da 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.h +++ b/editor/plugins/animation_blend_tree_editor_plugin.h @@ -61,7 +61,7 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { Tree *filters = nullptr; CheckBox *filter_enabled = nullptr; - Map<StringName, ProgressBar *> animations; + HashMap<StringName, ProgressBar *> animations; Vector<EditorProperty *> visible_properties; String to_node = ""; @@ -103,7 +103,7 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { void _open_in_editor(const String &p_which); 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 _delete_nodes_request(const TypedArray<StringName> &p_nodes); bool _update_filters(const Ref<AnimationNode> &anode); void _edit_filters(const String &p_which); diff --git a/editor/plugins/animation_library_editor.cpp b/editor/plugins/animation_library_editor.cpp index 2e9a82a7c2..cae33edecb 100644 --- a/editor/plugins/animation_library_editor.cpp +++ b/editor/plugins/animation_library_editor.cpp @@ -55,17 +55,15 @@ void AnimationLibraryEditor::_add_library_validate(const String &p_name) { ERR_FAIL_COND(al.is_null()); if (p_name == "") { error = TTR("Animation name can't be empty."); - - } else if (String(p_name).contains("/") || String(p_name).contains(":") || String(p_name).contains(",") || String(p_name).contains("[")) { + } else if (!AnimationLibrary::is_valid_animation_name(p_name)) { error = TTR("Animation name contains invalid characters: '/', ':', ',' or '['."); } else if (al->has_animation(p_name)) { error = TTR("Animation with the same name already exists."); } - } else { if (p_name == "" && bool(player->call("has_animation_library", ""))) { error = TTR("Enter a library name."); - } else if (String(p_name).contains("/") || String(p_name).contains(":") || String(p_name).contains(",") || String(p_name).contains("[")) { + } else if (!AnimationLibrary::is_valid_library_name(p_name)) { error = TTR("Library name contains invalid characters: '/', ':', ',' or '['."); } else if (bool(player->call("has_animation_library", p_name))) { error = TTR("Library with the same name already exists."); @@ -77,12 +75,16 @@ void AnimationLibraryEditor::_add_library_validate(const String &p_name) { add_library_validate->set_text(error); add_library_dialog->get_ok_button()->set_disabled(true); } else { - add_library_validate->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor"))); - if (p_name == "") { - add_library_validate->set_text(TTR("Global library will be created.")); + if (adding_animation) { + add_library_validate->set_text(TTR("Animation name is valid.")); } else { - add_library_validate->set_text(TTR("Library name is valid.")); + if (p_name == "") { + add_library_validate->set_text(TTR("Global library will be created.")); + } else { + add_library_validate->set_text(TTR("Library name is valid.")); + } } + add_library_validate->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor"))); add_library_dialog->get_ok_button()->set_disabled(false); } } @@ -258,7 +260,7 @@ void AnimationLibraryEditor::_load_file(String p_path) { } } - String name = p_path.get_file().get_basename(); + String name = AnimationLibrary::validate_library_name(p_path.get_file().get_basename()); int attempt = 1; @@ -417,12 +419,12 @@ void AnimationLibraryEditor::_item_renamed() { } } -void AnimationLibraryEditor::_button_pressed(TreeItem *p_item, int p_column, int p_button) { +void AnimationLibraryEditor::_button_pressed(TreeItem *p_item, int p_column, int p_id, MouseButton p_button) { if (p_item->get_parent() == tree->get_root()) { // Library StringName lib_name = p_item->get_metadata(0); Ref<AnimationLibrary> al = player->call("get_animation_library", lib_name); - switch (p_button) { + switch (p_id) { case LIB_BUTTON_ADD: { add_library_dialog->set_title(TTR("Animation Name:")); add_library_name->set_text(""); @@ -471,7 +473,7 @@ void AnimationLibraryEditor::_button_pressed(TreeItem *p_item, int p_column, int int attempt = 1; while (al->has_animation(name)) { attempt++; - name = base_name + " " + itos(attempt); + name = base_name + " (" + itos(attempt) + ")"; } UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); @@ -517,7 +519,7 @@ void AnimationLibraryEditor::_button_pressed(TreeItem *p_item, int p_column, int Ref<AnimationLibrary> al = player->call("get_animation_library", lib_name); Ref<Animation> anim = al->get_animation(anim_name); ERR_FAIL_COND(!anim.is_valid()); - switch (p_button) { + switch (p_id) { case ANIM_BUTTON_COPY: { if (anim->get_name() == "") { anim->set_name(anim_name); // Keep the name around @@ -675,7 +677,7 @@ AnimationLibraryEditor::AnimationLibraryEditor() { tree->set_v_size_flags(Control::SIZE_EXPAND_FILL); tree->connect("item_edited", callable_mp(this, &AnimationLibraryEditor::_item_renamed)); - tree->connect("button_pressed", callable_mp(this, &AnimationLibraryEditor::_button_pressed)); + tree->connect("button_clicked", callable_mp(this, &AnimationLibraryEditor::_button_pressed)); file_popup = memnew(PopupMenu); add_child(file_popup); diff --git a/editor/plugins/animation_library_editor.h b/editor/plugins/animation_library_editor.h index 5bd4e8d9e2..bf89508321 100644 --- a/editor/plugins/animation_library_editor.h +++ b/editor/plugins/animation_library_editor.h @@ -99,7 +99,7 @@ class AnimationLibraryEditor : public AcceptDialog { void _load_file(String p_path); void _item_renamed(); - void _button_pressed(TreeItem *p_item, int p_column, int p_button); + void _button_pressed(TreeItem *p_item, int p_column, int p_id, MouseButton p_button); void _file_popup_selected(int p_id); diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 17a1bd1048..98ccc1fdbe 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -303,17 +303,23 @@ void AnimationPlayerEditor::_animation_selected(int p_which) { } void AnimationPlayerEditor::_animation_new() { - name_dialog_op = TOOL_NEW_ANIM; - name_title->set_text(TTR("New Animation Name:")); - int count = 1; String base = TTR("New Anim"); + String current_library_name = ""; + if (animation->has_selectable_items()) { + String current_animation_name = animation->get_item_text(animation->get_selected()); + Ref<Animation> current_animation = player->get_animation(current_animation_name); + if (current_animation.is_valid()) { + current_library_name = player->find_animation_library(current_animation); + } + } + String attempt_prefix = (current_library_name == "") ? "" : current_library_name + "/"; while (true) { String attempt = base; if (count > 1) { attempt += " (" + itos(count) + ")"; } - if (player->has_animation(attempt)) { + if (player->has_animation(attempt_prefix + attempt)) { count++; continue; } @@ -321,22 +327,13 @@ void AnimationPlayerEditor::_animation_new() { break; } - List<StringName> libraries; - player->get_animation_library_list(&libraries); - library->clear(); - for (const StringName &K : libraries) { - library->add_item((K == StringName()) ? String(TTR("[Global]")) : String(K)); - library->set_item_metadata(0, String(K)); - } + _update_name_dialog_library_dropdown(); - if (libraries.size() > 1) { - library->show(); - } else { - library->hide(); - } - - name->set_text(base); + name_dialog_op = TOOL_NEW_ANIM; + name_dialog->set_title(TTR("Create New Animation")); name_dialog->popup_centered(Size2(300, 90)); + name_title->set_text(TTR("New Animation Name:")); + name->set_text(base); name->select_all(); name->grab_focus(); } @@ -348,6 +345,12 @@ void AnimationPlayerEditor::_animation_rename() { int selected = animation->get_selected(); String selected_name = animation->get_item_text(selected); + // Remove library prefix if present. + if (selected_name.contains("/")) { + selected_name = selected_name.get_slice("/", 1); + } + + name_dialog->set_title(TTR("Rename Animation")); name_title->set_text(TTR("Change Animation Name:")); name->set_text(selected_name); name_dialog_op = TOOL_RENAME_ANIM; @@ -375,6 +378,10 @@ void AnimationPlayerEditor::_animation_remove_confirmed() { Ref<AnimationLibrary> al = player->get_animation_library(player->find_animation_library(anim)); ERR_FAIL_COND(al.is_null()); + // For names of form lib_name/anim_name, remove library name prefix. + if (current.contains("/")) { + current = current.get_slice("/", 1); + } undo_redo->create_action(TTR("Remove Animation")); if (player->get_autoplay() == current) { undo_redo->add_do_method(player, "set_autoplay", ""); @@ -427,7 +434,7 @@ void AnimationPlayerEditor::_animation_name_edited() { player->stop(); String new_name = name->get_text(); - if (new_name.is_empty() || new_name.contains(":") || new_name.contains("/")) { + if (!AnimationLibrary::is_valid_animation_name(new_name)) { error_dialog->set_text(TTR("Invalid animation name!")); error_dialog->popup_centered(); return; @@ -438,8 +445,14 @@ void AnimationPlayerEditor::_animation_name_edited() { return; } - if (player->has_animation(new_name)) { - error_dialog->set_text(TTR("Animation name already exists!")); + String test_name_prefix = ""; + if (library->is_visible() && library->get_selected_id() != -1) { + test_name_prefix = library->get_item_metadata(library->get_selected_id()); + test_name_prefix += (test_name_prefix != "") ? "/" : ""; + } + + if (player->has_animation(test_name_prefix + new_name)) { + error_dialog->set_text(vformat(TTR("Animation '%s' already exists!"), test_name_prefix + new_name)); error_dialog->popup_centered(); return; } @@ -452,6 +465,13 @@ void AnimationPlayerEditor::_animation_name_edited() { Ref<AnimationLibrary> al = player->get_animation_library(player->find_animation_library(anim)); ERR_FAIL_COND(al.is_null()); + // Extract library prefix if present. + String new_library_prefix = ""; + if (current.contains("/")) { + new_library_prefix = current.get_slice("/", 0) + "/"; + current = current.get_slice("/", 1); + } + undo_redo->create_action(TTR("Rename Animation")); undo_redo->add_do_method(al.ptr(), "rename_animation", current, new_name); undo_redo->add_do_method(anim.ptr(), "set_name", new_name); @@ -461,19 +481,25 @@ void AnimationPlayerEditor::_animation_name_edited() { undo_redo->add_undo_method(this, "_animation_player_changed", player); undo_redo->commit_action(); - _select_anim_by_name(new_name); + _select_anim_by_name(new_library_prefix + new_name); } break; case TOOL_NEW_ANIM: { Ref<Animation> new_anim = Ref<Animation>(memnew(Animation)); new_anim->set_name(new_name); - + String library_name; Ref<AnimationLibrary> al; if (library->is_visible()) { - al = player->get_animation_library(library->get_item_metadata(library->get_selected())); + library_name = library->get_item_metadata(library->get_selected()); + // It's possible that [Global] was selected, but doesn't exist yet. + if (player->has_animation_library(library_name)) { + al = player->get_animation_library(library_name); + } + } else { if (player->has_animation_library("")) { al = player->get_animation_library(""); + library_name = ""; } } @@ -484,6 +510,7 @@ void AnimationPlayerEditor::_animation_name_edited() { al.instantiate(); lib_added = true; undo_redo->add_do_method(player, "add_animation_library", "", al); + library_name = ""; } undo_redo->add_do_method(al.ptr(), "add_animation", new_name, new_anim); @@ -499,7 +526,11 @@ void AnimationPlayerEditor::_animation_name_edited() { } undo_redo->commit_action(); - _select_anim_by_name(new_name); + if (library_name != "") { + library_name = library_name + "/"; + } + _select_anim_by_name(library_name + new_name); + } break; case TOOL_DUPLICATE_ANIM: { @@ -509,17 +540,44 @@ void AnimationPlayerEditor::_animation_name_edited() { Ref<Animation> new_anim = _animation_clone(anim); new_anim->set_name(new_name); - Ref<AnimationLibrary> library = player->get_animation_library(player->find_animation_library(anim)); + String library_name; + Ref<AnimationLibrary> al; + if (library->is_visible()) { + library_name = library->get_item_metadata(library->get_selected()); + // It's possible that [Global] was selected, but doesn't exist yet. + if (player->has_animation_library(library_name)) { + al = player->get_animation_library(library_name); + } + } else { + if (player->has_animation_library("")) { + al = player->get_animation_library(""); + library_name = ""; + } + } undo_redo->create_action(TTR("Duplicate Animation")); - undo_redo->add_do_method(library.ptr(), "add_animation", new_name, new_anim); - undo_redo->add_undo_method(library.ptr(), "remove_animation", new_name); - undo_redo->add_do_method(player, "animation_set_next", new_name, player->animation_get_next(current)); + + bool lib_added = false; + if (al.is_null()) { + al.instantiate(); + lib_added = true; + undo_redo->add_do_method(player, "add_animation_library", "", al); + library_name = ""; + } + + undo_redo->add_do_method(al.ptr(), "add_animation", new_name, new_anim); + undo_redo->add_undo_method(al.ptr(), "remove_animation", new_name); undo_redo->add_do_method(this, "_animation_player_changed", player); undo_redo->add_undo_method(this, "_animation_player_changed", player); + if (lib_added) { + undo_redo->add_undo_method(player, "remove_animation_library", ""); + } undo_redo->commit_action(); - _select_anim_by_name(new_name); + if (library_name != "") { + library_name = library_name + "/"; + } + _select_anim_by_name(library_name + new_name); } break; } @@ -851,6 +909,47 @@ void AnimationPlayerEditor::_update_animation_list_icons() { } } +void AnimationPlayerEditor::_update_name_dialog_library_dropdown() { + StringName current_library_name = StringName(); + if (animation->has_selectable_items()) { + String current_animation_name = animation->get_item_text(animation->get_selected()); + Ref<Animation> current_animation = player->get_animation(current_animation_name); + if (current_animation.is_valid()) { + current_library_name = player->find_animation_library(current_animation); + } + } + + List<StringName> libraries; + player->get_animation_library_list(&libraries); + library->clear(); + + // When [Global] isn't present, but other libraries are, add option of creating [Global]. + int index_offset = 0; + if (!player->has_animation_library(StringName())) { + library->add_item(String(TTR("[Global] (create)"))); + library->set_item_metadata(0, ""); + index_offset = 1; + } + + int current_lib_id = index_offset; // Don't default to [Global] if it doesn't exist yet. + for (int i = 0; i < libraries.size(); i++) { + StringName library_name = libraries[i]; + library->add_item((library_name == StringName()) ? String(TTR("[Global]")) : String(library_name)); + library->set_item_metadata(i + index_offset, String(library_name)); + // Default to duplicating into same library. + if (library_name == current_library_name) { + current_lib_id = i + index_offset; + } + } + + if (library->get_item_count() > 1) { + library->select(current_lib_id); + library->show(); + } else { + library->hide(); + } +} + void AnimationPlayerEditor::edit(AnimationPlayer *p_player) { if (player && pin->is_pressed()) { return; // Ignore, pinned. @@ -946,9 +1045,17 @@ void AnimationPlayerEditor::_animation_duplicate() { new_name = new_name + " (copy)"; } - name_title->set_text(TTR("New Animation Name:")); - name->set_text(new_name); + if (new_name.contains("/")) { + // Discard library prefix. + new_name = new_name.get_slice("/", 1); + } + + _update_name_dialog_library_dropdown(); + name_dialog_op = TOOL_DUPLICATE_ANIM; + name_dialog->set_title("Duplicate Animation"); + name_title->set_text(TTR("Duplicated Animation Name:")); + name->set_text(new_name); name_dialog->popup_centered(Size2(300, 90)); name->select_all(); name->grab_focus(); @@ -1338,7 +1445,7 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() { float pos = cpos + step_off * anim->get_step(); - bool valid = anim->get_loop_mode() != Animation::LoopMode::LOOP_NONE || (pos >= 0 && pos <= anim->get_length()); + bool valid = anim->get_loop_mode() != Animation::LOOP_NONE || (pos >= 0 && pos <= anim->get_length()); onion.captures_valid.write[cidx] = valid; if (valid) { player->seek(pos, true); @@ -1466,7 +1573,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug frame = memnew(SpinBox); hb->add_child(frame); - frame->set_custom_minimum_size(Size2(60, 0)); + frame->set_custom_minimum_size(Size2(80, 0) * EDSCALE); frame->set_stretch_ratio(2); frame->set_step(0.0001); frame->set_tooltip(TTR("Animation position (in seconds).")); @@ -1532,8 +1639,10 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug onion_skinning = memnew(MenuButton); onion_skinning->set_tooltip(TTR("Onion Skinning Options")); onion_skinning->get_popup()->add_separator(TTR("Directions")); + // TRANSLATORS: Opposite of "Future", refers to a direction in animation onion skinning. onion_skinning->get_popup()->add_check_item(TTR("Past"), ONION_SKINNING_PAST); onion_skinning->get_popup()->set_item_checked(-1, true); + // TRANSLATORS: Opposite of "Past", refers to a direction in animation onion skinning. onion_skinning->get_popup()->add_check_item(TTR("Future"), ONION_SKINNING_FUTURE); onion_skinning->get_popup()->add_separator(TTR("Depth")); onion_skinning->get_popup()->add_radio_check_item(TTR("1 step"), ONION_SKINNING_1_STEP); diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h index 0cc04460ca..3b1de070fa 100644 --- a/editor/plugins/animation_player_editor_plugin.h +++ b/editor/plugins/animation_player_editor_plugin.h @@ -188,6 +188,7 @@ class AnimationPlayerEditor : public VBoxContainer { void _update_animation(); void _update_player(); void _update_animation_list_icons(); + void _update_name_dialog_library_dropdown(); void _blend_edited(); void _animation_player_changed(Object *p_pl); diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index f0dabed652..2ba2466646 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -42,6 +42,8 @@ #include "scene/animation/animation_player.h" #include "scene/gui/menu_button.h" #include "scene/gui/panel.h" +#include "scene/gui/tree.h" +#include "scene/main/viewport.h" #include "scene/main/window.h" bool AnimationNodeStateMachineEditor::can_edit(const Ref<AnimationNode> &p_node) { @@ -55,7 +57,10 @@ void AnimationNodeStateMachineEditor::edit(const Ref<AnimationNode> &p_node) { if (state_machine.is_valid()) { selected_transition_from = StringName(); selected_transition_to = StringName(); + selected_transition_index = -1; + selected_multi_transition = TransitionLine(); selected_node = StringName(); + selected_nodes.clear(); _update_mode(); _update_graph(); } @@ -69,71 +74,39 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv Ref<InputEventKey> k = p_event; if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_keycode() == Key::KEY_DELETE && !k->is_echo()) { - if (selected_node != StringName() || selected_transition_to != StringName() || selected_transition_from != StringName()) { + if (selected_node != StringName() || !selected_nodes.is_empty() || selected_transition_to != StringName() || selected_transition_from != StringName()) { _erase_selected(); accept_event(); } } - Ref<InputEventMouseButton> mb = p_event; - - //Add new node - if (mb.is_valid() && mb->is_pressed() && ((tool_select->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) || (tool_create->is_pressed() && mb->get_button_index() == MouseButton::LEFT))) { - menu->clear(); - animations_menu->clear(); - animations_to_add.clear(); - List<StringName> classes; - classes.sort_custom<StringName::AlphCompare>(); - - ClassDB::get_inheriters_from_class("AnimationRootNode", &classes); - menu->add_submenu_item(TTR("Add Animation"), "animations"); - - AnimationTree *gp = AnimationTreeEditor::get_singleton()->get_tree(); - ERR_FAIL_COND(!gp); - if (gp && gp->has_node(gp->get_animation_player())) { - AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(gp->get_node(gp->get_animation_player())); - if (ap) { - List<StringName> names; - ap->get_animation_list(&names); - for (const StringName &E : names) { - animations_menu->add_icon_item(get_theme_icon(SNAME("Animation"), SNAME("EditorIcons")), E); - animations_to_add.push_back(E); - } - } - } + // Group selected nodes on a state machine + if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->is_ctrl_pressed() && !k->is_shift_pressed() && k->get_keycode() == Key::G && !k->is_echo()) { + _group_selected_nodes(); + } - for (const StringName &E : classes) { - String name = String(E).replace_first("AnimationNode", ""); - if (name == "Animation") { - continue; // nope - } - int idx = menu->get_item_count(); - menu->add_item(vformat(TTR("Add %s"), name), idx); - menu->set_item_metadata(idx, E); - } - Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard(); + // Ungroup state machine + if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->is_ctrl_pressed() && k->is_shift_pressed() && k->get_keycode() == Key::G && !k->is_echo()) { + _ungroup_selected_nodes(); + } - if (clipb.is_valid()) { - menu->add_separator(); - menu->add_item(TTR("Paste"), MENU_PASTE); - } - menu->add_separator(); - menu->add_item(TTR("Load..."), MENU_LOAD_FILE); + Ref<InputEventMouseButton> mb = p_event; - menu->set_position(state_machine_draw->get_screen_position() + mb->get_position()); - menu->reset_size(); - menu->popup(); - add_node_pos = mb->get_position() / EDSCALE + state_machine->get_graph_offset(); + // Add new node + if (mb.is_valid() && mb->is_pressed() && !box_selecting && !connecting && ((tool_select->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) || (tool_create->is_pressed() && mb->get_button_index() == MouseButton::LEFT))) { + connecting_from = StringName(); + _open_menu(mb->get_position()); } - // select node or push a field inside - if (mb.is_valid() && !mb->is_shift_pressed() && mb->is_pressed() && tool_select->is_pressed() && mb->get_button_index() == MouseButton::LEFT) { + // Select node or push a field inside + if (mb.is_valid() && !mb->is_shift_pressed() && !mb->is_ctrl_pressed() && mb->is_pressed() && tool_select->is_pressed() && mb->get_button_index() == MouseButton::LEFT) { selected_transition_from = StringName(); selected_transition_to = StringName(); + selected_transition_index = -1; + selected_multi_transition = TransitionLine(); selected_node = StringName(); for (int i = node_rects.size() - 1; i >= 0; i--) { //inverse to draw order - if (node_rects[i].play.has_point(mb->get_position())) { //edit name if (play_mode->get_selected() == 1 || !playback->is_playing()) { //start @@ -146,8 +119,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv return; } - if (node_rects[i].name.has_point(mb->get_position())) { //edit name - + if (node_rects[i].name.has_point(mb->get_position()) && state_machine->can_edit_node(node_rects[i].node_name)) { // edit name Ref<StyleBox> line_sb = get_theme_stylebox(SNAME("normal"), SNAME("LineEdit")); Rect2 edit_rect = node_rects[i].name; @@ -173,6 +145,12 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv if (node_rects[i].node.has_point(mb->get_position())) { //select node since nothing else was selected selected_node = node_rects[i].node_name; + if (!selected_nodes.has(selected_node)) { + selected_nodes.clear(); + } + + selected_nodes.insert(selected_node); + Ref<AnimationNode> anode = state_machine->get_node(selected_node); EditorNode::get_singleton()->push_item(anode.ptr(), "", true); state_machine_draw->update(); @@ -209,23 +187,53 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv if (closest >= 0) { selected_transition_from = transition_lines[closest].from_node; selected_transition_to = transition_lines[closest].to_node; + selected_transition_index = closest; Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(closest); EditorNode::get_singleton()->push_item(tr.ptr(), "", true); + + if (!transition_lines[closest].multi_transitions.is_empty()) { + selected_transition_index = -1; + selected_multi_transition = transition_lines[closest]; + + Ref<EditorAnimationMultiTransitionEdit> multi; + multi.instantiate(); + multi->add_transition(selected_transition_from, selected_transition_to, tr); + + for (int i = 0; i < transition_lines[closest].multi_transitions.size(); i++) { + int index = transition_lines[closest].multi_transitions[i].transition_index; + + Ref<AnimationNodeStateMachineTransition> transition = state_machine->get_transition(index); + StringName from = transition_lines[closest].multi_transitions[i].from_node; + StringName to = transition_lines[closest].multi_transitions[i].to_node; + + multi->add_transition(from, to, transition); + } + EditorNode::get_singleton()->push_item(multi.ptr(), "", true); + } } state_machine_draw->update(); _update_mode(); } - //end moving node + // End moving node if (mb.is_valid() && dragging_selected_attempt && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed()) { if (dragging_selected) { Ref<AnimationNode> an = state_machine->get_node(selected_node); updating = true; + undo_redo->create_action(TTR("Move Node")); - undo_redo->add_do_method(state_machine.ptr(), "set_node_position", selected_node, state_machine->get_node_position(selected_node) + drag_ofs / EDSCALE); - undo_redo->add_undo_method(state_machine.ptr(), "set_node_position", selected_node, state_machine->get_node_position(selected_node)); + + for (int i = 0; i < node_rects.size(); i++) { + if (!selected_nodes.has(node_rects[i].node_name)) { + continue; + } + + undo_redo->add_do_method(state_machine.ptr(), "set_node_position", node_rects[i].node_name, state_machine->get_node_position(node_rects[i].node_name) + drag_ofs / EDSCALE); + undo_redo->add_undo_method(state_machine.ptr(), "set_node_position", node_rects[i].node_name, state_machine->get_node_position(node_rects[i].node_name)); + } + undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -239,7 +247,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv state_machine_draw->update(); } - //connect nodes + // Connect nodes if (mb.is_valid() && ((tool_select->is_pressed() && mb->is_shift_pressed()) || tool_connect->is_pressed()) && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) { for (int i = node_rects.size() - 1; i >= 0; i--) { //inverse to draw order if (node_rects[i].node.has_point(mb->get_position())) { //select node since nothing else was selected @@ -252,47 +260,63 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv } } - //end connecting nodes + // End connecting nodes if (mb.is_valid() && connecting && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed()) { if (connecting_to_node != StringName()) { - if (state_machine->has_transition(connecting_from, connecting_to_node)) { - EditorNode::get_singleton()->show_warning(TTR("Transition exists!")); + Ref<AnimationNode> node = state_machine->get_node(connecting_to_node); + Ref<AnimationNodeStateMachine> anodesm = node; + Ref<AnimationNodeEndState> end_node = node; + if (state_machine->has_transition(connecting_from, connecting_to_node) && state_machine->can_edit_node(connecting_to_node) && !anodesm.is_valid()) { + EditorNode::get_singleton()->show_warning(TTR("Transition exists!")); + connecting = false; } else { - Ref<AnimationNodeStateMachineTransition> tr; - tr.instantiate(); - tr->set_switch_mode(AnimationNodeStateMachineTransition::SwitchMode(transition_mode->get_selected())); - - updating = true; - undo_redo->create_action(TTR("Add Transition")); - undo_redo->add_do_method(state_machine.ptr(), "add_transition", connecting_from, connecting_to_node, tr); - undo_redo->add_undo_method(state_machine.ptr(), "remove_transition", connecting_from, connecting_to_node); - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); - undo_redo->commit_action(); - updating = false; - - selected_transition_from = connecting_from; - selected_transition_to = connecting_to_node; - - EditorNode::get_singleton()->push_item(tr.ptr(), "", true); - _update_mode(); + if (anodesm.is_valid() || end_node.is_valid()) { + _open_connect_menu(mb->get_position()); + } else { + _add_transition(); + } } + } else { + _open_menu(mb->get_position()); } connecting_to_node = StringName(); - connecting = false; state_machine_draw->update(); } + // Start box selecting + if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT && tool_select->is_pressed()) { + box_selecting = true; + box_selecting_from = box_selecting_to = state_machine_draw->get_local_mouse_position(); + box_selecting_rect = Rect2(MIN(box_selecting_from.x, box_selecting_to.x), + MIN(box_selecting_from.y, box_selecting_to.y), + ABS(box_selecting_from.x - box_selecting_to.x), + ABS(box_selecting_from.y - box_selecting_to.y)); + + if (mb->is_ctrl_pressed() || mb->is_shift_pressed()) { + previous_selected = selected_nodes; + } else { + selected_nodes.clear(); + previous_selected.clear(); + } + } + + // End box selecting + if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed() && box_selecting) { + box_selecting = false; + state_machine_draw->update(); + _update_mode(); + } + Ref<InputEventMouseMotion> mm = p_event; - //pan window + // Pan window if (mm.is_valid() && (mm->get_button_mask() & MouseButton::MASK_MIDDLE) != MouseButton::NONE) { h_scroll->set_value(h_scroll->get_value() - mm->get_relative().x); v_scroll->set_value(v_scroll->get_value() - mm->get_relative().y); } - //move mouse while connecting + // Move mouse while connecting if (mm.is_valid() && connecting) { connecting_to = mm->get_position(); connecting_to_node = StringName(); @@ -306,7 +330,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv } } - //move mouse while moving a node + // Move mouse while moving a node if (mm.is_valid() && dragging_selected_attempt) { dragging_selected = true; drag_ofs = mm->get_position() - drag_from; @@ -346,6 +370,36 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv state_machine_draw->update(); } + // Move mouse while moving box select + if (mm.is_valid() && box_selecting) { + box_selecting_to = state_machine_draw->get_local_mouse_position(); + + box_selecting_rect = Rect2(MIN(box_selecting_from.x, box_selecting_to.x), + MIN(box_selecting_from.y, box_selecting_to.y), + ABS(box_selecting_from.x - box_selecting_to.x), + ABS(box_selecting_from.y - box_selecting_to.y)); + + for (int i = 0; i < node_rects.size(); i++) { + bool in_box = node_rects[i].node.intersects(box_selecting_rect); + + if (in_box) { + if (previous_selected.has(node_rects[i].node_name)) { + selected_nodes.erase(node_rects[i].node_name); + } else { + selected_nodes.insert(node_rects[i].node_name); + } + } else { + if (previous_selected.has(node_rects[i].node_name)) { + selected_nodes.insert(node_rects[i].node_name); + } else { + selected_nodes.erase(node_rects[i].node_name); + } + } + } + + state_machine_draw->update(); + } + if (mm.is_valid()) { state_machine_draw->grab_focus(); @@ -353,6 +407,11 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv int new_over_node_what = -1; if (tool_select->is_pressed()) { for (int i = node_rects.size() - 1; i >= 0; i--) { // Inverse to draw order. + + if (!state_machine->can_edit_node(node_rects[i].node_name)) { + continue; // start/end node can't be edited + } + if (node_rects[i].node.has_point(mm->get_position())) { new_over_node = node_rects[i].node_name; if (node_rects[i].play.has_point(mm->get_position())) { @@ -370,6 +429,43 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv over_node_what = new_over_node_what; state_machine_draw->update(); } + + // set tooltip for transition + if (tool_select->is_pressed()) { + int closest = -1; + float closest_d = 1e20; + for (int i = 0; i < transition_lines.size(); i++) { + Vector2 s[2] = { + transition_lines[i].from, + transition_lines[i].to + }; + Vector2 cpoint = Geometry2D::get_closest_point_to_segment(mm->get_position(), s); + float d = cpoint.distance_to(mm->get_position()); + if (d > transition_lines[i].width) { + continue; + } + + if (d < closest_d) { + closest = i; + closest_d = d; + } + } + + if (closest >= 0) { + String from = String(transition_lines[closest].from_node); + String to = String(transition_lines[closest].to_node); + String tooltip = from + " -> " + to; + + for (int i = 0; i < transition_lines[closest].multi_transitions.size(); i++) { + from = String(transition_lines[closest].multi_transitions[i].from_node); + to = String(transition_lines[closest].multi_transitions[i].to_node); + tooltip += "\n" + from + " -> " + to; + } + state_machine_draw->set_tooltip(tooltip); + } else { + state_machine_draw->set_tooltip(""); + } + } } Ref<InputEventPanGesture> pan_gesture = p_event; @@ -396,6 +492,458 @@ Control::CursorShape AnimationNodeStateMachineEditor::get_cursor_shape(const Poi return cursor_shape; } +void AnimationNodeStateMachineEditor::_group_selected_nodes() { + if (!selected_nodes.is_empty()) { + if (selected_nodes.size() == 1 && (*selected_nodes.begin() == state_machine->start_node || *selected_nodes.begin() == state_machine->end_node)) + return; + + Ref<AnimationNodeStateMachine> group_sm = memnew(AnimationNodeStateMachine); + Vector2 group_position; + + Vector<NodeUR> nodes_ur; + Vector<TransitionUR> transitions_ur; + + int base = 1; + String base_name = group_sm->get_caption(); + String group_name = base_name; + + while (state_machine->has_node(group_name) && !selected_nodes.has(group_name)) { + base++; + group_name = base_name + " " + itos(base); + } + + updating = true; + undo_redo->create_action("Group"); + + // Move selected nodes to the new state machine + for (const StringName &E : selected_nodes) { + if (!state_machine->can_edit_node(E)) { + continue; + } + + Ref<AnimationNode> node = state_machine->get_node(E); + Vector2 node_position = state_machine->get_node_position(E); + group_position += node_position; + + NodeUR new_node; + new_node.name = E; + new_node.node = node; + new_node.position = node_position; + + nodes_ur.push_back(new_node); + } + + // Add the transitions to the new state machine + for (int i = 0; i < state_machine->get_transition_count(); i++) { + String from = state_machine->get_transition_from(i); + String to = state_machine->get_transition_to(i); + + String local_from = from.get_slicec('/', 0); + String local_to = to.get_slicec('/', 0); + + String old_from = from; + String old_to = to; + + bool from_selected = false; + bool to_selected = false; + + if (selected_nodes.has(local_from) && local_from != state_machine->start_node) { + from_selected = true; + } + if (selected_nodes.has(local_to) && local_to != state_machine->end_node) { + to_selected = true; + } + if (!from_selected && !to_selected) { + continue; + } + + Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(i); + + if (!from_selected) { + from = "../" + old_from; + } + if (!to_selected) { + to = "../" + old_to; + } + + TransitionUR new_tr; + new_tr.new_from = from; + new_tr.new_to = to; + new_tr.old_from = old_from; + new_tr.old_to = old_to; + new_tr.transition = tr; + + transitions_ur.push_back(new_tr); + } + + for (int i = 0; i < nodes_ur.size(); i++) { + undo_redo->add_do_method(state_machine.ptr(), "remove_node", nodes_ur[i].name); + undo_redo->add_undo_method(group_sm.ptr(), "remove_node", nodes_ur[i].name); + } + + undo_redo->add_do_method(state_machine.ptr(), "add_node", group_name, group_sm, group_position / nodes_ur.size()); + undo_redo->add_undo_method(state_machine.ptr(), "remove_node", group_name); + + for (int i = 0; i < nodes_ur.size(); i++) { + undo_redo->add_do_method(group_sm.ptr(), "add_node", nodes_ur[i].name, nodes_ur[i].node, nodes_ur[i].position); + undo_redo->add_undo_method(state_machine.ptr(), "add_node", nodes_ur[i].name, nodes_ur[i].node, nodes_ur[i].position); + } + + for (int i = 0; i < transitions_ur.size(); i++) { + undo_redo->add_do_method(group_sm.ptr(), "add_transition", transitions_ur[i].new_from, transitions_ur[i].new_to, transitions_ur[i].transition); + undo_redo->add_undo_method(state_machine.ptr(), "add_transition", transitions_ur[i].old_from, transitions_ur[i].old_to, transitions_ur[i].transition); + } + + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); + updating = false; + + selected_nodes.clear(); + selected_nodes.insert(group_name); + state_machine_draw->update(); + accept_event(); + _update_mode(); + } +} + +void AnimationNodeStateMachineEditor::_ungroup_selected_nodes() { + bool find = false; + HashSet<StringName> new_selected_nodes; + + for (const StringName &E : selected_nodes) { + Ref<AnimationNodeStateMachine> group_sm = state_machine->get_node(E); + + if (group_sm.is_valid()) { + find = true; + + Vector2 group_position = state_machine->get_node_position(E); + StringName group_name = E; + + List<AnimationNode::ChildNode> nodes; + group_sm->get_child_nodes(&nodes); + + Vector<NodeUR> nodes_ur; + Vector<TransitionUR> transitions_ur; + + updating = true; + undo_redo->create_action("Ungroup"); + + // Move all child nodes to current state machine + for (int i = 0; i < nodes.size(); i++) { + if (!group_sm->can_edit_node(nodes[i].name)) { + continue; + } + + Vector2 node_position = group_sm->get_node_position(nodes[i].name); + + NodeUR new_node; + new_node.name = nodes[i].name; + new_node.position = node_position; + new_node.node = nodes[i].node; + + nodes_ur.push_back(new_node); + } + + for (int i = 0; i < group_sm->get_transition_count(); i++) { + String from = group_sm->get_transition_from(i); + String to = group_sm->get_transition_to(i); + Ref<AnimationNodeStateMachineTransition> tr = group_sm->get_transition(i); + + TransitionUR new_tr; + new_tr.new_from = from.replace_first("../", ""); + new_tr.new_to = to.replace_first("../", ""); + new_tr.old_from = from; + new_tr.old_to = to; + new_tr.transition = tr; + + transitions_ur.push_back(new_tr); + } + + for (int i = 0; i < nodes_ur.size(); i++) { + undo_redo->add_do_method(group_sm.ptr(), "remove_node", nodes_ur[i].name); + undo_redo->add_undo_method(state_machine.ptr(), "remove_node", nodes_ur[i].name); + } + + undo_redo->add_do_method(state_machine.ptr(), "remove_node", group_name); + undo_redo->add_undo_method(state_machine.ptr(), "add_node", group_name, group_sm, group_position); + + for (int i = 0; i < nodes_ur.size(); i++) { + new_selected_nodes.insert(nodes_ur[i].name); + undo_redo->add_do_method(state_machine.ptr(), "add_node", nodes_ur[i].name, nodes_ur[i].node, nodes_ur[i].position); + undo_redo->add_undo_method(group_sm.ptr(), "add_node", nodes_ur[i].name, nodes_ur[i].node, nodes_ur[i].position); + } + + for (int i = 0; i < transitions_ur.size(); i++) { + if (transitions_ur[i].old_from != state_machine->start_node && transitions_ur[i].old_to != state_machine->end_node) { + undo_redo->add_do_method(state_machine.ptr(), "add_transition", transitions_ur[i].new_from, transitions_ur[i].new_to, transitions_ur[i].transition); + } + + undo_redo->add_undo_method(group_sm.ptr(), "add_transition", transitions_ur[i].old_from, transitions_ur[i].old_to, transitions_ur[i].transition); + } + + for (int i = 0; i < state_machine->get_transition_count(); i++) { + String from = state_machine->get_transition_from(i); + String to = state_machine->get_transition_to(i); + Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(i); + + if (from == group_name || to == group_name) { + undo_redo->add_undo_method(state_machine.ptr(), "add_transition", from, to, tr); + } + } + + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); + updating = false; + } + } + + if (find) { + selected_nodes = new_selected_nodes; + selected_node = StringName(); + state_machine_draw->update(); + accept_event(); + _update_mode(); + } +} + +void AnimationNodeStateMachineEditor::_open_menu(const Vector2 &p_position) { + menu->clear(); + animations_menu->clear(); + animations_to_add.clear(); + List<StringName> classes; + classes.sort_custom<StringName::AlphCompare>(); + + ClassDB::get_inheriters_from_class("AnimationRootNode", &classes); + menu->add_submenu_item(TTR("Add Animation"), "animations"); + + AnimationTree *gp = AnimationTreeEditor::get_singleton()->get_tree(); + ERR_FAIL_COND(!gp); + if (gp && gp->has_node(gp->get_animation_player())) { + AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(gp->get_node(gp->get_animation_player())); + if (ap) { + List<StringName> names; + ap->get_animation_list(&names); + for (List<StringName>::Element *E = names.front(); E; E = E->next()) { + animations_menu->add_icon_item(get_theme_icon("Animation", "EditorIcons"), E->get()); + animations_to_add.push_back(E->get()); + } + } + } + + for (List<StringName>::Element *E = classes.front(); E; E = E->next()) { + String name = String(E->get()).replace_first("AnimationNode", ""); + if (name == "Animation" || name == "StartState" || name == "EndState") { + continue; // nope + } + int idx = menu->get_item_count(); + menu->add_item(vformat(TTR("Add %s"), name), idx); + menu->set_item_metadata(idx, E->get()); + } + Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard(); + + if (clipb.is_valid()) { + menu->add_separator(); + menu->add_item(TTR("Paste"), MENU_PASTE); + } + menu->add_separator(); + menu->add_item(TTR("Load..."), MENU_LOAD_FILE); + + menu->set_position(state_machine_draw->get_screen_transform().xform(p_position)); + menu->popup(); + add_node_pos = p_position / EDSCALE + state_machine->get_graph_offset(); +} + +void AnimationNodeStateMachineEditor::_open_connect_menu(const Vector2 &p_position) { + ERR_FAIL_COND(connecting_to_node == StringName()); + + Ref<AnimationNode> node = state_machine->get_node(connecting_to_node); + Ref<AnimationNodeStateMachine> anodesm = node; + Ref<AnimationNodeEndState> end_node = node; + ERR_FAIL_COND(!anodesm.is_valid() && !end_node.is_valid()); + + connect_menu->clear(); + state_machine_menu->clear(); + end_menu->clear(); + nodes_to_connect.clear(); + + for (int i = connect_menu->get_child_count() - 1; i >= 0; i--) { + Node *child = connect_menu->get_child(i); + + if (child->is_class("PopupMenu")) { + connect_menu->remove_child(child); + } + } + + connect_menu->reset_size(); + state_machine_menu->reset_size(); + end_menu->reset_size(); + + if (anodesm.is_valid()) { + _create_submenu(connect_menu, anodesm, connecting_to_node, connecting_to_node); + } else { + Ref<AnimationNodeStateMachine> prev = state_machine; + _create_submenu(connect_menu, prev, connecting_to_node, connecting_to_node, true); + } + + connect_menu->add_submenu_item(TTR("To") + " Animation", connecting_to_node); + + if (state_machine_menu->get_item_count() > 0 || !end_node.is_valid()) { + connect_menu->add_submenu_item(TTR("To") + " StateMachine", "state_machines"); + connect_menu->add_child(state_machine_menu); + } + + if (end_node.is_valid()) { + connect_menu->add_submenu_item(TTR("To") + " End", "end_nodes"); + connect_menu->add_child(end_menu); + } else { + state_machine_menu->add_item(connecting_to_node, nodes_to_connect.size()); + } + + nodes_to_connect.push_back(connecting_to_node); + + if (nodes_to_connect.size() == 1) { + _add_transition(); + return; + } + + connect_menu->set_position(state_machine_draw->get_screen_transform().xform(p_position)); + connect_menu->popup(); +} + +bool AnimationNodeStateMachineEditor::_create_submenu(PopupMenu *p_menu, Ref<AnimationNodeStateMachine> p_nodesm, const StringName &p_name, const StringName &p_path, bool from_root, Vector<Ref<AnimationNodeStateMachine>> p_parents) { + String prev_path; + Vector<Ref<AnimationNodeStateMachine>> parents = p_parents; + + if (from_root) { + Ref<AnimationNodeStateMachine> prev = p_nodesm->get_prev_state_machine(); + + while (prev.is_valid()) { + parents.push_back(prev); + p_nodesm = prev; + prev_path += "../"; + prev = prev->get_prev_state_machine(); + } + prev_path.remove_at(prev_path.size() - 1); + } + + List<StringName> nodes; + p_nodesm->get_node_list(&nodes); + + PopupMenu *nodes_menu = memnew(PopupMenu); + nodes_menu->set_name(p_name); + nodes_menu->connect("id_pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_connect_to)); + p_menu->add_child(nodes_menu); + + bool node_added = false; + for (const StringName &E : nodes) { + if (p_nodesm->can_edit_node(E)) { + Ref<AnimationNodeStateMachine> ansm = p_nodesm->get_node(E); + + String path; + if (from_root) { + path = prev_path + "/" + E; + } else { + path = String(p_path) + "/" + E; + } + + if (ansm == state_machine) { + end_menu->add_item(E, nodes_to_connect.size()); + nodes_to_connect.push_back(state_machine->end_node); + continue; + } + + if (ansm.is_valid()) { + bool found = false; + + for (int i = 0; i < parents.size(); i++) { + if (parents[i] == ansm) { + path = path.replace_first("/../" + E, ""); + found = true; + break; + } + } + + if (!found) { + state_machine_menu->add_item(E, nodes_to_connect.size()); + nodes_to_connect.push_back(path); + } else { + end_menu->add_item(E, nodes_to_connect.size()); + nodes_to_connect.push_back(path + "/" + state_machine->end_node); + } + + if (_create_submenu(nodes_menu, ansm, E, path, false, parents)) { + nodes_menu->add_submenu_item(E, E); + node_added = true; + } + } else { + nodes_menu->add_item(E, nodes_to_connect.size()); + nodes_to_connect.push_back(path); + node_added = true; + } + } + } + + return node_added; +} + +void AnimationNodeStateMachineEditor::_stop_connecting() { + connecting = false; + state_machine_draw->update(); +} + +void AnimationNodeStateMachineEditor::_delete_selected() { + TreeItem *item = delete_tree->get_next_selected(nullptr); + while (item) { + if (!updating) { + updating = true; + selected_multi_transition = TransitionLine(); + undo_redo->create_action("Transition(s) Removed"); + } + + Vector<String> path = item->get_text(0).split(" -> "); + + selected_transition_from = path[0]; + selected_transition_to = path[1]; + _erase_selected(true); + + item = delete_tree->get_next_selected(item); + } + + if (updating) { + undo_redo->commit_action(); + updating = false; + } +} + +void AnimationNodeStateMachineEditor::_delete_all() { + Vector<TransitionLine> multi_transitions = selected_multi_transition.multi_transitions; + selected_multi_transition = TransitionLine(); + + updating = true; + undo_redo->create_action("Transition(s) Removed"); + _erase_selected(true); + for (int i = 0; i < multi_transitions.size(); i++) { + selected_transition_from = multi_transitions[i].from_node; + selected_transition_to = multi_transitions[i].to_node; + _erase_selected(true); + } + undo_redo->commit_action(); + updating = false; + + delete_window->hide(); +} + +void AnimationNodeStateMachineEditor::_delete_tree_draw() { + TreeItem *item = delete_tree->get_next_selected(nullptr); + while (item) { + delete_window->get_cancel_button()->set_disabled(false); + return; + } + delete_window->get_cancel_button()->set_disabled(true); +} + void AnimationNodeStateMachineEditor::_file_opened(const String &p_file) { file_loaded = ResourceLoader::load(p_file); if (file_loaded.is_valid()) { @@ -456,6 +1004,8 @@ void AnimationNodeStateMachineEditor::_add_menu_type(int p_index) { undo_redo->add_undo_method(state_machine.ptr(), "remove_node", name); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); + connecting_to_node = name; + _add_transition(true); undo_redo->commit_action(); updating = false; @@ -482,13 +1032,58 @@ void AnimationNodeStateMachineEditor::_add_animation_type(int p_index) { undo_redo->add_undo_method(state_machine.ptr(), "remove_node", name); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); + connecting_to_node = name; + _add_transition(true); undo_redo->commit_action(); updating = false; state_machine_draw->update(); } -void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, bool p_auto_advance) { +void AnimationNodeStateMachineEditor::_connect_to(int p_index) { + connecting_to_node = nodes_to_connect[p_index]; + _add_transition(); +} + +void AnimationNodeStateMachineEditor::_add_transition(const bool p_nested_action) { + if (connecting_from != StringName() && connecting_to_node != StringName()) { + if (state_machine->has_transition(connecting_from, connecting_to_node)) { + EditorNode::get_singleton()->show_warning("Transition exists!"); + connecting = false; + return; + } + + Ref<AnimationNodeStateMachineTransition> tr; + tr.instantiate(); + tr->set_switch_mode(AnimationNodeStateMachineTransition::SwitchMode(transition_mode->get_selected())); + + if (!p_nested_action) { + updating = true; + } + + undo_redo->create_action(TTR("Add Transition")); + undo_redo->add_do_method(state_machine.ptr(), "add_transition", connecting_from, connecting_to_node, tr); + undo_redo->add_undo_method(state_machine.ptr(), "remove_transition", connecting_from, connecting_to_node); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); + + if (!p_nested_action) { + updating = false; + } + + selected_transition_from = connecting_from; + selected_transition_to = connecting_to_node; + selected_transition_index = transition_lines.size(); + + EditorNode::get_singleton()->push_item(tr.ptr(), "", true); + _update_mode(); + } + + connecting = false; +} + +void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, bool p_auto_advance, bool p_multi_transitions) { Color linecolor = get_theme_color(SNAME("font_color"), SNAME("Label")); Color icon_color(1, 1, 1); Color accent = get_theme_color(SNAME("accent_color"), SNAME("Editor")); @@ -516,39 +1111,46 @@ void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, co linecolor = accent; linecolor.set_hsv(1.0, linecolor.get_s(), linecolor.get_v()); } + state_machine_draw->draw_line(p_from, p_to, linecolor, 2); Ref<Texture2D> icon = icons[p_mode + (p_auto_advance ? 3 : 0)]; Transform2D xf; - xf.elements[0] = (p_to - p_from).normalized(); - xf.elements[1] = xf.elements[0].orthogonal(); - xf.elements[2] = (p_from + p_to) * 0.5 - xf.elements[1] * icon->get_height() * 0.5 - xf.elements[0] * icon->get_height() * 0.5; + xf.columns[0] = (p_to - p_from).normalized(); + xf.columns[1] = xf.columns[0].orthogonal(); + xf.columns[2] = (p_from + p_to) * 0.5 - xf.columns[1] * icon->get_height() * 0.5 - xf.columns[0] * icon->get_height() * 0.5; state_machine_draw->draw_set_transform_matrix(xf); - state_machine_draw->draw_texture(icon, Vector2(), icon_color); + if (p_multi_transitions) { + state_machine_draw->draw_texture(icons[0], Vector2(-icon->get_width(), 0), icon_color); + state_machine_draw->draw_texture(icons[0], Vector2(), icon_color); + state_machine_draw->draw_texture(icons[0], Vector2(icon->get_width(), 0), icon_color); + } else { + state_machine_draw->draw_texture(icon, Vector2(), icon_color); + } state_machine_draw->draw_set_transform_matrix(Transform2D()); } -void AnimationNodeStateMachineEditor::_clip_src_line_to_rect(Vector2 &r_from, Vector2 &r_to, const Rect2 &p_rect) { - if (r_to == r_from) { +void AnimationNodeStateMachineEditor::_clip_src_line_to_rect(Vector2 &r_from, const Vector2 &p_to, const Rect2 &p_rect) { + if (p_to == r_from) { return; } //this could be optimized... - Vector2 n = (r_to - r_from).normalized(); + Vector2 n = (p_to - r_from).normalized(); while (p_rect.has_point(r_from)) { r_from += n; } } -void AnimationNodeStateMachineEditor::_clip_dst_line_to_rect(Vector2 &r_from, Vector2 &r_to, const Rect2 &p_rect) { - if (r_to == r_from) { +void AnimationNodeStateMachineEditor::_clip_dst_line_to_rect(const Vector2 &p_from, Vector2 &r_to, const Rect2 &p_rect) { + if (r_to == p_from) { return; } //this could be optimized... - Vector2 n = (r_to - r_from).normalized(); + Vector2 n = (r_to - p_from).normalized(); while (p_rect.has_point(r_to)) { r_to -= n; } @@ -557,20 +1159,27 @@ void AnimationNodeStateMachineEditor::_clip_dst_line_to_rect(Vector2 &r_from, Ve void AnimationNodeStateMachineEditor::_state_machine_draw() { Ref<AnimationNodeStateMachinePlayback> playback = AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); - Ref<StyleBox> style = get_theme_stylebox(SNAME("state_machine_frame"), SNAME("GraphNode")); - Ref<StyleBox> style_selected = get_theme_stylebox(SNAME("state_machine_selectedframe"), SNAME("GraphNode")); + Ref<StyleBoxFlat> style = get_theme_stylebox(SNAME("state_machine_frame"), SNAME("GraphNode")); + Ref<StyleBoxFlat> style_selected = get_theme_stylebox(SNAME("state_machine_selected_frame"), SNAME("GraphNode")); Ref<Font> font = get_theme_font(SNAME("title_font"), SNAME("GraphNode")); int font_size = get_theme_font_size(SNAME("title_font_size"), SNAME("GraphNode")); Color font_color = get_theme_color(SNAME("title_color"), SNAME("GraphNode")); Ref<Texture2D> play = get_theme_icon(SNAME("Play"), SNAME("EditorIcons")); - Ref<Texture2D> auto_play = get_theme_icon(SNAME("AutoPlay"), SNAME("EditorIcons")); Ref<Texture2D> edit = get_theme_icon(SNAME("Edit"), SNAME("EditorIcons")); Color accent = get_theme_color(SNAME("accent_color"), SNAME("Editor")); Color linecolor = get_theme_color(SNAME("font_color"), SNAME("Label")); linecolor.a *= 0.3; Ref<StyleBox> playing_overlay = get_theme_stylebox(SNAME("position"), SNAME("GraphNode")); + Ref<StyleBoxFlat> start_overlay = style->duplicate(); + start_overlay->set_border_width_all(1 * EDSCALE); + start_overlay->set_border_color(Color::html("#80f6cf")); + + Ref<StyleBoxFlat> end_overlay = style->duplicate(); + end_overlay->set_border_width_all(1 * EDSCALE); + end_overlay->set_border_color(Color::html("#f26661")); + bool playing = false; StringName current; StringName blend_from; @@ -611,23 +1220,26 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { for (const StringName &E : nodes) { Ref<AnimationNode> anode = state_machine->get_node(E); String name = E; - bool needs_editor = EditorNode::get_singleton()->item_has_editor(anode.ptr()); - Ref<StyleBox> sb = E == selected_node ? style_selected : style; + bool needs_editor = AnimationTreeEditor::get_singleton()->can_edit(anode); + Ref<StyleBox> sb = selected_nodes.has(E) ? style_selected : style; Size2 s = sb->get_minimum_size(); int strsize = font->get_string_size(name, font_size).width; s.width += strsize; s.height += MAX(font->get_height(font_size), play->get_height()); s.width += sep + play->get_width(); + if (needs_editor) { s.width += sep + edit->get_width(); } Vector2 offset; offset += state_machine->get_node_position(E) * EDSCALE; - if (selected_node == E && dragging_selected) { + + if (selected_nodes.has(E) && dragging_selected) { offset += drag_ofs; } + offset -= s / 2; offset = offset.floor(); @@ -666,7 +1278,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { } } - _connection_draw(from, to, AnimationNodeStateMachineTransition::SwitchMode(transition_mode->get_selected()), true, false, false, false); + _connection_draw(from, to, AnimationNodeStateMachineTransition::SwitchMode(transition_mode->get_selected()), true, false, false, false, false); } Ref<Texture2D> tr_reference_icon = get_theme_icon(SNAME("TransitionImmediateBig"), SNAME("EditorIcons")); @@ -675,13 +1287,18 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { //draw transition lines for (int i = 0; i < state_machine->get_transition_count(); i++) { TransitionLine tl; + tl.transition_index = i; tl.from_node = state_machine->get_transition_from(i); - Vector2 ofs_from = (dragging_selected && tl.from_node == selected_node) ? drag_ofs : Vector2(); - tl.from = (state_machine->get_node_position(tl.from_node) * EDSCALE) + ofs_from - state_machine->get_graph_offset() * EDSCALE; + StringName local_from = String(tl.from_node).get_slicec('/', 0); + local_from = local_from == ".." ? state_machine->start_node : local_from; + Vector2 ofs_from = (dragging_selected && selected_nodes.has(local_from)) ? drag_ofs : Vector2(); + tl.from = (state_machine->get_node_position(local_from) * EDSCALE) + ofs_from - state_machine->get_graph_offset() * EDSCALE; tl.to_node = state_machine->get_transition_to(i); - Vector2 ofs_to = (dragging_selected && tl.to_node == selected_node) ? drag_ofs : Vector2(); - tl.to = (state_machine->get_node_position(tl.to_node) * EDSCALE) + ofs_to - state_machine->get_graph_offset() * EDSCALE; + StringName local_to = String(tl.to_node).get_slicec('/', 0); + local_to = local_to == ".." ? state_machine->end_node : local_to; + Vector2 ofs_to = (dragging_selected && selected_nodes.has(local_to)) ? drag_ofs : Vector2(); + tl.to = (state_machine->get_node_position(local_to) * EDSCALE) + ofs_to - state_machine->get_graph_offset() * EDSCALE; Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(i); tl.disabled = tr->is_disabled(); @@ -690,62 +1307,79 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { tl.advance_condition_state = false; tl.mode = tr->get_switch_mode(); tl.width = tr_bidi_offset; + tl.travel = false; + tl.hidden = false; - if (state_machine->has_transition(tl.to_node, tl.from_node)) { //offset if same exists + if (state_machine->has_local_transition(local_to, local_from)) { //offset if same exists Vector2 offset = -(tl.from - tl.to).normalized().orthogonal() * tr_bidi_offset; tl.from += offset; tl.to += offset; } for (int j = 0; j < node_rects.size(); j++) { - if (node_rects[j].node_name == tl.from_node) { + if (node_rects[j].node_name == local_from) { _clip_src_line_to_rect(tl.from, tl.to, node_rects[j].node); } - if (node_rects[j].node_name == tl.to_node) { + if (node_rects[j].node_name == local_to) { _clip_dst_line_to_rect(tl.from, tl.to, node_rects[j].node); } } - bool selected = selected_transition_from == tl.from_node && selected_transition_to == tl.to_node; - - bool travel = false; + tl.selected = selected_transition_from == tl.from_node && selected_transition_to == tl.to_node; - if (blend_from == tl.from_node && current == tl.to_node) { - travel = true; + if (blend_from == local_from && current == local_to) { + tl.travel = true; } if (travel_path.size()) { - if (current == tl.from_node && travel_path[0] == tl.to_node) { - travel = true; + if (current == local_from && travel_path[0] == local_to) { + tl.travel = true; } else { for (int j = 0; j < travel_path.size() - 1; j++) { - if (travel_path[j] == tl.from_node && travel_path[j + 1] == tl.to_node) { - travel = true; + if (travel_path[j] == local_from && travel_path[j + 1] == local_to) { + tl.travel = true; break; } } } } - bool auto_advance = tl.auto_advance; StringName fullpath = AnimationTreeEditor::get_singleton()->get_base_path() + String(tl.advance_condition_name); if (tl.advance_condition_name != StringName() && bool(AnimationTreeEditor::get_singleton()->get_tree()->get(fullpath))) { tl.advance_condition_state = true; - auto_advance = true; + tl.auto_advance = true; } - _connection_draw(tl.from, tl.to, tl.mode, !tl.disabled, selected, travel, auto_advance); + // check if already have this local transition + for (int j = 0; j < transition_lines.size(); j++) { + StringName from = String(transition_lines[j].from_node).get_slicec('/', 0); + StringName to = String(transition_lines[j].to_node).get_slicec('/', 0); + from = from == ".." ? state_machine->start_node : from; + to = to == ".." ? state_machine->end_node : to; + + if (from == local_from && to == local_to) { + tl.hidden = true; + transition_lines.write[j].disabled = transition_lines[j].disabled && tl.disabled; + transition_lines.write[j].multi_transitions.push_back(tl); + } + } transition_lines.push_back(tl); } + for (int i = 0; i < transition_lines.size(); i++) { + TransitionLine tl = transition_lines[i]; + if (!tl.hidden) { + _connection_draw(tl.from, tl.to, tl.mode, !tl.disabled, tl.selected, tl.travel, tl.auto_advance, !tl.multi_transitions.is_empty()); + } + } + //draw actual nodes for (int i = 0; i < node_rects.size(); i++) { String name = node_rects[i].node_name; Ref<AnimationNode> anode = state_machine->get_node(name); bool needs_editor = AnimationTreeEditor::get_singleton()->can_edit(anode); - Ref<StyleBox> sb = name == selected_node ? style_selected : style; + Ref<StyleBox> sb = selected_nodes.has(name) ? style_selected : style; int strsize = font->get_string_size(name, font_size).width; - NodeRect &nr = node_rects.write[i]; Vector2 offset = nr.node.position; @@ -756,18 +1390,16 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { //now scroll it to draw state_machine_draw->draw_style_box(sb, nr.node); - if (playing && (blend_from == name || current == name || travel_path.has(name))) { - state_machine_draw->draw_style_box(playing_overlay, nr.node); + if (state_machine->start_node == name) { + state_machine_draw->draw_style_box(sb == style_selected ? style_selected : start_overlay, nr.node); } - bool onstart = state_machine->get_start_node() == name; - if (onstart) { - state_machine_draw->draw_string(font, offset + Vector2(0, -font->get_height(font_size) - 3 * EDSCALE + font->get_ascent(font_size)), TTR("Start"), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color); + if (state_machine->end_node == name) { + state_machine_draw->draw_style_box(sb == style_selected ? style_selected : end_overlay, nr.node); } - if (state_machine->get_end_node() == name) { - int endofs = nr.node.size.x - font->get_string_size(TTR("End"), font_size).x; - state_machine_draw->draw_string(font, offset + Vector2(endofs, -font->get_height(font_size) - 3 * EDSCALE + font->get_ascent(font_size)), TTR("End"), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color); + if (playing && (blend_from == name || current == name || travel_path.has(name))) { + state_machine_draw->draw_style_box(playing_overlay, nr.node); } offset.x += sb->get_offset().x; @@ -775,13 +1407,14 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { nr.play.position = offset + Vector2(0, (h - play->get_height()) / 2).floor(); nr.play.size = play->get_size(); - Ref<Texture2D> play_tex = onstart ? auto_play : play; + Ref<Texture2D> play_tex = play; if (over_node == name && over_node_what == 0) { state_machine_draw->draw_texture(play_tex, nr.play.position, accent); } else { state_machine_draw->draw_texture(play_tex, nr.play.position); } + offset.x += sep + play->get_width(); nr.name.position = offset + Vector2(0, (h - font->get_height(font_size)) / 2).floor(); @@ -799,10 +1432,14 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { } else { state_machine_draw->draw_texture(edit, nr.edit.position); } - offset.x += sep + edit->get_width(); } } + //draw box select + if (box_selecting) { + state_machine_draw->draw_rect(box_selecting_rect, Color(0.7, 0.7, 1.0, 0.3)); + } + scroll_range.position -= state_machine_draw->get_size(); scroll_range.size += state_machine_draw->get_size() * 2.0; @@ -829,6 +1466,10 @@ void AnimationNodeStateMachineEditor::_state_machine_pos_draw() { return; } + if (playback->get_current_node() == state_machine->start_node || playback->get_current_node() == state_machine->end_node) { + return; + } + int idx = -1; for (int i = 0; i < node_rects.size(); i++) { if (node_rects[i].node_name == playback->get_current_node()) { @@ -902,8 +1543,8 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { transition_mode->add_icon_item(get_theme_icon(SNAME("TransitionEnd"), SNAME("EditorIcons")), TTR("At End")); tool_erase->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); - tool_autoplay->set_icon(get_theme_icon(SNAME("AutoPlay"), SNAME("EditorIcons"))); - tool_end->set_icon(get_theme_icon(SNAME("AutoEnd"), SNAME("EditorIcons"))); + tool_group->set_icon(get_theme_icon(SNAME("Group"), SNAME("EditorIcons"))); + tool_ungroup->set_icon(get_theme_icon(SNAME("Ungroup"), SNAME("EditorIcons"))); play_mode->clear(); play_mode->add_icon_item(get_theme_icon(SNAME("PlayTravel"), SNAME("EditorIcons")), TTR("Travel")); @@ -1109,94 +1750,121 @@ void AnimationNodeStateMachineEditor::_scroll_changed(double) { state_machine_draw->update(); } -void AnimationNodeStateMachineEditor::_erase_selected() { - if (selected_node != StringName() && state_machine->has_node(selected_node)) { - updating = true; +void AnimationNodeStateMachineEditor::_erase_selected(const bool p_nested_action) { + if (!selected_nodes.is_empty()) { + if (!p_nested_action) { + updating = true; + } undo_redo->create_action(TTR("Node Removed")); - undo_redo->add_do_method(state_machine.ptr(), "remove_node", selected_node); - undo_redo->add_undo_method(state_machine.ptr(), "add_node", selected_node, state_machine->get_node(selected_node), state_machine->get_node_position(selected_node)); - for (int i = 0; i < state_machine->get_transition_count(); i++) { - String from = state_machine->get_transition_from(i); - String to = state_machine->get_transition_to(i); - if (from == selected_node || to == selected_node) { - undo_redo->add_undo_method(state_machine.ptr(), "add_transition", from, to, state_machine->get_transition(i)); + + for (int i = 0; i < node_rects.size(); i++) { + if (node_rects[i].node_name == state_machine->start_node || node_rects[i].node_name == state_machine->end_node) { + continue; + } + + if (!selected_nodes.has(node_rects[i].node_name)) { + continue; + } + + undo_redo->add_do_method(state_machine.ptr(), "remove_node", node_rects[i].node_name); + undo_redo->add_undo_method(state_machine.ptr(), "add_node", node_rects[i].node_name, + state_machine->get_node(node_rects[i].node_name), + state_machine->get_node_position(node_rects[i].node_name)); + + for (int j = 0; j < state_machine->get_transition_count(); j++) { + String from = state_machine->get_transition_from(j); + String to = state_machine->get_transition_to(j); + String local_from = from.get_slicec('/', 0); + String local_to = to.get_slicec('/', 0); + + if (local_from == node_rects[i].node_name || local_to == node_rects[i].node_name) { + undo_redo->add_undo_method(state_machine.ptr(), "add_transition", from, to, state_machine->get_transition(j)); + } } } - if (String(state_machine->get_start_node()) == selected_node) { - undo_redo->add_undo_method(state_machine.ptr(), "set_start_node", selected_node); - } + undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); - updating = false; - selected_node = StringName(); + + if (!p_nested_action) { + updating = false; + } + + selected_nodes.clear(); + } + + if (!selected_multi_transition.multi_transitions.is_empty()) { + delete_tree->clear(); + + TreeItem *root = delete_tree->create_item(); + + TreeItem *item = delete_tree->create_item(root); + item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + item->set_text(0, String(selected_transition_from) + " -> " + selected_transition_to); + item->set_editable(0, true); + + for (int i = 0; i < selected_multi_transition.multi_transitions.size(); i++) { + String from = selected_multi_transition.multi_transitions[i].from_node; + String to = selected_multi_transition.multi_transitions[i].to_node; + + item = delete_tree->create_item(root); + item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + item->set_text(0, from + " -> " + to); + item->set_editable(0, true); + } + + delete_window->popup_centered(Vector2(400, 200)); + return; } if (selected_transition_to != StringName() && selected_transition_from != StringName() && state_machine->has_transition(selected_transition_from, selected_transition_to)) { Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(state_machine->find_transition(selected_transition_from, selected_transition_to)); - updating = true; + if (!p_nested_action) { + updating = true; + } undo_redo->create_action(TTR("Transition Removed")); undo_redo->add_do_method(state_machine.ptr(), "remove_transition", selected_transition_from, selected_transition_to); undo_redo->add_undo_method(state_machine.ptr(), "add_transition", selected_transition_from, selected_transition_to, tr); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); - updating = false; + if (!p_nested_action) { + updating = false; + } selected_transition_from = StringName(); selected_transition_to = StringName(); + selected_transition_index = -1; + selected_multi_transition = TransitionLine(); } state_machine_draw->update(); } -void AnimationNodeStateMachineEditor::_autoplay_selected() { - if (selected_node != StringName() && state_machine->has_node(selected_node)) { - StringName new_start_node; - if (state_machine->get_start_node() == selected_node) { //toggle it - new_start_node = StringName(); - } else { - new_start_node = selected_node; - } - - updating = true; - undo_redo->create_action(TTR("Set Start Node (Autoplay)")); - undo_redo->add_do_method(state_machine.ptr(), "set_start_node", new_start_node); - undo_redo->add_undo_method(state_machine.ptr(), "set_start_node", state_machine->get_start_node()); - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); - undo_redo->commit_action(); - updating = false; - state_machine_draw->update(); - } -} - -void AnimationNodeStateMachineEditor::_end_selected() { - if (selected_node != StringName() && state_machine->has_node(selected_node)) { - StringName new_end_node; - if (state_machine->get_end_node() == selected_node) { //toggle it - new_end_node = StringName(); - } else { - new_end_node = selected_node; - } - - updating = true; - undo_redo->create_action(TTR("Set Start Node (Autoplay)")); - undo_redo->add_do_method(state_machine.ptr(), "set_end_node", new_end_node); - undo_redo->add_undo_method(state_machine.ptr(), "set_end_node", state_machine->get_end_node()); - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); - undo_redo->commit_action(); - updating = false; - state_machine_draw->update(); - } -} - void AnimationNodeStateMachineEditor::_update_mode() { if (tool_select->is_pressed()) { tool_erase_hb->show(); - tool_erase->set_disabled(selected_node == StringName() && selected_transition_from == StringName() && selected_transition_to == StringName()); - tool_autoplay->set_disabled(selected_node == StringName()); - tool_end->set_disabled(selected_node == StringName()); + bool nothing_selected = selected_nodes.is_empty() && selected_transition_from == StringName() && selected_transition_to == StringName(); + bool start_end_selected = selected_nodes.size() == 1 && (*selected_nodes.begin() == state_machine->start_node || *selected_nodes.begin() == state_machine->end_node); + tool_erase->set_disabled(nothing_selected || start_end_selected); + + if (selected_nodes.is_empty() || start_end_selected) { + tool_group->set_disabled(true); + tool_group->set_visible(true); + tool_ungroup->set_visible(false); + } else { + Ref<AnimationNodeStateMachine> ansm = state_machine->get_node(*selected_nodes.begin()); + + if (selected_nodes.size() == 1 && ansm.is_valid()) { + tool_group->set_disabled(true); + tool_group->set_visible(false); + tool_ungroup->set_visible(true); + } else { + tool_group->set_disabled(false); + tool_group->set_visible(true); + tool_ungroup->set_visible(false); + } + } } else { tool_erase_hb->hide(); } @@ -1204,17 +1872,19 @@ void AnimationNodeStateMachineEditor::_update_mode() { void AnimationNodeStateMachineEditor::_bind_methods() { ClassDB::bind_method("_update_graph", &AnimationNodeStateMachineEditor::_update_graph); - ClassDB::bind_method("_removed_from_graph", &AnimationNodeStateMachineEditor::_removed_from_graph); - ClassDB::bind_method("_open_editor", &AnimationNodeStateMachineEditor::_open_editor); + ClassDB::bind_method("_connect_to", &AnimationNodeStateMachineEditor::_connect_to); + ClassDB::bind_method("_stop_connecting", &AnimationNodeStateMachineEditor::_stop_connecting); + ClassDB::bind_method("_delete_selected", &AnimationNodeStateMachineEditor::_delete_selected); + ClassDB::bind_method("_delete_all", &AnimationNodeStateMachineEditor::_delete_all); + ClassDB::bind_method("_delete_tree_draw", &AnimationNodeStateMachineEditor::_delete_tree_draw); } AnimationNodeStateMachineEditor *AnimationNodeStateMachineEditor::singleton = nullptr; AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { singleton = this; - updating = false; HBoxContainer *top_hb = memnew(HBoxContainer); add_child(top_hb); @@ -1228,7 +1898,7 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { tool_select->set_toggle_mode(true); tool_select->set_button_group(bg); tool_select->set_pressed(true); - tool_select->set_tooltip(TTR("Select and move nodes.\nRMB to add new nodes.\nShift+LMB to create connections.")); + tool_select->set_tooltip(TTR("Select and move nodes.\nRMB: Add node at position clicked.\nShift+LMB+Drag: Connects the selected node with another node or creates a new node if you select an area without nodes.")); tool_select->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_update_mode), varray(), CONNECT_DEFERRED); tool_create = memnew(Button); @@ -1250,31 +1920,30 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { tool_erase_hb = memnew(HBoxContainer); top_hb->add_child(tool_erase_hb); tool_erase_hb->add_child(memnew(VSeparator)); + + tool_group = memnew(Button); + tool_group->set_flat(true); + tool_group->set_tooltip(TTR("Group Selected Node(s)") + " (Ctrl+G)"); + tool_group->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_group_selected_nodes)); + tool_group->set_disabled(true); + tool_erase_hb->add_child(tool_group); + + tool_ungroup = memnew(Button); + tool_ungroup->set_flat(true); + tool_ungroup->set_tooltip(TTR("Ungroup Selected Node") + " (Ctrl+Shift+G)"); + tool_ungroup->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_ungroup_selected_nodes)); + tool_ungroup->set_visible(false); + tool_erase_hb->add_child(tool_ungroup); + tool_erase = memnew(Button); tool_erase->set_flat(true); tool_erase->set_tooltip(TTR("Remove selected node or transition.")); - tool_erase_hb->add_child(tool_erase); - tool_erase->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_erase_selected)); + tool_erase->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_erase_selected), varray(false)); tool_erase->set_disabled(true); - - tool_erase_hb->add_child(memnew(VSeparator)); - - tool_autoplay = memnew(Button); - tool_autoplay->set_flat(true); - tool_autoplay->set_tooltip(TTR("Toggle autoplay this animation on start, restart or seek to zero.")); - tool_erase_hb->add_child(tool_autoplay); - tool_autoplay->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_autoplay_selected)); - tool_autoplay->set_disabled(true); - - tool_end = memnew(Button); - tool_end->set_flat(true); - tool_end->set_tooltip(TTR("Set the end animation. This is useful for sub-transitions.")); - tool_erase_hb->add_child(tool_end); - tool_end->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_end_selected)); - tool_end->set_disabled(true); + tool_erase_hb->add_child(tool_erase); top_hb->add_child(memnew(VSeparator)); - top_hb->add_child(memnew(Label(TTR("Transition: ")))); + top_hb->add_child(memnew(Label(TTR("Transition:")))); transition_mode = memnew(OptionButton); top_hb->add_child(transition_mode); @@ -1327,12 +1996,28 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { menu = memnew(PopupMenu); add_child(menu); menu->connect("id_pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_add_menu_type)); + menu->connect("popup_hide", callable_mp(this, &AnimationNodeStateMachineEditor::_stop_connecting)); animations_menu = memnew(PopupMenu); menu->add_child(animations_menu); animations_menu->set_name("animations"); animations_menu->connect("index_pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_add_animation_type)); + connect_menu = memnew(PopupMenu); + add_child(connect_menu); + connect_menu->connect("id_pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_connect_to)); + connect_menu->connect("popup_hide", callable_mp(this, &AnimationNodeStateMachineEditor::_stop_connecting)); + + state_machine_menu = memnew(PopupMenu); + state_machine_menu->set_name("state_machines"); + state_machine_menu->connect("id_pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_connect_to)); + connect_menu->add_child(state_machine_menu); + + end_menu = memnew(PopupMenu); + end_menu->set_name("end_nodes"); + end_menu->connect("id_pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_connect_to)); + connect_menu->add_child(end_menu); + name_edit_popup = memnew(Popup); add_child(name_edit_popup); name_edit = memnew(LineEdit); @@ -1348,11 +2033,94 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { open_file->connect("file_selected", callable_mp(this, &AnimationNodeStateMachineEditor::_file_opened)); undo_redo = EditorNode::get_undo_redo(); + delete_window = memnew(ConfirmationDialog); + delete_window->set_flag(Window::FLAG_RESIZE_DISABLED, true); + add_child(delete_window); + + delete_tree = memnew(Tree); + delete_tree->set_hide_root(true); + delete_tree->connect("draw", callable_mp(this, &AnimationNodeStateMachineEditor::_delete_tree_draw)); + delete_window->add_child(delete_tree); + + Button *ok = delete_window->get_cancel_button(); + ok->set_text(TTR("Delete Selected")); + ok->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_delete_selected)); + + Button *delete_all = delete_window->add_button(TTR("Delete All"), true); + delete_all->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_delete_all)); + over_node_what = -1; dragging_selected_attempt = false; connecting = false; + selected_transition_index = -1; last_active = false; error_time = 0; } + +void EditorAnimationMultiTransitionEdit::add_transition(const StringName &p_from, const StringName &p_to, Ref<AnimationNodeStateMachineTransition> p_transition) { + Transition tr; + tr.from = p_from; + tr.to = p_to; + tr.transition = p_transition; + transitions.push_back(tr); +} + +bool EditorAnimationMultiTransitionEdit::_set(const StringName &p_name, const Variant &p_property) { + int index = String(p_name).get_slicec('/', 0).to_int(); + StringName prop = String(p_name).get_slicec('/', 1); + + bool found; + transitions.write[index].transition->set(prop, p_property, &found); + if (found) { + return true; + } + + return false; +} + +bool EditorAnimationMultiTransitionEdit::_get(const StringName &p_name, Variant &r_property) const { + int index = String(p_name).get_slicec('/', 0).to_int(); + StringName prop = String(p_name).get_slicec('/', 1); + + if (prop == "transition_path") { + r_property = String(transitions[index].from) + " -> " + transitions[index].to; + return true; + } + + bool found; + r_property = transitions[index].transition->get(prop, &found); + if (found) { + return true; + } + + return false; +} + +void EditorAnimationMultiTransitionEdit::_get_property_list(List<PropertyInfo> *p_list) const { + for (int i = 0; i < transitions.size(); i++) { + List<PropertyInfo> plist; + transitions[i].transition->get_property_list(&plist, true); + + PropertyInfo prop_transition_path; + prop_transition_path.type = Variant::STRING; + prop_transition_path.name = itos(i) + "/" + "transition_path"; + p_list->push_back(prop_transition_path); + + for (List<PropertyInfo>::Element *F = plist.front(); F; F = F->next()) { + if (F->get().name == "script" || F->get().name == "resource_name" || F->get().name == "resource_path" || F->get().name == "resource_local_to_scene") { + continue; + } + + if (F->get().usage != PROPERTY_USAGE_DEFAULT) { + continue; + } + + PropertyInfo prop = F->get(); + prop.name = itos(i) + "/" + prop.name; + + p_list->push_back(prop); + } + } +} diff --git a/editor/plugins/animation_state_machine_editor.h b/editor/plugins/animation_state_machine_editor.h index bf3f7e93cf..ea16abd64c 100644 --- a/editor/plugins/animation_state_machine_editor.h +++ b/editor/plugins/animation_state_machine_editor.h @@ -50,13 +50,13 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { Button *tool_select = nullptr; Button *tool_create = nullptr; Button *tool_connect = nullptr; + Button *tool_group = nullptr; + Button *tool_ungroup = nullptr; Popup *name_edit_popup = nullptr; LineEdit *name_edit = nullptr; HBoxContainer *tool_erase_hb = nullptr; Button *tool_erase = nullptr; - Button *tool_autoplay = nullptr; - Button *tool_end = nullptr; OptionButton *transition_mode = nullptr; OptionButton *play_mode = nullptr; @@ -64,6 +64,7 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { PanelContainer *panel = nullptr; StringName selected_node; + HashSet<StringName> selected_nodes; HScrollBar *h_scroll = nullptr; VScrollBar *v_scroll = nullptr; @@ -74,39 +75,53 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { PanelContainer *error_panel = nullptr; Label *error_label = nullptr; - bool updating; + bool updating = false; UndoRedo *undo_redo = nullptr; static AnimationNodeStateMachineEditor *singleton; void _state_machine_gui_input(const Ref<InputEvent> &p_event); - void _connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, bool p_auto_advance); + void _connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, bool p_auto_advance, bool p_multi_transitions); void _state_machine_draw(); void _state_machine_pos_draw(); void _update_graph(); PopupMenu *menu = nullptr; + PopupMenu *connect_menu = nullptr; + PopupMenu *state_machine_menu = nullptr; + PopupMenu *end_menu = nullptr; PopupMenu *animations_menu = nullptr; Vector<String> animations_to_add; + Vector<String> nodes_to_connect; Vector2 add_node_pos; - bool dragging_selected_attempt; - bool dragging_selected; + ConfirmationDialog *delete_window; + Tree *delete_tree; + + bool box_selecting = false; + Point2 box_selecting_from; + Point2 box_selecting_to; + Rect2 box_selecting_rect; + HashSet<StringName> previous_selected; + + bool dragging_selected_attempt = false; + bool dragging_selected = false; Vector2 drag_from; Vector2 drag_ofs; StringName snap_x; StringName snap_y; - bool connecting; + bool connecting = false; StringName connecting_from; Vector2 connecting_to; StringName connecting_to_node; void _add_menu_type(int p_index); void _add_animation_type(int p_index); + void _connect_to(int p_index); void _removed_from_graph(); @@ -131,15 +146,37 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { bool disabled = false; bool auto_advance = false; float width = 0; + bool selected; + bool travel; + bool hidden; + int transition_index; + Vector<TransitionLine> multi_transitions; }; Vector<TransitionLine> transition_lines; + struct NodeUR { + StringName name; + Ref<AnimationNode> node; + Vector2 position; + }; + + struct TransitionUR { + StringName new_from; + StringName new_to; + StringName old_from; + StringName old_to; + Ref<AnimationNodeStateMachineTransition> transition; + }; + StringName selected_transition_from; StringName selected_transition_to; + int selected_transition_index; + TransitionLine selected_multi_transition; + void _add_transition(const bool p_nested_action = false); StringName over_node; - int over_node_what; + int over_node_what = -1; String prev_name; void _name_edited(const String &p_text); @@ -147,23 +184,32 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { void _open_editor(const String &p_name); void _scroll_changed(double); - void _clip_src_line_to_rect(Vector2 &r_from, Vector2 &r_to, const Rect2 &p_rect); - void _clip_dst_line_to_rect(Vector2 &r_from, Vector2 &r_to, const Rect2 &p_rect); + void _clip_src_line_to_rect(Vector2 &r_from, const Vector2 &p_to, const Rect2 &p_rect); + void _clip_dst_line_to_rect(const Vector2 &p_from, Vector2 &r_to, const Rect2 &p_rect); - void _erase_selected(); + void _erase_selected(const bool p_nested_action = false); void _update_mode(); - void _autoplay_selected(); - void _end_selected(); + void _open_menu(const Vector2 &p_position); + void _open_connect_menu(const Vector2 &p_position); + bool _create_submenu(PopupMenu *p_menu, Ref<AnimationNodeStateMachine> p_nodesm, const StringName &p_name, const StringName &p_path, bool from_root = false, Vector<Ref<AnimationNodeStateMachine>> p_parents = Vector<Ref<AnimationNodeStateMachine>>()); + void _stop_connecting(); + + void _group_selected_nodes(); + void _ungroup_selected_nodes(); - bool last_active; + void _delete_selected(); + void _delete_all(); + void _delete_tree_draw(); + + bool last_active = false; StringName last_blend_from_node; StringName last_current_node; Vector<StringName> last_travel_path; - float last_play_pos; - float play_pos; - float current_length; + float last_play_pos = 0.0f; + float play_pos = 0.0f; + float current_length = 0.0f; - float error_time; + float error_time = 0.0f; String error_text; EditorFileDialog *open_file = nullptr; @@ -188,4 +234,26 @@ public: AnimationNodeStateMachineEditor(); }; +class EditorAnimationMultiTransitionEdit : public RefCounted { + GDCLASS(EditorAnimationMultiTransitionEdit, RefCounted); + + struct Transition { + StringName from; + StringName to; + Ref<AnimationNodeStateMachineTransition> transition; + }; + + Vector<Transition> transitions; + +protected: + bool _set(const StringName &p_name, const Variant &p_property); + bool _get(const StringName &p_name, Variant &r_property) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + +public: + void add_transition(const StringName &p_from, const StringName &p_to, Ref<AnimationNodeStateMachineTransition> p_transition); + + EditorAnimationMultiTransitionEdit(){}; +}; + #endif // ANIMATION_STATE_MACHINE_EDITOR_H diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 157eed02f4..249b3a6d6a 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -897,6 +897,19 @@ void EditorAssetLibrary::_request_image(ObjectID p_for, String p_image_url, Imag } void EditorAssetLibrary::_repository_changed(int p_repository_id) { + library_error->hide(); + library_info->set_text(TTR("Loading...")); + library_info->show(); + + asset_top_page->hide(); + asset_bottom_page->hide(); + asset_items->hide(); + + filter->set_editable(false); + sort->set_disabled(true); + categories->set_disabled(true); + support->set_disabled(true); + host = repository->get_item_metadata(p_repository_id); if (templates_only) { _api_request("configure", REQUESTING_CONFIG, "?type=project"); @@ -965,6 +978,10 @@ void EditorAssetLibrary::_filter_debounce_timer_timeout() { _search(); } +void EditorAssetLibrary::_request_current_config() { + _repository_changed(repository->get_selected()); +} + HBoxContainer *EditorAssetLibrary::_make_pages(int p_page, int p_page_count, int p_page_len, int p_total_items, int p_current_items) { HBoxContainer *hbc = memnew(HBoxContainer); @@ -1106,6 +1123,10 @@ void EditorAssetLibrary::_http_request_completed(int p_status, int p_code, const } if (error_abort) { + if (requesting == REQUESTING_CONFIG) { + library_info->hide(); + library_error->show(); + } error_hb->show(); return; } @@ -1140,17 +1161,16 @@ void EditorAssetLibrary::_http_request_completed(int p_status, int p_code, const } } + filter->set_editable(true); + sort->set_disabled(false); + categories->set_disabled(false); + support->set_disabled(false); + _search(); } break; case REQUESTING_SEARCH: { initial_loading = false; - // The loading text only needs to be displayed before the first page is loaded. - // Therefore, we don't need to show it again. - library_loading->hide(); - - library_error->hide(); - if (asset_items) { memdelete(asset_items); } @@ -1190,8 +1210,8 @@ void EditorAssetLibrary::_http_request_completed(int p_status, int p_code, const asset_items = memnew(GridContainer); asset_items->set_columns(2); - asset_items->add_theme_constant_override("hseparation", 10 * EDSCALE); - asset_items->add_theme_constant_override("vseparation", 10 * EDSCALE); + asset_items->add_theme_constant_override("h_separation", 10 * EDSCALE); + asset_items->add_theme_constant_override("v_separation", 10 * EDSCALE); library_vb->add_child(asset_items); @@ -1200,16 +1220,18 @@ void EditorAssetLibrary::_http_request_completed(int p_status, int p_code, const if (result.is_empty()) { if (!filter->get_text().is_empty()) { - library_error->set_text( + library_info->set_text( vformat(TTR("No results for \"%s\"."), filter->get_text())); } else { // No results, even though the user didn't search for anything specific. // This is typically because the version number changed recently // and no assets compatible with the new version have been published yet. - library_error->set_text( + library_info->set_text( vformat(TTR("No results compatible with %s %s."), String(VERSION_SHORT_NAME).capitalize(), String(VERSION_BRANCH))); } - library_error->show(); + library_info->show(); + } else { + library_info->hide(); } for (int i = 0; i < result.size(); i++) { @@ -1357,6 +1379,10 @@ void EditorAssetLibrary::disable_community_support() { support->get_popup()->set_item_checked(SUPPORT_COMMUNITY, false); } +void EditorAssetLibrary::set_columns(const int p_columns) { + asset_items->set_columns(p_columns); +} + void EditorAssetLibrary::_bind_methods() { ADD_SIGNAL(MethodInfo("install_asset", PropertyInfo(Variant::STRING, "zip_path"), PropertyInfo(Variant::STRING, "name"))); } @@ -1376,9 +1402,9 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) { filter = memnew(LineEdit); if (templates_only) { - filter->set_placeholder(TTR("Search templates, projects, and demos")); + filter->set_placeholder(TTR("Search Templates, Projects, and Demos")); } else { - filter->set_placeholder(TTR("Search assets (excluding templates, projects, and demos)")); + filter->set_placeholder(TTR("Search Assets (Excluding Templates, Projects, and Demos)")); } filter->set_clear_button_enabled(true); search_hb->add_child(filter); @@ -1424,6 +1450,7 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) { search_hb2->add_child(sort); sort->set_h_size_flags(Control::SIZE_EXPAND_FILL); + sort->set_clip_text(true); sort->connect("item_selected", callable_mp(this, &EditorAssetLibrary::_rerun_search)); search_hb2->add_child(memnew(VSeparator)); @@ -1433,6 +1460,7 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) { categories->add_item(TTR("All")); search_hb2->add_child(categories); categories->set_h_size_flags(Control::SIZE_EXPAND_FILL); + categories->set_clip_text(true); categories->connect("item_selected", callable_mp(this, &EditorAssetLibrary::_rerun_search)); search_hb2->add_child(memnew(VSeparator)); @@ -1446,6 +1474,7 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) { search_hb2->add_child(repository); repository->set_h_size_flags(Control::SIZE_EXPAND_FILL); + repository->set_clip_text(true); search_hb2->add_child(memnew(VSeparator)); @@ -1488,22 +1517,30 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) { library_vb_border->add_child(library_vb); - library_loading = memnew(Label(TTR("Loading..."))); - library_loading->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); - library_vb->add_child(library_loading); + library_info = memnew(Label); + library_info->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); + library_vb->add_child(library_info); - library_error = memnew(Label); - library_error->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); + library_error = memnew(VBoxContainer); library_error->hide(); library_vb->add_child(library_error); + library_error_label = memnew(Label(TTR("Failed to get repository configuration."))); + library_error_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); + library_error->add_child(library_error_label); + + library_error_retry = memnew(Button(TTR("Retry"))); + library_error_retry->set_h_size_flags(SIZE_SHRINK_CENTER); + library_error_retry->connect("pressed", callable_mp(this, &EditorAssetLibrary::_request_current_config)); + library_error->add_child(library_error_retry); + asset_top_page = memnew(HBoxContainer); library_vb->add_child(asset_top_page); asset_items = memnew(GridContainer); asset_items->set_columns(2); - asset_items->add_theme_constant_override("hseparation", 10 * EDSCALE); - asset_items->add_theme_constant_override("vseparation", 10 * EDSCALE); + asset_items->add_theme_constant_override("h_separation", 10 * EDSCALE); + asset_items->add_theme_constant_override("v_separation", 10 * EDSCALE); library_vb->add_child(asset_items); diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h index 96830c31fd..e09700b646 100644 --- a/editor/plugins/asset_library_editor_plugin.h +++ b/editor/plugins/asset_library_editor_plugin.h @@ -60,9 +60,9 @@ class EditorAssetLibraryItem : public PanelContainer { TextureRect *stars[5]; Label *price = nullptr; - int asset_id; - int category_id; - int author_id; + int asset_id = 0; + int category_id = 0; + int author_id = 0; void _asset_clicked(); void _category_clicked(); @@ -102,7 +102,7 @@ class EditorAssetLibraryItemDescription : public ConfirmationDialog { void set_image(int p_type, int p_index, const Ref<Texture2D> &p_image); - int asset_id; + int asset_id = 0; String download_url; String title; String sha256; @@ -146,7 +146,7 @@ class EditorAssetLibraryItemDownload : public MarginContainer { int prev_status; - int asset_id; + int asset_id = 0; bool external_install; @@ -186,8 +186,10 @@ class EditorAssetLibrary : public PanelContainer { PanelContainer *library_scroll_bg = nullptr; ScrollContainer *library_scroll = nullptr; VBoxContainer *library_vb = nullptr; - Label *library_loading = nullptr; - Label *library_error = nullptr; + Label *library_info = nullptr; + VBoxContainer *library_error = nullptr; + Label *library_error_label = nullptr; + Button *library_error_retry = nullptr; LineEdit *filter = nullptr; Timer *filter_debounce_timer = nullptr; OptionButton *categories = nullptr; @@ -250,7 +252,7 @@ class EditorAssetLibrary : public PanelContainer { }; int last_queue_id; - Map<int, ImageQueue> image_queue; + HashMap<int, ImageQueue> image_queue; void _image_update(bool use_cache, bool final, const PackedByteArray &p_data, int p_queue_id); void _image_request_completed(int p_status, int p_code, const PackedStringArray &headers, const PackedByteArray &p_data, int p_queue_id); @@ -291,6 +293,7 @@ class EditorAssetLibrary : public PanelContainer { void _api_request(const String &p_request, RequestType p_request_type, const String &p_arguments = ""); void _http_request_completed(int p_status, int p_code, const PackedStringArray &headers, const PackedByteArray &p_data); void _filter_debounce_timer_timeout(); + void _request_current_config(); EditorAssetLibraryItemDownload *_get_asset_in_progress(int p_asset_id) const; void _repository_changed(int p_repository_id); @@ -308,6 +311,7 @@ protected: public: void disable_community_support(); + void set_columns(int p_columns); EditorAssetLibrary(bool p_templates_only = false); }; diff --git a/editor/plugins/bit_map_editor_plugin.cpp b/editor/plugins/bit_map_editor_plugin.cpp new file mode 100644 index 0000000000..9003c4480b --- /dev/null +++ b/editor/plugins/bit_map_editor_plugin.cpp @@ -0,0 +1,86 @@ +/*************************************************************************/ +/* bit_map_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "bit_map_editor_plugin.h" + +#include "editor/editor_scale.h" + +void BitMapEditor::setup(const Ref<BitMap> &p_bitmap) { + Ref<ImageTexture> texture; + texture.instantiate(); + texture->create_from_image(p_bitmap->convert_to_image()); + texture_rect->set_texture(texture); + + size_label->set_text(vformat(String::utf8("%s×%s"), p_bitmap->get_size().width, p_bitmap->get_size().height)); +} + +BitMapEditor::BitMapEditor() { + texture_rect = memnew(TextureRect); + texture_rect->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED); + texture_rect->set_texture_filter(TEXTURE_FILTER_NEAREST); + texture_rect->set_custom_minimum_size(Size2(0, 250) * EDSCALE); + add_child(texture_rect); + + size_label = memnew(Label); + size_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); + add_child(size_label); + + // Reduce extra padding on top and bottom of size label. + Ref<StyleBoxEmpty> stylebox; + stylebox.instantiate(); + stylebox->set_default_margin(SIDE_RIGHT, 4 * EDSCALE); + size_label->add_theme_style_override("normal", stylebox); +} + +/////////////////////// + +bool EditorInspectorPluginBitMap::can_handle(Object *p_object) { + return Object::cast_to<BitMap>(p_object) != nullptr; +} + +void EditorInspectorPluginBitMap::parse_begin(Object *p_object) { + BitMap *bitmap = Object::cast_to<BitMap>(p_object); + if (!bitmap) { + return; + } + Ref<BitMap> bm(bitmap); + + BitMapEditor *editor = memnew(BitMapEditor); + editor->setup(bm); + add_custom_control(editor); +} + +/////////////////////// + +BitMapEditorPlugin::BitMapEditorPlugin() { + Ref<EditorInspectorPluginBitMap> plugin; + plugin.instantiate(); + add_inspector_plugin(plugin); +} diff --git a/editor/plugins/bit_map_editor_plugin.h b/editor/plugins/bit_map_editor_plugin.h new file mode 100644 index 0000000000..c883e5542f --- /dev/null +++ b/editor/plugins/bit_map_editor_plugin.h @@ -0,0 +1,64 @@ +/*************************************************************************/ +/* bit_map_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef BIT_MAP_PREVIEW_EDITOR_PLUGIN_H +#define BIT_MAP_PREVIEW_EDITOR_PLUGIN_H + +#include "editor/editor_plugin.h" +#include "scene/resources/bit_map.h" + +class BitMapEditor : public VBoxContainer { + GDCLASS(BitMapEditor, VBoxContainer); + + TextureRect *texture_rect = nullptr; + Label *size_label = nullptr; + +public: + void setup(const Ref<BitMap> &p_bitmap); + + BitMapEditor(); +}; + +class EditorInspectorPluginBitMap : public EditorInspectorPlugin { + GDCLASS(EditorInspectorPluginBitMap, EditorInspectorPlugin); + +public: + virtual bool can_handle(Object *p_object) override; + virtual void parse_begin(Object *p_object) override; +}; + +class BitMapEditorPlugin : public EditorPlugin { + GDCLASS(BitMapEditorPlugin, EditorPlugin); + +public: + BitMapEditorPlugin(); +}; + +#endif // BIT_MAP_PREVIEW_EDITOR_PLUGIN_H diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 8d0db697e2..1ea0299d4e 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -236,7 +236,7 @@ public: }; bool CanvasItemEditor::_is_node_locked(const Node *p_node) { - return p_node->has_meta("_edit_lock_") && p_node->get_meta("_edit_lock_"); + return p_node->get_meta("_edit_lock_", false); } bool CanvasItemEditor::_is_node_movable(const Node *p_node, bool p_popup_warning) { @@ -420,16 +420,14 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, unsig } if (((is_snap_active && snap_guides && (p_modes & SNAP_GUIDES)) || (p_forced_modes & SNAP_GUIDES)) && fmod(rotation, (real_t)360.0) == 0.0) { - // Guides - if (EditorNode::get_singleton()->get_edited_scene() && EditorNode::get_singleton()->get_edited_scene()->has_meta("_edit_vertical_guides_")) { - Array vguides = EditorNode::get_singleton()->get_edited_scene()->get_meta("_edit_vertical_guides_"); + // Guides. + if (Node *scene = EditorNode::get_singleton()->get_edited_scene()) { + Array vguides = scene->get_meta("_edit_vertical_guides_", Array()); for (int i = 0; i < vguides.size(); i++) { _snap_if_closer_float(p_target.x, output.x, snap_target[0], vguides[i], SNAP_TARGET_GUIDE); } - } - if (EditorNode::get_singleton()->get_edited_scene() && EditorNode::get_singleton()->get_edited_scene()->has_meta("_edit_horizontal_guides_")) { - Array hguides = EditorNode::get_singleton()->get_edited_scene()->get_meta("_edit_horizontal_guides_"); + Array hguides = scene->get_meta("_edit_horizontal_guides_", Array()); for (int i = 0; i < hguides.size(); i++) { _snap_if_closer_float(p_target.y, output.y, snap_target[1], hguides[i], SNAP_TARGET_GUIDE); } @@ -564,7 +562,11 @@ void CanvasItemEditor::_expand_encompassing_rect_using_children(Rect2 &r_rect, c } if (canvas_item && canvas_item->is_visible_in_tree() && (include_locked_nodes || !_is_node_locked(canvas_item))) { - Transform2D xform = p_parent_xform * p_canvas_xform * canvas_item->get_transform(); + Transform2D xform = p_canvas_xform; + if (!canvas_item->is_set_as_top_level()) { + xform *= p_parent_xform; + } + xform *= canvas_item->get_transform(); Rect2 rect = canvas_item->_edit_get_rect(); if (r_first) { r_rect = Rect2(xform.xform(rect.get_center()), Size2()); @@ -610,7 +612,11 @@ void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_no } if (canvas_item && canvas_item->is_visible_in_tree()) { - Transform2D xform = (p_parent_xform * p_canvas_xform * canvas_item->get_transform()).affine_inverse(); + Transform2D xform = p_canvas_xform; + if (!canvas_item->is_set_as_top_level()) { + xform *= p_parent_xform; + } + xform = (xform * canvas_item->get_transform()).affine_inverse(); const real_t local_grab_distance = xform.basis_xform(Vector2(grab_distance, 0)).length() / zoom; if (canvas_item->_edit_is_selected_on_click(xform.xform(p_pos), local_grab_distance)) { Node2D *node = Object::cast_to<Node2D>(canvas_item); @@ -681,7 +687,7 @@ void CanvasItemEditor::_find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_n Node *scene = EditorNode::get_singleton()->get_edited_scene(); bool editable = p_node == scene || p_node->get_owner() == scene || p_node == scene->get_deepest_editable_node(p_node); - bool lock_children = p_node->has_meta("_edit_group_") && p_node->get_meta("_edit_group_"); + bool lock_children = p_node->get_meta("_edit_group_", false); bool locked = _is_node_locked(p_node); if (!lock_children || !editable) { @@ -700,7 +706,11 @@ void CanvasItemEditor::_find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_n } if (canvas_item && canvas_item->is_visible_in_tree() && !locked && editable) { - Transform2D xform = p_parent_xform * p_canvas_xform * canvas_item->get_transform(); + Transform2D xform = p_canvas_xform; + if (!canvas_item->is_set_as_top_level()) { + xform *= p_parent_xform; + } + xform *= canvas_item->get_transform(); if (canvas_item->_edit_use_rect()) { Rect2 rect = canvas_item->_edit_get_rect(); @@ -1000,14 +1010,8 @@ bool CanvasItemEditor::_gui_input_rulers_and_guides(const Ref<InputEvent> &p_eve if (show_guides && show_rulers && EditorNode::get_singleton()->get_edited_scene()) { Transform2D xform = viewport_scrollable->get_transform() * transform; // Retrieve the guide lists - Array vguides; - if (EditorNode::get_singleton()->get_edited_scene()->has_meta("_edit_vertical_guides_")) { - vguides = EditorNode::get_singleton()->get_edited_scene()->get_meta("_edit_vertical_guides_"); - } - Array hguides; - if (EditorNode::get_singleton()->get_edited_scene()->has_meta("_edit_horizontal_guides_")) { - hguides = EditorNode::get_singleton()->get_edited_scene()->get_meta("_edit_horizontal_guides_"); - } + Array vguides = EditorNode::get_singleton()->get_edited_scene()->get_meta("_edit_vertical_guides_", Array()); + Array hguides = EditorNode::get_singleton()->get_edited_scene()->get_meta("_edit_horizontal_guides_", Array()); // Hover over guides real_t minimum = 1e20; @@ -1100,14 +1104,8 @@ bool CanvasItemEditor::_gui_input_rulers_and_guides(const Ref<InputEvent> &p_eve Transform2D xform = viewport_scrollable->get_transform() * transform; // Retrieve the guide lists - Array vguides; - if (EditorNode::get_singleton()->get_edited_scene()->has_meta("_edit_vertical_guides_")) { - vguides = EditorNode::get_singleton()->get_edited_scene()->get_meta("_edit_vertical_guides_"); - } - Array hguides; - if (EditorNode::get_singleton()->get_edited_scene()->has_meta("_edit_horizontal_guides_")) { - hguides = EditorNode::get_singleton()->get_edited_scene()->get_meta("_edit_horizontal_guides_"); - } + Array vguides = EditorNode::get_singleton()->get_edited_scene()->get_meta("_edit_vertical_guides_", Array()); + Array hguides = EditorNode::get_singleton()->get_edited_scene()->get_meta("_edit_horizontal_guides_", Array()); Point2 edited = snap_point(xform.affine_inverse().xform(b->get_position()), SNAP_GRID | SNAP_PIXEL | SNAP_OTHER_NODES); if (drag_type == DRAG_V_GUIDE) { @@ -1286,10 +1284,12 @@ void CanvasItemEditor::_pan_callback(Vector2 p_scroll_vec) { } void CanvasItemEditor::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { - zoom_widget->set_zoom_by_increments(-1, p_alt); - if (!Math::is_equal_approx(p_scroll_vec.y, (real_t)1.0)) { + int scroll_sign = (int)SIGN(p_scroll_vec.y); + // Inverted so that scrolling up (-1) zooms in, scrolling down (+1) zooms out. + zoom_widget->set_zoom_by_increments(-scroll_sign, p_alt); + if (!Math::is_equal_approx(ABS(p_scroll_vec.y), (real_t)1.0)) { // Handle high-precision (analog) scrolling. - zoom_widget->set_zoom(zoom * ((zoom_widget->get_zoom() / zoom - 1.f) * p_scroll_vec.y + 1.f)); + zoom_widget->set_zoom(zoom * ((zoom_widget->get_zoom() / zoom - 1.f) * ABS(p_scroll_vec.y) + 1.f)); } _zoom_on_position(zoom_widget->get_zoom(), p_origin); } @@ -2712,9 +2712,9 @@ void CanvasItemEditor::_draw_guides() { Color guide_color = EditorSettings::get_singleton()->get("editors/2d/guides_color"); Transform2D xform = viewport_scrollable->get_transform() * transform; - // Guides already there - if (EditorNode::get_singleton()->get_edited_scene() && EditorNode::get_singleton()->get_edited_scene()->has_meta("_edit_vertical_guides_")) { - Array vguides = EditorNode::get_singleton()->get_edited_scene()->get_meta("_edit_vertical_guides_"); + // Guides already there. + if (Node *scene = EditorNode::get_singleton()->get_edited_scene()) { + Array vguides = scene->get_meta("_edit_vertical_guides_", Array()); for (int i = 0; i < vguides.size(); i++) { if (drag_type == DRAG_V_GUIDE && i == dragged_guide_index) { continue; @@ -2722,10 +2722,8 @@ void CanvasItemEditor::_draw_guides() { real_t x = xform.xform(Point2(vguides[i], 0)).x; viewport->draw_line(Point2(x, 0), Point2(x, viewport->get_size().y), guide_color, Math::round(EDSCALE)); } - } - if (EditorNode::get_singleton()->get_edited_scene() && EditorNode::get_singleton()->get_edited_scene()->has_meta("_edit_horizontal_guides_")) { - Array hguides = EditorNode::get_singleton()->get_edited_scene()->get_meta("_edit_horizontal_guides_"); + Array hguides = scene->get_meta("_edit_horizontal_guides_", Array()); for (int i = 0; i < hguides.size(); i++) { if (drag_type == DRAG_H_GUIDE && i == dragged_guide_index) { continue; @@ -2735,7 +2733,7 @@ void CanvasItemEditor::_draw_guides() { } } - // Dragged guide + // Dragged guide. Color text_color = get_theme_color(SNAME("font_color"), SNAME("Editor")); Color outline_color = text_color.inverted(); const float outline_size = 2; @@ -3372,7 +3370,7 @@ void CanvasItemEditor::_draw_selection() { // Draw the resize handles if (tool == TOOL_SELECT && canvas_item->_edit_use_rect() && _is_node_movable(canvas_item)) { Rect2 rect = canvas_item->_edit_get_rect(); - Vector2 endpoints[4] = { + const Vector2 endpoints[4] = { xform.xform(rect.position), xform.xform(rect.position + Vector2(rect.size.x, 0)), xform.xform(rect.position + rect.size), @@ -3675,7 +3673,7 @@ void CanvasItemEditor::_draw_viewport() { // Update the transform transform = Transform2D(); transform.scale_basis(Size2(zoom, zoom)); - transform.elements[2] = -view_offset * zoom; + transform.columns[2] = -view_offset * zoom; EditorNode::get_singleton()->get_scene_root()->set_global_canvas_transform(transform); // hide/show buttons depending on the selection @@ -3781,7 +3779,7 @@ void CanvasItemEditor::_update_editor_settings() { key_auto_insert_button->add_theme_color_override("icon_pressed_color", key_auto_color.lerp(Color(1, 0, 0), 0.55)); animation_menu->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons"))); - _update_context_menu_stylebox(); + context_menu_container->add_theme_style_override("panel", get_theme_stylebox(SNAME("ContextualToolbar"), SNAME("EditorStyles"))); panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/2d_editor_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning"))); pan_speed = int(EditorSettings::get_singleton()->get("editors/panning/2d_editor_pan_speed")); @@ -3917,18 +3915,6 @@ 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(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); - context_menu_stylebox->set_border_width(SIDE_BOTTOM, Math::round(2 * EDSCALE)); - context_menu_stylebox->set_default_margin(SIDE_BOTTOM, 0); - context_menu_container->add_theme_style_override("panel", context_menu_stylebox); -} - void CanvasItemEditor::_update_scrollbars() { updating_scroll = true; @@ -4101,8 +4087,10 @@ 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) { - const Map<Node *, Object *> &selection = editor_selection->get_selection(); + const HashMap<Node *, Object *> &selection = editor_selection->get_selection(); + AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor(); + te->make_insert_queue(); 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()) { @@ -4117,13 +4105,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::get_singleton()->get_track_editor()->insert_node_value_key(n2d, "position", n2d->get_position(), p_on_existing); + te->insert_node_value_key(n2d, "position", n2d->get_position(), p_on_existing); } if (key_rot && p_rotation) { - AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(n2d, "rotation", n2d->get_rotation(), p_on_existing); + te->insert_node_value_key(n2d, "rotation", n2d->get_rotation(), p_on_existing); } if (key_scale && p_scale) { - AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(n2d, "scale", n2d->get_scale(), p_on_existing); + te->insert_node_value_key(n2d, "scale", n2d->get_scale(), p_on_existing); } if (n2d->has_meta("_edit_bone_") && n2d->get_parent_item()) { @@ -4149,13 +4137,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::get_singleton()->get_track_editor()->insert_node_value_key(F, "position", F->get_position(), p_on_existing); + te->insert_node_value_key(F, "position", F->get_position(), p_on_existing); } if (key_rot) { - AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(F, "rotation", F->get_rotation(), p_on_existing); + te->insert_node_value_key(F, "rotation", F->get_rotation(), p_on_existing); } if (key_scale) { - AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(F, "scale", F->get_scale(), p_on_existing); + te->insert_node_value_key(F, "scale", F->get_scale(), p_on_existing); } } } @@ -4165,16 +4153,17 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation, Control *ctrl = Object::cast_to<Control>(canvas_item); if (key_pos) { - AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(ctrl, "rect_position", ctrl->get_position(), p_on_existing); + te->insert_node_value_key(ctrl, "rect_position", ctrl->get_position(), p_on_existing); } if (key_rot) { - AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(ctrl, "rect_rotation", ctrl->get_rotation(), p_on_existing); + te->insert_node_value_key(ctrl, "rect_rotation", ctrl->get_rotation(), p_on_existing); } if (key_scale) { - AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(ctrl, "rect_size", ctrl->get_size(), p_on_existing); + te->insert_node_value_key(ctrl, "rect_size", ctrl->get_size(), p_on_existing); } } } + te->commit_insert_queue(); } void CanvasItemEditor::_update_override_camera_button(bool p_game_running) { @@ -4412,7 +4401,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { case ANIM_COPY_POSE: { pose_clipboard.clear(); - const Map<Node *, Object *> &selection = editor_selection->get_selection(); + const HashMap<Node *, Object *> &selection = editor_selection->get_selection(); for (const KeyValue<Node *, Object *> &E : selection) { CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E.key); @@ -4458,7 +4447,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { } break; case ANIM_CLEAR_POSE: { - Map<Node *, Object *> &selection = editor_selection->get_selection(); + HashMap<Node *, Object *> &selection = editor_selection->get_selection(); for (const KeyValue<Node *, Object *> &E : selection) { CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E.key); @@ -4527,7 +4516,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { } break; case SKELETON_MAKE_BONES: { - Map<Node *, Object *> &selection = editor_selection->get_selection(); + HashMap<Node *, Object *> &selection = editor_selection->get_selection(); 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)")); @@ -4575,7 +4564,7 @@ void CanvasItemEditor::_focus_selection(int p_op) { Rect2 rect; int count = 0; - Map<Node *, Object *> &selection = editor_selection->get_selection(); + const HashMap<Node *, Object *> &selection = editor_selection->get_selection(); for (const KeyValue<Node *, Object *> &E : selection) { CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E.key); if (!canvas_item) { @@ -4888,14 +4877,6 @@ CanvasItemEditor::CanvasItemEditor() { view_offset = Point2(-150 - RULER_WIDTH, -95 - RULER_WIDTH); previous_update_view_offset = view_offset; // Moves the view a little bit to the left so that (0,0) is visible. The values a relative to a 16/10 screen - grid_offset = Point2(); - grid_step = Point2(8, 8); // A power-of-two value works better as a default - primary_grid_steps = 8; // A power-of-two value works better as a default - grid_step_multiplier = 0; - - snap_rotation_offset = 0; - snap_rotation_step = Math::deg2rad(15.0); - snap_scale_step = 0.1f; snap_target[0] = SNAP_TARGET_NONE; snap_target[1] = SNAP_TARGET_NONE; @@ -4944,6 +4925,19 @@ CanvasItemEditor::CanvasItemEditor() { controls_vb = memnew(VBoxContainer); controls_vb->set_begin(Point2(5, 5)); + // To ensure that scripts can parse the list of shortcuts correctly, we have to define + // those shortcuts one by one. Define shortcut before using it (by EditorZoomWidget) + ED_SHORTCUT("canvas_item_editor/zoom_3.125_percent", TTR("Zoom to 3.125%"), KeyModifierMask::SHIFT | Key::KEY_5); + ED_SHORTCUT("canvas_item_editor/zoom_6.25_percent", TTR("Zoom to 6.25%"), KeyModifierMask::SHIFT | Key::KEY_4); + ED_SHORTCUT("canvas_item_editor/zoom_12.5_percent", TTR("Zoom to 12.5%"), KeyModifierMask::SHIFT | Key::KEY_3); + ED_SHORTCUT("canvas_item_editor/zoom_25_percent", TTR("Zoom to 25%"), KeyModifierMask::SHIFT | Key::KEY_2); + ED_SHORTCUT("canvas_item_editor/zoom_50_percent", TTR("Zoom to 50%"), KeyModifierMask::SHIFT | Key::KEY_1); + ED_SHORTCUT_ARRAY("canvas_item_editor/zoom_100_percent", TTR("Zoom to 100%"), { (int32_t)Key::KEY_1, (int32_t)(KeyModifierMask::CMD | Key::KEY_0) }); + ED_SHORTCUT("canvas_item_editor/zoom_200_percent", TTR("Zoom to 200%"), Key::KEY_2); + ED_SHORTCUT("canvas_item_editor/zoom_400_percent", TTR("Zoom to 400%"), Key::KEY_3); + ED_SHORTCUT("canvas_item_editor/zoom_800_percent", TTR("Zoom to 800%"), Key::KEY_4); + ED_SHORTCUT("canvas_item_editor/zoom_1600_percent", TTR("Zoom to 1600%"), Key::KEY_5); + zoom_widget = memnew(EditorZoomWidget); controls_vb->add_child(zoom_widget); zoom_widget->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT, Control::PRESET_MODE_MINSIZE, 2 * EDSCALE); @@ -5168,11 +5162,12 @@ CanvasItemEditor::CanvasItemEditor() { hb->add_child(memnew(VSeparator)); view_menu = memnew(MenuButton); - view_menu->set_shortcut_context(this); + // TRANSLATORS: Noun, name of the 2D/3D View menus. view_menu->set_text(TTR("View")); + view_menu->set_switch_on_hover(true); + view_menu->set_shortcut_context(this); hb->add_child(view_menu); view_menu->get_popup()->connect("id_pressed", callable_mp(this, &CanvasItemEditor::_popup_callback)); - view_menu->set_switch_on_hover(true); p = view_menu->get_popup(); p->set_hide_on_checkable_item_selection(false); @@ -5209,11 +5204,7 @@ CanvasItemEditor::CanvasItemEditor() { context_menu_container = memnew(PanelContainer); hbc_context_menu = memnew(HBoxContainer); context_menu_container->add_child(hbc_context_menu); - // Use a custom stylebox to make contextual menu items stand out from the rest. - // This helps with editor usability as contextual menu items change when selecting nodes, - // even though it may not be immediately obvious at first. hb->add_child(context_menu_container); - _update_context_menu_stylebox(); // Animation controls. animation_hb = memnew(HBoxContainer); @@ -5305,21 +5296,6 @@ CanvasItemEditor::CanvasItemEditor() { // Store the singleton instance. singleton = this; - // To ensure that scripts can parse the list of shortcuts correctly, we have to define - // those shortcuts one by one. - // Resetting zoom to 100% is a duplicate shortcut of `canvas_item_editor/reset_zoom`, - // but it ensures both 1 and Ctrl + 0 can be used to reset zoom. - ED_SHORTCUT("canvas_item_editor/zoom_3.125_percent", TTR("Zoom to 3.125%"), KeyModifierMask::SHIFT | Key::KEY_5); - ED_SHORTCUT("canvas_item_editor/zoom_6.25_percent", TTR("Zoom to 6.25%"), KeyModifierMask::SHIFT | Key::KEY_4); - ED_SHORTCUT("canvas_item_editor/zoom_12.5_percent", TTR("Zoom to 12.5%"), KeyModifierMask::SHIFT | Key::KEY_3); - ED_SHORTCUT("canvas_item_editor/zoom_25_percent", TTR("Zoom to 25%"), KeyModifierMask::SHIFT | Key::KEY_2); - ED_SHORTCUT("canvas_item_editor/zoom_50_percent", TTR("Zoom to 50%"), KeyModifierMask::SHIFT | Key::KEY_1); - ED_SHORTCUT("canvas_item_editor/zoom_100_percent", TTR("Zoom to 100%"), Key::KEY_1); - ED_SHORTCUT("canvas_item_editor/zoom_200_percent", TTR("Zoom to 200%"), Key::KEY_2); - ED_SHORTCUT("canvas_item_editor/zoom_400_percent", TTR("Zoom to 400%"), Key::KEY_3); - ED_SHORTCUT("canvas_item_editor/zoom_800_percent", TTR("Zoom to 800%"), Key::KEY_4); - ED_SHORTCUT("canvas_item_editor/zoom_1600_percent", TTR("Zoom to 1600%"), Key::KEY_5); - set_process_shortcut_input(true); // Update the menus' checkboxes @@ -5401,7 +5377,7 @@ void CanvasItemEditorViewport::_create_preview(const Vector<String> &files) cons bool add_preview = false; for (int i = 0; i < files.size(); i++) { String path = files[i]; - RES res = ResourceLoader::load(path); + Ref<Resource> res = ResourceLoader::load(path); ERR_FAIL_COND(res.is_null()); Ref<Texture2D> texture = Ref<Texture2D>(Object::cast_to<Texture2D>(*res)); Ref<PackedScene> scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*res)); @@ -5596,7 +5572,7 @@ void CanvasItemEditorViewport::_perform_drop_data() { for (int i = 0; i < selected_files.size(); i++) { String path = selected_files[i]; - RES res = ResourceLoader::load(path); + Ref<Resource> res = ResourceLoader::load(path); if (res.is_null()) { continue; } @@ -5651,7 +5627,7 @@ bool CanvasItemEditorViewport::can_drop_data(const Point2 &p_point, const Varian for (int i = 0; i < files.size(); i++) { // Check if dragged files with texture or scene extension can be created at least once. if (texture_extensions.find(files[i].get_extension()) || scene_extensions.find(files[i].get_extension())) { - RES res = ResourceLoader::load(files[i]); + Ref<Resource> res = ResourceLoader::load(files[i]); if (res.is_null()) { continue; } diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 26852ea8ed..c20a054800 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -180,7 +180,7 @@ private: GRID_VISIBILITY_HIDE, }; - bool selection_menu_additive_selection; + bool selection_menu_additive_selection = false; Tool tool = TOOL_SELECT; Control *viewport = nullptr; @@ -204,20 +204,20 @@ private: bool show_edit_locks = true; bool show_transformation_gizmos = true; - real_t zoom; + real_t zoom = 1.0; Point2 view_offset; Point2 previous_update_view_offset; bool selected_from_canvas = false; Point2 grid_offset; - Point2 grid_step; - int primary_grid_steps; - int grid_step_multiplier; + Point2 grid_step = Point2(8, 8); // A power-of-two value works better as a default. + int primary_grid_steps = 8; + int grid_step_multiplier = 0; - real_t snap_rotation_step; - real_t snap_rotation_offset; - real_t snap_scale_step; + real_t snap_rotation_step = 0.0; + real_t snap_rotation_offset = Math::deg2rad(15.0); + real_t snap_scale_step = 0.1f; bool smart_snap_active = false; bool grid_snap_active = false; @@ -241,7 +241,7 @@ private: bool pan_pressed = false; bool ruler_tool_active = false; - Point2 ruler_tool_origin = Point2(); + Point2 ruler_tool_origin; Point2 node_create_position; MenuOption last_option; @@ -283,7 +283,7 @@ private: } }; - Map<BoneKey, BoneList> bone_list; + HashMap<BoneKey, BoneList> bone_list; struct PoseClipboard { Vector2 pos; @@ -346,7 +346,7 @@ private: bool is_hovering_h_guide = false; bool is_hovering_v_guide = false; - bool updating_value_dialog; + bool updating_value_dialog = false; Point2 box_selecting_to; @@ -492,8 +492,6 @@ private: HSplitContainer *right_panel_split = nullptr; VSplitContainer *bottom_split = nullptr; - void _update_context_menu_stylebox(); - void _set_owner_for_node_and_children(Node *p_node, Node *p_owner); friend class CanvasItemEditorPlugin; diff --git a/editor/plugins/control_editor_plugin.cpp b/editor/plugins/control_editor_plugin.cpp index 5ca8216d4d..3adaf8f601 100644 --- a/editor/plugins/control_editor_plugin.cpp +++ b/editor/plugins/control_editor_plugin.cpp @@ -120,7 +120,7 @@ ControlPositioningWarning::ControlPositioningWarning() { grid->add_child(title_icon); title_label = memnew(Label); - title_label->set_autowrap_mode(Label::AutowrapMode::AUTOWRAP_WORD); + title_label->set_autowrap_mode(TextServer::AutowrapMode::AUTOWRAP_WORD); title_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); title_label->set_vertical_alignment(VerticalAlignment::VERTICAL_ALIGNMENT_CENTER); grid->add_child(title_label); @@ -135,7 +135,7 @@ ControlPositioningWarning::ControlPositioningWarning() { grid->add_child(hint_filler_left); hint_label = memnew(Label); - hint_label->set_autowrap_mode(Label::AutowrapMode::AUTOWRAP_WORD); + hint_label->set_autowrap_mode(TextServer::AutowrapMode::AUTOWRAP_WORD); hint_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); hint_label->set_vertical_alignment(VerticalAlignment::VERTICAL_ALIGNMENT_CENTER); hint_label->hide(); @@ -335,7 +335,7 @@ void EditorPropertySizeFlags::setup(const Vector<String> &p_options, bool p_vert return; } - Map<int, String> flags; + HashMap<int, String> flags; for (int i = 0, j = 0; i < p_options.size(); i++, j++) { Vector<String> text_split = p_options[i].split(":"); int64_t current_val = text_split[1].to_int(); @@ -509,7 +509,7 @@ void ControlEditorToolbar::_set_anchors_and_offsets_to_keep_ratio() { undo_redo->add_do_method(control, "set_anchor", SIDE_BOTTOM, bottom_right_anchor.y, false, true); undo_redo->add_do_method(control, "set_meta", "_edit_use_anchors_", true); - const bool use_anchors = control->has_meta("_edit_use_anchors_") && control->get_meta("_edit_use_anchors_"); + const bool use_anchors = control->get_meta("_edit_use_anchors_", false); undo_redo->add_undo_method(control, "_edit_set_state", control->_edit_get_state()); if (use_anchors) { undo_redo->add_undo_method(control, "set_meta", "_edit_use_anchors_", true); @@ -617,7 +617,7 @@ void ControlEditorToolbar::_button_toggle_anchor_mode(bool p_status) { } bool ControlEditorToolbar::_is_node_locked(const Node *p_node) { - return p_node->has_meta("_edit_lock_") && p_node->get_meta("_edit_lock_"); + return p_node->get_meta("_edit_lock_", false); } List<Control *> ControlEditorToolbar::_get_edited_controls(bool retrieve_locked, bool remove_controls_if_parent_in_selection) { @@ -798,7 +798,7 @@ void ControlEditorToolbar::_selection_changed() { } nb_valid_controls++; - if (control->has_meta("_edit_use_anchors_") && control->get_meta("_edit_use_anchors_")) { + if (control->get_meta("_edit_use_anchors_", false)) { nb_anchors_mode++; } } diff --git a/editor/plugins/debugger_editor_plugin.cpp b/editor/plugins/debugger_editor_plugin.cpp index c8d5aa3fdc..8ea50c4529 100644 --- a/editor/plugins/debugger_editor_plugin.cpp +++ b/editor/plugins/debugger_editor_plugin.cpp @@ -55,7 +55,7 @@ DebuggerEditorPlugin::DebuggerEditorPlugin(MenuButton *p_debug_menu) { EditorDebuggerNode *debugger = memnew(EditorDebuggerNode); Button *db = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Debugger"), debugger); // Add separation for the warning/error icon that is displayed later. - db->add_theme_constant_override("hseparation", 6 * EDSCALE); + db->add_theme_constant_override("h_separation", 6 * EDSCALE); debugger->set_tool_button(db); // Main editor debug menu. diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index b8556220d2..bb0cfcba25 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -79,7 +79,7 @@ bool EditorTexturePreviewPlugin::generate_small_preview_automatically() const { return true; } -Ref<Texture2D> EditorTexturePreviewPlugin::generate(const RES &p_from, const Size2 &p_size) const { +Ref<Texture2D> EditorTexturePreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const { Ref<Image> img; Ref<AtlasTexture> atex = p_from; if (atex.is_valid()) { @@ -145,7 +145,7 @@ bool EditorImagePreviewPlugin::handles(const String &p_type) const { return p_type == "Image"; } -Ref<Texture2D> EditorImagePreviewPlugin::generate(const RES &p_from, const Size2 &p_size) const { +Ref<Texture2D> EditorImagePreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const { Ref<Image> img = p_from; if (img.is_null() || img->is_empty()) { @@ -194,7 +194,7 @@ bool EditorBitmapPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "BitMap"); } -Ref<Texture2D> EditorBitmapPreviewPlugin::generate(const RES &p_from, const Size2 &p_size) const { +Ref<Texture2D> EditorBitmapPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const { Ref<BitMap> bm = p_from; if (bm->get_size() == Size2()) { @@ -261,7 +261,7 @@ bool EditorPackedScenePreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "PackedScene"); } -Ref<Texture2D> EditorPackedScenePreviewPlugin::generate(const RES &p_from, const Size2 &p_size) const { +Ref<Texture2D> EditorPackedScenePreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const { return generate_from_path(p_from->get_path(), p_size); } @@ -316,7 +316,7 @@ bool EditorMaterialPreviewPlugin::generate_small_preview_automatically() const { return true; } -Ref<Texture2D> EditorMaterialPreviewPlugin::generate(const RES &p_from, const Size2 &p_size) const { +Ref<Texture2D> EditorMaterialPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const { Ref<Material> material = p_from; ERR_FAIL_COND_V(material.is_null(), Ref<Texture2D>()); @@ -467,7 +467,7 @@ bool EditorScriptPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "Script"); } -Ref<Texture2D> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size2 &p_size) const { +Ref<Texture2D> EditorScriptPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const { Ref<Script> scr = p_from; if (scr.is_null()) { return Ref<Texture2D>(); @@ -481,8 +481,8 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size List<String> kwors; scr->get_language()->get_reserved_words(&kwors); - Set<String> control_flow_keywords; - Set<String> keywords; + HashSet<String> control_flow_keywords; + HashSet<String> keywords; for (const String &E : kwors) { if (scr->get_language()->is_control_flow_keyword(E)) { @@ -609,7 +609,7 @@ bool EditorAudioStreamPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "AudioStream"); } -Ref<Texture2D> EditorAudioStreamPreviewPlugin::generate(const RES &p_from, const Size2 &p_size) const { +Ref<Texture2D> EditorAudioStreamPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const { Ref<AudioStream> stream = p_from; ERR_FAIL_COND_V(stream.is_null(), Ref<Texture2D>()); @@ -703,7 +703,7 @@ bool EditorMeshPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "Mesh"); // Any mesh. } -Ref<Texture2D> EditorMeshPreviewPlugin::generate(const RES &p_from, const Size2 &p_size) const { +Ref<Texture2D> EditorMeshPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const { Ref<Mesh> mesh = p_from; ERR_FAIL_COND_V(mesh.is_null(), Ref<Texture2D>()); @@ -816,7 +816,7 @@ bool EditorFontPreviewPlugin::handles(const String &p_type) const { } Ref<Texture2D> EditorFontPreviewPlugin::generate_from_path(const String &p_path, const Size2 &p_size) const { - RES res = ResourceLoader::load(p_path); + Ref<Resource> res = ResourceLoader::load(p_path); ERR_FAIL_COND_V(res.is_null(), Ref<Texture2D>()); Ref<Font> sampled_font; if (res->is_class("Font")) { @@ -877,7 +877,7 @@ Ref<Texture2D> EditorFontPreviewPlugin::generate_from_path(const String &p_path, return ptex; } -Ref<Texture2D> EditorFontPreviewPlugin::generate(const RES &p_from, const Size2 &p_size) const { +Ref<Texture2D> EditorFontPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const { String path = p_from->get_path(); if (!FileAccess::exists(path)) { return Ref<Texture2D>(); @@ -904,3 +904,34 @@ EditorFontPreviewPlugin::~EditorFontPreviewPlugin() { RS::get_singleton()->free(canvas); RS::get_singleton()->free(viewport); } + +//////////////////////////////////////////////////////////////////////////// + +static const real_t GRADIENT_PREVIEW_TEXTURE_SCALE_FACTOR = 4.0; + +bool EditorGradientPreviewPlugin::handles(const String &p_type) const { + return ClassDB::is_parent_class(p_type, "Gradient"); +} + +bool EditorGradientPreviewPlugin::generate_small_preview_automatically() const { + return true; +} + +Ref<Texture2D> EditorGradientPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const { + Ref<Gradient> gradient = p_from; + if (gradient.is_valid()) { + Ref<GradientTexture1D> ptex; + ptex.instantiate(); + ptex->set_width(p_size.width * GRADIENT_PREVIEW_TEXTURE_SCALE_FACTOR * EDSCALE); + ptex->set_gradient(gradient); + + Ref<ImageTexture> itex; + itex.instantiate(); + itex->create_from_image(ptex->get_image()); + return itex; + } + return Ref<Texture2D>(); +} + +EditorGradientPreviewPlugin::EditorGradientPreviewPlugin() { +} diff --git a/editor/plugins/editor_preview_plugins.h b/editor/plugins/editor_preview_plugins.h index 803f03f17e..f548683b70 100644 --- a/editor/plugins/editor_preview_plugins.h +++ b/editor/plugins/editor_preview_plugins.h @@ -42,7 +42,7 @@ class EditorTexturePreviewPlugin : public EditorResourcePreviewGenerator { public: virtual bool handles(const String &p_type) const override; virtual bool generate_small_preview_automatically() const override; - virtual Ref<Texture2D> generate(const RES &p_from, const Size2 &p_size) const override; + virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override; EditorTexturePreviewPlugin(); }; @@ -53,7 +53,7 @@ class EditorImagePreviewPlugin : public EditorResourcePreviewGenerator { public: virtual bool handles(const String &p_type) const override; virtual bool generate_small_preview_automatically() const override; - virtual Ref<Texture2D> generate(const RES &p_from, const Size2 &p_size) const override; + virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override; EditorImagePreviewPlugin(); }; @@ -64,7 +64,7 @@ class EditorBitmapPreviewPlugin : public EditorResourcePreviewGenerator { public: virtual bool handles(const String &p_type) const override; virtual bool generate_small_preview_automatically() const override; - virtual Ref<Texture2D> generate(const RES &p_from, const Size2 &p_size) const override; + virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override; EditorBitmapPreviewPlugin(); }; @@ -72,7 +72,7 @@ public: class EditorPackedScenePreviewPlugin : public EditorResourcePreviewGenerator { public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture2D> generate(const RES &p_from, const Size2 &p_size) const; + virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const; virtual Ref<Texture2D> generate_from_path(const String &p_path, const Size2 &p_size) const; EditorPackedScenePreviewPlugin(); @@ -99,7 +99,7 @@ class EditorMaterialPreviewPlugin : public EditorResourcePreviewGenerator { public: virtual bool handles(const String &p_type) const override; virtual bool generate_small_preview_automatically() const override; - virtual Ref<Texture2D> generate(const RES &p_from, const Size2 &p_size) const override; + virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override; EditorMaterialPreviewPlugin(); ~EditorMaterialPreviewPlugin(); @@ -108,7 +108,7 @@ public: class EditorScriptPreviewPlugin : public EditorResourcePreviewGenerator { public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture2D> generate(const RES &p_from, const Size2 &p_size) const; + virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const; EditorScriptPreviewPlugin(); }; @@ -116,7 +116,7 @@ public: class EditorAudioStreamPreviewPlugin : public EditorResourcePreviewGenerator { public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture2D> generate(const RES &p_from, const Size2 &p_size) const; + virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const; EditorAudioStreamPreviewPlugin(); }; @@ -140,7 +140,7 @@ class EditorMeshPreviewPlugin : public EditorResourcePreviewGenerator { public: virtual bool handles(const String &p_type) const override; - virtual Ref<Texture2D> generate(const RES &p_from, const Size2 &p_size) const override; + virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override; EditorMeshPreviewPlugin(); ~EditorMeshPreviewPlugin(); @@ -160,7 +160,7 @@ class EditorFontPreviewPlugin : public EditorResourcePreviewGenerator { public: virtual bool handles(const String &p_type) const override; - virtual Ref<Texture2D> generate(const RES &p_from, const Size2 &p_size) const override; + virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override; virtual Ref<Texture2D> generate_from_path(const String &p_path, const Size2 &p_size) const override; EditorFontPreviewPlugin(); @@ -177,9 +177,20 @@ class EditorTileMapPatternPreviewPlugin : public EditorResourcePreviewGenerator public: virtual bool handles(const String &p_type) const override; - virtual Ref<Texture2D> generate(const RES &p_from, const Size2 &p_size) const override; + virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override; EditorTileMapPatternPreviewPlugin(); ~EditorTileMapPatternPreviewPlugin(); }; + +class EditorGradientPreviewPlugin : public EditorResourcePreviewGenerator { + GDCLASS(EditorGradientPreviewPlugin, EditorResourcePreviewGenerator); + +public: + virtual bool handles(const String &p_type) const override; + virtual bool generate_small_preview_automatically() const override; + virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override; + + EditorGradientPreviewPlugin(); +}; #endif // EDITORPREVIEWPLUGINS_H diff --git a/editor/plugins/gdextension_export_plugin.h b/editor/plugins/gdextension_export_plugin.h index 8ed72b1c42..b91a17d9e5 100644 --- a/editor/plugins/gdextension_export_plugin.h +++ b/editor/plugins/gdextension_export_plugin.h @@ -35,10 +35,10 @@ class GDExtensionExportPlugin : public EditorExportPlugin { protected: - virtual void _export_file(const String &p_path, const String &p_type, const Set<String> &p_features); + virtual void _export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features); }; -void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p_type, const Set<String> &p_features) { +void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features) { if (p_type != "NativeExtension") { return; } @@ -47,14 +47,9 @@ void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p config.instantiate(); Error err = config->load(p_path); + ERR_FAIL_COND_MSG(err, "Failed to load GDExtension file: " + p_path); - if (err != OK) { - return; - } - - if (!config->has_section_key("configuration", "entry_symbol")) { - return; - } + ERR_FAIL_COND_MSG(!config->has_section_key("configuration", "entry_symbol"), "Failed to export GDExtension file, missing entry symbol: " + p_path); String entry_symbol = config->get_value("configuration", "entry_symbol"); @@ -62,6 +57,7 @@ void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p config->get_section_keys("libraries", &libraries); + bool could_export = false; for (const String &E : libraries) { Vector<String> tags = E.split("."); bool all_tags_met = true; @@ -101,13 +97,23 @@ void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p String linker_flags = "-Wl,-U,_" + entry_symbol; add_ios_linker_flags(linker_flags); } + could_export = true; break; } } + if (!could_export) { + Vector<String> tags; + for (const String &E : p_features) { + tags.append(E); + } + ERR_FAIL_MSG(vformat("Couldn't export extension: %s. No suitable library found for export flags: %s", p_path, String(", ").join(tags))); + } List<String> dependencies; + if (config->has_section("dependencies")) { + config->get_section_keys("dependencies", &dependencies); + } - config->get_section_keys("dependencies", &dependencies); for (const String &E : libraries) { Vector<String> tags = E.split("."); bool all_tags_met = true; diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.cpp b/editor/plugins/gpu_particles_2d_editor_plugin.cpp index b15aec87d9..72caa15e9c 100644 --- a/editor/plugins/gpu_particles_2d_editor_plugin.cpp +++ b/editor/plugins/gpu_particles_2d_editor_plugin.cpp @@ -290,7 +290,7 @@ void GPUParticles2DEditorPlugin::_generate_emission_mask() { { uint8_t *tw = texdata.ptrw(); - float *twf = (float *)tw; + float *twf = reinterpret_cast<float *>(tw); for (int i = 0; i < vpc; i++) { twf[i * 2 + 0] = valid_positions[i].x; twf[i * 2 + 1] = valid_positions[i].y; @@ -334,7 +334,7 @@ void GPUParticles2DEditorPlugin::_generate_emission_mask() { { uint8_t *tw = normdata.ptrw(); - float *twf = (float *)tw; + float *twf = reinterpret_cast<float *>(tw); for (int i = 0; i < vpc; i++) { twf[i * 2 + 0] = valid_normals[i].x; twf[i * 2 + 1] = valid_normals[i].y; diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.cpp b/editor/plugins/gpu_particles_3d_editor_plugin.cpp index 35cbff53f4..fa971679e6 100644 --- a/editor/plugins/gpu_particles_3d_editor_plugin.cpp +++ b/editor/plugins/gpu_particles_3d_editor_plugin.cpp @@ -42,7 +42,7 @@ bool GPUParticles3DEditorBase::_generate(Vector<Vector3> &points, Vector<Vector3 if (emission_fill->get_selected() < 2) { float area_accum = 0; - Map<float, int> triangle_area_map; + RBMap<float, int> triangle_area_map; for (int i = 0; i < geometry.size(); i++) { float area = geometry[i].get_area(); @@ -63,9 +63,9 @@ bool GPUParticles3DEditorBase::_generate(Vector<Vector3> &points, Vector<Vector3 for (int i = 0; i < emissor_count; i++) { float areapos = Math::random(0.0f, area_accum); - Map<float, int>::Element *E = triangle_area_map.find_closest(areapos); + RBMap<float, int>::Iterator E = triangle_area_map.find_closest(areapos); ERR_FAIL_COND_V(!E, false); - int index = E->get(); + int index = E->value; ERR_FAIL_INDEX_V(index, geometry.size(), false); // ok FINALLY get face @@ -213,7 +213,7 @@ GPUParticles3DEditorBase::GPUParticles3DEditorBase() { emission_fill->add_item(TTR("Surface Points")); emission_fill->add_item(TTR("Surface Points+Normal (Directed)")); emission_fill->add_item(TTR("Volume")); - emd_vb->add_margin_child(TTR("Emission Source: "), emission_fill); + emd_vb->add_margin_child(TTR("Emission Source:"), emission_fill); emission_dialog->get_ok_button()->set_text(TTR("Create")); emission_dialog->connect("confirmed", callable_mp(this, &GPUParticles3DEditorBase::_generate_emission_points)); @@ -354,7 +354,7 @@ void GPUParticles3DEditor::_generate_emission_points() { uint8_t *iw = point_img.ptrw(); memset(iw, 0, w * h * 3 * sizeof(float)); const Vector3 *r = points.ptr(); - float *wf = (float *)iw; + float *wf = reinterpret_cast<float *>(iw); for (int i = 0; i < point_count; i++) { wf[i * 3 + 0] = r[i].x; wf[i * 3 + 1] = r[i].y; @@ -383,7 +383,7 @@ void GPUParticles3DEditor::_generate_emission_points() { uint8_t *iw = point_img2.ptrw(); memset(iw, 0, w * h * 3 * sizeof(float)); const Vector3 *r = normals.ptr(); - float *wf = (float *)iw; + float *wf = reinterpret_cast<float *>(iw); for (int i = 0; i < point_count; i++) { wf[i * 3 + 0] = r[i].x; wf[i * 3 + 1] = r[i].y; diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.h b/editor/plugins/gpu_particles_3d_editor_plugin.h index 190fb9954b..6ba6d102ef 100644 --- a/editor/plugins/gpu_particles_3d_editor_plugin.h +++ b/editor/plugins/gpu_particles_3d_editor_plugin.h @@ -55,7 +55,7 @@ protected: Vector<Face3> geometry; bool _generate(Vector<Vector3> &points, Vector<Vector3> &normals); - virtual void _generate_emission_points() = 0; + virtual void _generate_emission_points(){}; void _node_selected(const NodePath &p_path); static void _bind_methods(); diff --git a/editor/plugins/gradient_editor_plugin.cpp b/editor/plugins/gradient_editor_plugin.cpp index e9d7808684..1386f03662 100644 --- a/editor/plugins/gradient_editor_plugin.cpp +++ b/editor/plugins/gradient_editor_plugin.cpp @@ -55,7 +55,7 @@ void GradientEditor::_gradient_changed() { void GradientEditor::_ramp_changed() { editing = true; UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); - undo_redo->create_action(TTR("Gradient Edited")); + undo_redo->create_action(TTR("Gradient Edited"), UndoRedo::MERGE_ENDS); undo_redo->add_do_method(gradient.ptr(), "set_offsets", get_offsets()); undo_redo->add_do_method(gradient.ptr(), "set_colors", get_colors()); undo_redo->add_do_method(gradient.ptr(), "set_interpolation_mode", get_interpolation_mode()); diff --git a/editor/plugins/input_event_editor_plugin.cpp b/editor/plugins/input_event_editor_plugin.cpp index fb0e260388..153eab32d2 100644 --- a/editor/plugins/input_event_editor_plugin.cpp +++ b/editor/plugins/input_event_editor_plugin.cpp @@ -80,12 +80,11 @@ void InputEventConfigContainer::set_event(const Ref<InputEvent> &p_event) { InputEventConfigContainer::InputEventConfigContainer() { input_event_text = memnew(Label); input_event_text->set_h_size_flags(SIZE_EXPAND_FILL); - input_event_text->set_autowrap_mode(Label::AutowrapMode::AUTOWRAP_WORD_SMART); + input_event_text->set_autowrap_mode(TextServer::AutowrapMode::AUTOWRAP_WORD_SMART); input_event_text->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); add_child(input_event_text); - open_config_button = memnew(Button); - open_config_button->set_text(TTR("Configure")); + open_config_button = EditorInspector::create_inspector_action_button(TTR("Configure")); open_config_button->connect("pressed", callable_mp(this, &InputEventConfigContainer::_configure_pressed)); add_child(open_config_button); diff --git a/editor/plugins/light_occluder_2d_editor_plugin.cpp b/editor/plugins/light_occluder_2d_editor_plugin.cpp index 4fbc8188b9..e7ef65c32b 100644 --- a/editor/plugins/light_occluder_2d_editor_plugin.cpp +++ b/editor/plugins/light_occluder_2d_editor_plugin.cpp @@ -96,7 +96,7 @@ void LightOccluder2DEditor::_create_resource() { undo_redo->create_action(TTR("Create Occluder Polygon")); undo_redo->add_do_method(node, "set_occluder_polygon", Ref<OccluderPolygon2D>(memnew(OccluderPolygon2D))); - undo_redo->add_undo_method(node, "set_occluder_polygon", Variant(REF())); + undo_redo->add_undo_method(node, "set_occluder_polygon", Variant(Ref<RefCounted>())); undo_redo->commit_action(); _menu_option(MODE_CREATE); diff --git a/editor/plugins/mesh_editor_plugin.cpp b/editor/plugins/mesh_editor_plugin.cpp index 4760b61dc8..7029768479 100644 --- a/editor/plugins/mesh_editor_plugin.cpp +++ b/editor/plugins/mesh_editor_plugin.cpp @@ -112,7 +112,7 @@ MeshEditor::MeshEditor() { viewport->set_world_3d(world_3d); //use own world add_child(viewport); viewport->set_disable_input(true); - viewport->set_msaa(Viewport::MSAA_2X); + viewport->set_msaa(Viewport::MSAA_4X); set_stretch(true); camera = memnew(Camera3D); camera->set_transform(Transform3D(Basis(), Vector3(0, 0, 1.1))); diff --git a/editor/plugins/mesh_instance_3d_editor_plugin.cpp b/editor/plugins/mesh_instance_3d_editor_plugin.cpp index d33803213a..d1f858315c 100644 --- a/editor/plugins/mesh_instance_3d_editor_plugin.cpp +++ b/editor/plugins/mesh_instance_3d_editor_plugin.cpp @@ -77,12 +77,14 @@ void MeshInstance3DEditor::_menu_option(int p_option) { StaticBody3D *body = memnew(StaticBody3D); body->add_child(cshape, true); - Node *owner = node == get_tree()->get_edited_scene_root() ? node : node->get_owner(); + Node *owner = get_tree()->get_edited_scene_root(); ur->create_action(TTR("Create Static Trimesh Body")); ur->add_do_method(node, "add_child", body, true); ur->add_do_method(body, "set_owner", owner); ur->add_do_method(cshape, "set_owner", owner); + ur->add_do_method(Node3DEditor::get_singleton(), "_request_gizmo", body); + ur->add_do_method(Node3DEditor::get_singleton(), "_request_gizmo", cshape); ur->add_do_reference(body); ur->add_undo_method(node, "remove_child", body); ur->commit_action(); @@ -112,11 +114,13 @@ void MeshInstance3DEditor::_menu_option(int p_option) { StaticBody3D *body = memnew(StaticBody3D); body->add_child(cshape, true); - Node *owner = instance == get_tree()->get_edited_scene_root() ? instance : instance->get_owner(); + Node *owner = get_tree()->get_edited_scene_root(); ur->add_do_method(instance, "add_child", body, true); ur->add_do_method(body, "set_owner", owner); ur->add_do_method(cshape, "set_owner", owner); + ur->add_do_method(Node3DEditor::get_singleton(), "_request_gizmo", body); + ur->add_do_method(Node3DEditor::get_singleton(), "_request_gizmo", cshape); ur->add_do_reference(body); ur->add_undo_method(instance, "remove_child", body); } @@ -141,7 +145,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) { cshape->set_shape(shape); cshape->set_transform(node->get_transform()); - Node *owner = node->get_owner(); + Node *owner = get_tree()->get_edited_scene_root(); UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); @@ -150,6 +154,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) { ur->add_do_method(node->get_parent(), "add_child", cshape, true); ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1); ur->add_do_method(cshape, "set_owner", owner); + ur->add_do_method(Node3DEditor::get_singleton(), "_request_gizmo", cshape); ur->add_do_reference(cshape); ur->add_undo_method(node->get_parent(), "remove_child", cshape); ur->commit_action(); @@ -184,11 +189,12 @@ void MeshInstance3DEditor::_menu_option(int p_option) { cshape->set_shape(shape); cshape->set_transform(node->get_transform()); - Node *owner = node->get_owner(); + Node *owner = get_tree()->get_edited_scene_root(); ur->add_do_method(node->get_parent(), "add_child", cshape, true); ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1); ur->add_do_method(cshape, "set_owner", owner); + ur->add_do_method(Node3DEditor::get_singleton(), "_request_gizmo", cshape); ur->add_do_reference(cshape); ur->add_undo_method(node->get_parent(), "remove_child", cshape); @@ -217,14 +223,17 @@ void MeshInstance3DEditor::_menu_option(int p_option) { for (int i = 0; i < shapes.size(); i++) { CollisionShape3D *cshape = memnew(CollisionShape3D); + cshape->set_name("CollisionShape3D"); + cshape->set_shape(shapes[i]); cshape->set_transform(node->get_transform()); - Node *owner = node->get_owner(); + Node *owner = get_tree()->get_edited_scene_root(); ur->add_do_method(node->get_parent(), "add_child", cshape); ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1); ur->add_do_method(cshape, "set_owner", owner); + ur->add_do_method(Node3DEditor::get_singleton(), "_request_gizmo", cshape); ur->add_do_reference(cshape); ur->add_undo_method(node->get_parent(), "remove_child", cshape); } @@ -243,13 +252,14 @@ void MeshInstance3DEditor::_menu_option(int p_option) { NavigationRegion3D *nmi = memnew(NavigationRegion3D); nmi->set_navigation_mesh(nmesh); - Node *owner = node == get_tree()->get_edited_scene_root() ? node : node->get_owner(); + Node *owner = get_tree()->get_edited_scene_root(); UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); ur->create_action(TTR("Create Navigation Mesh")); ur->add_do_method(node, "add_child", nmi, true); ur->add_do_method(nmi, "set_owner", owner); + ur->add_do_method(Node3DEditor::get_singleton(), "_request_gizmo", nmi); ur->add_do_reference(nmi); ur->add_undo_method(node, "remove_child", nmi); @@ -267,13 +277,52 @@ void MeshInstance3DEditor::_menu_option(int p_option) { return; } - Error err = mesh2->lightmap_unwrap(node->get_global_transform()); + String path = mesh2->get_path(); + int srpos = path.find("::"); + if (srpos != -1) { + String base = path.substr(0, srpos); + if (ResourceLoader::get_resource_type(base) == "PackedScene") { + if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) { + err_dialog->set_text(TTR("Mesh cannot unwrap UVs because it does not belong to the edited scene. Make it unique first.")); + err_dialog->popup_centered(); + return; + } + } else { + if (FileAccess::exists(path + ".import")) { + err_dialog->set_text(TTR("Mesh cannot unwrap UVs because it belongs to another resource which was imported from another file type. Make it unique first.")); + err_dialog->popup_centered(); + return; + } + } + } else { + if (FileAccess::exists(path + ".import")) { + err_dialog->set_text(TTR("Mesh cannot unwrap UVs because it was imported from another file type. Make it unique first.")); + err_dialog->popup_centered(); + return; + } + } + + Ref<ArrayMesh> unwrapped_mesh = mesh2->duplicate(false); + + Error err = unwrapped_mesh->lightmap_unwrap(node->get_global_transform()); if (err != OK) { err_dialog->set_text(TTR("UV Unwrap failed, mesh may not be manifold?")); err_dialog->popup_centered(); return; } + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Unwrap UV2")); + + ur->add_do_method(node, "set_mesh", unwrapped_mesh); + ur->add_do_reference(node); + ur->add_do_reference(mesh2.ptr()); + + ur->add_undo_method(node, "set_mesh", mesh2); + ur->add_undo_reference(unwrapped_mesh.ptr()); + + ur->commit_action(); + } break; case MENU_OPTION_DEBUG_UV1: { Ref<Mesh> mesh2 = node->get_mesh(); @@ -300,12 +349,13 @@ struct MeshInstance3DEditorEdgeSort { Vector2 a; Vector2 b; - bool operator<(const MeshInstance3DEditorEdgeSort &p_b) const { - if (a == p_b.a) { - return b < p_b.b; - } else { - return a < p_b.a; - } + static uint32_t hash(const MeshInstance3DEditorEdgeSort &p_edge) { + uint32_t h = hash_murmur3_one_32(HashMapHasherDefault::hash(p_edge.a)); + return hash_fmix32(hash_murmur3_one_32(HashMapHasherDefault::hash(p_edge.b), h)); + } + + bool operator==(const MeshInstance3DEditorEdgeSort &p_b) const { + return a == p_b.a && b == p_b.b; } MeshInstance3DEditorEdgeSort() {} @@ -324,7 +374,7 @@ void MeshInstance3DEditor::_create_uv_lines(int p_layer) { Ref<Mesh> mesh = node->get_mesh(); ERR_FAIL_COND(!mesh.is_valid()); - Set<MeshInstance3DEditorEdgeSort> edges; + HashSet<MeshInstance3DEditorEdgeSort, MeshInstance3DEditorEdgeSort> edges; uv_lines.clear(); for (int i = 0; i < mesh->get_surface_count(); i++) { if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) { @@ -418,10 +468,7 @@ void MeshInstance3DEditor::_create_outline_mesh() { MeshInstance3D *mi = memnew(MeshInstance3D); mi->set_mesh(mesho); - Node *owner = node->get_owner(); - if (get_tree()->get_edited_scene_root() == node) { - owner = node; - } + Node *owner = get_tree()->get_edited_scene_root(); UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); @@ -429,6 +476,7 @@ void MeshInstance3DEditor::_create_outline_mesh() { ur->add_do_method(node, "add_child", mi, true); ur->add_do_method(mi, "set_owner", owner); + ur->add_do_method(Node3DEditor::get_singleton(), "_request_gizmo", mi); ur->add_do_reference(mi); ur->add_undo_method(node, "remove_child", mi); diff --git a/editor/plugins/mesh_library_editor_plugin.cpp b/editor/plugins/mesh_library_editor_plugin.cpp index 423ec5f4ed..914ccb54c1 100644 --- a/editor/plugins/mesh_library_editor_plugin.cpp +++ b/editor/plugins/mesh_library_editor_plugin.cpp @@ -71,7 +71,7 @@ void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library, p_library->clear(); } - Map<int, MeshInstance3D *> mesh_instances; + HashMap<int, MeshInstance3D *> mesh_instances; for (int i = 0; i < p_scene->get_child_count(); i++) { Node *child = p_scene->get_child(i); diff --git a/editor/plugins/mesh_library_editor_plugin.h b/editor/plugins/mesh_library_editor_plugin.h index 85ead35534..f4b4288a5f 100644 --- a/editor/plugins/mesh_library_editor_plugin.h +++ b/editor/plugins/mesh_library_editor_plugin.h @@ -47,8 +47,8 @@ class MeshLibraryEditor : public Control { ConfirmationDialog *cd_remove = nullptr; ConfirmationDialog *cd_update = nullptr; EditorFileDialog *file = nullptr; - bool apply_xforms; - int to_erase; + bool apply_xforms = false; + int to_erase = 0; enum { MENU_OPTION_ADD_ITEM, @@ -58,7 +58,7 @@ class MeshLibraryEditor : public Control { MENU_OPTION_IMPORT_FROM_SCENE_APPLY_XFORMS }; - int option; + int option = 0; void _import_scene_cbk(const String &p_str); void _menu_cbk(int p_option); void _menu_remove_confirm(); diff --git a/editor/plugins/multimesh_editor_plugin.cpp b/editor/plugins/multimesh_editor_plugin.cpp index 850c673c12..0fab3aed0d 100644 --- a/editor/plugins/multimesh_editor_plugin.cpp +++ b/editor/plugins/multimesh_editor_plugin.cpp @@ -141,7 +141,7 @@ void MultiMeshEditor::_populate() { const Face3 *r = faces.ptr(); float area_accum = 0; - Map<float, int> triangle_area_map; + RBMap<float, int> triangle_area_map; for (int i = 0; i < facecount; i++) { float area = r[i].get_area(); if (area < CMP_EPSILON) { @@ -180,9 +180,9 @@ void MultiMeshEditor::_populate() { for (int i = 0; i < instance_count; i++) { float areapos = Math::random(0.0f, area_accum); - Map<float, int>::Element *E = triangle_area_map.find_closest(areapos); + RBMap<float, int>::Iterator E = triangle_area_map.find_closest(areapos); ERR_FAIL_COND(!E); - int index = E->get(); + int index = E->value; ERR_FAIL_INDEX(index, facecount); // ok FINALLY get face @@ -200,9 +200,9 @@ void MultiMeshEditor::_populate() { Basis post_xform; - post_xform.rotate(xform.basis.get_axis(1), -Math::random(-_rotate_random, _rotate_random) * Math_PI); - post_xform.rotate(xform.basis.get_axis(2), -Math::random(-_tilt_random, _tilt_random) * Math_PI); - post_xform.rotate(xform.basis.get_axis(0), -Math::random(-_tilt_random, _tilt_random) * Math_PI); + post_xform.rotate(xform.basis.get_column(1), -Math::random(-_rotate_random, _rotate_random) * Math_PI); + post_xform.rotate(xform.basis.get_column(2), -Math::random(-_tilt_random, _tilt_random) * Math_PI); + post_xform.rotate(xform.basis.get_column(0), -Math::random(-_tilt_random, _tilt_random) * Math_PI); xform.basis = post_xform * xform.basis; //xform.basis.orthonormalize(); diff --git a/editor/plugins/multimesh_editor_plugin.h b/editor/plugins/multimesh_editor_plugin.h index 9f5e85216c..5773989d0d 100644 --- a/editor/plugins/multimesh_editor_plugin.h +++ b/editor/plugins/multimesh_editor_plugin.h @@ -46,7 +46,7 @@ class MultiMeshEditor : public Control { AcceptDialog *err_dialog = nullptr; MenuButton *options = nullptr; MultiMeshInstance3D *_last_pp_node = nullptr; - bool browsing_source; + bool browsing_source = false; Panel *panel = nullptr; MultiMeshInstance3D *node = nullptr; diff --git a/editor/plugins/navigation_polygon_editor_plugin.cpp b/editor/plugins/navigation_polygon_editor_plugin.cpp index 17eaf45bb5..8f3553b8cf 100644 --- a/editor/plugins/navigation_polygon_editor_plugin.cpp +++ b/editor/plugins/navigation_polygon_editor_plugin.cpp @@ -106,7 +106,7 @@ void NavigationPolygonEditor::_create_resource() { undo_redo->create_action(TTR("Create Navigation Polygon")); undo_redo->add_do_method(node, "set_navigation_polygon", Ref<NavigationPolygon>(memnew(NavigationPolygon))); - undo_redo->add_undo_method(node, "set_navigation_polygon", Variant(REF())); + undo_redo->add_undo_method(node, "set_navigation_polygon", Variant(Ref<RefCounted>())); undo_redo->commit_action(); _menu_option(MODE_CREATE); diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp index 51e2f6ff00..64aeb9f2a8 100644 --- a/editor/plugins/node_3d_editor_gizmos.cpp +++ b/editor/plugins/node_3d_editor_gizmos.cpp @@ -47,6 +47,7 @@ #include "scene/3d/gpu_particles_3d.h" #include "scene/3d/gpu_particles_collision_3d.h" #include "scene/3d/joint_3d.h" +#include "scene/3d/label_3d.h" #include "scene/3d/light_3d.h" #include "scene/3d/lightmap_gi.h" #include "scene/3d/lightmap_probe.h" @@ -475,7 +476,7 @@ void EditorNode3DGizmo::add_handles(const Vector<Vector3> &p_handles, const Ref< } } -void EditorNode3DGizmo::add_solid_box(Ref<Material> &p_material, Vector3 p_size, Vector3 p_position, const Transform3D &p_xform) { +void EditorNode3DGizmo::add_solid_box(const Ref<Material> &p_material, Vector3 p_size, Vector3 p_position, const Transform3D &p_xform) { ERR_FAIL_COND(!spatial_node); BoxMesh box_mesh; @@ -588,7 +589,7 @@ void EditorNode3DGizmo::handles_intersect_ray(Camera3D *p_camera, const Vector2 Transform3D camera_xform = p_camera->get_global_transform(); Transform3D t = spatial_node->get_global_transform(); if (billboard_handle) { - t.set_look_at(t.origin, t.origin - camera_xform.basis.get_axis(2), camera_xform.basis.get_axis(1)); + t.set_look_at(t.origin, t.origin - camera_xform.basis.get_column(2), camera_xform.basis.get_column(1)); } float min_d = 1e20; @@ -664,7 +665,7 @@ bool EditorNode3DGizmo::intersect_ray(Camera3D *p_camera, const Point2 &p_point, Transform3D orig_camera_transform = p_camera->get_camera_transform(); if (!orig_camera_transform.origin.is_equal_approx(t.origin) && - ABS(orig_camera_transform.basis.get_axis(Vector3::AXIS_Z).dot(Vector3(0, 1, 0))) < 0.99) { + ABS(orig_camera_transform.basis.get_column(Vector3::AXIS_Z).dot(Vector3(0, 1, 0))) < 0.99) { p_camera->look_at(t.origin); } @@ -688,13 +689,13 @@ bool EditorNode3DGizmo::intersect_ray(Camera3D *p_camera, const Point2 &p_point, } if (collision_segments.size()) { - Plane camp(-p_camera->get_transform().basis.get_axis(2).normalized(), p_camera->get_transform().origin); + Plane camp(-p_camera->get_transform().basis.get_column(2).normalized(), p_camera->get_transform().origin); int vc = collision_segments.size(); const Vector3 *vptr = collision_segments.ptr(); Transform3D t = spatial_node->get_global_transform(); if (billboard_handle) { - t.set_look_at(t.origin, t.origin - p_camera->get_transform().basis.get_axis(2), p_camera->get_transform().basis.get_axis(1)); + t.set_look_at(t.origin, t.origin - p_camera->get_transform().basis.get_column(2), p_camera->get_transform().basis.get_column(1)); } Vector3 cp; @@ -741,7 +742,7 @@ bool EditorNode3DGizmo::intersect_ray(Camera3D *p_camera, const Point2 &p_point, Transform3D gt = spatial_node->get_global_transform(); if (billboard_handle) { - gt.set_look_at(gt.origin, gt.origin - p_camera->get_transform().basis.get_axis(2), p_camera->get_transform().basis.get_axis(1)); + gt.set_look_at(gt.origin, gt.origin - p_camera->get_transform().basis.get_column(2), p_camera->get_transform().basis.get_column(1)); } Transform3D ai = gt.affine_inverse(); @@ -1318,7 +1319,7 @@ void Light3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, light->set_param(Light3D::PARAM_RANGE, d); } else if (Object::cast_to<OmniLight3D>(light)) { - Plane cp = Plane(p_camera->get_transform().basis.get_axis(2), gt.origin); + Plane cp = Plane(p_camera->get_transform().basis.get_column(2), gt.origin); Vector3 inters; if (cp.intersects_ray(ray_from, ray_dir, &inters)) { @@ -1496,6 +1497,9 @@ AudioStreamPlayer3DGizmoPlugin::AudioStreamPlayer3DGizmoPlugin() { create_icon_material("stream_player_3d_icon", Node3DEditor::get_singleton()->get_theme_icon(SNAME("Gizmo3DSamplePlayer"), SNAME("EditorIcons"))); create_material("stream_player_3d_material_primary", gizmo_color); create_material("stream_player_3d_material_secondary", gizmo_color * Color(1, 1, 1, 0.35)); + // Enable vertex colors for the billboard material as the gizmo color depends on the + // AudioStreamPlayer3D attenuation type and source (Unit Size or Max Distance). + create_material("stream_player_3d_material_billboard", Color(1, 1, 1), true, false, true); create_handle_material("handles"); } @@ -1579,6 +1583,88 @@ void AudioStreamPlayer3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { const Ref<Material> icon = get_material("stream_player_3d_icon", p_gizmo); + if (player->get_attenuation_model() != AudioStreamPlayer3D::ATTENUATION_DISABLED || player->get_max_distance() > CMP_EPSILON) { + // Draw a circle to represent sound volume attenuation. + // Use only a billboard circle to represent radius. + // This helps distinguish AudioStreamPlayer3D gizmos from OmniLight3D gizmos. + const Ref<Material> lines_billboard_material = get_material("stream_player_3d_material_billboard", p_gizmo); + + // Soft distance cap varies depending on attenuation model, as some will fade out more aggressively than others. + // Multipliers were empirically determined through testing. + float soft_multiplier; + switch (player->get_attenuation_model()) { + case AudioStreamPlayer3D::ATTENUATION_INVERSE_DISTANCE: + soft_multiplier = 12.0; + break; + case AudioStreamPlayer3D::ATTENUATION_INVERSE_SQUARE_DISTANCE: + soft_multiplier = 4.0; + break; + case AudioStreamPlayer3D::ATTENUATION_LOGARITHMIC: + soft_multiplier = 3.25; + break; + default: + // Ensures Max Distance's radius visualization is not capped by Unit Size + // (when the attenuation mode is Disabled). + soft_multiplier = 10000.0; + break; + } + + // Draw the distance at which the sound can be reasonably heard. + // This can be either a hard distance cap with the Max Distance property (if set above 0.0), + // or a soft distance cap with the Unit Size property (sound never reaches true zero). + // When Max Distance is 0.0, `r` represents the distance above which the + // sound can't be heard in *most* (but not all) scenarios. + float r; + if (player->get_max_distance() > CMP_EPSILON) { + r = MIN(player->get_unit_size() * soft_multiplier, player->get_max_distance()); + } else { + r = player->get_unit_size() * soft_multiplier; + } + Vector<Vector3> points_billboard; + + for (int i = 0; i < 120; i++) { + // Create a circle. + const float ra = Math::deg2rad((float)(i * 3)); + const float rb = Math::deg2rad((float)((i + 1) * 3)); + const Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; + const Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; + + // Draw a billboarded circle. + points_billboard.push_back(Vector3(a.x, a.y, 0)); + points_billboard.push_back(Vector3(b.x, b.y, 0)); + } + + Color color; + switch (player->get_attenuation_model()) { + // Pick cold colors for all attenuation models (except Disabled), + // so that soft caps can be easily distinguished from hard caps + // (which use warm colors). + case AudioStreamPlayer3D::ATTENUATION_INVERSE_DISTANCE: + color = Color(0.4, 0.8, 1); + break; + case AudioStreamPlayer3D::ATTENUATION_INVERSE_SQUARE_DISTANCE: + color = Color(0.4, 0.5, 1); + break; + case AudioStreamPlayer3D::ATTENUATION_LOGARITHMIC: + color = Color(0.4, 0.2, 1); + break; + default: + // Disabled attenuation mode. + // This is never reached when Max Distance is 0, but the + // hue-inverted form of this color will be used if Max Distance is greater than 0. + color = Color(1, 1, 1); + break; + } + + if (player->get_max_distance() > CMP_EPSILON) { + // Sound is hard-capped by max distance. The attenuation model still matters, + // so invert the hue of the color that was chosen above. + color.set_h(color.get_h() + 0.5); + } + + p_gizmo->add_lines(points_billboard, lines_billboard_material, true, color); + } + if (player->is_emission_angle_enabled()) { const float pc = player->get_emission_angle(); const float ofs = -Math::cos(Math::deg2rad(pc)); @@ -2170,6 +2256,38 @@ void Sprite3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { /// +Label3DGizmoPlugin::Label3DGizmoPlugin() { +} + +bool Label3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to<Label3D>(p_spatial) != nullptr; +} + +String Label3DGizmoPlugin::get_gizmo_name() const { + return "Label3D"; +} + +int Label3DGizmoPlugin::get_priority() const { + return -1; +} + +bool Label3DGizmoPlugin::can_be_hidden() const { + return false; +} + +void Label3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + Label3D *label = Object::cast_to<Label3D>(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Ref<TriangleMesh> tm = label->generate_triangle_mesh(); + if (tm.is_valid()) { + p_gizmo->add_collision_triangles(tm); + } +} + +/// + Position3DGizmoPlugin::Position3DGizmoPlugin() { pos3d_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); cursor_points = Vector<Vector3>(); @@ -2960,10 +3078,15 @@ void GPUParticles3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { //// GPUParticlesCollision3DGizmoPlugin::GPUParticlesCollision3DGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/particle_collision", Color(0.5, 0.7, 1)); - create_material("shape_material", gizmo_color); - gizmo_color.a = 0.15; - create_material("shape_material_internal", gizmo_color); + Color gizmo_color_attractor = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/particle_attractor", Color(1, 0.7, 0.5)); + create_material("shape_material_attractor", gizmo_color_attractor); + gizmo_color_attractor.a = 0.15; + create_material("shape_material_attractor_internal", gizmo_color_attractor); + + Color gizmo_color_collision = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/particle_collision", Color(0.5, 0.7, 1)); + create_material("shape_material_collision", gizmo_color_collision); + gizmo_color_collision.a = 0.15; + create_material("shape_material_collision_internal", gizmo_color_collision); create_handle_material("handles"); } @@ -3089,12 +3212,17 @@ void GPUParticlesCollision3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { p_gizmo->clear(); - const Ref<Material> material = - get_material("shape_material", p_gizmo); - const Ref<Material> material_internal = - get_material("shape_material_internal", p_gizmo); + Ref<Material> material; + Ref<Material> material_internal; + if (Object::cast_to<GPUParticlesAttractor3D>(cs)) { + material = get_material("shape_material_attractor", p_gizmo); + material_internal = get_material("shape_material_attractor_internal", p_gizmo); + } else { + material = get_material("shape_material_collision", p_gizmo); + material_internal = get_material("shape_material_collision_internal", p_gizmo); + } - Ref<Material> handles_material = get_material("handles"); + const Ref<Material> handles_material = get_material("handles"); if (Object::cast_to<GPUParticlesCollisionSphere3D>(cs) || Object::cast_to<GPUParticlesAttractorSphere3D>(cs)) { float r = cs->call("get_radius"); @@ -3756,7 +3884,7 @@ void LightmapGIGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { p_gizmo->clear(); Vector<Vector3> lines; - Set<Vector2i> lines_found; + HashSet<Vector2i> lines_found; Vector<Vector3> points = data->get_capture_points(); if (points.size() == 0) { @@ -4722,7 +4850,7 @@ void NavigationRegion3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { return; } - Map<_EdgeKey, bool> edge_map; + HashMap<_EdgeKey, bool, _EdgeKey> edge_map; Vector<Vector3> tmeshfaces; tmeshfaces.resize(faces.size() * 3); @@ -4740,10 +4868,10 @@ void NavigationRegion3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { SWAP(ek.from, ek.to); } - Map<_EdgeKey, bool>::Element *F = edge_map.find(ek); + HashMap<_EdgeKey, bool, _EdgeKey>::Iterator F = edge_map.find(ek); if (F) { - F->get() = false; + F->value = false; } else { edge_map[ek] = true; @@ -4799,7 +4927,7 @@ Basis JointGizmosDrawer::look_body(const Transform3D &p_joint_transform, const T v_y.normalize(); Basis base; - base.set(v_x, v_y, v_z); + base.set_columns(v_x, v_y, v_z); // Absorb current joint transform base = p_joint_transform.basis.inverse() * base; @@ -4824,7 +4952,7 @@ Basis JointGizmosDrawer::look_body_toward_x(const Transform3D &p_joint_transform const Vector3 &p_eye(p_joint_transform.origin); const Vector3 &p_target(p_body_transform.origin); - const Vector3 p_front(p_joint_transform.basis.get_axis(0)); + const Vector3 p_front(p_joint_transform.basis.get_column(0)); Vector3 v_x, v_y, v_z; @@ -4843,7 +4971,7 @@ Basis JointGizmosDrawer::look_body_toward_x(const Transform3D &p_joint_transform v_x.normalize(); Basis base; - base.set(v_x, v_y, v_z); + base.set_columns(v_x, v_y, v_z); // Absorb current joint transform base = p_joint_transform.basis.inverse() * base; @@ -4855,7 +4983,7 @@ Basis JointGizmosDrawer::look_body_toward_y(const Transform3D &p_joint_transform const Vector3 &p_eye(p_joint_transform.origin); const Vector3 &p_target(p_body_transform.origin); - const Vector3 p_up(p_joint_transform.basis.get_axis(1)); + const Vector3 p_up(p_joint_transform.basis.get_column(1)); Vector3 v_x, v_y, v_z; @@ -4874,7 +5002,7 @@ Basis JointGizmosDrawer::look_body_toward_y(const Transform3D &p_joint_transform v_y.normalize(); Basis base; - base.set(v_x, v_y, v_z); + base.set_columns(v_x, v_y, v_z); // Absorb current joint transform base = p_joint_transform.basis.inverse() * base; @@ -4886,7 +5014,7 @@ Basis JointGizmosDrawer::look_body_toward_z(const Transform3D &p_joint_transform const Vector3 &p_eye(p_joint_transform.origin); const Vector3 &p_target(p_body_transform.origin); - const Vector3 p_lateral(p_joint_transform.basis.get_axis(2)); + const Vector3 p_lateral(p_joint_transform.basis.get_column(2)); Vector3 v_x, v_y, v_z; @@ -4905,7 +5033,7 @@ Basis JointGizmosDrawer::look_body_toward_z(const Transform3D &p_joint_transform v_x.normalize(); Basis base; - base.set(v_x, v_y, v_z); + base.set_columns(v_x, v_y, v_z); // Absorb current joint transform base = p_joint_transform.basis.inverse() * base; diff --git a/editor/plugins/node_3d_editor_gizmos.h b/editor/plugins/node_3d_editor_gizmos.h index 3b67b898e3..a0d7715347 100644 --- a/editor/plugins/node_3d_editor_gizmos.h +++ b/editor/plugins/node_3d_editor_gizmos.h @@ -31,8 +31,8 @@ #ifndef NODE_3D_EDITOR_GIZMOS_H #define NODE_3D_EDITOR_GIZMOS_H +#include "core/templates/hash_map.h" #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" @@ -99,7 +99,7 @@ public: void add_collision_triangles(const Ref<TriangleMesh> &p_tmesh); void add_unscaled_billboard(const Ref<Material> &p_material, real_t p_scale = 1, const Color &p_modulate = Color(1, 1, 1)); void add_handles(const Vector<Vector3> &p_handles, const Ref<Material> &p_material, const Vector<int> &p_ids = Vector<int>(), bool p_billboard = false, bool p_secondary = false); - void add_solid_box(Ref<Material> &p_material, Vector3 p_size, Vector3 p_position = Vector3(), const Transform3D &p_xform = Transform3D()); + void add_solid_box(const Ref<Material> &p_material, Vector3 p_size, Vector3 p_position = Vector3(), const Transform3D &p_xform = Transform3D()); virtual bool is_handle_highlighted(int p_id, bool p_secondary) const; virtual String get_handle_name(int p_id, bool p_secondary) const; @@ -321,6 +321,19 @@ public: Sprite3DGizmoPlugin(); }; +class Label3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(Label3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() const override; + bool can_be_hidden() const override; + void redraw(EditorNode3DGizmo *p_gizmo) override; + + Label3DGizmoPlugin(); +}; + class Position3DGizmoPlugin : public EditorNode3DGizmoPlugin { GDCLASS(Position3DGizmoPlugin, EditorNode3DGizmoPlugin); @@ -588,7 +601,13 @@ class NavigationRegion3DGizmoPlugin : public EditorNode3DGizmoPlugin { Vector3 from; Vector3 to; - bool operator<(const _EdgeKey &p_with) const { return from == p_with.from ? to < p_with.to : from < p_with.from; } + static uint32_t hash(const _EdgeKey &p_key) { + return HashMapHasherDefault::hash(p_key.from) ^ HashMapHasherDefault::hash(p_key.to); + } + + bool operator==(const _EdgeKey &p_with) const { + return HashMapComparatorDefault<Vector3>::compare(from, p_with.from) && HashMapComparatorDefault<Vector3>::compare(to, p_with.to); + } }; public: diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index f2ca1fcfeb..815d0a2425 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -159,7 +159,7 @@ void ViewportRotationControl::_get_sorted_axis(Vector<Axis2D> &r_axis) { const Basis camera_basis = viewport->to_camera_transform(viewport->cursor).get_basis().inverse(); for (int i = 0; i < 3; ++i) { - Vector3 axis_3d = camera_basis.get_axis(i); + Vector3 axis_3d = camera_basis.get_column(i); Vector2i axis_vector = Vector2(axis_3d.x, -axis_3d.y) * radius; if (Math::abs(axis_3d.z) < 1.0) { @@ -369,7 +369,7 @@ Transform3D Node3DEditorViewport::to_camera_transform(const Cursor &p_cursor) co } int Node3DEditorViewport::get_selected_count() const { - const Map<Node *, Object *> &selection = editor_selection->get_selection(); + const HashMap<Node *, Object *> &selection = editor_selection->get_selection(); int count = 0; @@ -411,6 +411,12 @@ void Node3DEditorViewport::cancel_transform() { set_message(TTR("Transform Aborted."), 3); } +void Node3DEditorViewport::_update_shrink() { + bool shrink = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_HALF_RESOLUTION)); + subviewport_container->set_stretch_shrink(shrink ? 2 : 1); + subviewport_container->set_texture_filter(shrink ? TEXTURE_FILTER_NEAREST : TEXTURE_FILTER_PARENT_NODE); +} + float Node3DEditorViewport::get_znear() const { return CLAMP(spatial_editor->get_znear(), MIN_Z, MAX_Z); } @@ -440,7 +446,7 @@ Vector3 Node3DEditorViewport::_get_ray_pos(const Vector2 &p_pos) const { } Vector3 Node3DEditorViewport::_get_camera_normal() const { - return -_get_camera_transform().basis.get_axis(2); + return -_get_camera_transform().basis.get_column(2); } Vector3 Node3DEditorViewport::_get_ray(const Vector2 &p_pos) const { @@ -518,7 +524,7 @@ ObjectID Node3DEditorViewport::_select_ray(const Point2 &p_pos) { } Vector<ObjectID> instances = RenderingServer::get_singleton()->instances_cull_ray(pos, pos + ray * camera->get_far(), get_tree()->get_root()->get_world_3d()->get_scenario()); - Set<Ref<EditorNode3DGizmo>> found_gizmos; + HashSet<Ref<EditorNode3DGizmo>> found_gizmos; Node *edited_scene = get_tree()->get_edited_scene_root(); ObjectID closest; @@ -581,7 +587,7 @@ void Node3DEditorViewport::_find_items_at_pos(const Point2 &p_pos, Vector<_RayRe Vector3 pos = _get_ray_pos(p_pos); Vector<ObjectID> instances = RenderingServer::get_singleton()->instances_cull_ray(pos, pos + ray * camera->get_far(), get_tree()->get_root()->get_world_3d()->get_scenario()); - Set<Node3D *> found_nodes; + HashSet<Node3D *> found_nodes; for (int i = 0; i < instances.size(); i++) { Node3D *spat = Object::cast_to<Node3D>(ObjectDB::get_instance(instances[i])); @@ -764,7 +770,7 @@ void Node3DEditorViewport::_select_region() { } Vector<ObjectID> instances = RenderingServer::get_singleton()->instances_cull_convex(frustum, get_tree()->get_root()->get_world_3d()->get_scenario()); - Set<Node3D *> found_nodes; + HashSet<Node3D *> found_nodes; Vector<Node *> selected; Node *edited_scene = get_tree()->get_edited_scene_root(); @@ -983,7 +989,7 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b real_t col_d = 1e20; for (int i = 0; i < 3; i++) { - const Vector3 grabber_pos = gt.origin + gt.basis.get_axis(i).normalized() * gizmo_scale * (GIZMO_ARROW_OFFSET + (GIZMO_ARROW_SIZE * 0.5)); + const Vector3 grabber_pos = gt.origin + gt.basis.get_column(i).normalized() * gizmo_scale * (GIZMO_ARROW_OFFSET + (GIZMO_ARROW_SIZE * 0.5)); const real_t grabber_radius = gizmo_scale * GIZMO_ARROW_SIZE; Vector3 r; @@ -1003,15 +1009,15 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b col_d = 1e20; for (int i = 0; i < 3; i++) { - Vector3 ivec2 = gt.basis.get_axis((i + 1) % 3).normalized(); - Vector3 ivec3 = gt.basis.get_axis((i + 2) % 3).normalized(); + Vector3 ivec2 = gt.basis.get_column((i + 1) % 3).normalized(); + Vector3 ivec3 = gt.basis.get_column((i + 2) % 3).normalized(); // Allow some tolerance to make the plane easier to click, // even if the click is actually slightly outside the plane. const Vector3 grabber_pos = gt.origin + (ivec2 + ivec3) * gizmo_scale * (GIZMO_PLANE_SIZE + GIZMO_PLANE_DST * 0.6667); Vector3 r; - Plane plane(gt.basis.get_axis(i).normalized(), gt.origin); + Plane plane(gt.basis.get_column(i).normalized(), gt.origin); if (plane.intersects_ray(ray_pos, ray, &r)) { const real_t dist = r.distance_to(grabber_pos); @@ -1064,7 +1070,7 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b float col_d = 1e20; for (int i = 0; i < 3; i++) { - Plane plane(gt.basis.get_axis(i).normalized(), gt.origin); + Plane plane(gt.basis.get_column(i).normalized(), gt.origin); Vector3 r; if (!plane.intersects_ray(ray_pos, ray, &r)) { continue; @@ -1103,7 +1109,7 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b float col_d = 1e20; for (int i = 0; i < 3; i++) { - const Vector3 grabber_pos = gt.origin + gt.basis.get_axis(i).normalized() * gizmo_scale * GIZMO_SCALE_OFFSET; + const Vector3 grabber_pos = gt.origin + gt.basis.get_column(i).normalized() * gizmo_scale * GIZMO_SCALE_OFFSET; const real_t grabber_radius = gizmo_scale * GIZMO_ARROW_SIZE; Vector3 r; @@ -1123,15 +1129,15 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b col_d = 1e20; for (int i = 0; i < 3; i++) { - const Vector3 ivec2 = gt.basis.get_axis((i + 1) % 3).normalized(); - const Vector3 ivec3 = gt.basis.get_axis((i + 2) % 3).normalized(); + const Vector3 ivec2 = gt.basis.get_column((i + 1) % 3).normalized(); + const Vector3 ivec3 = gt.basis.get_column((i + 2) % 3).normalized(); // Allow some tolerance to make the plane easier to click, // even if the click is actually slightly outside the plane. const Vector3 grabber_pos = gt.origin + (ivec2 + ivec3) * gizmo_scale * (GIZMO_PLANE_SIZE + GIZMO_PLANE_DST * 0.6667); Vector3 r; - Plane plane(gt.basis.get_axis(i).normalized(), gt.origin); + Plane plane(gt.basis.get_column(i).normalized(), gt.origin); if (plane.intersects_ray(ray_pos, ray, &r)) { const real_t dist = r.distance_to(grabber_pos); @@ -1265,7 +1271,7 @@ void Node3DEditorViewport::_surface_focus_exit() { } bool Node3DEditorViewport ::_is_node_locked(const Node *p_node) { - return p_node->has_meta("_edit_lock_") && p_node->get_meta("_edit_lock_"); + return p_node->get_meta("_edit_lock_", false); } void Node3DEditorViewport::_list_select(Ref<InputEventMouseButton> b) { @@ -1888,7 +1894,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { } if (EditorSettings::get_singleton()->get("editors/3d/navigation/emulate_numpad")) { - const Key code = k->get_keycode(); + const Key code = k->get_physical_keycode(); if (code >= Key::KEY_0 && code <= Key::KEY_9) { k->set_keycode(code - Key::KEY_0 + Key::KP_0); } @@ -2087,9 +2093,8 @@ void Node3DEditorViewport::_nav_pan(Ref<InputEventWithModifiers> p_event, const const NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int(); real_t pan_speed = 1 / 150.0; - int pan_speed_modifier = 10; if (nav_scheme == NAVIGATION_MAYA && p_event->is_shift_pressed()) { - pan_speed *= pan_speed_modifier; + pan_speed *= 10; } Transform3D camera_transform; @@ -2112,9 +2117,8 @@ void Node3DEditorViewport::_nav_zoom(Ref<InputEventWithModifiers> p_event, const const NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int(); real_t zoom_speed = 1 / 80.0; - int zoom_speed_modifier = 10; if (nav_scheme == NAVIGATION_MAYA && p_event->is_shift_pressed()) { - zoom_speed *= zoom_speed_modifier; + zoom_speed *= 10; } NavigationZoomStyle zoom_style = (NavigationZoomStyle)EditorSettings::get_singleton()->get("editors/3d/navigation/zoom_style").operator int(); @@ -2389,11 +2393,7 @@ void Node3DEditorViewport::_project_settings_changed() { viewport->set_shadow_atlas_quadrant_subdiv(2, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q2)); viewport->set_shadow_atlas_quadrant_subdiv(3, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q3)); - bool shrink = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_HALF_RESOLUTION)); - - if (shrink != (subviewport_container->get_stretch_shrink() > 1)) { - subviewport_container->set_stretch_shrink(shrink ? 2 : 1); - } + _update_shrink(); // Update MSAA, screen-space AA and debanding if changed @@ -2401,6 +2401,9 @@ void Node3DEditorViewport::_project_settings_changed() { viewport->set_msaa(Viewport::MSAA(msaa_mode)); const int ssaa_mode = GLOBAL_GET("rendering/anti_aliasing/quality/screen_space_aa"); viewport->set_screen_space_aa(Viewport::ScreenSpaceAA(ssaa_mode)); + const bool use_taa = GLOBAL_GET("rendering/anti_aliasing/quality/use_taa"); + viewport->set_use_taa(use_taa); + const bool use_debanding = GLOBAL_GET("rendering/anti_aliasing/quality/use_debanding"); viewport->set_use_debanding(use_debanding); @@ -2409,6 +2412,18 @@ void Node3DEditorViewport::_project_settings_changed() { const float mesh_lod_threshold = GLOBAL_GET("rendering/mesh_lod/lod_change/threshold_pixels"); viewport->set_mesh_lod_threshold(mesh_lod_threshold); + + const Viewport::Scaling3DMode scaling_3d_mode = Viewport::Scaling3DMode(int(GLOBAL_GET("rendering/scaling_3d/mode"))); + viewport->set_scaling_3d_mode(scaling_3d_mode); + + const float scaling_3d_scale = GLOBAL_GET("rendering/scaling_3d/scale"); + viewport->set_scaling_3d_scale(scaling_3d_scale); + + const float fsr_sharpness = GLOBAL_GET("rendering/scaling_3d/fsr_sharpness"); + viewport->set_fsr_sharpness(fsr_sharpness); + + const float fsr_mipmap_bias = GLOBAL_GET("rendering/scaling_3d/fsr_mipmap_bias"); + viewport->set_fsr_mipmap_bias(fsr_mipmap_bias); } void Node3DEditorViewport::_notification(int p_what) { @@ -2467,7 +2482,7 @@ void Node3DEditorViewport::_notification(int p_what) { _update_camera(delta); - const Map<Node *, Object *> &selection = editor_selection->get_selection(); + const HashMap<Node *, Object *> &selection = editor_selection->get_selection(); bool changed = false; bool exist = false; @@ -2818,7 +2833,7 @@ void Node3DEditorViewport::_draw() { real_t scale_length = (max_speed - min_speed); if (!Math::is_zero_approx(scale_length)) { - real_t logscale_t = 1.0 - Math::log(1 + freelook_speed - min_speed) / Math::log(1 + scale_length); + real_t logscale_t = 1.0 - Math::log1p(freelook_speed - min_speed) / Math::log1p(scale_length); // Display the freelook speed to help the user get a better sense of scale. const int precision = freelook_speed < 1.0 ? 2 : 1; @@ -2841,7 +2856,7 @@ void Node3DEditorViewport::_draw() { real_t scale_length = (max_distance - min_distance); if (!Math::is_zero_approx(scale_length)) { - real_t logscale_t = 1.0 - Math::log(1 + cursor.distance - min_distance) / Math::log(1 + scale_length); + real_t logscale_t = 1.0 - Math::log1p(cursor.distance - min_distance) / Math::log1p(scale_length); // Display the zoom center distance to help the user get a better sense of scale. const int precision = cursor.distance < 1.0 ? 2 : 1; @@ -2992,7 +3007,7 @@ void Node3DEditorViewport::_menu_option(int p_option) { bool current = view_menu->get_popup()->is_item_checked(idx); current = !current; if (current) { - camera->set_environment(RES()); + camera->set_environment(Ref<Resource>()); } else { camera->set_environment(Node3DEditor::get_singleton()->get_viewport_environment()); } @@ -3087,8 +3102,8 @@ void Node3DEditorViewport::_menu_option(int p_option) { case VIEW_HALF_RESOLUTION: { int idx = view_menu->get_popup()->get_item_index(VIEW_HALF_RESOLUTION); bool current = view_menu->get_popup()->is_item_checked(idx); - current = !current; - view_menu->get_popup()->set_item_checked(idx, current); + view_menu->get_popup()->set_item_checked(idx, !current); + _update_shrink(); } break; case VIEW_INFORMATION: { int idx = view_menu->get_popup()->get_item_index(VIEW_INFORMATION); @@ -3126,7 +3141,8 @@ void Node3DEditorViewport::_menu_option(int p_option) { case VIEW_DISPLAY_DEBUG_CLUSTER_SPOT_LIGHTS: case VIEW_DISPLAY_DEBUG_CLUSTER_DECALS: case VIEW_DISPLAY_DEBUG_CLUSTER_REFLECTION_PROBES: - case VIEW_DISPLAY_DEBUG_OCCLUDERS: { + case VIEW_DISPLAY_DEBUG_OCCLUDERS: + case VIEW_DISPLAY_MOTION_VECTORS: { static const int display_options[] = { VIEW_DISPLAY_NORMAL, VIEW_DISPLAY_WIREFRAME, @@ -3154,6 +3170,7 @@ void Node3DEditorViewport::_menu_option(int p_option) { VIEW_DISPLAY_DEBUG_CLUSTER_DECALS, VIEW_DISPLAY_DEBUG_CLUSTER_REFLECTION_PROBES, VIEW_DISPLAY_DEBUG_OCCLUDERS, + VIEW_DISPLAY_MOTION_VECTORS, VIEW_MAX }; static const Viewport::DebugDraw debug_draw_modes[] = { @@ -3183,6 +3200,7 @@ void Node3DEditorViewport::_menu_option(int p_option) { Viewport::DEBUG_DRAW_CLUSTER_DECALS, Viewport::DEBUG_DRAW_CLUSTER_REFLECTION_PROBES, Viewport::DEBUG_DRAW_OCCLUDERS, + Viewport::DEBUG_DRAW_MOTION_VECTORS, }; int idx = 0; @@ -3391,8 +3409,8 @@ void Node3DEditorViewport::update_transform_gizmo_view() { return; } - const Vector3 camz = -camera_xform.get_basis().get_axis(2).normalized(); - const Vector3 camy = -camera_xform.get_basis().get_axis(1).normalized(); + const Vector3 camz = -camera_xform.get_basis().get_column(2).normalized(); + const Vector3 camy = -camera_xform.get_basis().get_column(1).normalized(); const Plane p = Plane(camz, camera_xform.origin); const real_t gizmo_d = MAX(Math::abs(p.distance_to(xform.origin)), CMP_EPSILON); const real_t d0 = camera->unproject_position(camera_xform.origin + camz * gizmo_d).y; @@ -3426,8 +3444,8 @@ void Node3DEditorViewport::update_transform_gizmo_view() { for (int i = 0; i < 3; i++) { Transform3D axis_angle = Transform3D(); - if (xform.basis.get_axis(i).normalized().dot(xform.basis.get_axis((i + 1) % 3).normalized()) < 1.0) { - axis_angle = axis_angle.looking_at(xform.basis.get_axis(i).normalized(), xform.basis.get_axis((i + 1) % 3).normalized()); + if (xform.basis.get_column(i).normalized().dot(xform.basis.get_column((i + 1) % 3).normalized()) < 1.0) { + axis_angle = axis_angle.looking_at(xform.basis.get_column(i).normalized(), xform.basis.get_column((i + 1) % 3).normalized()); } axis_angle.basis.scale(scale); axis_angle.origin = xform.origin; @@ -3644,7 +3662,7 @@ void Node3DEditorViewport::focus_selection() { Vector3 center; int count = 0; - List<Node *> &selection = editor_selection->get_selected_node_list(); + const List<Node *> &selection = editor_selection->get_selected_node_list(); for (Node *E : selection) { Node3D *sp = Object::cast_to<Node3D>(E); @@ -3769,7 +3787,7 @@ Node *Node3DEditorViewport::_sanitize_preview_node(Node *p_node) const { void Node3DEditorViewport::_create_preview(const Vector<String> &files) const { for (int i = 0; i < files.size(); i++) { String path = files[i]; - RES res = ResourceLoader::load(path); + Ref<Resource> res = ResourceLoader::load(path); ERR_CONTINUE(res.is_null()); Ref<PackedScene> scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*res)); Ref<Mesh> mesh = Ref<Mesh>(Object::cast_to<Mesh>(*res)); @@ -3820,7 +3838,7 @@ bool Node3DEditorViewport::_cyclical_dependency_exists(const String &p_target_sc } bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Point2 &p_point) { - RES res = ResourceLoader::load(path); + Ref<Resource> res = ResourceLoader::load(path); ERR_FAIL_COND_V(res.is_null(), false); Ref<PackedScene> scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*res)); @@ -3910,7 +3928,7 @@ void Node3DEditorViewport::_perform_drop_data() { for (int i = 0; i < selected_files.size(); i++) { String path = selected_files[i]; - RES res = ResourceLoader::load(path); + Ref<Resource> res = ResourceLoader::load(path); if (res.is_null()) { continue; } @@ -3953,7 +3971,7 @@ bool Node3DEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant for (int i = 0; i < files.size(); i++) { // Check if dragged files with mesh or scene extension can be created at least once. if (mesh_extensions.find(files[i].get_extension()) || scene_extensions.find(files[i].get_extension())) { - RES res = ResourceLoader::load(files[i]); + Ref<Resource> res = ResourceLoader::load(files[i]); if (res.is_null()) { continue; } @@ -4089,30 +4107,30 @@ void Node3DEditorViewport::update_transform(Point2 p_mousepos, bool p_shift) { plane = Plane(_get_camera_normal(), _edit.center); break; case TRANSFORM_X_AXIS: - motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized(); + motion_mask = spatial_editor->get_gizmo_transform().basis.get_column(0).normalized(); plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center); break; case TRANSFORM_Y_AXIS: - motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized(); + motion_mask = spatial_editor->get_gizmo_transform().basis.get_column(1).normalized(); plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center); break; case TRANSFORM_Z_AXIS: - motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized(); + motion_mask = spatial_editor->get_gizmo_transform().basis.get_column(2).normalized(); plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center); break; case TRANSFORM_YZ: - motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized() + spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized(); - plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized(), _edit.center); + motion_mask = spatial_editor->get_gizmo_transform().basis.get_column(2).normalized() + spatial_editor->get_gizmo_transform().basis.get_column(1).normalized(); + plane = Plane(spatial_editor->get_gizmo_transform().basis.get_column(0).normalized(), _edit.center); plane_mv = true; break; case TRANSFORM_XZ: - motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized() + spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized(); - plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized(), _edit.center); + motion_mask = spatial_editor->get_gizmo_transform().basis.get_column(2).normalized() + spatial_editor->get_gizmo_transform().basis.get_column(0).normalized(); + plane = Plane(spatial_editor->get_gizmo_transform().basis.get_column(1).normalized(), _edit.center); plane_mv = true; break; case TRANSFORM_XY: - motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized() + spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized(); - plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized(), _edit.center); + motion_mask = spatial_editor->get_gizmo_transform().basis.get_column(0).normalized() + spatial_editor->get_gizmo_transform().basis.get_column(1).normalized(); + plane = Plane(spatial_editor->get_gizmo_transform().basis.get_column(2).normalized(), _edit.center); plane_mv = true; break; } @@ -4161,7 +4179,8 @@ void Node3DEditorViewport::update_transform(Point2 p_mousepos, bool p_shift) { Vector3 motion_snapped = motion; motion_snapped.snap(Vector3(snap, snap, snap)); // This might not be necessary anymore after issue #288 is solved (in 4.0?). - set_message(TTR("Scaling: ") + "(" + String::num(motion_snapped.x, snap_step_decimals) + ", " + + // TRANSLATORS: Refers to changing the scale of a node in the 3D editor. + set_message(TTR("Scaling:") + " (" + String::num(motion_snapped.x, snap_step_decimals) + ", " + String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")"); motion = _edit.original.basis.inverse().xform(motion); @@ -4211,27 +4230,27 @@ void Node3DEditorViewport::update_transform(Point2 p_mousepos, bool p_shift) { plane = Plane(_get_camera_normal(), _edit.center); break; case TRANSFORM_X_AXIS: - motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized(); + motion_mask = spatial_editor->get_gizmo_transform().basis.get_column(0).normalized(); plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center); break; case TRANSFORM_Y_AXIS: - motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized(); + motion_mask = spatial_editor->get_gizmo_transform().basis.get_column(1).normalized(); plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center); break; case TRANSFORM_Z_AXIS: - motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized(); + motion_mask = spatial_editor->get_gizmo_transform().basis.get_column(2).normalized(); plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center); break; case TRANSFORM_YZ: - plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized(), _edit.center); + plane = Plane(spatial_editor->get_gizmo_transform().basis.get_column(0).normalized(), _edit.center); plane_mv = true; break; case TRANSFORM_XZ: - plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized(), _edit.center); + plane = Plane(spatial_editor->get_gizmo_transform().basis.get_column(1).normalized(), _edit.center); plane_mv = true; break; case TRANSFORM_XY: - plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized(), _edit.center); + plane = Plane(spatial_editor->get_gizmo_transform().basis.get_column(2).normalized(), _edit.center); plane_mv = true; break; } @@ -4261,7 +4280,8 @@ void Node3DEditorViewport::update_transform(Point2 p_mousepos, bool p_shift) { } Vector3 motion_snapped = motion; motion_snapped.snap(Vector3(snap, snap, snap)); - set_message(TTR("Translating: ") + "(" + String::num(motion_snapped.x, snap_step_decimals) + ", " + + // TRANSLATORS: Refers to changing the position of a node in the 3D editor. + set_message(TTR("Translating:") + " (" + String::num(motion_snapped.x, snap_step_decimals) + ", " + String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")"); motion = spatial_editor->get_gizmo_transform().basis.inverse().xform(motion); @@ -4547,6 +4567,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p display_submenu->add_radio_check_item(TTR("Decal Cluster"), VIEW_DISPLAY_DEBUG_CLUSTER_DECALS); display_submenu->add_radio_check_item(TTR("ReflectionProbe Cluster"), VIEW_DISPLAY_DEBUG_CLUSTER_REFLECTION_PROBES); display_submenu->add_radio_check_item(TTR("Occlusion Culling Buffer"), VIEW_DISPLAY_DEBUG_OCCLUDERS); + display_submenu->add_radio_check_item(TTR("Motion Vectors"), VIEW_DISPLAY_MOTION_VECTORS); display_submenu->set_name("display_advanced"); view_menu->get_popup()->add_submenu_item(TTR("Display Advanced..."), "display_advanced", VIEW_DISPLAY_ADVANCED); @@ -5515,7 +5536,7 @@ void Node3DEditor::_xform_dialog_action() { undo_redo->create_action(TTR("XForm Dialog")); - List<Node *> &selection = editor_selection->get_selected_node_list(); + const List<Node *> &selection = editor_selection->get_selected_node_list(); for (Node *E : selection) { Node3D *sp = Object::cast_to<Node3D>(E); @@ -6353,18 +6374,6 @@ void fragment() { _generate_selection_boxes(); } -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(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); - context_menu_stylebox->set_border_width(SIDE_BOTTOM, Math::round(2 * EDSCALE)); - context_menu_stylebox->set_default_margin(SIDE_BOTTOM, 0); - context_menu_container->add_theme_style_override("panel", context_menu_stylebox); -} - void Node3DEditor::_update_gizmos_menu() { gizmos_menu->clear(); @@ -6471,7 +6480,7 @@ void Node3DEditor::_init_grid() { if (orthogonal) { camera_distance = camera->get_size() / 2.0; - Vector3 camera_direction = -camera->get_global_transform().get_basis().get_axis(2); + Vector3 camera_direction = -camera->get_global_transform().get_basis().get_column(2); Plane grid_plane = Plane(normal); Vector3 intersection; if (grid_plane.intersects_ray(camera_position, camera_direction, &intersection)) { @@ -6687,8 +6696,8 @@ void Node3DEditor::_refresh_menu_icons() { } template <typename T> -Set<T *> _get_child_nodes(Node *parent_node) { - Set<T *> nodes = Set<T *>(); +HashSet<T *> _get_child_nodes(Node *parent_node) { + HashSet<T *> nodes = HashSet<T *>(); T *node = Node::cast_to<T>(parent_node); if (node) { nodes.insert(node); @@ -6696,31 +6705,31 @@ Set<T *> _get_child_nodes(Node *parent_node) { for (int i = 0; i < parent_node->get_child_count(); i++) { Node *child_node = parent_node->get_child(i); - Set<T *> child_nodes = _get_child_nodes<T>(child_node); - for (typename Set<T *>::Element *I = child_nodes.front(); I; I = I->next()) { - nodes.insert(I->get()); + HashSet<T *> child_nodes = _get_child_nodes<T>(child_node); + for (T *I : child_nodes) { + nodes.insert(I); } } return nodes; } -Set<RID> _get_physics_bodies_rid(Node *node) { - Set<RID> rids = Set<RID>(); +HashSet<RID> _get_physics_bodies_rid(Node *node) { + HashSet<RID> rids = HashSet<RID>(); PhysicsBody3D *pb = Node::cast_to<PhysicsBody3D>(node); if (pb) { rids.insert(pb->get_rid()); } - Set<PhysicsBody3D *> child_nodes = _get_child_nodes<PhysicsBody3D>(node); - for (Set<PhysicsBody3D *>::Element *I = child_nodes.front(); I; I = I->next()) { - rids.insert(I->get()->get_rid()); + HashSet<PhysicsBody3D *> child_nodes = _get_child_nodes<PhysicsBody3D>(node); + for (const PhysicsBody3D *I : child_nodes) { + rids.insert(I->get_rid()); } return rids; } void Node3DEditor::snap_selected_nodes_to_floor() { - List<Node *> &selection = editor_selection->get_selected_node_list(); + const List<Node *> &selection = editor_selection->get_selected_node_list(); Dictionary snap_data; for (Node *E : selection) { @@ -6730,20 +6739,21 @@ void Node3DEditor::snap_selected_nodes_to_floor() { Vector3 position_offset = Vector3(); // Priorities for snapping to floor are CollisionShapes, VisualInstances and then origin - Set<VisualInstance3D *> vi = _get_child_nodes<VisualInstance3D>(sp); - Set<CollisionShape3D *> cs = _get_child_nodes<CollisionShape3D>(sp); + HashSet<VisualInstance3D *> vi = _get_child_nodes<VisualInstance3D>(sp); + HashSet<CollisionShape3D *> cs = _get_child_nodes<CollisionShape3D>(sp); bool found_valid_shape = false; if (cs.size()) { AABB aabb; - Set<CollisionShape3D *>::Element *I = cs.front(); - if (I->get()->get_shape().is_valid()) { - CollisionShape3D *collision_shape = cs.front()->get(); + HashSet<CollisionShape3D *>::Iterator I = cs.begin(); + if ((*I)->get_shape().is_valid()) { + CollisionShape3D *collision_shape = *cs.begin(); aabb = collision_shape->get_global_transform().xform(collision_shape->get_shape()->get_debug_mesh()->get_aabb()); found_valid_shape = true; } - for (I = I->next(); I; I = I->next()) { - CollisionShape3D *col_shape = I->get(); + + for (++I; I; ++I) { + CollisionShape3D *col_shape = *I; if (col_shape->get_shape().is_valid()) { aabb.merge_with(col_shape->get_global_transform().xform(col_shape->get_shape()->get_debug_mesh()->get_aabb())); found_valid_shape = true; @@ -6756,9 +6766,9 @@ void Node3DEditor::snap_selected_nodes_to_floor() { } } if (!found_valid_shape && vi.size()) { - AABB aabb = vi.front()->get()->get_transformed_aabb(); - for (Set<VisualInstance3D *>::Element *I = vi.front(); I; I = I->next()) { - aabb.merge_with(I->get()->get_transformed_aabb()); + AABB aabb = (*vi.begin())->get_transformed_aabb(); + for (const VisualInstance3D *I : vi) { + aabb.merge_with(I->get_transformed_aabb()); } Vector3 size = aabb.size * Vector3(0.5, 0.0, 0.5); from = aabb.position + size; @@ -6800,7 +6810,7 @@ void Node3DEditor::snap_selected_nodes_to_floor() { Dictionary d = snap_data[node]; Vector3 from = d["from"]; Vector3 to = from - Vector3(0.0, max_snap_height, 0.0); - Set<RID> excluded = _get_physics_bodies_rid(sp); + HashSet<RID> excluded = _get_physics_bodies_rid(sp); PhysicsDirectSpaceState3D::RayParameters ray_params; ray_params.from = from; @@ -6822,7 +6832,7 @@ void Node3DEditor::snap_selected_nodes_to_floor() { Dictionary d = snap_data[node]; Vector3 from = d["from"]; Vector3 to = from - Vector3(0.0, max_snap_height, 0.0); - Set<RID> excluded = _get_physics_bodies_rid(sp); + HashSet<RID> excluded = _get_physics_bodies_rid(sp); PhysicsDirectSpaceState3D::RayParameters ray_params; ray_params.from = from; @@ -6951,6 +6961,12 @@ void Node3DEditor::_update_theme() { sun_title->add_theme_font_override("font", get_theme_font(SNAME("title_font"), SNAME("Window"))); environ_title->add_theme_font_override("font", get_theme_font(SNAME("title_font"), SNAME("Window"))); + + sun_color->set_custom_minimum_size(Size2(0, get_theme_constant(SNAME("color_picker_button_height"), SNAME("Editor")))); + environ_sky_color->set_custom_minimum_size(Size2(0, get_theme_constant(SNAME("color_picker_button_height"), SNAME("Editor")))); + environ_ground_color->set_custom_minimum_size(Size2(0, get_theme_constant(SNAME("color_picker_button_height"), SNAME("Editor")))); + + context_menu_container->add_theme_style_override("panel", get_theme_stylebox(SNAME("ContextualToolbar"), SNAME("EditorStyles"))); } void Node3DEditor::_notification(int p_what) { @@ -6989,7 +7005,6 @@ void Node3DEditor::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: { _update_theme(); _update_gizmos_menu_theme(); - _update_context_menu_stylebox(); sun_title->add_theme_font_override("font", get_theme_font(SNAME("title_font"), SNAME("Window"))); environ_title->add_theme_font_override("font", get_theme_font(SNAME("title_font"), SNAME("Window"))); } break; @@ -7278,6 +7293,7 @@ void Node3DEditor::_register_all_gizmos() { add_gizmo_plugin(Ref<OccluderInstance3DGizmoPlugin>(memnew(OccluderInstance3DGizmoPlugin))); add_gizmo_plugin(Ref<SoftDynamicBody3DGizmoPlugin>(memnew(SoftDynamicBody3DGizmoPlugin))); add_gizmo_plugin(Ref<Sprite3DGizmoPlugin>(memnew(Sprite3DGizmoPlugin))); + add_gizmo_plugin(Ref<Label3DGizmoPlugin>(memnew(Label3DGizmoPlugin))); add_gizmo_plugin(Ref<Position3DGizmoPlugin>(memnew(Position3DGizmoPlugin))); add_gizmo_plugin(Ref<RayCast3DGizmoPlugin>(memnew(RayCast3DGizmoPlugin))); add_gizmo_plugin(Ref<SpringArm3DGizmoPlugin>(memnew(SpringArm3DGizmoPlugin))); @@ -7339,7 +7355,7 @@ void Node3DEditor::clear() { void Node3DEditor::_sun_direction_draw() { sun_direction->draw_rect(Rect2(Vector2(), sun_direction->get_size()), Color(1, 1, 1, 1)); - Vector3 z_axis = preview_sun->get_transform().basis.get_axis(Vector3::AXIS_Z); + Vector3 z_axis = preview_sun->get_transform().basis.get_column(Vector3::AXIS_Z); z_axis = get_editor_viewport(0)->camera->get_camera_transform().basis.xform_inv(z_axis); sun_direction_material->set_shader_param("sun_direction", Vector3(z_axis.x, -z_axis.y, z_axis.z)); Color color = sun_color->get_pick_color() * sun_energy->get_value(); @@ -7698,6 +7714,7 @@ Node3DEditor::Node3DEditor() { p->connect("id_pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed)); view_menu = memnew(MenuButton); + // TRANSLATORS: Noun, name of the 2D/3D View menus. view_menu->set_text(TTR("View")); view_menu->set_switch_on_hover(true); view_menu->set_shortcut_context(this); @@ -7708,11 +7725,7 @@ Node3DEditor::Node3DEditor() { context_menu_container = memnew(PanelContainer); hbc_context_menu = memnew(HBoxContainer); context_menu_container->add_child(hbc_context_menu); - // Use a custom stylebox to make contextual menu items stand out from the rest. - // This helps with editor usability as contextual menu items change when selecting nodes, - // even though it may not be immediately obvious at first. hbc_menu->add_child(context_menu_container); - _update_context_menu_stylebox(); // Get the view menu popup and have it stay open when a checkable item is selected p = view_menu->get_popup(); @@ -8122,7 +8135,13 @@ void Node3DEditorPlugin::edit(Object *p_object) { } bool Node3DEditorPlugin::handles(Object *p_object) const { - return p_object->is_class("Node3D"); + if (p_object->is_class("Node3D")) { + return true; + } else { + // This ensures that gizmos are cleared when selecting a non-Node3D node. + const_cast<Node3DEditorPlugin *>(this)->edit((Object *)nullptr); + return false; + } } Dictionary Node3DEditorPlugin::get_state() const { diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index d5d50c743c..8a602be08b 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -141,6 +141,7 @@ class Node3DEditorViewport : public Control { VIEW_DISPLAY_DEBUG_CLUSTER_DECALS, VIEW_DISPLAY_DEBUG_CLUSTER_REFLECTION_PROBES, VIEW_DISPLAY_DEBUG_OCCLUDERS, + VIEW_DISPLAY_MOTION_VECTORS, VIEW_LOCK_ROTATION, VIEW_CINEMATIC_PREVIEW, @@ -211,7 +212,7 @@ private: Control *surface = nullptr; SubViewport *viewport = nullptr; Camera3D *camera = nullptr; - bool transforming; + bool transforming = false; bool orthogonal; bool auto_orthogonal; bool lock_rotation; @@ -251,6 +252,7 @@ private: Transform3D _get_camera_transform() const; int get_selected_count() const; void cancel_transform(); + void _update_shrink(); Vector3 _get_camera_position() const; Vector3 _get_camera_normal() const; @@ -271,7 +273,7 @@ private: ObjectID clicked; Vector<_RayResult> selection_results; - bool clicked_wants_append; + bool clicked_wants_append = false; bool selection_in_progress = false; PopupMenu *selection_menu = nullptr; @@ -461,7 +463,7 @@ public: RID sbox_instance_xray; RID sbox_instance_xray_offset; Ref<EditorNode3DGizmo> gizmo; - Map<int, Transform3D> subgizmos; // map ID -> initial transform + HashMap<int, Transform3D> subgizmos; // map ID -> initial transform Node3DEditorSelectedItem() { sp = nullptr; @@ -551,12 +553,12 @@ private: RID origin; RID origin_instance; - bool origin_enabled; + bool origin_enabled = false; RID grid[3]; RID grid_instance[3]; bool grid_visible[3]; //currently visible bool grid_enable[3]; //should be always visible if true - bool grid_enabled; + bool grid_enabled = false; bool grid_init_draw = false; Camera3D::Projection grid_camera_last_update_perspective = Camera3D::PROJECTION_PERSPECTIVE; Vector3 grid_camera_last_update_position = Vector3(); @@ -674,7 +676,6 @@ private: int camera_override_viewport_id; void _init_indicators(); - void _update_context_menu_stylebox(); void _update_gizmos_menu(); void _update_gizmos_menu_theme(); void _init_grid(); diff --git a/editor/plugins/occluder_instance_3d_editor_plugin.cpp b/editor/plugins/occluder_instance_3d_editor_plugin.cpp index 79cf4c394e..d5fc51aea4 100644 --- a/editor/plugins/occluder_instance_3d_editor_plugin.cpp +++ b/editor/plugins/occluder_instance_3d_editor_plugin.cpp @@ -63,7 +63,7 @@ void OccluderInstance3DEditorPlugin::_bake_select_file(const String &p_file) { break; } case OccluderInstance3D::BAKE_ERROR_CANT_SAVE: { - EditorNode::get_singleton()->show_warning(TTR("Could not save the new occluder at the specified path: ") + p_file); + EditorNode::get_singleton()->show_warning(TTR("Could not save the new occluder at the specified path:") + " " + p_file); break; } default: { diff --git a/editor/plugins/ot_features_plugin.cpp b/editor/plugins/ot_features_plugin.cpp index 936eb747b0..f8526fb715 100644 --- a/editor/plugins/ot_features_plugin.cpp +++ b/editor/plugins/ot_features_plugin.cpp @@ -30,6 +30,9 @@ #include "ot_features_plugin.h" +#include "scene/3d/label_3d.h" +#include "scene/resources/primitive_meshes.h" + void OpenTypeFeaturesEditor::_value_changed(double val) { if (setting) { return; @@ -116,7 +119,28 @@ void OpenTypeFeaturesAdd::setup(Object *p_object) { bool have_ss = false; bool have_cv = false; bool have_cu = false; - Dictionary features = Object::cast_to<Control>(edited_object)->get_theme_font(SNAME("font"))->get_feature_list(); + + Ref<Font> font; + + Control *ctrl = Object::cast_to<Control>(edited_object); + if (ctrl != nullptr) { + font = ctrl->get_theme_font(SNAME("font")); + } + Label3D *l3d = Object::cast_to<Label3D>(edited_object); + if (l3d != nullptr) { + font = l3d->_get_font_or_default(); + } + TextMesh *tm = Object::cast_to<TextMesh>(edited_object); + if (tm != nullptr) { + font = tm->_get_font_or_default(); + } + + if (font.is_null()) { + return; + } + + Dictionary features = font->get_feature_list(); + for (const Variant *ftr = features.next(nullptr); ftr != nullptr; ftr = features.next(ftr)) { String ftr_name = TS->tag_to_name(*ftr); if (ftr_name.begins_with("stylistic_set_")) { @@ -185,7 +209,7 @@ OpenTypeFeaturesAdd::OpenTypeFeaturesAdd() { /*************************************************************************/ bool EditorInspectorPluginOpenTypeFeatures::can_handle(Object *p_object) { - return (Object::cast_to<Control>(p_object) != nullptr); + return (Object::cast_to<Control>(p_object) != nullptr) || (Object::cast_to<Label3D>(p_object) != nullptr) || (Object::cast_to<TextMesh>(p_object) != nullptr); } bool EditorInspectorPluginOpenTypeFeatures::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) { diff --git a/editor/plugins/packed_scene_translation_parser_plugin.cpp b/editor/plugins/packed_scene_translation_parser_plugin.cpp index 9a8584f4a2..8d083d28b2 100644 --- a/editor/plugins/packed_scene_translation_parser_plugin.cpp +++ b/editor/plugins/packed_scene_translation_parser_plugin.cpp @@ -43,7 +43,7 @@ Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path, // These properties are translated with the tr() function in the C++ code when being set or updated. Error err; - RES loaded_res = ResourceLoader::load(p_path, "PackedScene", ResourceFormatLoader::CACHE_MODE_REUSE, &err); + Ref<Resource> loaded_res = ResourceLoader::load(p_path, "PackedScene", ResourceFormatLoader::CACHE_MODE_REUSE, &err); if (err) { ERR_PRINT("Failed to load " + p_path); return err; diff --git a/editor/plugins/packed_scene_translation_parser_plugin.h b/editor/plugins/packed_scene_translation_parser_plugin.h index ecd090b31b..1bfb500933 100644 --- a/editor/plugins/packed_scene_translation_parser_plugin.h +++ b/editor/plugins/packed_scene_translation_parser_plugin.h @@ -37,9 +37,9 @@ class PackedSceneEditorTranslationParserPlugin : public EditorTranslationParserP GDCLASS(PackedSceneEditorTranslationParserPlugin, EditorTranslationParserPlugin); // Scene Node's properties that contain translation strings. - Set<String> lookup_properties; + HashSet<String> lookup_properties; // Properties from specific Nodes that should be ignored. - Map<String, Vector<String>> exception_list; + HashMap<String, Vector<String>> 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.h b/editor/plugins/path_2d_editor_plugin.h index ff74aeedf7..720f5c090f 100644 --- a/editor/plugins/path_2d_editor_plugin.h +++ b/editor/plugins/path_2d_editor_plugin.h @@ -82,11 +82,11 @@ class Path2DEditor : public HBoxContainer { }; Action action; - int action_point; + int action_point = 0; Point2 moving_from; Point2 moving_screen_from; - float orig_in_length; - float orig_out_length; + float orig_in_length = 0.0f; + float orig_out_length = 0.0f; Vector2 edge_point; void _mode_selected(int p_mode); diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp index e52b274908..36f559b2ae 100644 --- a/editor/plugins/path_3d_editor_plugin.cpp +++ b/editor/plugins/path_3d_editor_plugin.cpp @@ -37,6 +37,19 @@ #include "node_3d_editor_plugin.h" #include "scene/resources/curve.h" +static bool _is_in_handle(int p_id, int p_num_points) { + int t = (p_id + 1) % 2; + int idx = (p_id + 1) / 2; + // order of points is [out_0, out_1, in_1, out_2, in_2, ... out_n-1, in_n-1, in_n] + if (idx == 0) { + return false; + } else if (idx == (p_num_points - 1)) { + return true; + } else { + return (t == 1); + } +} + String Path3DGizmo::get_handle_name(int p_id, bool p_secondary) const { Ref<Curve3D> c = path->get_curve(); if (c.is_null()) { @@ -47,12 +60,10 @@ String Path3DGizmo::get_handle_name(int p_id, bool p_secondary) const { return TTR("Curve Point #") + itos(p_id); } - p_id += 1; // Account for the first point only having an "out" handle - - int idx = p_id / 2; - int t = p_id % 2; + // (p_id + 1) Accounts for the first point only having an "out" handle + int idx = (p_id + 1) / 2; String n = TTR("Curve Point #") + itos(idx); - if (t == 0) { + if (_is_in_handle(p_id, c->get_point_count())) { n += " In"; } else { n += " Out"; @@ -72,13 +83,11 @@ Variant Path3DGizmo::get_handle_value(int p_id, bool p_secondary) const { return original; } - p_id += 1; // Account for the first point only having an "out" handle - - int idx = p_id / 2; - int t = p_id % 2; + // (p_id + 1) Accounts for the first point only having an "out" handle + int idx = (p_id + 1) / 2; Vector3 ofs; - if (t == 0) { + if (_is_in_handle(p_id, c->get_point_count())) { ofs = c->get_point_in(idx); } else { ofs = c->get_point_out(idx); @@ -102,7 +111,7 @@ void Path3DGizmo::set_handle(int p_id, bool p_secondary, Camera3D *p_camera, con // Setting curve point positions if (!p_secondary) { - const Plane p = Plane(p_camera->get_transform().basis.get_axis(2), gt.xform(original)); + const Plane p = Plane(p_camera->get_transform().basis.get_column(2), gt.xform(original)); Vector3 inters; @@ -119,14 +128,12 @@ void Path3DGizmo::set_handle(int p_id, bool p_secondary, Camera3D *p_camera, con return; } - p_id += 1; // Account for the first point only having an "out" handle - - int idx = p_id / 2; - int t = p_id % 2; + // (p_id + 1) Accounts for the first point only having an "out" handle + int idx = (p_id + 1) / 2; Vector3 base = c->get_point_position(idx); - Plane p(p_camera->get_transform().basis.get_axis(2), gt.xform(original)); + Plane p(p_camera->get_transform().basis.get_column(2), gt.xform(original)); Vector3 inters; @@ -144,7 +151,7 @@ void Path3DGizmo::set_handle(int p_id, bool p_secondary, Camera3D *p_camera, con local.snap(Vector3(snap, snap, snap)); } - if (t == 0) { + if (_is_in_handle(p_id, c->get_point_count())) { c->set_point_in(idx, local); if (Path3DEditorPlugin::singleton->mirror_angle_enabled()) { c->set_point_out(idx, Path3DEditorPlugin::singleton->mirror_length_enabled() ? -local : (-local.normalized() * orig_out_length)); @@ -179,12 +186,10 @@ void Path3DGizmo::commit_handle(int p_id, bool p_secondary, const Variant &p_res return; } - p_id += 1; // Account for the first point only having an "out" handle - - int idx = p_id / 2; - int t = p_id % 2; + // (p_id + 1) Accounts for the first point only having an "out" handle + int idx = (p_id + 1) / 2; - if (t == 0) { + if (_is_in_handle(p_id, c->get_point_count())) { if (p_cancel) { c->set_point_in(p_id, p_restore); return; @@ -263,17 +268,17 @@ void Path3DGizmo::redraw() { for (int i = 0; i < c->get_point_count(); i++) { Vector3 p = c->get_point_position(i); handles.push_back(p); - if (i > 0) { - v3p.push_back(p); - v3p.push_back(p + c->get_point_in(i)); - sec_handles.push_back(p + c->get_point_in(i)); - } - + // push Out points first so they get selected if the In and Out points are on top of each other. if (i < c->get_point_count() - 1) { v3p.push_back(p); v3p.push_back(p + c->get_point_out(i)); sec_handles.push_back(p + c->get_point_out(i)); } + if (i > 0) { + v3p.push_back(p); + v3p.push_back(p + c->get_point_in(i)); + sec_handles.push_back(p + c->get_point_in(i)); + } } if (v3p.size() > 1) { @@ -320,14 +325,14 @@ EditorPlugin::AfterGUIInput Path3DEditorPlugin::forward_spatial_gui_input(Camera if (mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT && (curve_create->is_pressed() || (curve_edit->is_pressed() && mb->is_ctrl_pressed()))) { //click into curve, break it down Vector<Vector3> v3a = c->tessellate(); - int idx = 0; int rc = v3a.size(); int closest_seg = -1; Vector3 closest_seg_point; - float closest_d = 1e20; if (rc >= 2) { + int idx = 0; const Vector3 *r = v3a.ptr(); + float closest_d = 1e20; if (p_camera->unproject_position(gt.xform(c->get_point_position(0))).distance_to(mbpos) < click_dist) { return EditorPlugin::AFTER_GUI_INPUT_PASS; //nope, existing @@ -396,7 +401,7 @@ EditorPlugin::AfterGUIInput Path3DEditorPlugin::forward_spatial_gui_input(Camera } else { origin = gt.xform(c->get_point_position(c->get_point_count() - 1)); } - Plane p(p_camera->get_transform().basis.get_axis(2), origin); + Plane p(p_camera->get_transform().basis.get_column(2), origin); Vector3 ray_from = p_camera->project_ray_origin(mbpos); Vector3 ray_dir = p_camera->project_ray_normal(mbpos); diff --git a/editor/plugins/path_3d_editor_plugin.h b/editor/plugins/path_3d_editor_plugin.h index 72c24732c0..ee31fcf43d 100644 --- a/editor/plugins/path_3d_editor_plugin.h +++ b/editor/plugins/path_3d_editor_plugin.h @@ -84,7 +84,7 @@ class Path3DEditorPlugin : public EditorPlugin { void _mode_changed(int p_idx); void _close_curve(); void _handle_option_pressed(int p_option); - bool handle_clicked; + bool handle_clicked = false; bool mirror_handle_angle; bool mirror_handle_length; diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index 8566e92b22..a682bb455c 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -455,7 +455,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { } Transform2D mtx; - mtx.elements[2] = -uv_draw_ofs; + mtx.columns[2] = -uv_draw_ofs; mtx.scale_basis(Vector2(uv_draw_zoom, uv_draw_zoom)); Ref<InputEventMouseButton> mb = p_input; @@ -970,7 +970,7 @@ void Polygon2DEditor::_uv_draw() { String warning; Transform2D mtx; - mtx.elements[2] = -uv_draw_ofs; + mtx.columns[2] = -uv_draw_ofs; mtx.scale_basis(Vector2(uv_draw_zoom, uv_draw_zoom)); RS::get_singleton()->canvas_item_add_set_transform(uv_edit_draw->get_canvas_item(), mtx); diff --git a/editor/plugins/polygon_2d_editor_plugin.h b/editor/plugins/polygon_2d_editor_plugin.h index 4403d1e9c7..d878d3f9af 100644 --- a/editor/plugins/polygon_2d_editor_plugin.h +++ b/editor/plugins/polygon_2d_editor_plugin.h @@ -96,7 +96,7 @@ class Polygon2DEditor : public AbstractPolygon2DEditor { SpinBox *bone_paint_radius = nullptr; Label *bone_paint_radius_label = nullptr; bool bone_painting; - int bone_painting_bone; + int bone_painting_bone = 0; Vector<float> prev_weights; Vector2 bone_paint_pos; AcceptDialog *grid_settings = nullptr; @@ -110,7 +110,7 @@ class Polygon2DEditor : public AbstractPolygon2DEditor { Vector<Vector2> uv_create_uv_prev; Vector<Vector2> uv_create_poly_prev; Vector<Color> uv_create_colors_prev; - int uv_create_prev_internal_vertices; + int uv_create_prev_internal_vertices = 0; Array uv_create_bones_prev; Array polygons_prev; diff --git a/editor/plugins/polygon_3d_editor_plugin.cpp b/editor/plugins/polygon_3d_editor_plugin.cpp index 2d4812c55b..1c69e0d635 100644 --- a/editor/plugins/polygon_3d_editor_plugin.cpp +++ b/editor/plugins/polygon_3d_editor_plugin.cpp @@ -117,7 +117,7 @@ EditorPlugin::AfterGUIInput Polygon3DEditor::forward_spatial_gui_input(Camera3D Transform3D gt = node->get_global_transform(); Transform3D gi = gt.affine_inverse(); float depth = _get_depth() * 0.5; - Vector3 n = gt.basis.get_axis(2).normalized(); + Vector3 n = gt.basis.get_column(2).normalized(); Plane p(n, gt.origin + n * depth); Ref<InputEventMouseButton> mb = p_event; diff --git a/editor/plugins/polygon_3d_editor_plugin.h b/editor/plugins/polygon_3d_editor_plugin.h index 3ad7a4df58..e1e1261250 100644 --- a/editor/plugins/polygon_3d_editor_plugin.h +++ b/editor/plugins/polygon_3d_editor_plugin.h @@ -66,14 +66,14 @@ class Polygon3DEditor : public HBoxContainer { MenuButton *options = nullptr; - int edited_point; + int edited_point = 0; Vector2 edited_point_pos; PackedVector2Array pre_move_edit; PackedVector2Array wip; bool wip_active; bool snap_ignore; - float prev_depth; + float prev_depth = 0.0f; void _wip_close(); void _polygon_draw(); diff --git a/editor/plugins/ray_cast_2d_editor_plugin.cpp b/editor/plugins/ray_cast_2d_editor_plugin.cpp new file mode 100644 index 0000000000..6f247a37ef --- /dev/null +++ b/editor/plugins/ray_cast_2d_editor_plugin.cpp @@ -0,0 +1,151 @@ +/*************************************************************************/ +/* ray_cast_2d_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "ray_cast_2d_editor_plugin.h" + +#include "canvas_item_editor_plugin.h" +#include "editor/editor_node.h" + +void RayCast2DEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + get_tree()->connect("node_removed", callable_mp(this, &RayCast2DEditor::_node_removed)); + } break; + + case NOTIFICATION_EXIT_TREE: { + get_tree()->disconnect("node_removed", callable_mp(this, &RayCast2DEditor::_node_removed)); + } break; + } +} + +void RayCast2DEditor::_node_removed(Node *p_node) { + if (p_node == node) { + node = nullptr; + } +} + +bool RayCast2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_event) { + if (!node || !node->is_visible_in_tree()) { + return false; + } + + Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); + + Ref<InputEventMouseButton> mb = p_event; + if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) { + if (mb->is_pressed()) { + if (xform.xform(node->get_target_position()).distance_to(mb->get_position()) < 8) { + pressed = true; + original_target_position = node->get_target_position(); + + return true; + } else { + pressed = false; + + return false; + } + } else if (pressed) { + undo_redo->create_action(TTR("Set target_position")); + undo_redo->add_do_method(node, "set_target_position", node->get_target_position()); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); + undo_redo->add_undo_method(node, "set_target_position", original_target_position); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); + undo_redo->commit_action(); + + pressed = false; + + return true; + } + } + + Ref<InputEventMouseMotion> mm = p_event; + if (mm.is_valid() && pressed) { + Vector2 point = canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(mm->get_position())); + point = node->get_global_transform().affine_inverse().xform(point); + + node->set_target_position(point); + canvas_item_editor->update_viewport(); + node->notify_property_list_changed(); + + return true; + } + + return false; +} + +void RayCast2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { + if (!node || !node->is_visible_in_tree()) { + return; + } + + Transform2D gt = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); + + const Ref<Texture2D> handle = get_theme_icon(SNAME("EditorHandle"), SNAME("EditorIcons")); + p_overlay->draw_texture(handle, gt.xform(node->get_target_position()) - handle->get_size() / 2); +} + +void RayCast2DEditor::edit(Node *p_node) { + if (!canvas_item_editor) { + canvas_item_editor = CanvasItemEditor::get_singleton(); + } + + if (p_node) { + node = Object::cast_to<RayCast2D>(p_node); + } else { + node = nullptr; + } + + canvas_item_editor->update_viewport(); +} + +RayCast2DEditor::RayCast2DEditor() { + undo_redo = EditorNode::get_singleton()->get_undo_redo(); +} + +/////////////////////// + +void RayCast2DEditorPlugin::edit(Object *p_object) { + ray_cast_2d_editor->edit(Object::cast_to<RayCast2D>(p_object)); +} + +bool RayCast2DEditorPlugin::handles(Object *p_object) const { + return Object::cast_to<RayCast2D>(p_object) != nullptr; +} + +void RayCast2DEditorPlugin::make_visible(bool p_visible) { + if (!p_visible) { + edit(nullptr); + } +} + +RayCast2DEditorPlugin::RayCast2DEditorPlugin() { + ray_cast_2d_editor = memnew(RayCast2DEditor); + EditorNode::get_singleton()->get_gui_base()->add_child(ray_cast_2d_editor); +} diff --git a/editor/plugins/ray_cast_2d_editor_plugin.h b/editor/plugins/ray_cast_2d_editor_plugin.h new file mode 100644 index 0000000000..74628da0e4 --- /dev/null +++ b/editor/plugins/ray_cast_2d_editor_plugin.h @@ -0,0 +1,79 @@ +/*************************************************************************/ +/* ray_cast_2d_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RAY_CAST_2D_EDITOR_PLUGIN_H +#define RAY_CAST_2D_EDITOR_PLUGIN_H + +#include "editor/editor_plugin.h" +#include "scene/2d/ray_cast_2d.h" + +class CanvasItemEditor; + +class RayCast2DEditor : public Control { + GDCLASS(RayCast2DEditor, Control); + + UndoRedo *undo_redo = nullptr; + CanvasItemEditor *canvas_item_editor = nullptr; + RayCast2D *node; + + bool pressed = false; + Point2 original_target_position; + +protected: + void _notification(int p_what); + void _node_removed(Node *p_node); + +public: + bool forward_canvas_gui_input(const Ref<InputEvent> &p_event); + void forward_canvas_draw_over_viewport(Control *p_overlay); + void edit(Node *p_node); + + RayCast2DEditor(); +}; + +class RayCast2DEditorPlugin : public EditorPlugin { + GDCLASS(RayCast2DEditorPlugin, EditorPlugin); + + RayCast2DEditor *ray_cast_2d_editor = nullptr; + +public: + virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override { return ray_cast_2d_editor->forward_canvas_gui_input(p_event); } + virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override { ray_cast_2d_editor->forward_canvas_draw_over_viewport(p_overlay); } + + virtual String get_name() const override { return "RayCast2D"; } + bool has_main_screen() const override { return false; } + virtual void edit(Object *p_object) override; + virtual bool handles(Object *p_object) const override; + virtual void make_visible(bool visible) override; + + RayCast2DEditorPlugin(); +}; + +#endif // RAY_CAST_2D_EDITOR_PLUGIN_H diff --git a/editor/plugins/replication_editor_plugin.cpp b/editor/plugins/replication_editor_plugin.cpp index 2a7b3c7a55..72fe3c5f20 100644 --- a/editor/plugins/replication_editor_plugin.cpp +++ b/editor/plugins/replication_editor_plugin.cpp @@ -37,6 +37,129 @@ #include "scene/gui/tree.h" #include "scene/multiplayer/multiplayer_synchronizer.h" +void ReplicationEditor::_pick_node_filter_text_changed(const String &p_newtext) { + TreeItem *root_item = pick_node->get_scene_tree()->get_scene_tree()->get_root(); + + Vector<Node *> select_candidates; + Node *to_select = nullptr; + + String filter = pick_node->get_filter_line_edit()->get_text(); + + _pick_node_select_recursive(root_item, filter, select_candidates); + + if (!select_candidates.is_empty()) { + for (int i = 0; i < select_candidates.size(); ++i) { + Node *candidate = select_candidates[i]; + + if (((String)candidate->get_name()).to_lower().begins_with(filter.to_lower())) { + to_select = candidate; + break; + } + } + + if (!to_select) { + to_select = select_candidates[0]; + } + } + + pick_node->get_scene_tree()->set_selected(to_select); +} + +void ReplicationEditor::_pick_node_select_recursive(TreeItem *p_item, const String &p_filter, Vector<Node *> &p_select_candidates) { + if (!p_item) { + return; + } + + NodePath np = p_item->get_metadata(0); + Node *node = get_node(np); + + if (!p_filter.is_empty() && ((String)node->get_name()).findn(p_filter) != -1) { + p_select_candidates.push_back(node); + } + + TreeItem *c = p_item->get_first_child(); + + while (c) { + _pick_node_select_recursive(c, p_filter, p_select_candidates); + c = c->get_next(); + } +} + +void ReplicationEditor::_pick_node_filter_input(const Ref<InputEvent> &p_ie) { + Ref<InputEventKey> k = p_ie; + + if (k.is_valid()) { + switch (k->get_keycode()) { + case Key::UP: + case Key::DOWN: + case Key::PAGEUP: + case Key::PAGEDOWN: { + pick_node->get_scene_tree()->get_scene_tree()->gui_input(k); + pick_node->get_filter_line_edit()->accept_event(); + } break; + default: + break; + } + } +} + +void ReplicationEditor::_pick_node_selected(NodePath p_path) { + Node *root = current->get_node(current->get_root_path()); + ERR_FAIL_COND(!root); + Node *node = get_node(p_path); + ERR_FAIL_COND(!node); + NodePath path_to = root->get_path_to(node); + adding_node_path = path_to; + prop_selector->select_property_from_instance(node); +} + +void ReplicationEditor::_pick_new_property() { + if (current == nullptr) { + EditorNode::get_singleton()->show_warning(TTR("Select a replicator node in order to pick a property to add to it.")); + return; + } + Node *root = current->get_node(current->get_root_path()); + if (!root) { + EditorNode::get_singleton()->show_warning(TTR("Not possible to add a new property to synchronize without a root.")); + return; + } + pick_node->popup_scenetree_dialog(); + pick_node->get_filter_line_edit()->clear(); + pick_node->get_filter_line_edit()->grab_focus(); +} + +void ReplicationEditor::_add_sync_property(String p_path) { + config = current->get_replication_config(); + + if (config.is_valid() && config->has_property(p_path)) { + EditorNode::get_singleton()->show_warning(TTR("Property is already being synchronized.")); + return; + } + + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + undo_redo->create_action(TTR("Add property to synchronizer")); + + if (config.is_null()) { + config.instantiate(); + current->set_replication_config(config); + undo_redo->add_do_method(current, "set_replication_config", config); + undo_redo->add_undo_method(current, "set_replication_config", Ref<SceneReplicationConfig>()); + _update_config(); + } + + undo_redo->add_do_method(config.ptr(), "add_property", p_path); + undo_redo->add_undo_method(config.ptr(), "remove_property", p_path); + undo_redo->add_do_method(this, "_update_config"); + undo_redo->add_undo_method(this, "_update_config"); + undo_redo->commit_action(); +} + +void ReplicationEditor::_pick_node_property_selected(String p_name) { + String adding_prop_path = String(adding_node_path) + ":" + p_name; + + _add_sync_property(adding_prop_path); +} + /// ReplicationEditor ReplicationEditor::ReplicationEditor() { set_v_size_flags(SIZE_EXPAND_FILL); @@ -56,16 +179,44 @@ ReplicationEditor::ReplicationEditor() { vb->set_v_size_flags(SIZE_EXPAND_FILL); add_child(vb); + pick_node = memnew(SceneTreeDialog); + add_child(pick_node); + pick_node->register_text_enter(pick_node->get_filter_line_edit()); + pick_node->set_title(TTR("Pick a node to synchronize:")); + pick_node->connect("selected", callable_mp(this, &ReplicationEditor::_pick_node_selected)); + pick_node->get_filter_line_edit()->connect("text_changed", callable_mp(this, &ReplicationEditor::_pick_node_filter_text_changed)); + pick_node->get_filter_line_edit()->connect("gui_input", callable_mp(this, &ReplicationEditor::_pick_node_filter_input)); + + prop_selector = memnew(PropertySelector); + add_child(prop_selector); + prop_selector->connect("selected", callable_mp(this, &ReplicationEditor::_pick_node_property_selected)); + HBoxContainer *hb = memnew(HBoxContainer); vb->add_child(hb); + + add_pick_button = memnew(Button); + add_pick_button->connect("pressed", callable_mp(this, &ReplicationEditor::_pick_new_property)); + add_pick_button->set_text(TTR("Add property to sync..")); + hb->add_child(add_pick_button); + VSeparator *vs = memnew(VSeparator); + vs->set_custom_minimum_size(Size2(30 * EDSCALE, 0)); + hb->add_child(vs); + hb->add_child(memnew(Label(TTR("Path:")))); np_line_edit = memnew(LineEdit); np_line_edit->set_placeholder(":property"); np_line_edit->set_h_size_flags(SIZE_EXPAND_FILL); hb->add_child(np_line_edit); - add_button = memnew(Button); - add_button->connect("pressed", callable_mp(this, &ReplicationEditor::_add_pressed)); - add_button->set_text(TTR("Add")); - hb->add_child(add_button); + add_from_path_button = memnew(Button); + add_from_path_button->connect("pressed", callable_mp(this, &ReplicationEditor::_add_pressed)); + add_from_path_button->set_text(TTR("Add from path")); + hb->add_child(add_from_path_button); + vs = memnew(VSeparator); + vs->set_custom_minimum_size(Size2(30 * EDSCALE, 0)); + hb->add_child(vs); + pin = memnew(Button); + pin->set_flat(true); + pin->set_toggle_mode(true); + hb->add_child(pin); tree = memnew(Tree); tree->set_hide_root(true); @@ -81,23 +232,92 @@ ReplicationEditor::ReplicationEditor() { tree->set_column_expand(2, false); tree->set_column_expand(3, false); tree->create_item(); - tree->connect("button_pressed", callable_mp(this, &ReplicationEditor::_tree_button_pressed)); + tree->connect("button_clicked", callable_mp(this, &ReplicationEditor::_tree_button_pressed)); tree->connect("item_edited", callable_mp(this, &ReplicationEditor::_tree_item_edited)); tree->set_v_size_flags(SIZE_EXPAND_FILL); vb->add_child(tree); + + drop_label = memnew(Label); + drop_label->set_text(TTR("Add properties using the buttons above or\ndrag them them from the inspector and drop them here.")); + drop_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); + drop_label->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER); + tree->add_child(drop_label); + drop_label->set_anchors_and_offsets_preset(Control::PRESET_WIDE); + + tree->set_drag_forwarding(this); } void ReplicationEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_config"), &ReplicationEditor::_update_config); ClassDB::bind_method(D_METHOD("_update_checked", "property", "column", "checked"), &ReplicationEditor::_update_checked); + ClassDB::bind_method("_can_drop_data_fw", &ReplicationEditor::_can_drop_data_fw); + ClassDB::bind_method("_drop_data_fw", &ReplicationEditor::_drop_data_fw); + ADD_SIGNAL(MethodInfo("keying_changed")); } +bool ReplicationEditor::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { + Dictionary d = p_data; + if (!d.has("type")) { + return false; + } + String t = d["type"]; + if (t != "obj_property") { + return false; + } + Object *obj = d["object"]; + if (!obj) { + return false; + } + Node *node = Object::cast_to<Node>(obj); + if (!node) { + return false; + } + + return true; +} + +void ReplicationEditor::_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) { + if (current == nullptr) { + EditorNode::get_singleton()->show_warning(TTR("Select a replicator node in order to pick a property to add to it.")); + return; + } + Node *root = current->get_node(current->get_root_path()); + if (!root) { + EditorNode::get_singleton()->show_warning(TTR("Not possible to add a new property to synchronize without a root.")); + return; + } + + Dictionary d = p_data; + if (!d.has("type")) { + return; + } + String t = d["type"]; + if (t != "obj_property") { + return; + } + Object *obj = d["object"]; + if (!obj) { + return; + } + Node *node = Object::cast_to<Node>(obj); + if (!node) { + return; + } + + String path = root->get_path_to(node); + path += ":" + String(d["property"]); + + _add_sync_property(path); +} + void ReplicationEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { add_theme_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox(SNAME("panel"), SNAME("Panel"))); + add_pick_button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); + pin->set_icon(get_theme_icon(SNAME("Pin"), SNAME("EditorIcons"))); } break; case NOTIFICATION_VISIBILITY_CHANGED: { @@ -167,7 +387,11 @@ void ReplicationEditor::_tree_item_edited() { undo_redo->commit_action(); } -void ReplicationEditor::_tree_button_pressed(Object *p_item, int p_column, int p_id) { +void ReplicationEditor::_tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button) { + if (p_button != MouseButton::LEFT) { + return; + } + TreeItem *ti = Object::cast_to<TreeItem>(p_item); if (!ti) { return; @@ -236,11 +460,15 @@ void ReplicationEditor::_update_config() { deleting = NodePath(); tree->clear(); tree->create_item(); + drop_label->set_visible(true); if (!config.is_valid()) { update_keying(); return; } TypedArray<NodePath> props = config->get_properties(); + if (props.size()) { + drop_label->set_visible(false); + } for (int i = 0; i < props.size(); i++) { const NodePath path = props[i]; _add_property(path, config->property_get_spawn(path), config->property_get_sync(path)); @@ -341,7 +569,9 @@ void ReplicationEditor::property_keyed(const String &p_property) { /// ReplicationEditorPlugin ReplicationEditorPlugin::ReplicationEditorPlugin() { repl_editor = memnew(ReplicationEditor); - EditorNode::get_singleton()->add_bottom_panel_item(TTR("Replication"), repl_editor); + button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Replication"), repl_editor); + button->hide(); + repl_editor->get_pin()->connect("pressed", callable_mp(this, &ReplicationEditorPlugin::_pinned)); } ReplicationEditorPlugin::~ReplicationEditorPlugin() { @@ -378,6 +608,17 @@ void ReplicationEditorPlugin::_node_removed(Node *p_node) { if (repl_editor->is_visible_in_tree()) { EditorNode::get_singleton()->hide_bottom_panel(); } + button->hide(); + repl_editor->get_pin()->set_pressed(false); + } +} + +void ReplicationEditorPlugin::_pinned() { + if (!repl_editor->get_pin()->is_pressed()) { + if (repl_editor->is_visible_in_tree()) { + EditorNode::get_singleton()->hide_bottom_panel(); + } + button->hide(); } } @@ -391,6 +632,14 @@ bool ReplicationEditorPlugin::handles(Object *p_object) const { void ReplicationEditorPlugin::make_visible(bool p_visible) { if (p_visible) { + //editor->hide_animation_player_editors(); + //editor->animation_panel_make_visible(true); + button->show(); EditorNode::get_singleton()->make_bottom_panel_item_visible(repl_editor); + } else if (!repl_editor->get_pin()->is_pressed()) { + if (repl_editor->is_visible_in_tree()) { + EditorNode::get_singleton()->hide_bottom_panel(); + } + button->hide(); } } diff --git a/editor/plugins/replication_editor_plugin.h b/editor/plugins/replication_editor_plugin.h index 08e86d1617..df3d97f884 100644 --- a/editor/plugins/replication_editor_plugin.h +++ b/editor/plugins/replication_editor_plugin.h @@ -34,6 +34,10 @@ #include "editor/editor_plugin.h" #include "scene/resources/scene_replication_config.h" +#include "editor/editor_spin_slider.h" +#include "editor/property_editor.h" +#include "editor/property_selector.h" + class ConfirmationDialog; class MultiplayerSynchronizer; class Tree; @@ -46,24 +50,46 @@ private: AcceptDialog *error_dialog = nullptr; ConfirmationDialog *delete_dialog = nullptr; - Button *add_button = nullptr; + Button *add_pick_button = nullptr; + Button *add_from_path_button = nullptr; LineEdit *np_line_edit = nullptr; + Label *drop_label = nullptr; + Ref<SceneReplicationConfig> config; NodePath deleting; Tree *tree = nullptr; bool keying = false; + PropertySelector *prop_selector = nullptr; + SceneTreeDialog *pick_node = nullptr; + NodePath adding_node_path; + + Button *pin = nullptr; + Ref<Texture2D> _get_class_icon(const Node *p_node); void _add_pressed(); void _tree_item_edited(); - void _tree_button_pressed(Object *p_item, int p_column, int p_id); + void _tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button); void _update_checked(const NodePath &p_prop, int p_column, bool p_checked); void _update_config(); void _dialog_closed(bool p_confirmed); void _add_property(const NodePath &p_property, bool p_spawn = true, bool p_sync = true); + void _pick_node_filter_text_changed(const String &p_newtext); + void _pick_node_select_recursive(TreeItem *p_item, const String &p_filter, Vector<Node *> &p_select_candidates); + void _pick_node_filter_input(const Ref<InputEvent> &p_ie); + void _pick_node_selected(NodePath p_path); + + void _pick_new_property(); + void _pick_node_property_selected(String p_name); + + 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 _add_sync_property(String p_path); + protected: static void _bind_methods(); @@ -76,6 +102,7 @@ public: MultiplayerSynchronizer *get_current() const { return current; } void property_keyed(const String &p_property); + Button *get_pin() { return pin; } ReplicationEditor(); ~ReplicationEditor() {} }; @@ -84,12 +111,15 @@ class ReplicationEditorPlugin : public EditorPlugin { GDCLASS(ReplicationEditorPlugin, EditorPlugin); private: + Button *button = nullptr; ReplicationEditor *repl_editor = nullptr; void _node_removed(Node *p_node); void _keying_changed(); void _property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance); + void _pinned(); + protected: void _notification(int p_what); diff --git a/editor/plugins/resource_preloader_editor_plugin.cpp b/editor/plugins/resource_preloader_editor_plugin.cpp index eafc53c72b..79fc304242 100644 --- a/editor/plugins/resource_preloader_editor_plugin.cpp +++ b/editor/plugins/resource_preloader_editor_plugin.cpp @@ -50,7 +50,7 @@ void ResourcePreloaderEditor::_files_load_request(const Vector<String> &p_paths) for (int i = 0; i < p_paths.size(); i++) { String path = p_paths[i]; - RES resource; + Ref<Resource> resource; resource = ResourceLoader::load(path); if (resource.is_null()) { @@ -113,7 +113,7 @@ void ResourcePreloaderEditor::_item_edited() { return; } - RES samp = preloader->get_resource(old_name); + Ref<Resource> samp = preloader->get_resource(old_name); undo_redo->create_action(TTR("Rename Resource")); undo_redo->add_do_method(preloader, "remove_resource", old_name); undo_redo->add_do_method(preloader, "add_resource", new_name, samp); @@ -135,7 +135,7 @@ void ResourcePreloaderEditor::_remove_resource(const String &p_to_remove) { } void ResourcePreloaderEditor::_paste_pressed() { - RES r = EditorSettings::get_singleton()->get_resource_clipboard(); + Ref<Resource> r = EditorSettings::get_singleton()->get_resource_clipboard(); if (!r.is_valid()) { dialog->set_text(TTR("Resource clipboard is empty!")); dialog->set_title(TTR("Error!")); @@ -190,7 +190,7 @@ void ResourcePreloaderEditor::_update_library() { ti->set_text(0, E); ti->set_metadata(0, E); - RES r = preloader->get_resource(E); + Ref<Resource> r = preloader->get_resource(E); ERR_CONTINUE(r.is_null()); @@ -213,7 +213,11 @@ void ResourcePreloaderEditor::_update_library() { //player->add_resource("default",resource); } -void ResourcePreloaderEditor::_cell_button_pressed(Object *p_item, int p_column, int p_id) { +void ResourcePreloaderEditor::_cell_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button) { + if (p_button != MouseButton::LEFT) { + return; + } + TreeItem *item = Object::cast_to<TreeItem>(p_item); ERR_FAIL_COND(!item); @@ -222,7 +226,7 @@ void ResourcePreloaderEditor::_cell_button_pressed(Object *p_item, int p_column, EditorInterface::get_singleton()->open_scene_from_path(rpath); } else if (p_id == BUTTON_EDIT_RESOURCE) { - RES r = preloader->get_resource(item->get_text(0)); + Ref<Resource> r = preloader->get_resource(item->get_text(0)); EditorInterface::get_singleton()->edit_resource(r); } else if (p_id == BUTTON_REMOVE) { @@ -249,7 +253,7 @@ Variant ResourcePreloaderEditor::get_drag_data_fw(const Point2 &p_point, Control String name = ti->get_metadata(0); - RES res = preloader->get_resource(name); + Ref<Resource> res = preloader->get_resource(name); if (!res.is_valid()) { return Variant(); } @@ -269,7 +273,7 @@ bool ResourcePreloaderEditor::can_drop_data_fw(const Point2 &p_point, const Vari } if (String(d["type"]) == "resource" && d.has("resource")) { - RES r = d["resource"]; + Ref<Resource> r = d["resource"]; return r.is_valid(); } @@ -294,7 +298,7 @@ void ResourcePreloaderEditor::drop_data_fw(const Point2 &p_point, const Variant } if (String(d["type"]) == "resource" && d.has("resource")) { - RES r = d["resource"]; + Ref<Resource> r = d["resource"]; if (r.is_valid()) { String basename; @@ -359,7 +363,7 @@ ResourcePreloaderEditor::ResourcePreloaderEditor() { add_child(file); tree = memnew(Tree); - tree->connect("button_pressed", callable_mp(this, &ResourcePreloaderEditor::_cell_button_pressed)); + tree->connect("button_clicked", callable_mp(this, &ResourcePreloaderEditor::_cell_button_pressed)); tree->set_columns(2); tree->set_column_expand_ratio(0, 2); tree->set_column_clip_content(0, true); @@ -416,11 +420,8 @@ ResourcePreloaderEditorPlugin::ResourcePreloaderEditorPlugin() { preloader_editor = memnew(ResourcePreloaderEditor); preloader_editor->set_custom_minimum_size(Size2(0, 250) * EDSCALE); - button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("ResourcePreloader"), preloader_editor); + button = EditorNode::get_singleton()->add_bottom_panel_item("ResourcePreloader", preloader_editor); button->hide(); - - //preloader_editor->set_anchor( MARGIN_TOP, Control::ANCHOR_END); - //preloader_editor->set_margin( MARGIN_TOP, 120 ); } ResourcePreloaderEditorPlugin::~ResourcePreloaderEditorPlugin() { diff --git a/editor/plugins/resource_preloader_editor_plugin.h b/editor/plugins/resource_preloader_editor_plugin.h index 0b799c13c6..96cef3de21 100644 --- a/editor/plugins/resource_preloader_editor_plugin.h +++ b/editor/plugins/resource_preloader_editor_plugin.h @@ -63,7 +63,7 @@ class ResourcePreloaderEditor : public PanelContainer { void _paste_pressed(); void _remove_resource(const String &p_to_remove); void _update_library(); - void _cell_button_pressed(Object *p_item, int p_column, int p_id); + void _cell_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button); void _item_edited(); UndoRedo *undo_redo = nullptr; diff --git a/editor/plugins/root_motion_editor_plugin.cpp b/editor/plugins/root_motion_editor_plugin.cpp index bfb672d694..681dd476e3 100644 --- a/editor/plugins/root_motion_editor_plugin.cpp +++ b/editor/plugins/root_motion_editor_plugin.cpp @@ -65,7 +65,7 @@ void EditorPropertyRootMotion::_node_assign() { return; } - Set<String> paths; + HashSet<String> paths; { List<StringName> animations; player->get_animation_list(&animations); @@ -81,10 +81,10 @@ void EditorPropertyRootMotion::_node_assign() { filters->clear(); TreeItem *root = filters->create_item(); - Map<String, TreeItem *> parenthood; + HashMap<String, TreeItem *> parenthood; - for (Set<String>::Element *E = paths.front(); E; E = E->next()) { - NodePath path = E->get(); + for (const String &E : paths) { + NodePath path = E; TreeItem *ti = nullptr; String accum; for (int i = 0; i < path.get_name_count(); i++) { diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 906edb006c..6ab2366a44 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -119,9 +119,9 @@ void EditorStandardSyntaxHighlighter::_update_cache() { } /* Autoloads. */ - 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(); + HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); + for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : autoloads) { + const ProjectSettings::AutoloadInfo &info = E.value; if (info.is_singleton) { highlighter->add_keyword_color(info.name, usertype_color); } @@ -232,24 +232,24 @@ void ScriptEditorBase::_bind_methods() { class EditorScriptCodeCompletionCache : public ScriptCodeCompletionCache { struct Cache { uint64_t time_loaded = 0; - RES cache; + Ref<Resource> cache; }; - Map<String, Cache> cached; + HashMap<String, Cache> cached; public: uint64_t max_time_cache = 5 * 60 * 1000; //minutes, five - int max_cache_size = 128; + uint32_t max_cache_size = 128; void cleanup() { - List<Map<String, Cache>::Element *> to_clean; + List<String> to_clean; - Map<String, Cache>::Element *I = cached.front(); + HashMap<String, Cache>::Iterator I = cached.begin(); while (I) { - if ((OS::get_singleton()->get_ticks_msec() - I->get().time_loaded) > max_time_cache) { - to_clean.push_back(I); + if ((OS::get_singleton()->get_ticks_msec() - I->value.time_loaded) > max_time_cache) { + to_clean.push_back(I->key); } - I = I->next(); + ++I; } while (to_clean.front()) { @@ -258,35 +258,35 @@ public: } } - virtual RES get_cached_resource(const String &p_path) { - Map<String, Cache>::Element *E = cached.find(p_path); + virtual Ref<Resource> get_cached_resource(const String &p_path) { + HashMap<String, Cache>::Iterator E = cached.find(p_path); if (!E) { Cache c; c.cache = ResourceLoader::load(p_path); E = cached.insert(p_path, c); } - E->get().time_loaded = OS::get_singleton()->get_ticks_msec(); + E->value.time_loaded = OS::get_singleton()->get_ticks_msec(); if (cached.size() > max_cache_size) { uint64_t older; - Map<String, Cache>::Element *O = cached.front(); - older = O->get().time_loaded; - Map<String, Cache>::Element *I = O; + HashMap<String, Cache>::Iterator O = cached.begin(); + older = O->value.time_loaded; + HashMap<String, Cache>::Iterator I = O; while (I) { - if (I->get().time_loaded < older) { - older = I->get().time_loaded; + if (I->value.time_loaded < older) { + older = I->value.time_loaded; O = I; } - I = I->next(); + ++I; } if (O != E) { //should never happen.. - cached.erase(O); + cached.remove(O); } } - return E->get().cache; + return E->value.cache; } virtual ~EditorScriptCodeCompletionCache() {} @@ -428,7 +428,7 @@ void ScriptEditor::_goto_script_line2(int p_line) { } } -void ScriptEditor::_goto_script_line(REF p_script, int p_line) { +void ScriptEditor::_goto_script_line(Ref<RefCounted> p_script, int p_line) { Ref<Script> script = Object::cast_to<Script>(*p_script); if (script.is_valid() && (script->has_source_code() || script->get_path().is_resource_file())) { if (edit(p_script, p_line, 0)) { @@ -444,7 +444,7 @@ void ScriptEditor::_goto_script_line(REF p_script, int p_line) { } } -void ScriptEditor::_set_execution(REF p_script, int p_line) { +void ScriptEditor::_set_execution(Ref<RefCounted> p_script, int p_line) { Ref<Script> script = Object::cast_to<Script>(*p_script); if (script.is_valid() && (script->has_source_code() || script->get_path().is_resource_file())) { for (int i = 0; i < tab_container->get_tab_count(); i++) { @@ -460,7 +460,7 @@ void ScriptEditor::_set_execution(REF p_script, int p_line) { } } -void ScriptEditor::_clear_execution(REF p_script) { +void ScriptEditor::_clear_execution(Ref<RefCounted> p_script) { Ref<Script> script = Object::cast_to<Script>(*p_script); if (script.is_valid() && (script->has_source_code() || script->get_path().is_resource_file())) { for (int i = 0; i < tab_container->get_tab_count(); i++) { @@ -476,7 +476,7 @@ void ScriptEditor::_clear_execution(REF p_script) { } } -void ScriptEditor::_set_breakpoint(REF p_script, int p_line, bool p_enabled) { +void ScriptEditor::_set_breakpoint(Ref<RefCounted> 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. @@ -758,7 +758,7 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) { ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tselected); if (current) { - RES file = current->get_edited_resource(); + Ref<Resource> 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). @@ -849,7 +849,7 @@ void ScriptEditor::_close_docs_tab() { void ScriptEditor::_copy_script_path() { ScriptEditorBase *se = _get_current_editor(); if (se) { - RES script = se->get_edited_resource(); + Ref<Resource> script = se->get_edited_resource(); DisplayServer::get_singleton()->clipboard_set(script->get_path()); } } @@ -906,7 +906,7 @@ void ScriptEditor::_resave_scripts(const String &p_str) { continue; } - RES script = se->get_edited_resource(); + Ref<Resource> script = se->get_edited_resource(); if (script->is_built_in()) { continue; //internal script, who cares @@ -947,7 +947,7 @@ void ScriptEditor::_reload_scripts() { continue; } - RES edited_res = se->get_edited_resource(); + Ref<Resource> edited_res = se->get_edited_resource(); if (edited_res->is_built_in()) { continue; //internal script, who cares @@ -966,7 +966,7 @@ void ScriptEditor::_reload_scripts() { ERR_CONTINUE(!rel_script.is_valid()); script->set_source_code(rel_script->get_source_code()); script->set_last_modified_time(rel_script->get_last_modified_time()); - script->reload(); + script->reload(true); } Ref<TextFile> text_file = edited_res; @@ -991,7 +991,7 @@ void ScriptEditor::_res_saved_callback(const Ref<Resource> &p_res) { continue; } - RES script = se->get_edited_resource(); + Ref<Resource> script = se->get_edited_resource(); if (script == p_res) { se->tag_saved_version(); @@ -1010,7 +1010,7 @@ void ScriptEditor::_scene_saved_callback(const String &p_path) { continue; } - RES edited_res = se->get_edited_resource(); + Ref<Resource> edited_res = se->get_edited_resource(); if (!edited_res->is_built_in()) { continue; // External script, who cares. @@ -1039,7 +1039,7 @@ void ScriptEditor::_live_auto_reload_running_scripts() { EditorDebuggerNode::get_singleton()->reload_scripts(); } -bool ScriptEditor::_test_script_times_on_disk(RES p_for_script) { +bool ScriptEditor::_test_script_times_on_disk(Ref<Resource> p_for_script) { disk_changed_list->clear(); TreeItem *r = disk_changed_list->create_item(); disk_changed_list->set_hide_root(true); @@ -1051,7 +1051,7 @@ bool ScriptEditor::_test_script_times_on_disk(RES p_for_script) { for (int i = 0; i < tab_container->get_tab_count(); i++) { ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_tab_control(i)); if (se) { - RES edited_res = se->get_edited_resource(); + Ref<Resource> edited_res = se->get_edited_resource(); if (p_for_script.is_valid() && edited_res.is_valid() && p_for_script != edited_res) { continue; } @@ -1117,7 +1117,7 @@ void ScriptEditor::_file_dialog_action(String p_file) { case FILE_SAVE_AS: { ScriptEditorBase *current = _get_current_editor(); if (current) { - RES resource = current->get_edited_resource(); + Ref<Resource> resource = current->get_edited_resource(); String path = ProjectSettings::get_singleton()->localize_path(p_file); Error err = _save_text_file(resource, path); @@ -1323,7 +1323,7 @@ void ScriptEditor::_menu_option(int p_option) { } } - RES resource = current->get_edited_resource(); + Ref<Resource> resource = current->get_edited_resource(); Ref<TextFile> text_file = resource; Ref<Script> script = resource; @@ -1365,9 +1365,17 @@ void ScriptEditor::_menu_option(int p_option) { } } break; - case FILE_TOOL_RELOAD: case FILE_TOOL_RELOAD_SOFT: { - current->reload(p_option == FILE_TOOL_RELOAD_SOFT); + Ref<Script> scr = current->get_edited_resource(); + if (scr == nullptr || scr.is_null()) { + EditorNode::get_singleton()->show_warning(TTR("Can't obtain the script for reloading.")); + break; + } + if (!scr->is_tool()) { + EditorNode::get_singleton()->show_warning(TTR("Reload only takes effect on tool scripts.")); + return; + } + scr->reload(true); } break; case FILE_RUN: { @@ -1376,6 +1384,10 @@ void ScriptEditor::_menu_option(int p_option) { EditorNode::get_singleton()->show_warning(TTR("Can't obtain the script for running.")); break; } + if (!scr->is_tool()) { + EditorNode::get_singleton()->show_warning(TTR("Script is not in tool mode, will not be able to run.")); + return; + } current->apply_code(); Error err = scr->reload(false); //hard reload script before running always @@ -1384,10 +1396,6 @@ void ScriptEditor::_menu_option(int p_option) { EditorNode::get_singleton()->show_warning(TTR("Script failed reloading, check console for errors.")); return; } - if (!scr->is_tool()) { - EditorNode::get_singleton()->show_warning(TTR("Script is not in tool mode, will not be able to run.")); - return; - } if (!ClassDB::is_parent_class(scr->get_instance_base_type(), "EditorScript")) { EditorNode::get_singleton()->show_warning(TTR("To run this script, it must inherit EditorScript and be set to tool mode.")); @@ -1413,7 +1421,7 @@ void ScriptEditor::_menu_option(int p_option) { _copy_script_path(); } break; case SHOW_IN_FILE_SYSTEM: { - const RES script = current->get_edited_resource(); + const Ref<Resource> script = current->get_edited_resource(); String path = script->get_path(); if (!path.is_empty()) { if (script->is_built_in()) { @@ -1613,7 +1621,7 @@ void ScriptEditor::_notification(int p_what) { case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_THEME_CHANGED: { help_search->set_icon(get_theme_icon(SNAME("HelpSearch"), SNAME("EditorIcons"))); - site_search->set_icon(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons"))); + site_search->set_icon(get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons"))); if (is_layout_rtl()) { script_forward->set_icon(get_theme_icon(SNAME("Back"), SNAME("EditorIcons"))); @@ -1707,7 +1715,7 @@ void ScriptEditor::notify_script_changed(const Ref<Script> &p_script) { } void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) { - Set<String> loaded_scripts; + HashSet<String> loaded_scripts; for (int i = 0; i < tab_container->get_tab_count(); i++) { ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_tab_control(i)); if (!se) { @@ -1792,7 +1800,7 @@ void ScriptEditor::ensure_select_current() { _update_selected_editor_menu(); } -void ScriptEditor::_find_scripts(Node *p_base, Node *p_current, Set<Ref<Script>> &used) { +void ScriptEditor::_find_scripts(Node *p_base, Node *p_current, HashSet<Ref<Script>> &used) { if (p_current != p_base && p_current->get_owner() != p_base) { return; } @@ -1950,11 +1958,11 @@ void ScriptEditor::_update_script_colors() { script_list->set_item_custom_bg_color(i, Color(0, 0, 0, 0)); if (script_temperature_enabled) { - if (!n->has_meta("__editor_pass")) { + int pass = n->get_meta("__editor_pass", -1); + if (pass < 0) { continue; } - int pass = n->get_meta("__editor_pass"); int h = edit_pass - pass; if (h > hist_size) { continue; @@ -1972,7 +1980,7 @@ void ScriptEditor::_update_script_names() { return; } - Set<Ref<Script>> used; + HashSet<Ref<Script>> used; Node *edited = EditorNode::get_singleton()->get_edited_scene(); if (edited) { _find_scripts(edited, edited, used); @@ -2188,7 +2196,7 @@ Ref<TextFile> ScriptEditor::_load_text_file(const String &p_path, Error *r_error Ref<TextFile> text_res(text_file); Error err = text_file->load_text(path); - ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load text file '" + path + "'."); + ERR_FAIL_COND_V_MSG(err != OK, Ref<Resource>(), "Cannot load text file '" + path + "'."); text_file->set_file_path(local_path); text_file->set_path(local_path, true); @@ -2230,7 +2238,7 @@ Error ScriptEditor::_save_text_file(Ref<TextFile> p_text_file, const String &p_p return OK; } -bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_grab_focus) { +bool ScriptEditor::edit(const Ref<Resource> &p_resource, int p_line, int p_col, bool p_grab_focus) { if (p_resource.is_null()) { return false; } @@ -2447,7 +2455,7 @@ void ScriptEditor::save_current_script() { } } - RES resource = current->get_edited_resource(); + Ref<Resource> resource = current->get_edited_resource(); Ref<TextFile> text_file = resource; Ref<Script> script = resource; @@ -2516,7 +2524,7 @@ void ScriptEditor::save_all_scripts() { continue; } - RES edited_res = se->get_edited_resource(); + Ref<Resource> edited_res = se->get_edited_resource(); if (edited_res.is_valid()) { se->apply_code(); } @@ -2589,14 +2597,14 @@ void ScriptEditor::open_text_file_create_dialog(const String &p_base_path, const open_textfile_after_create = false; } -RES ScriptEditor::open_file(const String &p_file) { +Ref<Resource> 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()) { EditorNode::get_singleton()->show_warning(TTR("Could not load file at:") + "\n\n" + p_file, TTR("Error!")); - return RES(); + return Ref<Resource>(); } edit(scr); @@ -2607,14 +2615,14 @@ RES ScriptEditor::open_file(const String &p_file) { Ref<TextFile> text_file = _load_text_file(p_file, &error); if (error != OK) { EditorNode::get_singleton()->show_warning(TTR("Could not load file at:") + "\n\n" + p_file, TTR("Error!")); - return RES(); + return Ref<Resource>(); } if (text_file.is_valid()) { edit(text_file); return text_file; } - return RES(); + return Ref<Resource>(); } void ScriptEditor::_editor_stop() { @@ -2980,7 +2988,7 @@ void ScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Co continue; } - RES res = open_file(file); + Ref<Resource> res = open_file(file); if (res.is_valid()) { if (tab_container->get_tab_count() > num_tabs_before) { tab_container->move_child(tab_container->get_tab_control(tab_container->get_tab_count() - 1), new_index); @@ -3091,8 +3099,8 @@ void ScriptEditor::_make_script_list_context_menu() { if (se) { Ref<Script> scr = se->get_edited_resource(); if (scr != nullptr) { - context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/reload_script_soft"), FILE_TOOL_RELOAD_SOFT); if (!scr.is_null() && scr->is_tool()) { + context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/reload_script_soft"), FILE_TOOL_RELOAD_SOFT); context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/run_file"), FILE_RUN); context_menu->add_separator(); } @@ -3135,7 +3143,7 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) { restoring_layout = true; - Set<String> loaded_scripts; + HashSet<String> loaded_scripts; List<String> extensions; ResourceLoader::get_recognized_extensions_for_type("Script", &extensions); @@ -3533,13 +3541,13 @@ void ScriptEditor::_on_replace_in_files_requested(String text) { void ScriptEditor::_on_find_in_files_result_selected(String fpath, int line_number, int begin, int end) { if (ResourceLoader::exists(fpath)) { - RES res = ResourceLoader::load(fpath); + Ref<Resource> res = ResourceLoader::load(fpath); if (fpath.get_extension() == "gdshader") { ShaderEditorPlugin *shader_editor = Object::cast_to<ShaderEditorPlugin>(EditorNode::get_singleton()->get_editor_data().get_editor("Shader")); shader_editor->edit(res.ptr()); shader_editor->make_visible(true); - shader_editor->get_shader_editor()->goto_line_selection(line_number - 1, begin, end); + shader_editor->get_shader_editor(res)->goto_line_selection(line_number - 1, begin, end); return; } else if (fpath.get_extension() == "tscn") { EditorNode::get_singleton()->load_scene(fpath); @@ -3666,7 +3674,7 @@ ScriptEditor::ScriptEditor() { list_split->add_child(scripts_vbox); filter_scripts = memnew(LineEdit); - filter_scripts->set_placeholder(TTR("Filter scripts")); + filter_scripts->set_placeholder(TTR("Filter Scripts")); filter_scripts->set_clear_button_enabled(true); filter_scripts->connect("text_changed", callable_mp(this, &ScriptEditor::_filter_scripts_text_changed)); scripts_vbox->add_child(filter_scripts); @@ -3709,7 +3717,7 @@ ScriptEditor::ScriptEditor() { buttons_hbox->add_child(members_overview_alphabeta_sort_button); filter_methods = memnew(LineEdit); - filter_methods->set_placeholder(TTR("Filter methods")); + filter_methods->set_placeholder(TTR("Filter Methods")); filter_methods->set_clear_button_enabled(true); filter_methods->connect("text_changed", callable_mp(this, &ScriptEditor::_filter_methods_text_changed)); overview_vbox->add_child(filter_methods); @@ -3774,7 +3782,7 @@ ScriptEditor::ScriptEditor() { file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_as", TTR("Save As...")), FILE_SAVE_AS); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_all", TTR("Save All"), KeyModifierMask::SHIFT | KeyModifierMask::ALT | Key::S), FILE_SAVE_ALL); file_menu->get_popup()->add_separator(); - file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reload_script_soft", TTR("Soft Reload Script"), KeyModifierMask::CMD | KeyModifierMask::ALT | Key::R), FILE_TOOL_RELOAD_SOFT); + file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reload_script_soft", TTR("Soft Reload Tool Script"), KeyModifierMask::CMD | KeyModifierMask::ALT | Key::R), FILE_TOOL_RELOAD_SOFT); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/copy_path", TTR("Copy Script Path")), FILE_COPY_PATH); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/show_in_file_system", TTR("Show in FileSystem")), SHOW_IN_FILE_SYSTEM); file_menu->get_popup()->add_separator(); diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index 7885ffe2c5..9f088aac49 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -53,7 +53,7 @@ class EditorSyntaxHighlighter : public SyntaxHighlighter { GDCLASS(EditorSyntaxHighlighter, SyntaxHighlighter) private: - REF edited_resourse; + Ref<RefCounted> edited_resourse; protected: static void _bind_methods(); @@ -65,8 +65,8 @@ public: virtual String _get_name() const; virtual Array _get_supported_languages() const; - void _set_edited_resource(const RES &p_res) { edited_resourse = p_res; } - REF _get_edited_resource() { return edited_resourse; } + void _set_edited_resource(const Ref<Resource> &p_res) { edited_resourse = p_res; } + Ref<RefCounted> _get_edited_resource() { return edited_resourse; } virtual Ref<EditorSyntaxHighlighter> _create() const; }; @@ -136,9 +136,9 @@ public: virtual void set_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) = 0; virtual void apply_code() = 0; - virtual RES get_edited_resource() const = 0; + virtual Ref<Resource> get_edited_resource() const = 0; virtual Vector<String> get_functions() = 0; - virtual void set_edited_resource(const RES &p_res) = 0; + virtual void set_edited_resource(const Ref<Resource> &p_res) = 0; virtual void enable_editor() = 0; virtual void reload_text() = 0; virtual String get_name() = 0; @@ -179,7 +179,7 @@ public: ScriptEditorBase() {} }; -typedef ScriptEditorBase *(*CreateScriptEditorFunc)(const RES &p_resource); +typedef ScriptEditorBase *(*CreateScriptEditorFunc)(const Ref<Resource> &p_resource); class EditorScriptCodeCompletionCache; class FindInFilesDialog; @@ -206,7 +206,6 @@ class ScriptEditor : public PanelContainer { TOGGLE_SCRIPTS_PANEL, SHOW_IN_FILE_SYSTEM, FILE_COPY_PATH, - FILE_TOOL_RELOAD, FILE_TOOL_RELOAD_SOFT, SEARCH_IN_FILES, REPLACE_IN_FILES, @@ -249,7 +248,7 @@ class ScriptEditor : public PanelContainer { MenuButton *debug_menu = nullptr; PopupMenu *context_menu = nullptr; Timer *autosave_timer = nullptr; - uint64_t idle; + uint64_t idle = 0; PopupMenu *recent_scripts = nullptr; PopupMenu *theme_submenu = nullptr; @@ -330,7 +329,7 @@ class ScriptEditor : public PanelContainer { void _resave_scripts(const String &p_str); void _reload_scripts(); - bool _test_script_times_on_disk(RES p_for_script = Ref<Resource>()); + bool _test_script_times_on_disk(Ref<Resource> p_for_script = Ref<Resource>()); void _add_recent_script(String p_path); void _update_recent_scripts(); @@ -377,12 +376,12 @@ class ScriptEditor : public PanelContainer { bool convert_indent_on_save; void _goto_script_line2(int p_line); - void _goto_script_line(REF p_script, int p_line); - void _set_execution(REF p_script, int p_line); - void _clear_execution(REF p_script); + void _goto_script_line(Ref<RefCounted> p_script, int p_line); + void _set_execution(Ref<RefCounted> p_script, int p_line); + void _clear_execution(Ref<RefCounted> p_script); void _breaked(bool p_breaked, bool p_can_debug); void _script_created(Ref<Script> p_script); - void _set_breakpoint(REF p_scrpt, int p_line, bool p_enabled); + void _set_breakpoint(Ref<RefCounted> p_scrpt, int p_line, bool p_enabled); void _clear_breakpoints(); Array _get_cached_breakpoints_for_script(const String &p_path) const; @@ -415,7 +414,7 @@ class ScriptEditor : public PanelContainer { void _update_help_overview(); void _help_overview_selected(int p_idx); - void _find_scripts(Node *p_base, Node *p_current, Set<Ref<Script>> &used); + void _find_scripts(Node *p_base, Node *p_current, HashSet<Ref<Script>> &used); void _tree_changed(); @@ -455,7 +454,7 @@ class ScriptEditor : public PanelContainer { Ref<Script> _get_current_script(); Array _get_open_scripts() const; - Set<String> textfile_extensions; + HashSet<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); @@ -481,12 +480,12 @@ public: 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); + Ref<Resource> open_file(const String &p_file); void ensure_select_current(); - _FORCE_INLINE_ bool edit(const RES &p_resource, bool p_grab_focus = true) { return edit(p_resource, -1, 0, p_grab_focus); } - bool edit(const RES &p_resource, int p_line, int p_col, bool p_grab_focus = true); + _FORCE_INLINE_ bool edit(const Ref<Resource> &p_resource, bool p_grab_focus = true) { return edit(p_resource, -1, 0, p_grab_focus); } + bool edit(const Ref<Resource> &p_resource, int p_line, int p_col, bool p_grab_focus = true); void get_breakpoints(List<String> *p_breakpoints); diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 4626f10b8d..05c707c065 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -133,11 +133,11 @@ void ScriptTextEditor::apply_code() { code_editor->get_text_editor()->get_syntax_highlighter()->update_cache(); } -RES ScriptTextEditor::get_edited_resource() const { +Ref<Resource> ScriptTextEditor::get_edited_resource() const { return script; } -void ScriptTextEditor::set_edited_resource(const RES &p_res) { +void ScriptTextEditor::set_edited_resource(const Ref<Resource> &p_res) { ERR_FAIL_COND(script.is_valid()); ERR_FAIL_COND(p_res.is_null()); @@ -239,6 +239,29 @@ void ScriptTextEditor::_show_warnings_panel(bool p_show) { void ScriptTextEditor::_warning_clicked(Variant p_line) { if (p_line.get_type() == Variant::INT) { goto_line_centered(p_line.operator int64_t()); + } else if (p_line.get_type() == Variant::DICTIONARY) { + Dictionary meta = p_line.operator Dictionary(); + const int line = meta["line"].operator int64_t() - 1; + + CodeEdit *text_editor = code_editor->get_text_editor(); + String prev_line = line > 0 ? text_editor->get_line(line - 1) : ""; + if (prev_line.contains("@warning_ignore")) { + const int closing_bracket_idx = prev_line.find(")"); + const String text_to_insert = ", " + meta["code"].operator String(); + prev_line = prev_line.insert(closing_bracket_idx, text_to_insert); + text_editor->set_line(line - 1, prev_line); + } else { + const int indent = text_editor->get_indent_level(line) / text_editor->get_indent_size(); + String annotation_indent; + if (!text_editor->is_indent_using_spaces()) { + annotation_indent = String("\t").repeat(indent); + } else { + annotation_indent = String(" ").repeat(text_editor->get_indent_size() * indent); + } + text_editor->insert_line_at(line, annotation_indent + "@warning_ignore(" + meta["code"].operator String() + ")"); + } + + _validate_script(); } } @@ -482,8 +505,20 @@ void ScriptTextEditor::_update_warnings() { } // Add script warnings. - warnings_panel->push_table(2); + warnings_panel->push_table(3); for (const ScriptLanguage::Warning &w : warnings) { + Dictionary ignore_meta; + ignore_meta["line"] = w.start_line; + ignore_meta["code"] = w.string_code.to_lower(); + warnings_panel->push_cell(); + warnings_panel->push_meta(ignore_meta); + warnings_panel->push_color( + warnings_panel->get_theme_color(SNAME("accent_color"), SNAME("Editor")).lerp(warnings_panel->get_theme_color(SNAME("mono_color"), SNAME("Editor")), 0.5f)); + warnings_panel->add_text(TTR("[Ignore]")); + warnings_panel->pop(); // Color. + warnings_panel->pop(); // Meta ignore. + warnings_panel->pop(); // Cell. + warnings_panel->push_cell(); warnings_panel->push_meta(w.start_line - 1); warnings_panel->push_color(warnings_panel->get_theme_color(SNAME("warning_color"), SNAME("Editor"))); @@ -629,7 +664,7 @@ static Node *_find_node_for_script(Node *p_base, Node *p_current, const Ref<Scri return nullptr; } -static void _find_changed_scripts_for_external_editor(Node *p_base, Node *p_current, Set<Ref<Script>> &r_scripts) { +static void _find_changed_scripts_for_external_editor(Node *p_base, Node *p_current, HashSet<Ref<Script>> &r_scripts) { if (p_current->get_owner() != p_base && p_base != p_current) { return; } @@ -651,15 +686,15 @@ void ScriptEditor::_update_modified_scripts_for_external_editor(Ref<Script> p_fo ERR_FAIL_COND(!get_tree()); - Set<Ref<Script>> scripts; + HashSet<Ref<Script>> scripts; Node *base = get_tree()->get_edited_scene_root(); if (base) { _find_changed_scripts_for_external_editor(base, base, scripts); } - for (Set<Ref<Script>>::Element *E = scripts.front(); E; E = E->next()) { - Ref<Script> script = E->get(); + for (const Ref<Script> &E : scripts) { + Ref<Script> script = E; if (p_for_script.is_valid() && p_for_script != script) { continue; @@ -820,6 +855,21 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c emit_signal(SNAME("go_to_help"), "class_method:" + result.class_name + ":" + result.class_member); } break; + case ScriptLanguage::LOOKUP_RESULT_CLASS_SIGNAL: { + StringName cname = result.class_name; + + while (true) { + if (ClassDB::has_signal(cname, result.class_member)) { + result.class_name = cname; + cname = ClassDB::get_parent_class(cname); + } else { + break; + } + } + + emit_signal(SNAME("go_to_help"), "class_signal:" + result.class_name + ":" + result.class_member); + + } break; case ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM: { StringName cname = result.class_name; StringName success; @@ -920,7 +970,7 @@ void ScriptTextEditor::_update_connected_methods() { } Vector<Node *> nodes = _find_all_node_for_script(base, base, script); - Set<StringName> methods_found; + HashSet<StringName> methods_found; for (int i = 0; i < nodes.size(); i++) { List<Connection> connections; nodes[i]->get_signals_connected_to_this(&connections); @@ -1321,11 +1371,11 @@ void ScriptTextEditor::add_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_hig void ScriptTextEditor::set_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) { ERR_FAIL_COND(p_highlighter.is_null()); - Map<String, Ref<EditorSyntaxHighlighter>>::Element *el = highlighters.front(); - while (el != nullptr) { - int highlighter_index = highlighter_menu->get_item_idx_from_text(el->key()); - highlighter_menu->set_item_checked(highlighter_index, el->value() == p_highlighter); - el = el->next(); + HashMap<String, Ref<EditorSyntaxHighlighter>>::Iterator el = highlighters.begin(); + while (el) { + int highlighter_index = highlighter_menu->get_item_idx_from_text(el->key); + highlighter_menu->set_item_checked(highlighter_index, el->value == p_highlighter); + ++el; } CodeEdit *te = code_editor->get_text_editor(); @@ -1474,6 +1524,7 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data te->set_caret_line(row); te->set_caret_column(col); te->insert_text_at_caret(res->get_path()); + te->grab_focus(); } if (d.has("type") && (String(d["type"]) == "files" || String(d["type"]) == "files_and_dirs")) { @@ -1496,6 +1547,7 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data te->set_caret_line(row); te->set_caret_column(col); te->insert_text_at_caret(text_to_drop); + te->grab_focus(); } if (d.has("type") && String(d["type"]) == "nodes") { @@ -1508,24 +1560,73 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data Array nodes = d["nodes"]; String text_to_drop; - for (int i = 0; i < nodes.size(); i++) { - if (i > 0) { - text_to_drop += ","; - } - NodePath np = nodes[i]; - Node *node = get_node(np); - if (!node) { - continue; + if (Input::get_singleton()->is_key_pressed(Key::CTRL)) { + bool use_type = EDITOR_GET("text_editor/completion/add_type_hints"); + for (int i = 0; i < nodes.size(); i++) { + NodePath np = nodes[i]; + Node *node = get_node(np); + if (!node) { + continue; + } + + bool is_unique = false; + String path; + if (node->is_unique_name_in_owner()) { + path = node->get_name(); + is_unique = true; + } else { + path = sn->get_path_to(node); + } + for (const String &segment : path.split("/")) { + if (!segment.is_valid_identifier()) { + path = path.c_escape().quote(quote_style); + break; + } + } + + String variable_name = String(node->get_name()).camelcase_to_underscore(true).validate_identifier(); + if (use_type) { + text_to_drop += vformat("@onready var %s: %s = %s%s\n", variable_name, node->get_class_name(), is_unique ? "%" : "$", path); + } else { + text_to_drop += vformat("@onready var %s = %s%s\n", variable_name, is_unique ? "%" : "$", path); + } } + } else { + for (int i = 0; i < nodes.size(); i++) { + if (i > 0) { + text_to_drop += ", "; + } + + NodePath np = nodes[i]; + Node *node = get_node(np); + if (!node) { + continue; + } - String path = sn->get_path_to(node); - text_to_drop += path.c_escape().quote(quote_style); + bool is_unique = false; + String path; + if (node->is_unique_name_in_owner()) { + path = node->get_name(); + is_unique = true; + } else { + path = sn->get_path_to(node); + } + + for (const String &segment : path.split("/")) { + if (!segment.is_valid_identifier()) { + path = path.c_escape().quote(quote_style); + break; + } + } + text_to_drop += (is_unique ? "%" : "$") + path; + } } te->set_caret_line(row); te->set_caret_column(col); te->insert_text_at_caret(text_to_drop); + te->grab_focus(); } if (d.has("type") && String(d["type"]) == "obj_property") { @@ -1534,6 +1635,7 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data te->set_caret_line(row); te->set_caret_column(col); te->insert_text_at_caret(text_to_drop); + te->grab_focus(); } } @@ -1963,7 +2065,7 @@ ScriptTextEditor::~ScriptTextEditor() { } } -static ScriptEditorBase *create_editor(const RES &p_resource) { +static ScriptEditorBase *create_editor(const Ref<Resource> &p_resource) { if (Object::cast_to<Script>(*p_resource)) { return memnew(ScriptTextEditor); } diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h index c1c4b0af54..fc87c84a2c 100644 --- a/editor/plugins/script_text_editor.h +++ b/editor/plugins/script_text_editor.h @@ -64,7 +64,7 @@ class ScriptTextEditor : public ScriptEditorBase { Vector<String> functions; List<ScriptLanguage::Warning> warnings; List<ScriptLanguage::ScriptError> errors; - Set<int> safe_lines; + HashSet<int> safe_lines; List<Connection> missing_connections; @@ -175,7 +175,7 @@ protected: void _notification(int p_what); static void _bind_methods(); - Map<String, Ref<EditorSyntaxHighlighter>> highlighters; + HashMap<String, Ref<EditorSyntaxHighlighter>> highlighters; void _change_syntax_highlighter(int p_idx); void _edit_option(int p_op); @@ -205,8 +205,8 @@ public: void update_toggle_scripts_button() override; virtual void apply_code() override; - virtual RES get_edited_resource() const override; - virtual void set_edited_resource(const RES &p_res) override; + virtual Ref<Resource> get_edited_resource() const override; + virtual void set_edited_resource(const Ref<Resource> &p_res) override; virtual void enable_editor() override; virtual Vector<String> get_functions() override; virtual void reload_text() override; diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 070f1fac1e..04b407ce65 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -38,8 +38,12 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/filesystem_dock.h" +#include "editor/plugins/visual_shader_editor_plugin.h" #include "editor/project_settings_editor.h" #include "editor/property_editor.h" +#include "editor/shader_create_dialog.h" +#include "scene/gui/split_container.h" #include "servers/display_server.h" #include "servers/rendering/shader_types.h" @@ -47,7 +51,7 @@ static bool saved_warnings_enabled = false; static bool saved_treat_warning_as_errors = false; -static Map<ShaderWarning::Code, bool> saved_warnings; +static HashMap<ShaderWarning::Code, bool> saved_warnings; static uint32_t saved_warning_flags = 0U; void ShaderTextEditor::_notification(int p_what) { @@ -439,7 +443,7 @@ void ShaderEditor::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { PopupMenu *popup = help_menu->get_popup(); - popup->set_item_icon(popup->get_item_index(HELP_DOCS), get_theme_icon(SNAME("Instance"), SNAME("EditorIcons"))); + popup->set_item_icon(popup->get_item_index(HELP_DOCS), get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons"))); } break; case NOTIFICATION_WM_WINDOW_FOCUS_IN: { @@ -836,50 +840,216 @@ ShaderEditor::ShaderEditor() { _editor_settings_changed(); } +void ShaderEditorPlugin::_update_shader_list() { + shader_list->clear(); + for (uint32_t i = 0; i < edited_shaders.size(); i++) { + String text; + String path = edited_shaders[i].shader->get_path(); + String _class = edited_shaders[i].shader->get_class(); + + if (path.is_resource_file()) { + text = path.get_file(); + } else if (edited_shaders[i].shader->get_name() != "") { + text = edited_shaders[i].shader->get_name(); + } else { + text = _class + ":" + itos(edited_shaders[i].shader->get_instance_id()); + } + + if (!shader_list->has_theme_icon(_class, SNAME("EditorIcons"))) { + _class = "Resource"; + } + Ref<Texture2D> icon = shader_list->get_theme_icon(_class, SNAME("EditorIcons")); + + shader_list->add_item(text, icon); + shader_list->set_item_tooltip(shader_list->get_item_count() - 1, path); + } + + if (shader_tabs->get_tab_count()) { + shader_list->select(shader_tabs->get_current_tab()); + } + + for (int i = 1; i < FILE_MAX; i++) { + file_menu->get_popup()->set_item_disabled(file_menu->get_popup()->get_item_index(i), edited_shaders.size() == 0); + } +} + void ShaderEditorPlugin::edit(Object *p_object) { Shader *s = Object::cast_to<Shader>(p_object); - shader_editor->edit(s); + for (uint32_t i = 0; i < edited_shaders.size(); i++) { + if (edited_shaders[i].shader.ptr() == s) { + // Exists, select. + shader_tabs->set_current_tab(i); + shader_list->select(i); + return; + } + } + // Add. + EditedShader es; + es.shader = Ref<Shader>(s); + Ref<VisualShader> vs = es.shader; + if (vs.is_valid()) { + es.visual_shader_editor = memnew(VisualShaderEditor); + es.visual_shader_editor->edit(vs.ptr()); + shader_tabs->add_child(es.visual_shader_editor); + } else { + es.shader_editor = memnew(ShaderEditor); + es.shader_editor->edit(s); + shader_tabs->add_child(es.shader_editor); + } + shader_tabs->set_current_tab(shader_tabs->get_tab_count() - 1); + edited_shaders.push_back(es); + _update_shader_list(); } bool ShaderEditorPlugin::handles(Object *p_object) const { - Shader *shader = Object::cast_to<Shader>(p_object); - return shader != nullptr && shader->is_text_shader(); + return Object::cast_to<Shader>(p_object) != nullptr; } void ShaderEditorPlugin::make_visible(bool p_visible) { if (p_visible) { - button->show(); - EditorNode::get_singleton()->make_bottom_panel_item_visible(shader_editor); - - } else { - button->hide(); - if (shader_editor->is_visible_in_tree()) { - EditorNode::get_singleton()->hide_bottom_panel(); - } - shader_editor->apply_shaders(); + EditorNode::get_singleton()->make_bottom_panel_item_visible(main_split); } } void ShaderEditorPlugin::selected_notify() { - shader_editor->ensure_select_current(); +} + +ShaderEditor *ShaderEditorPlugin::get_shader_editor(const Ref<Shader> &p_for_shader) { + for (uint32_t i = 0; i < edited_shaders.size(); i++) { + if (edited_shaders[i].shader == p_for_shader) { + return edited_shaders[i].shader_editor; + } + } + return nullptr; } void ShaderEditorPlugin::save_external_data() { - shader_editor->save_external_data(); + for (uint32_t i = 0; i < edited_shaders.size(); i++) { + if (edited_shaders[i].shader_editor) { + edited_shaders[i].shader_editor->save_external_data(); + } + } } void ShaderEditorPlugin::apply_changes() { - shader_editor->apply_shaders(); + for (uint32_t i = 0; i < edited_shaders.size(); i++) { + if (edited_shaders[i].shader_editor) { + edited_shaders[i].shader_editor->apply_shaders(); + } + } +} + +void ShaderEditorPlugin::_shader_selected(int p_index) { + shader_tabs->set_current_tab(p_index); +} + +void ShaderEditorPlugin::_close_shader(int p_index) { + int index = shader_tabs->get_current_tab(); + ERR_FAIL_INDEX(index, shader_tabs->get_tab_count()); + Control *c = shader_tabs->get_tab_control(index); + memdelete(c); + edited_shaders.remove_at(index); + _update_shader_list(); +} + +void ShaderEditorPlugin::_resource_saved(Object *obj) { + // May have been renamed on save. + for (uint32_t i = 0; i < edited_shaders.size(); i++) { + if (edited_shaders[i].shader.ptr() == obj) { + _update_shader_list(); + return; + } + } +} + +void ShaderEditorPlugin::_menu_item_pressed(int p_index) { + switch (p_index) { + case FILE_NEW: { + String base_path = FileSystemDock::get_singleton()->get_current_path(); + shader_create_dialog->config(base_path.plus_file("new_shader"), false, false, 0); + shader_create_dialog->popup_centered(); + } break; + case FILE_OPEN: { + InspectorDock::get_singleton()->open_resource("Shader"); + } break; + case FILE_SAVE: { + int index = shader_tabs->get_current_tab(); + ERR_FAIL_INDEX(index, shader_tabs->get_tab_count()); + EditorNode::get_singleton()->save_resource(edited_shaders[index].shader); + } break; + case FILE_SAVE_AS: { + int index = shader_tabs->get_current_tab(); + ERR_FAIL_INDEX(index, shader_tabs->get_tab_count()); + String path = edited_shaders[index].shader->get_path(); + if (!path.is_resource_file()) { + path = ""; + } + EditorNode::get_singleton()->save_resource_as(edited_shaders[index].shader, path); + } break; + case FILE_INSPECT: { + int index = shader_tabs->get_current_tab(); + ERR_FAIL_INDEX(index, shader_tabs->get_tab_count()); + EditorNode::get_singleton()->push_item(edited_shaders[index].shader.ptr()); + } break; + case FILE_CLOSE: { + _close_shader(shader_tabs->get_current_tab()); + } break; + } +} + +void ShaderEditorPlugin::_shader_created(Ref<Shader> p_shader) { + EditorNode::get_singleton()->push_item(p_shader.ptr()); } ShaderEditorPlugin::ShaderEditorPlugin() { - shader_editor = memnew(ShaderEditor); + main_split = memnew(HSplitContainer); + + VBoxContainer *vb = memnew(VBoxContainer); + + HBoxContainer *file_hb = memnew(HBoxContainer); + vb->add_child(file_hb); + file_menu = memnew(MenuButton); + file_menu->set_text(TTR("File")); + file_menu->get_popup()->add_item(TTR("New Shader"), FILE_NEW); + file_menu->get_popup()->add_separator(); + file_menu->get_popup()->add_item(TTR("Load Shader"), FILE_OPEN); + file_menu->get_popup()->add_item(TTR("Save Shader"), FILE_SAVE); + file_menu->get_popup()->add_item(TTR("Save Shader As"), FILE_SAVE_AS); + file_menu->get_popup()->add_separator(); + file_menu->get_popup()->add_item(TTR("Open Shader in Inspector"), FILE_INSPECT); + file_menu->get_popup()->add_separator(); + file_menu->get_popup()->add_item(TTR("Close Shader"), FILE_CLOSE); + file_menu->get_popup()->connect("id_pressed", callable_mp(this, &ShaderEditorPlugin::_menu_item_pressed)); + file_hb->add_child(file_menu); + + for (int i = 1; i < FILE_MAX; i++) { + file_menu->get_popup()->set_item_disabled(file_menu->get_popup()->get_item_index(i), true); + } + + shader_list = memnew(ItemList); + shader_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); + vb->add_child(shader_list); + shader_list->connect("item_selected", callable_mp(this, &ShaderEditorPlugin::_shader_selected)); + + main_split->add_child(vb); + vb->set_custom_minimum_size(Size2(200, 300) * EDSCALE); + + shader_tabs = memnew(TabContainer); + shader_tabs->set_tabs_visible(false); + shader_tabs->set_h_size_flags(Control::SIZE_EXPAND_FILL); + main_split->add_child(shader_tabs); + Ref<StyleBoxEmpty> empty; + empty.instantiate(); + shader_tabs->add_theme_style_override("panel", empty); + + button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Shader Editor"), main_split); - shader_editor->set_custom_minimum_size(Size2(0, 300) * EDSCALE); - button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Shader"), shader_editor); - button->hide(); + // Defer connect because Editor class is not in the binding system yet. + EditorNode::get_singleton()->call_deferred("connect", "resource_saved", callable_mp(this, &ShaderEditorPlugin::_resource_saved), varray(), CONNECT_DEFERRED); - _2d = false; + shader_create_dialog = memnew(ShaderCreateDialog); + vb->add_child(shader_create_dialog); + shader_create_dialog->connect("shader_created", callable_mp(this, &ShaderEditorPlugin::_shader_created)); } ShaderEditorPlugin::~ShaderEditorPlugin() { diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h index 067711c75c..e1e815f939 100644 --- a/editor/plugins/shader_editor_plugin.h +++ b/editor/plugins/shader_editor_plugin.h @@ -42,6 +42,11 @@ #include "scene/resources/shader.h" #include "servers/rendering/shader_warnings.h" +class ItemList; +class VisualShaderEditor; +class HSplitContainer; +class ShaderCreateDialog; + class ShaderTextEditor : public CodeTextEditor { GDCLASS(ShaderTextEditor, CodeTextEditor); @@ -114,7 +119,7 @@ class ShaderEditor : public PanelContainer { MenuButton *help_menu = nullptr; PopupMenu *context_menu = nullptr; RichTextLabel *warnings_panel = nullptr; - uint64_t idle; + uint64_t idle = 0; GotoLineDialog *goto_line_dialog = nullptr; ConfirmationDialog *erase_tab_confirm = nullptr; @@ -160,9 +165,40 @@ public: class ShaderEditorPlugin : public EditorPlugin { GDCLASS(ShaderEditorPlugin, EditorPlugin); - bool _2d; - ShaderEditor *shader_editor = nullptr; + struct EditedShader { + Ref<Shader> shader; + ShaderEditor *shader_editor = nullptr; + VisualShaderEditor *visual_shader_editor = nullptr; + }; + + LocalVector<EditedShader> edited_shaders; + + enum { + FILE_NEW, + FILE_OPEN, + FILE_SAVE, + FILE_SAVE_AS, + FILE_INSPECT, + FILE_CLOSE, + FILE_MAX + }; + + HSplitContainer *main_split = nullptr; + ItemList *shader_list = nullptr; + TabContainer *shader_tabs = nullptr; + Button *button = nullptr; + MenuButton *file_menu = nullptr; + + ShaderCreateDialog *shader_create_dialog = nullptr; + + void _update_shader_list(); + void _shader_selected(int p_index); + void _menu_item_pressed(int p_index); + void _resource_saved(Object *obj); + void _close_shader(int p_index); + + void _shader_created(Ref<Shader> p_shader); public: virtual String get_name() const override { return "Shader"; } @@ -172,7 +208,7 @@ public: virtual void make_visible(bool p_visible) override; virtual void selected_notify() override; - ShaderEditor *get_shader_editor() const { return shader_editor; } + ShaderEditor *get_shader_editor(const Ref<Shader> &p_for_shader); virtual void save_external_data() override; virtual void apply_changes() override; diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index 065683d632..8845fe9eca 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -357,7 +357,7 @@ void Skeleton3DEditor::pose_to_rest(const bool p_all_bones) { void Skeleton3DEditor::create_physical_skeleton() { UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); ERR_FAIL_COND(!get_tree()); - Node *owner = skeleton == get_tree()->get_edited_scene_root() ? skeleton : skeleton->get_owner(); + Node *owner = get_tree()->get_edited_scene_root(); const int bc = skeleton->get_bone_count(); @@ -368,37 +368,47 @@ void Skeleton3DEditor::create_physical_skeleton() { Vector<BoneInfo> bones_infos; bones_infos.resize(bc); - for (int bone_id = 0; bc > bone_id; ++bone_id) { - const int parent = skeleton->get_bone_parent(bone_id); + if (bc > 0) { + ur->create_action(TTR("Create physical bones"), UndoRedo::MERGE_ALL); + for (int bone_id = 0; bc > bone_id; ++bone_id) { + const int parent = skeleton->get_bone_parent(bone_id); - if (parent < 0) { - bones_infos.write[bone_id].relative_rest = skeleton->get_bone_rest(bone_id); - - } else { - const int parent_parent = skeleton->get_bone_parent(parent); - - bones_infos.write[bone_id].relative_rest = bones_infos[parent].relative_rest * skeleton->get_bone_rest(bone_id); - - // 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); - - ur->create_action(TTR("Create physical bones"), UndoRedo::MERGE_ALL); - ur->add_do_method(skeleton, "add_child", bones_infos[parent].physical_bone); - ur->add_do_reference(bones_infos[parent].physical_bone); - ur->add_undo_method(skeleton, "remove_child", bones_infos[parent].physical_bone); - ur->commit_action(); + if (parent < 0) { + bones_infos.write[bone_id].relative_rest = skeleton->get_bone_rest(bone_id); + } else { + const int parent_parent = skeleton->get_bone_parent(parent); + + bones_infos.write[bone_id].relative_rest = bones_infos[parent].relative_rest * skeleton->get_bone_rest(bone_id); + + // Create physical bone on parent. + if (!bones_infos[parent].physical_bone) { + PhysicalBone3D *physical_bone = create_physical_bone(parent, bone_id, bones_infos); + if (physical_bone && physical_bone->get_child(0)) { + CollisionShape3D *collision_shape = Object::cast_to<CollisionShape3D>(physical_bone->get_child(0)); + if (collision_shape) { + bones_infos.write[parent].physical_bone = physical_bone; + + ur->add_do_method(skeleton, "add_child", physical_bone); + ur->add_do_method(physical_bone, "set_owner", owner); + ur->add_do_method(collision_shape, "set_owner", owner); + ur->add_do_property(physical_bone, "bone_name", skeleton->get_bone_name(parent)); + + // Create joint between parent of parent. + if (parent_parent != -1) { + ur->add_do_method(physical_bone, "set_joint_type", PhysicalBone3D::JOINT_TYPE_PIN); + } - bones_infos[parent].physical_bone->set_bone_name(skeleton->get_bone_name(parent)); - bones_infos[parent].physical_bone->set_owner(owner); - bones_infos[parent].physical_bone->get_child(0)->set_owner(owner); // set shape owner + ur->add_do_method(Node3DEditor::get_singleton(), "_request_gizmo", physical_bone); + ur->add_do_method(Node3DEditor::get_singleton(), "_request_gizmo", collision_shape); - // Create joint between parent of parent. - if (-1 != parent_parent) { - bones_infos[parent].physical_bone->set_joint_type(PhysicalBone3D::JOINT_TYPE_PIN); + ur->add_do_reference(physical_bone); + ur->add_undo_method(skeleton, "remove_child", physical_bone); + } + } } } } + ur->commit_action(); } } @@ -414,6 +424,7 @@ PhysicalBone3D *Skeleton3DEditor::create_physical_bone(int bone_id, int bone_chi CollisionShape3D *bone_shape = memnew(CollisionShape3D); bone_shape->set_shape(bone_shape_capsule); + bone_shape->set_name("CollisionShape3D"); Transform3D capsule_transform; capsule_transform.basis = Basis(Vector3(1, 0, 0), Vector3(0, 0, 1), Vector3(0, -1, 0)); @@ -558,7 +569,7 @@ void Skeleton3DEditor::_joint_tree_selection_changed() { } // May be not used with single select mode. -void Skeleton3DEditor::_joint_tree_rmb_select(const Vector2 &p_pos) { +void Skeleton3DEditor::_joint_tree_rmb_select(const Vector2 &p_pos, MouseButton p_button) { } void Skeleton3DEditor::_update_properties() { @@ -577,7 +588,7 @@ void Skeleton3DEditor::update_joint_tree() { TreeItem *root = joint_tree->create_item(); - Map<int, TreeItem *> items; + HashMap<int, TreeItem *> items; items.insert(-1, root); @@ -589,7 +600,7 @@ void Skeleton3DEditor::update_joint_tree() { bones_to_process.erase(current_bone_idx); const int parent_idx = skeleton->get_bone_parent(current_bone_idx); - TreeItem *parent_item = items.find(parent_idx)->get(); + TreeItem *parent_item = items.find(parent_idx)->value; TreeItem *joint_item = joint_tree->create_item(parent_item); items.insert(current_bone_idx, joint_item); @@ -755,7 +766,7 @@ void Skeleton3DEditor::_notification(int p_what) { update_joint_tree(); update_editors(); 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)); + joint_tree->connect("item_mouse_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)); @@ -810,7 +821,7 @@ Skeleton3DEditor::Skeleton3DEditor(EditorInspectorPluginSkeleton *e_plugin, Skel shader_type spatial; render_mode unshaded, shadows_disabled, depth_draw_always; -uniform sampler2D texture_albedo : hint_albedo; +uniform sampler2D texture_albedo : source_color; uniform float point_size : hint_range(0,128) = 32; void vertex() { if (!OUTPUT_IS_SRGB) { @@ -1209,8 +1220,7 @@ void Skeleton3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gi t.basis = to_local * p_transform.get_basis(); // Origin. - Vector3 orig = Vector3(); - orig = skeleton->get_bone_pose(p_id).origin; + 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); @@ -1283,9 +1293,6 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { surface_tool->set_material(unselected_mat); } - Vector<Transform3D> grests; - grests.resize(skeleton->get_bone_count()); - LocalVector<int> bones; LocalVector<float> weights; bones.resize(4); @@ -1309,11 +1316,6 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { 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) { @@ -1322,10 +1324,8 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { 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 v0 = skeleton->get_bone_global_rest(current_bone_idx).origin; + Vector3 v1 = skeleton->get_bone_global_rest(child_bone_idx).origin; Vector3 d = (v1 - v0).normalized(); real_t dist = v0.distance_to(v1); @@ -1333,7 +1333,7 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { int closest = -1; real_t closest_d = 0.0; for (int j = 0; j < 3; j++) { - real_t dp = Math::abs(grests[current_bone_idx].basis[j].normalized().dot(d)); + real_t dp = Math::abs(skeleton->get_bone_global_rest(current_bone_idx).basis[j].normalized().dot(d)); if (j == 0 || dp > closest_d) { closest = j; } @@ -1360,7 +1360,7 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { for (int j = 0; j < 3; j++) { Vector3 axis; if (first == Vector3()) { - axis = d.cross(d.cross(grests[current_bone_idx].basis[j])).normalized(); + axis = d.cross(d.cross(skeleton->get_bone_global_rest(current_bone_idx).basis[j])).normalized(); first = axis; } else { axis = d.cross(first).normalized(); @@ -1415,7 +1415,7 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { surface_tool->add_vertex(v0); surface_tool->set_bones(bones); surface_tool->set_weights(weights); - surface_tool->add_vertex(v0 + (grests[current_bone_idx].basis.inverse())[j].normalized() * dist * bone_axis_length); + surface_tool->add_vertex(v0 + (skeleton->get_bone_global_rest(current_bone_idx).basis.inverse())[j].normalized() * dist * bone_axis_length); if (j == closest) { continue; @@ -1432,7 +1432,7 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { surface_tool->add_vertex(v1); surface_tool->set_bones(bones); surface_tool->set_weights(weights); - surface_tool->add_vertex(v1 + (grests[child_bone_idx].basis.inverse())[j].normalized() * dist * bone_axis_length); + surface_tool->add_vertex(v1 + (skeleton->get_bone_global_rest(child_bone_idx).basis.inverse())[j].normalized() * dist * bone_axis_length); if (j == closest) { continue; diff --git a/editor/plugins/skeleton_3d_editor_plugin.h b/editor/plugins/skeleton_3d_editor_plugin.h index 911e39a34f..8f03e7c8db 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.h +++ b/editor/plugins/skeleton_3d_editor_plugin.h @@ -131,7 +131,7 @@ class Skeleton3DEditor : public VBoxContainer { EditorFileDialog *file_dialog = nullptr; - bool keyable; + bool keyable = false; static Skeleton3DEditor *singleton; @@ -181,7 +181,7 @@ class Skeleton3DEditor : public VBoxContainer { void _draw_handles(); void _joint_tree_selection_changed(); - void _joint_tree_rmb_select(const Vector2 &p_pos); + void _joint_tree_rmb_select(const Vector2 &p_pos, MouseButton p_button); void _update_properties(); void _subgizmo_selection_change(); diff --git a/editor/plugins/sprite_2d_editor_plugin.cpp b/editor/plugins/sprite_2d_editor_plugin.cpp index 6a63875324..ad817f9a41 100644 --- a/editor/plugins/sprite_2d_editor_plugin.cpp +++ b/editor/plugins/sprite_2d_editor_plugin.cpp @@ -546,7 +546,7 @@ Sprite2DEditor::Sprite2DEditor() { debug_uv_dialog->connect("confirmed", callable_mp(this, &Sprite2DEditor::_create_node)); HBoxContainer *hb = memnew(HBoxContainer); - hb->add_child(memnew(Label(TTR("Simplification: ")))); + hb->add_child(memnew(Label(TTR("Simplification:")))); simplification = memnew(SpinBox); simplification->set_min(0.01); simplification->set_max(10.00); @@ -554,7 +554,7 @@ Sprite2DEditor::Sprite2DEditor() { simplification->set_value(2); hb->add_child(simplification); hb->add_spacer(); - hb->add_child(memnew(Label(TTR("Shrink (Pixels): ")))); + hb->add_child(memnew(Label(TTR("Shrink (Pixels):")))); shrink_pixels = memnew(SpinBox); shrink_pixels->set_min(0); shrink_pixels->set_max(10); @@ -562,7 +562,7 @@ Sprite2DEditor::Sprite2DEditor() { shrink_pixels->set_value(0); hb->add_child(shrink_pixels); hb->add_spacer(); - hb->add_child(memnew(Label(TTR("Grow (Pixels): ")))); + hb->add_child(memnew(Label(TTR("Grow (Pixels):")))); grow_pixels = memnew(SpinBox); grow_pixels->set_min(0); grow_pixels->set_max(10); diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index 27160f8c86..8a40ffbe38 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -48,9 +48,6 @@ static void _draw_shadowed_line(Control *p_control, const Point2 &p_from, const p_control->draw_line(p_from + p_shadow_offset, p_from + p_size + p_shadow_offset, p_shadow_color); } -void SpriteFramesEditor::gui_input(const Ref<InputEvent> &p_event) { -} - void SpriteFramesEditor::_open_sprite_sheet() { file_split_sheet->clear_filters(); List<String> extensions; @@ -128,8 +125,8 @@ void SpriteFramesEditor::_sheet_preview_draw() { Color accent = get_theme_color("accent_color", "Editor"); - for (Set<int>::Element *E = frames_selected.front(); E; E = E->next()) { - const int idx = E->get(); + for (const int &E : frames_selected) { + const int idx = E; const int x = idx % frame_count.x; const int y = idx / frame_count.x; const Point2 pos = draw_offset + Point2(x, y) * (draw_frame_size + draw_sep); @@ -251,8 +248,8 @@ void SpriteFramesEditor::_sheet_add_frames() { int fc = frames->get_frame_count(edited_anim); - for (Set<int>::Element *E = frames_selected.front(); E; E = E->next()) { - int idx = E->get(); + for (const int &E : frames_selected) { + int idx = E; const Point2 frame_coords(idx % frame_count.x, idx / frame_count.x); Ref<AtlasTexture> at; @@ -1015,7 +1012,7 @@ Variant SpriteFramesEditor::get_drag_data_fw(const Point2 &p_point, Control *p_f return Variant(); } - RES frame = frames->get_frame(edited_anim, idx); + Ref<Resource> frame = frames->get_frame(edited_anim, idx); if (frame.is_null()) { return Variant(); @@ -1039,7 +1036,7 @@ bool SpriteFramesEditor::can_drop_data_fw(const Point2 &p_point, const Variant & } if (String(d["type"]) == "resource" && d.has("resource")) { - RES r = d["resource"]; + Ref<Resource> r = d["resource"]; Ref<Texture2D> texture = r; @@ -1083,7 +1080,7 @@ void SpriteFramesEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da int at_pos = tree->get_item_at_position(p_point, true); if (String(d["type"]) == "resource" && d.has("resource")) { - RES r = d["resource"]; + Ref<Resource> r = d["resource"]; Ref<Texture2D> texture = r; diff --git a/editor/plugins/sprite_frames_editor_plugin.h b/editor/plugins/sprite_frames_editor_plugin.h index 9a00fe5771..3c8c5ef19d 100644 --- a/editor/plugins/sprite_frames_editor_plugin.h +++ b/editor/plugins/sprite_frames_editor_plugin.h @@ -50,7 +50,7 @@ class SpriteFramesEditor : public HSplitContainer { enum { PARAM_USE_CURRENT, // Used in callbacks to indicate `dominant_param` should be not updated. - PARAM_FRAME_COUNT, // Keep "Horizontal" & "Vertial" values. + PARAM_FRAME_COUNT, // Keep "Horizontal" & "Vertical" values. PARAM_SIZE, // Keep "Size" values. }; int dominant_param = PARAM_FRAME_COUNT; @@ -103,9 +103,9 @@ class SpriteFramesEditor : public HSplitContainer { Button *split_sheet_zoom_reset = nullptr; Button *split_sheet_zoom_in = nullptr; EditorFileDialog *file_split_sheet = nullptr; - Set<int> frames_selected; - Set<int> frames_toggled_by_mouse_hover; - int last_frame_selected; + HashSet<int> frames_selected; + HashSet<int> frames_toggled_by_mouse_hover; + int last_frame_selected = 0; float scale_ratio; int thumbnail_default_size; @@ -170,7 +170,6 @@ class SpriteFramesEditor : public HSplitContainer { protected: void _notification(int p_what); - virtual void gui_input(const Ref<InputEvent> &p_event) override; static void _bind_methods(); public: diff --git a/editor/plugins/style_box_editor_plugin.cpp b/editor/plugins/style_box_editor_plugin.cpp index a3cbaf527e..1281ce0cfd 100644 --- a/editor/plugins/style_box_editor_plugin.cpp +++ b/editor/plugins/style_box_editor_plugin.cpp @@ -32,6 +32,13 @@ #include "editor/editor_scale.h" +bool StyleBoxPreview::grid_preview_enabled = true; + +void StyleBoxPreview::_grid_preview_toggled(bool p_active) { + grid_preview_enabled = p_active; + preview->update(); +} + bool EditorInspectorPluginStyleBox::can_handle(Object *p_object) { return Object::cast_to<StyleBox>(p_object) != nullptr; } @@ -53,6 +60,8 @@ void StyleBoxPreview::edit(const Ref<StyleBox> &p_stylebox) { preview->add_theme_style_override("panel", stylebox); stylebox->connect("changed", callable_mp(this, &StyleBoxPreview::_sb_changed)); } + Ref<StyleBoxTexture> sbt = p_stylebox; + grid_preview->set_visible(sbt.is_valid()); _sb_changed(); } @@ -60,9 +69,31 @@ void StyleBoxPreview::_sb_changed() { preview->update(); } +void StyleBoxPreview::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: { + if (!is_inside_tree()) { + // TODO: This is a workaround because `NOTIFICATION_THEME_CHANGED` + // is getting called for some reason when the `TexturePreview` is + // getting destroyed, which causes `get_theme_font()` to return `nullptr`. + // See https://github.com/godotengine/godot/issues/50743. + break; + } + grid_preview->set_normal_texture(get_theme_icon(SNAME("StyleBoxGridInvisible"), SNAME("EditorIcons"))); + grid_preview->set_pressed_texture(get_theme_icon(SNAME("StyleBoxGridVisible"), SNAME("EditorIcons"))); + grid_preview->set_hover_texture(get_theme_icon(SNAME("StyleBoxGridVisible"), SNAME("EditorIcons"))); + checkerboard->set_texture(get_theme_icon(SNAME("Checkerboard"), SNAME("EditorIcons"))); + } break; + } +} + void StyleBoxPreview::_redraw() { if (stylebox.is_valid()) { + Ref<Texture2D> grid_texture_disabled = get_theme_icon(SNAME("StyleBoxGridInvisible"), SNAME("EditorIcons")); Rect2 preview_rect = preview->get_rect(); + preview_rect.position += grid_texture_disabled->get_size(); + preview_rect.size -= grid_texture_disabled->get_size() * 2; // Re-adjust preview panel to fit all drawn content Rect2 draw_rect = stylebox->get_draw_rect(preview_rect); @@ -70,6 +101,21 @@ void StyleBoxPreview::_redraw() { preview_rect.position -= draw_rect.position - preview_rect.position; preview->draw_style_box(stylebox, preview_rect); + + Ref<StyleBoxTexture> sbt = stylebox; + if (sbt.is_valid() && grid_preview->is_pressed()) { + for (int i = 0; i < 2; i++) { + Color c = i == 1 ? Color(1, 1, 1, 0.8) : Color(0, 0, 0, 0.4); + int x = draw_rect.position.x + sbt->get_margin(SIDE_LEFT) + (1 - i); + preview->draw_line(Point2(x, 0), Point2(x, preview->get_size().height), c); + int x2 = draw_rect.position.x + draw_rect.size.width - sbt->get_margin(SIDE_RIGHT) + (1 - i); + preview->draw_line(Point2(x2, 0), Point2(x2, preview->get_size().height), c); + int y = draw_rect.position.y + sbt->get_margin(SIDE_TOP) + (1 - i); + preview->draw_line(Point2(0, y), Point2(preview->get_size().width, y), c); + int y2 = draw_rect.position.y + draw_rect.size.height - sbt->get_margin(SIDE_BOTTOM) + (1 - i); + preview->draw_line(Point2(0, y2), Point2(preview->get_size().width, y2), c); + } + } } } @@ -77,11 +123,23 @@ void StyleBoxPreview::_bind_methods() { } StyleBoxPreview::StyleBoxPreview() { + checkerboard = memnew(TextureRect); + checkerboard->set_stretch_mode(TextureRect::STRETCH_TILE); + checkerboard->set_texture_repeat(CanvasItem::TEXTURE_REPEAT_ENABLED); + checkerboard->set_custom_minimum_size(Size2(0.0, 150.0) * EDSCALE); + preview = memnew(Control); - preview->set_custom_minimum_size(Size2(0, 150 * EDSCALE)); preview->set_clip_contents(true); preview->connect("draw", callable_mp(this, &StyleBoxPreview::_redraw)); - add_margin_child(TTR("Preview:"), preview); + checkerboard->add_child(preview); + preview->set_anchors_and_offsets_preset(PRESET_WIDE); + + add_margin_child(TTR("Preview:"), checkerboard); + grid_preview = memnew(TextureButton); + preview->add_child(grid_preview); + grid_preview->set_toggle_mode(true); + grid_preview->connect("toggled", callable_mp(this, &StyleBoxPreview::_grid_preview_toggled)); + grid_preview->set_pressed(grid_preview_enabled); } StyleBoxEditorPlugin::StyleBoxEditorPlugin() { diff --git a/editor/plugins/style_box_editor_plugin.h b/editor/plugins/style_box_editor_plugin.h index 663440ae31..a072745d8f 100644 --- a/editor/plugins/style_box_editor_plugin.h +++ b/editor/plugins/style_box_editor_plugin.h @@ -40,11 +40,16 @@ class StyleBoxPreview : public VBoxContainer { GDCLASS(StyleBoxPreview, VBoxContainer); + TextureRect *checkerboard = nullptr; + TextureButton *grid_preview = nullptr; Control *preview = nullptr; Ref<StyleBox> stylebox; void _sb_changed(); void _redraw(); + void _notification(int p_what); + static bool grid_preview_enabled; + void _grid_preview_toggled(bool p_active); protected: static void _bind_methods(); diff --git a/editor/plugins/text_control_editor_plugin.cpp b/editor/plugins/text_control_editor_plugin.cpp index 4290888e94..2a5faba4a2 100644 --- a/editor/plugins/text_control_editor_plugin.cpp +++ b/editor/plugins/text_control_editor_plugin.cpp @@ -92,8 +92,8 @@ void TextControlEditor::_update_fonts_menu() { } int id = FONT_INFO_ID; - for (Map<String, Map<String, String>>::Element *E = fonts.front(); E; E = E->next()) { - font_list->add_item(E->key(), id++); + for (const KeyValue<String, HashMap<String, String>> &E : fonts) { + font_list->add_item(E.key, id++); } if (font_list->get_item_count() > 1) { @@ -107,8 +107,8 @@ void TextControlEditor::_update_styles_menu() { font_style_list->clear(); if ((font_list->get_selected_id() >= FONT_INFO_ID)) { const String &name = font_list->get_item_text(font_list->get_selected()); - for (Map<String, String>::Element *E = fonts[name].front(); E; E = E->next()) { - font_style_list->add_item(E->key()); + for (KeyValue<String, String> &E : fonts[name]) { + font_style_list->add_item(E.key); } } else if (font_list->get_selected() >= 0) { font_style_list->add_item("Default"); diff --git a/editor/plugins/text_control_editor_plugin.h b/editor/plugins/text_control_editor_plugin.h index a475f6eba3..cf069338b6 100644 --- a/editor/plugins/text_control_editor_plugin.h +++ b/editor/plugins/text_control_editor_plugin.h @@ -52,7 +52,7 @@ class TextControlEditor : public HBoxContainer { FONT_INFO_ID = 100, }; - Map<String, Map<String, String>> fonts; + HashMap<String, HashMap<String, String>> fonts; OptionButton *font_list = nullptr; SpinBox *font_size_list = nullptr; diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index 34f3ec73c0..7ca65c073d 100644 --- a/editor/plugins/text_editor.cpp +++ b/editor/plugins/text_editor.cpp @@ -43,11 +43,11 @@ void TextEditor::add_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlight void TextEditor::set_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) { ERR_FAIL_COND(p_highlighter.is_null()); - Map<String, Ref<EditorSyntaxHighlighter>>::Element *el = highlighters.front(); - while (el != nullptr) { - int highlighter_index = highlighter_menu->get_item_idx_from_text(el->key()); - highlighter_menu->set_item_checked(highlighter_index, el->value() == p_highlighter); - el = el->next(); + HashMap<String, Ref<EditorSyntaxHighlighter>>::Iterator el = highlighters.begin(); + while (el) { + int highlighter_index = highlighter_menu->get_item_idx_from_text(el->key); + highlighter_menu->set_item_checked(highlighter_index, el->value == p_highlighter); + ++el; } CodeEdit *te = code_editor->get_text_editor(); @@ -89,11 +89,11 @@ Ref<Texture2D> TextEditor::get_theme_icon() { return EditorNode::get_singleton()->get_object_icon(text_file.ptr(), ""); } -RES TextEditor::get_edited_resource() const { +Ref<Resource> TextEditor::get_edited_resource() const { return text_file; } -void TextEditor::set_edited_resource(const RES &p_res) { +void TextEditor::set_edited_resource(const Ref<Resource> &p_res) { ERR_FAIL_COND(text_file.is_valid()); ERR_FAIL_COND(p_res.is_null()); @@ -412,7 +412,7 @@ void TextEditor::_convert_case(CodeTextEditor::CaseStyle p_case) { code_editor->convert_case(p_case); } -static ScriptEditorBase *create_editor(const RES &p_resource) { +static ScriptEditorBase *create_editor(const Ref<Resource> &p_resource) { if (Object::cast_to<TextFile>(*p_resource)) { return memnew(TextEditor); } diff --git a/editor/plugins/text_editor.h b/editor/plugins/text_editor.h index d03385d79e..4f0121da52 100644 --- a/editor/plugins/text_editor.h +++ b/editor/plugins/text_editor.h @@ -92,7 +92,7 @@ protected: void _text_edit_gui_input(const Ref<InputEvent> &ev); void _prepare_edit_menu(); - Map<String, Ref<EditorSyntaxHighlighter>> highlighters; + HashMap<String, Ref<EditorSyntaxHighlighter>> highlighters; void _change_syntax_highlighter(int p_idx); void _load_theme_settings(); @@ -109,8 +109,8 @@ public: virtual String get_name() override; virtual Ref<Texture2D> get_theme_icon() override; - virtual RES get_edited_resource() const override; - virtual void set_edited_resource(const RES &p_res) override; + virtual Ref<Resource> get_edited_resource() const override; + virtual void set_edited_resource(const Ref<Resource> &p_res) override; virtual void enable_editor() override; virtual void reload_text() override; virtual void apply_code() override; diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index adb8590246..3f4f9a4f4d 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -37,6 +37,7 @@ #include "editor/editor_scale.h" #include "scene/gui/check_box.h" #include "scene/gui/view_panner.h" +#include "scene/resources/texture.h" void draw_margin_line(Control *edit_draw, Vector2 from, Vector2 to) { Vector2 line = (to - from).normalized() * 10; @@ -78,10 +79,11 @@ void TextureRegionEditor::_region_draw() { } Transform2D mtx; - mtx.elements[2] = -draw_ofs * draw_zoom; + mtx.columns[2] = -draw_ofs * draw_zoom; mtx.scale_basis(Vector2(draw_zoom, draw_zoom)); RS::get_singleton()->canvas_item_add_set_transform(edit_draw->get_canvas_item(), mtx); + edit_draw->draw_rect(Rect2(Point2(), base_tex->get_size()), Color(0.5, 0.5, 0.5, 0.5), false); edit_draw->draw_texture(base_tex, Point2()); RS::get_singleton()->canvas_item_add_set_transform(edit_draw->get_canvas_item(), Transform2D()); @@ -145,7 +147,7 @@ void TextureRegionEditor::_region_draw() { } } else if (snap_mode == SNAP_AUTOSLICE) { for (const Rect2 &r : autoslice_cache) { - Vector2 endpoints[4] = { + const Vector2 endpoints[4] = { mtx.basis_xform(r.position), mtx.basis_xform(r.position + Vector2(r.size.x, 0)), mtx.basis_xform(r.position + r.size), @@ -228,11 +230,19 @@ void TextureRegionEditor::_region_draw() { Size2 vmin = vscroll->get_combined_minimum_size(); // Avoid scrollbar overlapping. - hscroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, vscroll->is_visible() ? -vmin.width : 0); - vscroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, hscroll->is_visible() ? -hmin.height : 0); + hscroll->set_anchor_and_offset(SIDE_RIGHT, Control::ANCHOR_END, vscroll->is_visible() ? -vmin.width : 0); + vscroll->set_anchor_and_offset(SIDE_BOTTOM, Control::ANCHOR_END, hscroll->is_visible() ? -hmin.height : 0); updating_scroll = false; + if (request_center && hscroll->get_min() < 0) { + hscroll->set_value((hscroll->get_min() + hscroll->get_max() - hscroll->get_page()) / 2); + vscroll->set_value((vscroll->get_min() + vscroll->get_max() - vscroll->get_page()) / 2); + // This ensures that the view is updated correctly. + callable_bind(callable_mp(this, &TextureRegionEditor::_pan_callback), Vector2(1, 0)).call_deferred(nullptr, 0); + request_center = false; + } + if (node_ninepatch || obj_styleBox.is_valid()) { float margins[4] = { 0 }; if (node_ninepatch) { @@ -267,7 +277,7 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { } Transform2D mtx; - mtx.elements[2] = -draw_ofs * draw_zoom; + mtx.columns[2] = -draw_ofs * draw_zoom; mtx.scale_basis(Vector2(draw_zoom, draw_zoom)); const real_t handle_radius = 8 * EDSCALE; @@ -817,8 +827,8 @@ void TextureRegionEditor::_notification(int p_what) { zoom_reset->set_icon(get_theme_icon(SNAME("ZoomReset"), SNAME("EditorIcons"))); zoom_in->set_icon(get_theme_icon(SNAME("ZoomMore"), SNAME("EditorIcons"))); - vscroll->set_anchors_and_offsets_preset(PRESET_RIGHT_WIDE); - hscroll->set_anchors_and_offsets_preset(PRESET_BOTTOM_WIDE); + vscroll->set_anchors_and_offsets_preset(Control::PRESET_RIGHT_WIDE); + hscroll->set_anchors_and_offsets_preset(Control::PRESET_BOTTOM_WIDE); [[fallthrough]]; } case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { @@ -921,12 +931,8 @@ void TextureRegionEditor::edit(Object *p_obj) { atlas_tex = Ref<AtlasTexture>(nullptr); } edit_draw->update(); - 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) { - set_process(false); - } + popup_centered_ratio(0.5); + request_center = true; } void TextureRegionEditor::_texture_changed() { @@ -983,6 +989,9 @@ Vector2 TextureRegionEditor::snap_point(Vector2 p_target) const { } TextureRegionEditor::TextureRegionEditor() { + get_ok_button()->set_text(TTR("Close")); + VBoxContainer *vb = memnew(VBoxContainer); + add_child(vb); node_sprite_2d = nullptr; node_sprite_3d = nullptr; node_ninepatch = nullptr; @@ -998,7 +1007,7 @@ TextureRegionEditor::TextureRegionEditor() { drag = false; HBoxContainer *hb_tools = memnew(HBoxContainer); - add_child(hb_tools); + vb->add_child(hb_tools); hb_tools->add_child(memnew(Label(TTR("Snap Mode:")))); snap_mode_button = memnew(OptionButton); @@ -1082,12 +1091,12 @@ TextureRegionEditor::TextureRegionEditor() { panner->set_callbacks(callable_mp(this, &TextureRegionEditor::_scroll_callback), callable_mp(this, &TextureRegionEditor::_pan_callback), callable_mp(this, &TextureRegionEditor::_zoom_callback)); edit_draw = memnew(Panel); - add_child(edit_draw); - edit_draw->set_v_size_flags(SIZE_EXPAND_FILL); + vb->add_child(edit_draw); + edit_draw->set_v_size_flags(Control::SIZE_EXPAND_FILL); edit_draw->connect("draw", callable_mp(this, &TextureRegionEditor::_region_draw)); edit_draw->connect("gui_input", callable_mp(this, &TextureRegionEditor::_region_input)); edit_draw->connect("focus_exited", callable_mp(panner.ptr(), &ViewPanner::release_pan_key)); - edit_draw->set_focus_mode(FOCUS_CLICK); + edit_draw->set_focus_mode(Control::FOCUS_CLICK); draw_zoom = 1.0; edit_draw->set_clip_contents(true); @@ -1125,88 +1134,39 @@ TextureRegionEditor::TextureRegionEditor() { updating_scroll = false; autoslice_is_dirty = true; -} -void TextureRegionEditorPlugin::edit(Object *p_object) { - region_editor->edit(p_object); + set_title(TTR("Region Editor")); } -bool TextureRegionEditorPlugin::handles(Object *p_object) const { - return p_object->is_class("Sprite2D") || p_object->is_class("Sprite3D") || p_object->is_class("NinePatchRect") || p_object->is_class("StyleBoxTexture") || p_object->is_class("AtlasTexture"); -} +//////////////////////// -void TextureRegionEditorPlugin::_editor_visiblity_changed() { - manually_hidden = !region_editor->is_visible_in_tree(); +bool EditorInspectorPluginTextureRegion::can_handle(Object *p_object) { + return Object::cast_to<Sprite2D>(p_object) || Object::cast_to<Sprite3D>(p_object) || Object::cast_to<NinePatchRect>(p_object) || Object::cast_to<StyleBoxTexture>(p_object) || Object::cast_to<AtlasTexture>(p_object); } -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(); - 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()) { - EditorNode::get_singleton()->make_bottom_panel_item_visible(region_editor); - } - } else { - if (region_editor->is_visible_in_tree()) { - EditorNode::get_singleton()->hide_bottom_panel(); - manually_hidden = false; - } - texture_region_button->hide(); - region_editor->edit(nullptr); - } +void EditorInspectorPluginTextureRegion::_region_edit(Object *p_object) { + texture_region_editor->edit(p_object); } -Dictionary TextureRegionEditorPlugin::get_state() const { - Dictionary state; - state["snap_offset"] = region_editor->snap_offset; - state["snap_step"] = region_editor->snap_step; - state["snap_separation"] = region_editor->snap_separation; - state["snap_mode"] = region_editor->snap_mode; - return state; -} - -void TextureRegionEditorPlugin::set_state(const Dictionary &p_state) { - Dictionary state = p_state; - if (state.has("snap_step")) { - Vector2 s = state["snap_step"]; - region_editor->sb_step_x->set_value(s.x); - region_editor->sb_step_y->set_value(s.y); - region_editor->snap_step = s; - } - - if (state.has("snap_offset")) { - Vector2 ofs = state["snap_offset"]; - region_editor->sb_off_x->set_value(ofs.x); - region_editor->sb_off_y->set_value(ofs.y); - region_editor->snap_offset = ofs; - } - - if (state.has("snap_separation")) { - Vector2 sep = state["snap_separation"]; - region_editor->sb_sep_x->set_value(sep.x); - region_editor->sb_sep_y->set_value(sep.y); - region_editor->snap_separation = sep; - } - - if (state.has("snap_mode")) { - region_editor->_set_snap_mode(state["snap_mode"]); - region_editor->snap_mode_button->select(state["snap_mode"]); +bool EditorInspectorPluginTextureRegion::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_type == Variant::RECT2 || p_type == Variant::RECT2I)) { + if (((Object::cast_to<Sprite2D>(p_object) || Object::cast_to<Sprite3D>(p_object) || Object::cast_to<NinePatchRect>(p_object) || Object::cast_to<StyleBoxTexture>(p_object)) && p_path == "region_rect") || (Object::cast_to<AtlasTexture>(p_object) && p_path == "region")) { + Button *button = EditorInspector::create_inspector_action_button(TTR("Edit Region")); + button->set_icon(texture_region_editor->get_theme_icon(SNAME("RegionEdit"), SNAME("EditorIcons"))); + button->connect("pressed", callable_mp(this, &EditorInspectorPluginTextureRegion::_region_edit), varray(p_object)); + add_property_editor(p_path, button, true); + } } + return false; //not exclusive } -void TextureRegionEditorPlugin::_bind_methods() { +EditorInspectorPluginTextureRegion::EditorInspectorPluginTextureRegion() { + texture_region_editor = memnew(TextureRegionEditor); + EditorNode::get_singleton()->get_gui_base()->add_child(texture_region_editor); } TextureRegionEditorPlugin::TextureRegionEditorPlugin() { - manually_hidden = false; - - region_editor = memnew(TextureRegionEditor); - region_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE); - region_editor->hide(); - region_editor->connect("visibility_changed", callable_mp(this, &TextureRegionEditorPlugin::_editor_visiblity_changed)); - - texture_region_button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("TextureRegion"), region_editor); - texture_region_button->hide(); + Ref<EditorInspectorPluginTextureRegion> inspector_plugin; + inspector_plugin.instantiate(); + add_inspector_plugin(inspector_plugin); } diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h index 1e1cc2b7b2..a18c87f153 100644 --- a/editor/plugins/texture_region_editor_plugin.h +++ b/editor/plugins/texture_region_editor_plugin.h @@ -41,8 +41,8 @@ class ViewPanner; -class TextureRegionEditor : public VBoxContainer { - GDCLASS(TextureRegionEditor, VBoxContainer); +class TextureRegionEditor : public AcceptDialog { + GDCLASS(TextureRegionEditor, AcceptDialog); enum SnapMode { SNAP_NONE, @@ -71,10 +71,10 @@ class TextureRegionEditor : public VBoxContainer { UndoRedo *undo_redo = nullptr; Vector2 draw_ofs; - float draw_zoom; - bool updating_scroll; + float draw_zoom = 0.0; + bool updating_scroll = false; - int snap_mode; + int snap_mode = 0; Vector2 snap_offset; Vector2 snap_step; Vector2 snap_separation; @@ -87,16 +87,17 @@ class TextureRegionEditor : public VBoxContainer { Rect2 rect; Rect2 rect_prev; - float prev_margin; - int edited_margin; - Map<RID, List<Rect2>> cache_map; + float prev_margin = 0.0f; + int edited_margin = 0; + HashMap<RID, List<Rect2>> cache_map; List<Rect2> autoslice_cache; - bool autoslice_is_dirty; + bool autoslice_is_dirty = false; - bool drag; - bool creating; + bool drag = false; + bool creating = false; Vector2 drag_from; - int drag_index; + int drag_index = 0; + bool request_center = false; Ref<ViewPanner> panner; void _scroll_callback(Vector2 p_scroll_vec, bool p_alt); @@ -142,26 +143,27 @@ public: TextureRegionEditor(); }; -class TextureRegionEditorPlugin : public EditorPlugin { - GDCLASS(TextureRegionEditorPlugin, EditorPlugin); +// - bool manually_hidden; - Button *texture_region_button = nullptr; - TextureRegionEditor *region_editor = nullptr; +class EditorInspectorPluginTextureRegion : public EditorInspectorPlugin { + GDCLASS(EditorInspectorPluginTextureRegion, EditorInspectorPlugin); -protected: - static void _bind_methods(); + TextureRegionEditor *texture_region_editor = nullptr; + + void _region_edit(Object *p_object); - void _editor_visiblity_changed(); +public: + virtual bool can_handle(Object *p_object) override; + virtual bool 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) override; + + EditorInspectorPluginTextureRegion(); +}; + +class TextureRegionEditorPlugin : public EditorPlugin { + GDCLASS(TextureRegionEditorPlugin, EditorPlugin); public: virtual String get_name() const override { return "TextureRegion"; } - bool has_main_screen() const override { return false; } - virtual void edit(Object *p_object) override; - virtual bool handles(Object *p_object) const override; - virtual void make_visible(bool p_visible) override; - void set_state(const Dictionary &p_state) override; - Dictionary get_state() const override; TextureRegionEditorPlugin(); }; diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index e17bea0f2a..c7dc61b3a8 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -834,6 +834,8 @@ void ThemeItemImportTree::_notification(int p_what) { select_icons_warning_icon->set_texture(get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons"))); select_icons_warning->add_theme_color_override("font_color", get_theme_color(SNAME("disabled_font_color"), SNAME("Editor"))); + import_items_filter->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); + // Bottom panel buttons. import_collapse_types_button->set_icon(get_theme_icon(SNAME("CollapseTree"), SNAME("EditorIcons"))); import_expand_types_button->set_icon(get_theme_icon(SNAME("ExpandTree"), SNAME("EditorIcons"))); @@ -881,15 +883,10 @@ void ThemeItemImportTree::_bind_methods() { } ThemeItemImportTree::ThemeItemImportTree() { - HBoxContainer *import_items_filter_hb = memnew(HBoxContainer); - add_child(import_items_filter_hb); - Label *import_items_filter_label = memnew(Label); - import_items_filter_label->set_text(TTR("Filter:")); - import_items_filter_hb->add_child(import_items_filter_label); import_items_filter = memnew(LineEdit); + import_items_filter->set_placeholder(TTR("Filter Items")); import_items_filter->set_clear_button_enabled(true); - import_items_filter->set_h_size_flags(Control::SIZE_EXPAND_FILL); - import_items_filter_hb->add_child(import_items_filter); + add_child(import_items_filter); import_items_filter->connect("text_changed", callable_mp(this, &ThemeItemImportTree::_filter_text_changed)); HBoxContainer *import_main_hb = memnew(HBoxContainer); @@ -1122,7 +1119,7 @@ ThemeItemImportTree::ThemeItemImportTree() { select_icons_warning = memnew(Label); select_icons_warning->set_text(TTR("Caution: Adding icon data may considerably increase the size of your Theme resource.")); - select_icons_warning->set_autowrap_mode(Label::AUTOWRAP_WORD_SMART); + select_icons_warning->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART); select_icons_warning->set_h_size_flags(Control::SIZE_EXPAND_FILL); select_icons_warning_hb->add_child(select_icons_warning); } @@ -1287,7 +1284,11 @@ void ThemeItemEditorDialog::_edited_type_selected() { _update_edit_item_tree(selected_type); } -void ThemeItemEditorDialog::_edited_type_button_pressed(Object *p_item, int p_column, int p_id) { +void ThemeItemEditorDialog::_edited_type_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button) { + if (p_button != MouseButton::LEFT) { + return; + } + TreeItem *item = Object::cast_to<TreeItem>(p_item); if (!item) { return; @@ -1461,7 +1462,11 @@ void ThemeItemEditorDialog::_update_edit_item_tree(String p_item_type) { } } -void ThemeItemEditorDialog::_item_tree_button_pressed(Object *p_item, int p_column, int p_id) { +void ThemeItemEditorDialog::_item_tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button) { + if (p_button != MouseButton::LEFT) { + return; + } + TreeItem *item = Object::cast_to<TreeItem>(p_item); if (!item) { return; @@ -1863,9 +1868,6 @@ void ThemeItemEditorDialog::_notification(int p_what) { edit_items_remove_all->set_icon(get_theme_icon(SNAME("ThemeRemoveAllItems"), SNAME("EditorIcons"))); import_another_theme_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons"))); - - tc->add_theme_style_override("tab_selected", get_theme_stylebox(SNAME("tab_selected_odd"), SNAME("TabContainer"))); - tc->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel_odd"), SNAME("TabContainer"))); } break; } } @@ -1888,6 +1890,7 @@ ThemeItemEditorDialog::ThemeItemEditorDialog(ThemeTypeEditor *p_theme_type_edito tc = memnew(TabContainer); add_child(tc); + tc->set_theme_type_variation("TabContainerOdd"); // Edit Items tab. HSplitContainer *edit_dialog_hs = memnew(HSplitContainer); @@ -1909,7 +1912,7 @@ ThemeItemEditorDialog::ThemeItemEditorDialog(ThemeTypeEditor *p_theme_type_edito edit_type_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); edit_dialog_side_vb->add_child(edit_type_list); edit_type_list->connect("item_selected", callable_mp(this, &ThemeItemEditorDialog::_edited_type_selected)); - edit_type_list->connect("button_pressed", callable_mp(this, &ThemeItemEditorDialog::_edited_type_button_pressed)); + edit_type_list->connect("button_clicked", callable_mp(this, &ThemeItemEditorDialog::_edited_type_button_pressed)); Label *edit_add_type_label = memnew(Label); edit_add_type_label->set_text(TTR("Add Type:")); @@ -2011,14 +2014,14 @@ ThemeItemEditorDialog::ThemeItemEditorDialog(ThemeTypeEditor *p_theme_type_edito edit_items_tree->set_hide_root(true); edit_items_tree->set_columns(1); edit_items_vb->add_child(edit_items_tree); - edit_items_tree->connect("button_pressed", callable_mp(this, &ThemeItemEditorDialog::_item_tree_button_pressed)); + edit_items_tree->connect("button_clicked", callable_mp(this, &ThemeItemEditorDialog::_item_tree_button_pressed)); edit_items_message = memnew(Label); edit_items_message->set_anchors_and_offsets_preset(Control::PRESET_WIDE); edit_items_message->set_mouse_filter(Control::MOUSE_FILTER_STOP); edit_items_message->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); edit_items_message->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER); - edit_items_message->set_autowrap_mode(Label::AUTOWRAP_WORD); + edit_items_message->set_autowrap_mode(TextServer::AUTOWRAP_WORD); edit_items_tree->add_child(edit_items_message); edit_theme_item_dialog = memnew(ConfirmationDialog); @@ -2339,8 +2342,8 @@ void ThemeTypeEditor::_update_type_list_debounced() { update_debounce_timer->start(); } -OrderedHashMap<StringName, bool> ThemeTypeEditor::_get_type_items(String p_type_name, void (Theme::*get_list_func)(StringName, List<StringName> *) const, bool include_default) { - OrderedHashMap<StringName, bool> items; +HashMap<StringName, bool> ThemeTypeEditor::_get_type_items(String p_type_name, void (Theme::*get_list_func)(StringName, List<StringName> *) const, bool include_default) { + HashMap<StringName, bool> items; List<StringName> names; if (include_default) { @@ -2367,12 +2370,12 @@ OrderedHashMap<StringName, bool> ThemeTypeEditor::_get_type_items(String p_type_ } List<StringName> keys; - for (OrderedHashMap<StringName, bool>::Element E = items.front(); E; E = E.next()) { - keys.push_back(E.key()); + for (const KeyValue<StringName, bool> &E : items) { + keys.push_back(E.key); } keys.sort_custom<StringName::AlphCompare>(); - OrderedHashMap<StringName, bool> ordered_items; + HashMap<StringName, bool> ordered_items; for (const StringName &E : keys) { ordered_items[E] = items[E]; } @@ -2464,18 +2467,18 @@ void ThemeTypeEditor::_update_type_items() { color_items_list->remove_child(node); } - OrderedHashMap<StringName, bool> color_items = _get_type_items(edited_type, &Theme::get_color_list, show_default); - for (OrderedHashMap<StringName, bool>::Element E = color_items.front(); E; E = E.next()) { - HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_COLOR, E.key(), E.get()); + HashMap<StringName, bool> color_items = _get_type_items(edited_type, &Theme::get_color_list, show_default); + for (const KeyValue<StringName, bool> &E : color_items) { + HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_COLOR, E.key, E.value); ColorPickerButton *item_editor = memnew(ColorPickerButton); item_editor->set_h_size_flags(SIZE_EXPAND_FILL); item_control->add_child(item_editor); - if (E.get()) { - item_editor->set_pick_color(edited_theme->get_color(E.key(), edited_type)); - item_editor->connect("color_changed", callable_mp(this, &ThemeTypeEditor::_color_item_changed), varray(E.key())); + if (E.value) { + item_editor->set_pick_color(edited_theme->get_color(E.key, edited_type)); + item_editor->connect("color_changed", callable_mp(this, &ThemeTypeEditor::_color_item_changed), varray(E.key)); } else { - item_editor->set_pick_color(Theme::get_default()->get_color(E.key(), edited_type)); + item_editor->set_pick_color(Theme::get_default()->get_color(E.key, edited_type)); item_editor->set_disabled(true); } @@ -2492,9 +2495,9 @@ void ThemeTypeEditor::_update_type_items() { constant_items_list->remove_child(node); } - OrderedHashMap<StringName, bool> constant_items = _get_type_items(edited_type, &Theme::get_constant_list, show_default); - for (OrderedHashMap<StringName, bool>::Element E = constant_items.front(); E; E = E.next()) { - HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_CONSTANT, E.key(), E.get()); + HashMap<StringName, bool> constant_items = _get_type_items(edited_type, &Theme::get_constant_list, show_default); + for (const KeyValue<StringName, bool> &E : constant_items) { + HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_CONSTANT, E.key, E.value); SpinBox *item_editor = memnew(SpinBox); item_editor->set_h_size_flags(SIZE_EXPAND_FILL); item_editor->set_min(-100000); @@ -2504,11 +2507,11 @@ void ThemeTypeEditor::_update_type_items() { item_editor->set_allow_greater(true); item_control->add_child(item_editor); - if (E.get()) { - item_editor->set_value(edited_theme->get_constant(E.key(), edited_type)); - item_editor->connect("value_changed", callable_mp(this, &ThemeTypeEditor::_constant_item_changed), varray(E.key())); + if (E.value) { + item_editor->set_value(edited_theme->get_constant(E.key, edited_type)); + item_editor->connect("value_changed", callable_mp(this, &ThemeTypeEditor::_constant_item_changed), varray(E.key)); } else { - item_editor->set_value(Theme::get_default()->get_constant(E.key(), edited_type)); + item_editor->set_value(Theme::get_default()->get_constant(E.key, edited_type)); item_editor->set_editable(false); } @@ -2525,27 +2528,27 @@ void ThemeTypeEditor::_update_type_items() { font_items_list->remove_child(node); } - OrderedHashMap<StringName, bool> font_items = _get_type_items(edited_type, &Theme::get_font_list, show_default); - for (OrderedHashMap<StringName, bool>::Element E = font_items.front(); E; E = E.next()) { - HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_FONT, E.key(), E.get()); + HashMap<StringName, bool> font_items = _get_type_items(edited_type, &Theme::get_font_list, show_default); + for (const KeyValue<StringName, bool> &E : font_items) { + HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_FONT, E.key, E.value); EditorResourcePicker *item_editor = memnew(EditorResourcePicker); item_editor->set_h_size_flags(SIZE_EXPAND_FILL); item_editor->set_base_type("Font"); item_control->add_child(item_editor); - if (E.get()) { - if (edited_theme->has_font(E.key(), edited_type)) { - item_editor->set_edited_resource(edited_theme->get_font(E.key(), edited_type)); + if (E.value) { + if (edited_theme->has_font(E.key, edited_type)) { + item_editor->set_edited_resource(edited_theme->get_font(E.key, edited_type)); } else { - item_editor->set_edited_resource(RES()); + item_editor->set_edited_resource(Ref<Resource>()); } item_editor->connect("resource_selected", callable_mp(this, &ThemeTypeEditor::_edit_resource_item)); - item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_font_item_changed), varray(E.key())); + item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_font_item_changed), varray(E.key)); } else { - if (Theme::get_default()->has_font(E.key(), edited_type)) { - item_editor->set_edited_resource(Theme::get_default()->get_font(E.key(), edited_type)); + if (Theme::get_default()->has_font(E.key, edited_type)) { + item_editor->set_edited_resource(Theme::get_default()->get_font(E.key, edited_type)); } else { - item_editor->set_edited_resource(RES()); + item_editor->set_edited_resource(Ref<Resource>()); } item_editor->set_editable(false); } @@ -2563,9 +2566,9 @@ void ThemeTypeEditor::_update_type_items() { font_size_items_list->remove_child(node); } - OrderedHashMap<StringName, bool> font_size_items = _get_type_items(edited_type, &Theme::get_font_size_list, show_default); - for (OrderedHashMap<StringName, bool>::Element E = font_size_items.front(); E; E = E.next()) { - HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_FONT_SIZE, E.key(), E.get()); + HashMap<StringName, bool> font_size_items = _get_type_items(edited_type, &Theme::get_font_size_list, show_default); + for (const KeyValue<StringName, bool> &E : font_size_items) { + HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_FONT_SIZE, E.key, E.value); SpinBox *item_editor = memnew(SpinBox); item_editor->set_h_size_flags(SIZE_EXPAND_FILL); item_editor->set_min(-100000); @@ -2575,11 +2578,11 @@ void ThemeTypeEditor::_update_type_items() { item_editor->set_allow_greater(true); item_control->add_child(item_editor); - if (E.get()) { - item_editor->set_value(edited_theme->get_font_size(E.key(), edited_type)); - item_editor->connect("value_changed", callable_mp(this, &ThemeTypeEditor::_font_size_item_changed), varray(E.key())); + if (E.value) { + item_editor->set_value(edited_theme->get_font_size(E.key, edited_type)); + item_editor->connect("value_changed", callable_mp(this, &ThemeTypeEditor::_font_size_item_changed), varray(E.key)); } else { - item_editor->set_value(Theme::get_default()->get_font_size(E.key(), edited_type)); + item_editor->set_value(Theme::get_default()->get_font_size(E.key, edited_type)); item_editor->set_editable(false); } @@ -2596,27 +2599,27 @@ void ThemeTypeEditor::_update_type_items() { icon_items_list->remove_child(node); } - OrderedHashMap<StringName, bool> icon_items = _get_type_items(edited_type, &Theme::get_icon_list, show_default); - for (OrderedHashMap<StringName, bool>::Element E = icon_items.front(); E; E = E.next()) { - HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_ICON, E.key(), E.get()); + HashMap<StringName, bool> icon_items = _get_type_items(edited_type, &Theme::get_icon_list, show_default); + for (const KeyValue<StringName, bool> &E : icon_items) { + HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_ICON, E.key, E.value); EditorResourcePicker *item_editor = memnew(EditorResourcePicker); item_editor->set_h_size_flags(SIZE_EXPAND_FILL); item_editor->set_base_type("Texture2D"); item_control->add_child(item_editor); - if (E.get()) { - if (edited_theme->has_icon(E.key(), edited_type)) { - item_editor->set_edited_resource(edited_theme->get_icon(E.key(), edited_type)); + if (E.value) { + if (edited_theme->has_icon(E.key, edited_type)) { + item_editor->set_edited_resource(edited_theme->get_icon(E.key, edited_type)); } else { - item_editor->set_edited_resource(RES()); + item_editor->set_edited_resource(Ref<Resource>()); } item_editor->connect("resource_selected", callable_mp(this, &ThemeTypeEditor::_edit_resource_item)); - item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_icon_item_changed), varray(E.key())); + item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_icon_item_changed), varray(E.key)); } else { - if (Theme::get_default()->has_icon(E.key(), edited_type)) { - item_editor->set_edited_resource(Theme::get_default()->get_icon(E.key(), edited_type)); + if (Theme::get_default()->has_icon(E.key, edited_type)) { + item_editor->set_edited_resource(Theme::get_default()->get_icon(E.key, edited_type)); } else { - item_editor->set_edited_resource(RES()); + item_editor->set_edited_resource(Ref<Resource>()); } item_editor->set_editable(false); } @@ -2655,7 +2658,7 @@ void ThemeTypeEditor::_update_type_items() { if (edited_theme->has_stylebox(leading_stylebox.item_name, edited_type)) { item_editor->set_edited_resource(leading_stylebox.stylebox); } else { - item_editor->set_edited_resource(RES()); + item_editor->set_edited_resource(Ref<Resource>()); } item_editor->connect("resource_selected", callable_mp(this, &ThemeTypeEditor::_edit_resource_item)); item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_stylebox_item_changed), varray(leading_stylebox.item_name)); @@ -2664,26 +2667,26 @@ void ThemeTypeEditor::_update_type_items() { stylebox_items_list->add_child(memnew(HSeparator)); } - OrderedHashMap<StringName, bool> stylebox_items = _get_type_items(edited_type, &Theme::get_stylebox_list, show_default); - for (OrderedHashMap<StringName, bool>::Element E = stylebox_items.front(); E; E = E.next()) { - if (leading_stylebox.pinned && leading_stylebox.item_name == E.key()) { + HashMap<StringName, bool> stylebox_items = _get_type_items(edited_type, &Theme::get_stylebox_list, show_default); + for (const KeyValue<StringName, bool> &E : stylebox_items) { + if (leading_stylebox.pinned && leading_stylebox.item_name == E.key) { continue; } - HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_STYLEBOX, E.key(), E.get()); + HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_STYLEBOX, E.key, E.value); EditorResourcePicker *item_editor = memnew(EditorResourcePicker); item_editor->set_h_size_flags(SIZE_EXPAND_FILL); item_editor->set_stretch_ratio(1.5); item_editor->set_base_type("StyleBox"); - if (E.get()) { - if (edited_theme->has_stylebox(E.key(), edited_type)) { - item_editor->set_edited_resource(edited_theme->get_stylebox(E.key(), edited_type)); + if (E.value) { + if (edited_theme->has_stylebox(E.key, edited_type)) { + item_editor->set_edited_resource(edited_theme->get_stylebox(E.key, edited_type)); } else { - item_editor->set_edited_resource(RES()); + item_editor->set_edited_resource(Ref<Resource>()); } item_editor->connect("resource_selected", callable_mp(this, &ThemeTypeEditor::_edit_resource_item)); - item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_stylebox_item_changed), varray(E.key())); + item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_stylebox_item_changed), varray(E.key)); Button *pin_leader_button = memnew(Button); pin_leader_button->set_flat(true); @@ -2691,12 +2694,12 @@ void ThemeTypeEditor::_update_type_items() { pin_leader_button->set_icon(get_theme_icon(SNAME("Pin"), SNAME("EditorIcons"))); pin_leader_button->set_tooltip(TTR("Pin this StyleBox as a main style. Editing its properties will update the same properties in all other StyleBoxes of this type.")); item_control->add_child(pin_leader_button); - pin_leader_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_on_pin_leader_button_pressed), varray(item_editor, E.key())); + pin_leader_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_on_pin_leader_button_pressed), varray(item_editor, E.key)); } else { - if (Theme::get_default()->has_stylebox(E.key(), edited_type)) { - item_editor->set_edited_resource(Theme::get_default()->get_stylebox(E.key(), edited_type)); + if (Theme::get_default()->has_stylebox(E.key, edited_type)) { + item_editor->set_edited_resource(Theme::get_default()->get_stylebox(E.key, edited_type)); } else { - item_editor->set_edited_resource(RES()); + item_editor->set_edited_resource(Ref<Resource>()); } item_editor->set_editable(false); } @@ -3065,7 +3068,7 @@ void ThemeTypeEditor::_font_size_item_changed(float p_value, String p_item_name) ur->commit_action(); } -void ThemeTypeEditor::_edit_resource_item(RES p_resource, bool p_edit) { +void ThemeTypeEditor::_edit_resource_item(Ref<Resource> p_resource, bool p_edit) { EditorNode::get_singleton()->edit_resource(p_resource); } @@ -3131,7 +3134,7 @@ void ThemeTypeEditor::_change_pinned_stylebox() { Ref<StyleBox> new_stylebox = edited_theme->get_stylebox(leading_stylebox.item_name, edited_type); leading_stylebox.stylebox = new_stylebox; - leading_stylebox.ref_stylebox = (new_stylebox.is_valid() ? new_stylebox->duplicate() : RES()); + leading_stylebox.ref_stylebox = (new_stylebox.is_valid() ? new_stylebox->duplicate() : Ref<Resource>()); if (leading_stylebox.stylebox.is_valid()) { new_stylebox->connect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading)); @@ -3169,7 +3172,7 @@ void ThemeTypeEditor::_pin_leading_stylebox(String p_item_name, Ref<StyleBox> p_ leader.pinned = true; leader.item_name = p_item_name; leader.stylebox = p_stylebox; - leader.ref_stylebox = (p_stylebox.is_valid() ? p_stylebox->duplicate() : RES()); + leader.ref_stylebox = (p_stylebox.is_valid() ? p_stylebox->duplicate() : Ref<Resource>()); leading_stylebox = leader; if (p_stylebox.is_valid()) { @@ -3211,11 +3214,13 @@ void ThemeTypeEditor::_update_stylebox_from_leading() { edited_theme->get_stylebox_list(edited_type, &names); List<Ref<StyleBox>> styleboxes; for (const StringName &E : names) { - if (E == leading_stylebox.item_name) { + Ref<StyleBox> sb = edited_theme->get_stylebox(E, edited_type); + + // Avoid itself, stylebox can be shared between items. + if (sb == leading_stylebox.stylebox) { continue; } - Ref<StyleBox> sb = edited_theme->get_stylebox(E, edited_type); if (sb->get_class() == leading_stylebox.stylebox->get_class()) { styleboxes.push_back(sb); } @@ -3295,9 +3300,6 @@ void ThemeTypeEditor::_notification(int p_what) { data_type_tabs->set_tab_icon(5, get_theme_icon(SNAME("StyleBoxFlat"), SNAME("EditorIcons"))); data_type_tabs->set_tab_icon(6, get_theme_icon(SNAME("Tools"), SNAME("EditorIcons"))); - data_type_tabs->add_theme_style_override("tab_selected", get_theme_stylebox(SNAME("tab_selected_odd"), SNAME("TabContainer"))); - data_type_tabs->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel_odd"), SNAME("TabContainer"))); - type_variation_button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); } break; } @@ -3397,6 +3399,7 @@ ThemeTypeEditor::ThemeTypeEditor() { main_vb->add_child(data_type_tabs); data_type_tabs->set_v_size_flags(SIZE_EXPAND_FILL); data_type_tabs->set_use_hidden_tabs_for_min_size(true); + data_type_tabs->set_theme_type_variation("TabContainerOdd"); color_items_list = _create_item_list(Theme::DATA_TYPE_COLOR); constant_items_list = _create_item_list(Theme::DATA_TYPE_CONSTANT); @@ -3440,7 +3443,7 @@ ThemeTypeEditor::ThemeTypeEditor() { type_variation_locked = memnew(Label); type_variation_vb->add_child(type_variation_locked); type_variation_locked->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); - type_variation_locked->set_autowrap_mode(Label::AUTOWRAP_WORD); + type_variation_locked->set_autowrap_mode(TextServer::AUTOWRAP_WORD); type_variation_locked->set_text(TTR("A type associated with a built-in class cannot be marked as a variation of another type.")); type_variation_locked->hide(); @@ -3579,7 +3582,7 @@ void ThemeEditor::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: { preview_tabs->add_theme_style_override("tab_selected", get_theme_stylebox(SNAME("ThemeEditorPreviewFG"), SNAME("EditorStyles"))); preview_tabs->add_theme_style_override("tab_unselected", get_theme_stylebox(SNAME("ThemeEditorPreviewBG"), SNAME("EditorStyles"))); - preview_tabs_content->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel_odd"), SNAME("TabContainer"))); + preview_tabs_content->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("TabContainerOdd"))); add_preview_button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); } break; diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h index 4b49fbb186..543113a5eb 100644 --- a/editor/plugins/theme_editor_plugin.h +++ b/editor/plugins/theme_editor_plugin.h @@ -73,7 +73,7 @@ class ThemeItemImportTree : public VBoxContainer { SELECT_IMPORT_FULL, }; - Map<ThemeItem, ItemCheckedState> selected_items; + RBMap<ThemeItem, ItemCheckedState> selected_items; LineEdit *import_items_filter = nullptr; @@ -249,10 +249,10 @@ class ThemeItemEditorDialog : public AcceptDialog { void _dialog_about_to_show(); void _update_edit_types(); void _edited_type_selected(); - void _edited_type_button_pressed(Object *p_item, int p_column, int p_id); + void _edited_type_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button); void _update_edit_item_tree(String p_item_type); - void _item_tree_button_pressed(Object *p_item, int p_column, int p_id); + void _item_tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button); void _add_theme_type(const String &p_new_text); void _add_theme_item(Theme::DataType p_data_type, String p_item_name, String p_item_type); @@ -363,7 +363,7 @@ class ThemeTypeEditor : public MarginContainer { VBoxContainer *_create_item_list(Theme::DataType p_data_type); void _update_type_list(); void _update_type_list_debounced(); - OrderedHashMap<StringName, bool> _get_type_items(String p_type_name, void (Theme::*get_list_func)(StringName, List<StringName> *) const, bool include_default); + HashMap<StringName, bool> _get_type_items(String p_type_name, void (Theme::*get_list_func)(StringName, List<StringName> *) const, bool include_default); HBoxContainer *_create_property_control(Theme::DataType p_data_type, String p_item_name, bool p_editable); void _add_focusable(Control *p_control); void _update_type_items(); @@ -384,7 +384,7 @@ class ThemeTypeEditor : public MarginContainer { void _color_item_changed(Color p_value, String p_item_name); void _constant_item_changed(float p_value, String p_item_name); void _font_size_item_changed(float p_value, String p_item_name); - void _edit_resource_item(RES p_resource, bool p_edit); + void _edit_resource_item(Ref<Resource> p_resource, bool p_edit); void _font_item_changed(Ref<Font> p_value, String p_item_name); void _icon_item_changed(Ref<Texture2D> p_value, String p_item_name); void _stylebox_item_changed(Ref<StyleBox> p_value, String p_item_name); diff --git a/editor/plugins/theme_editor_preview.cpp b/editor/plugins/theme_editor_preview.cpp index 252a19a7db..a6e34cf5e0 100644 --- a/editor/plugins/theme_editor_preview.cpp +++ b/editor/plugins/theme_editor_preview.cpp @@ -269,6 +269,15 @@ ThemeEditorPreview::ThemeEditorPreview() { picker_overlay->connect("mouse_exited", callable_mp(this, &ThemeEditorPreview::_reset_picker_overlay)); } +void DefaultThemeEditorPreview::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: { + test_color_picker_button->set_custom_minimum_size(Size2(0, get_theme_constant(SNAME("color_picker_button_height"), SNAME("Editor")))); + } break; + } +} + DefaultThemeEditorPreview::DefaultThemeEditorPreview() { Panel *main_panel = memnew(Panel); preview_content->add_child(main_panel); @@ -343,7 +352,8 @@ DefaultThemeEditorPreview::DefaultThemeEditorPreview() { test_option_button->add_item(TTR("Many")); test_option_button->add_item(TTR("Options")); first_vb->add_child(test_option_button); - first_vb->add_child(memnew(ColorPickerButton)); + test_color_picker_button = memnew(ColorPickerButton); + first_vb->add_child(test_color_picker_button); VBoxContainer *second_vb = memnew(VBoxContainer); second_vb->set_h_size_flags(SIZE_EXPAND_FILL); diff --git a/editor/plugins/theme_editor_preview.h b/editor/plugins/theme_editor_preview.h index d05916afae..4d209ac788 100644 --- a/editor/plugins/theme_editor_preview.h +++ b/editor/plugins/theme_editor_preview.h @@ -38,6 +38,8 @@ #include "scene/gui/scroll_container.h" #include "scene/resources/theme.h" +class ColorPickerButton; + class ThemeEditorPreview : public VBoxContainer { GDCLASS(ThemeEditorPreview, VBoxContainer); @@ -87,6 +89,11 @@ public: class DefaultThemeEditorPreview : public ThemeEditorPreview { GDCLASS(DefaultThemeEditorPreview, ThemeEditorPreview); + ColorPickerButton *test_color_picker_button = nullptr; + +protected: + void _notification(int p_what); + public: DefaultThemeEditorPreview(); }; diff --git a/editor/plugins/tiles/atlas_merging_dialog.cpp b/editor/plugins/tiles/atlas_merging_dialog.cpp index 086588f5a5..e37878ff98 100644 --- a/editor/plugins/tiles/atlas_merging_dialog.cpp +++ b/editor/plugins/tiles/atlas_merging_dialog.cpp @@ -62,7 +62,7 @@ void AtlasMergingDialog::_generate_merged(Vector<Ref<TileSetAtlasSource>> p_atla int line_height = 0; for (int source_index = 0; source_index < p_atlas_sources.size(); source_index++) { Ref<TileSetAtlasSource> atlas_source = p_atlas_sources[source_index]; - merged_mapping.push_back(Map<Vector2i, Vector2i>()); + merged_mapping.push_back(HashMap<Vector2i, Vector2i>()); // Layout the tiles. Vector2i atlas_size; diff --git a/editor/plugins/tiles/atlas_merging_dialog.h b/editor/plugins/tiles/atlas_merging_dialog.h index 9502d93f6b..c54e259594 100644 --- a/editor/plugins/tiles/atlas_merging_dialog.h +++ b/editor/plugins/tiles/atlas_merging_dialog.h @@ -46,7 +46,7 @@ private: int commited_actions_count = 0; bool delete_original_atlases = true; Ref<TileSetAtlasSource> merged; - LocalVector<Map<Vector2i, Vector2i>> merged_mapping; + LocalVector<HashMap<Vector2i, Vector2i>> merged_mapping; Ref<TileSet> tile_set; UndoRedo *undo_redo = nullptr; diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp index 71947ae185..3073c8a7f2 100644 --- a/editor/plugins/tiles/tile_atlas_view.cpp +++ b/editor/plugins/tiles/tile_atlas_view.cpp @@ -350,7 +350,7 @@ void TileAtlasView::_draw_alternatives() { bool transposed = tile_data->get_transpose(); // Update the y to max value. - Vector2i offset_pos = current_pos; + Vector2i offset_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); @@ -480,7 +480,7 @@ void TileAtlasView::_update_alternative_tiles_rect_cache() { // Update the rect. if (!alternative_tiles_rect_cache.has(tile_id)) { - alternative_tiles_rect_cache[tile_id] = Map<int, Rect2i>(); + alternative_tiles_rect_cache[tile_id] = HashMap<int, Rect2i>(); } alternative_tiles_rect_cache[tile_id][alternative_id] = current; @@ -494,7 +494,7 @@ void TileAtlasView::_update_alternative_tiles_rect_cache() { } Vector3i TileAtlasView::get_alternative_tile_at_pos(const Vector2 p_pos) const { - for (const KeyValue<Vector2, Map<int, Rect2i>> &E_coords : alternative_tiles_rect_cache) { + for (const KeyValue<Vector2, HashMap<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); diff --git a/editor/plugins/tiles/tile_atlas_view.h b/editor/plugins/tiles/tile_atlas_view.h index 6e74858b08..ff46b7871f 100644 --- a/editor/plugins/tiles/tile_atlas_view.h +++ b/editor/plugins/tiles/tile_atlas_view.h @@ -69,7 +69,7 @@ private: void _pan_callback(Vector2 p_scroll_vec); void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt); - Map<Vector2, Map<int, Rect2i>> alternative_tiles_rect_cache; + HashMap<Vector2, HashMap<int, Rect2i>> alternative_tiles_rect_cache; void _update_alternative_tiles_rect_cache(); MarginContainer *margin_container = nullptr; diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index 6c12573cc4..468681c967 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -776,13 +776,13 @@ GenericTilePolygonEditor::GenericTilePolygonEditor() { button_advanced_menu = memnew(MenuButton); button_advanced_menu->set_flat(true); button_advanced_menu->set_toggle_mode(true); - button_advanced_menu->get_popup()->add_item(TTR("Reset to default tile shape"), RESET_TO_DEFAULT_TILE); - button_advanced_menu->get_popup()->add_item(TTR("Clear"), CLEAR_TILE); + button_advanced_menu->get_popup()->add_item(TTR("Reset to default tile shape"), RESET_TO_DEFAULT_TILE, Key::F); + button_advanced_menu->get_popup()->add_item(TTR("Clear"), CLEAR_TILE, Key::C); button_advanced_menu->get_popup()->add_separator(); - button_advanced_menu->get_popup()->add_icon_item(get_theme_icon(SNAME("RotateRight"), SNAME("EditorIcons")), TTR("Rotate Right"), ROTATE_RIGHT); - button_advanced_menu->get_popup()->add_icon_item(get_theme_icon(SNAME("RotateLeft"), SNAME("EditorIcons")), TTR("Rotate Left"), ROTATE_LEFT); - button_advanced_menu->get_popup()->add_icon_item(get_theme_icon(SNAME("MirrorX"), SNAME("EditorIcons")), TTR("Flip Horizontally"), FLIP_HORIZONTALLY); - button_advanced_menu->get_popup()->add_icon_item(get_theme_icon(SNAME("MirrorY"), SNAME("EditorIcons")), TTR("Flip Vertically"), FLIP_VERTICALLY); + button_advanced_menu->get_popup()->add_icon_item(get_theme_icon(SNAME("RotateRight"), SNAME("EditorIcons")), TTR("Rotate Right"), ROTATE_RIGHT, Key::R); + button_advanced_menu->get_popup()->add_icon_item(get_theme_icon(SNAME("RotateLeft"), SNAME("EditorIcons")), TTR("Rotate Left"), ROTATE_LEFT, Key::E); + button_advanced_menu->get_popup()->add_icon_item(get_theme_icon(SNAME("MirrorX"), SNAME("EditorIcons")), TTR("Flip Horizontally"), FLIP_HORIZONTALLY, Key::H); + button_advanced_menu->get_popup()->add_icon_item(get_theme_icon(SNAME("MirrorY"), SNAME("EditorIcons")), TTR("Flip Vertically"), FLIP_VERTICALLY, Key::V); button_advanced_menu->get_popup()->connect("id_pressed", callable_mp(this, &GenericTilePolygonEditor::_advanced_menu_item_pressed)); button_advanced_menu->set_focus_mode(FOCUS_ALL); toolbar->add_child(button_advanced_menu); @@ -862,7 +862,7 @@ Variant TileDataDefaultEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_s return tile_data->get(property); } -void TileDataDefaultEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map<TileMapCell, Variant> p_previous_values, Variant p_new_value) { +void TileDataDefaultEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant 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); @@ -882,7 +882,7 @@ void TileDataDefaultEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas_ rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()))); rect = rect.abs(); - Set<TileMapCell> edited; + RBSet<TileMapCell> edited; for (int x = rect.get_position().x; x <= rect.get_end().x; x++) { for (int y = rect.get_position().y; y <= rect.get_end().y; y++) { Vector2i coords = Vector2i(x, y); @@ -897,8 +897,8 @@ void TileDataDefaultEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas_ } } - for (Set<TileMapCell>::Element *E = edited.front(); E; E = E->next()) { - Vector2i coords = E->get().get_atlas_coords(); + for (const TileMapCell &E : edited) { + Vector2i coords = E.get_atlas_coords(); p_canvas_item->draw_rect(p_tile_set_atlas_source->get_tile_texture_region(coords), selection_color, false); } p_canvas_item->draw_set_transform_matrix(Transform2D()); @@ -1147,7 +1147,7 @@ void TileDataDefaultEditor::setup_property_editor(Variant::Type p_type, String p property_editor = EditorInspectorDefaultPlugin::get_editor_for_property(dummy_object, p_type, p_property, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT); property_editor->set_object_and_property(dummy_object, p_property); if (p_label.is_empty()) { - property_editor->set_label(p_property); + property_editor->set_label(EditorPropertyNameProcessor::get_singleton()->process_name(p_property, EditorPropertyNameProcessor::get_default_inspector_style())); } else { property_editor->set_label(p_label); } @@ -1173,6 +1173,7 @@ TileDataDefaultEditor::TileDataDefaultEditor() { label = memnew(Label); label->set_text(TTR("Painting:")); + label->set_theme_type_variation("HeaderSmall"); add_child(label); toolbar->add_child(memnew(VSeparator)); @@ -1299,7 +1300,7 @@ Variant TileDataOcclusionShapeEditor::_get_value(TileSetAtlasSource *p_tile_set_ return tile_data->get_occluder(occlusion_layer); } -void TileDataOcclusionShapeEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map<TileMapCell, Variant> p_previous_values, Variant p_new_value) { +void TileDataOcclusionShapeEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant 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); @@ -1479,7 +1480,7 @@ Variant TileDataCollisionEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas return dict; } -void TileDataCollisionEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map<TileMapCell, Variant> p_previous_values, Variant p_new_value) { +void TileDataCollisionEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) { Array new_array = p_new_value; for (KeyValue<TileMapCell, Variant> &E : p_previous_values) { Array old_array = E.value; @@ -1676,10 +1677,15 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas Vector<Color> color; color.push_back(Color(1.0, 1.0, 1.0, 0.5)); + Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set); + if (Geometry2D::is_point_in_polygon(xform.affine_inverse().xform(mouse_pos), polygon)) { + p_canvas_item->draw_set_transform_matrix(p_transform * xform); + p_canvas_item->draw_polygon(polygon, color); + } 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)) { - Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit); + if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) { + polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit); if (Geometry2D::is_point_in_polygon(xform.affine_inverse().xform(mouse_pos), polygon)) { p_canvas_item->draw_set_transform_matrix(p_transform * xform); p_canvas_item->draw_polygon(polygon, color); @@ -1740,7 +1746,7 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()))); rect = rect.abs(); - Set<TileMapCell> edited; + RBSet<TileMapCell> edited; for (int x = rect.get_position().x; x <= rect.get_end().x; x++) { for (int y = rect.get_position().y; y <= rect.get_end().y; y++) { Vector2i coords = Vector2i(x, y); @@ -1755,8 +1761,8 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas } } - for (Set<TileMapCell>::Element *E = edited.front(); E; E = E->next()) { - Vector2i coords = E->get().get_atlas_coords(); + for (const TileMapCell &E : edited) { + Vector2i coords = E.get_atlas_coords(); p_canvas_item->draw_rect(p_tile_set_atlas_source->get_tile_texture_region(coords), selection_color, false); } p_canvas_item->draw_set_transform_matrix(Transform2D()); @@ -1770,7 +1776,7 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()))); rect = rect.abs(); - Set<TileMapCell> edited; + RBSet<TileMapCell> edited; for (int x = rect.get_position().x; x <= rect.get_end().x; x++) { for (int y = rect.get_position().y; y <= rect.get_end().y; y++) { Vector2i coords = Vector2i(x, y); @@ -1800,16 +1806,25 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas p_canvas_item->draw_set_transform_matrix(p_transform); - for (Set<TileMapCell>::Element *E = edited.front(); E; E = E->next()) { - Vector2i coords = E->get().get_atlas_coords(); + for (const TileMapCell &E : edited) { + Vector2i coords = E.get_atlas_coords(); Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); 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_polygon(terrain_set); + for (int j = 0; j < polygon.size(); j++) { + polygon.write[j] += position; + } + if (!Geometry2D::intersect_polygons(polygon, mouse_pos_rect_polygon).is_empty()) { + // Draw terrain. + p_canvas_item->draw_polygon(polygon, color); + } + 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)) { - Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit); + if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) { + polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit); for (int j = 0; j < polygon.size(); j++) { polygon.write[j] += position; } @@ -1850,10 +1865,16 @@ void TileDataTerrainsEditor::forward_draw_over_alternatives(TileAtlasView *p_til Vector<Color> color; color.push_back(Color(1.0, 1.0, 1.0, 0.5)); + Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set); + if (Geometry2D::is_point_in_polygon(xform.affine_inverse().xform(mouse_pos), polygon)) { + p_canvas_item->draw_set_transform_matrix(p_transform * xform); + p_canvas_item->draw_polygon(polygon, color); + } + 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)) { - Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit); + if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) { + polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit); if (Geometry2D::is_point_in_polygon(xform.affine_inverse().xform(mouse_pos), polygon)) { p_canvas_item->draw_set_transform_matrix(p_transform * xform); p_canvas_item->draw_polygon(polygon, color); @@ -1926,10 +1947,11 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t if (!drag_modified.has(cell)) { Dictionary dict; dict["terrain_set"] = tile_data->get_terrain_set(); + dict["terrain"] = tile_data->get_terrain(); Array array; for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); - array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1); + array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1); } dict["terrain_peering_bits"] = array; drag_modified[cell] = dict; @@ -1958,10 +1980,11 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t if (!drag_modified.has(cell)) { Dictionary dict; dict["terrain_set"] = tile_data->get_terrain_set(); + dict["terrain"] = tile_data->get_terrain(); Array array; for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); - array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1); + array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1); } dict["terrain_peering_bits"] = array; drag_modified[cell] = dict; @@ -1970,12 +1993,17 @@ 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.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0); + + Vector<Vector2> polygon = tile_set->get_terrain_polygon(tile_data->get_terrain_set()); + if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) { + tile_data->set_terrain(terrain); + } 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)) { - Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(tile_data->get_terrain_set(), bit); + if (tile_data->is_valid_terrain_peering_bit(bit)) { + polygon = tile_set->get_terrain_peering_bit_polygon(tile_data->get_terrain_set(), bit); if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) { - tile_data->set_peering_bit_terrain(bit, terrain); + tile_data->set_terrain_peering_bit(bit, terrain); } } } @@ -2000,12 +2028,17 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t 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); + + Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set); + if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) { + dummy_object->set("terrain", tile_data->get_terrain()); + } 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)) { - Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit); + if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) { + polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit); if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) { - dummy_object->set("terrain", tile_data->get_peering_bit_terrain(bit)); + dummy_object->set("terrain", tile_data->get_terrain_peering_bit(bit)); } } } @@ -2044,10 +2077,11 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t // Save the old terrain_set and terrains bits. Dictionary dict; dict["terrain_set"] = tile_data->get_terrain_set(); + dict["terrain"] = tile_data->get_terrain(); Array array; for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); - array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1); + array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1); } dict["terrain_peering_bits"] = array; drag_modified[cell] = dict; @@ -2057,7 +2091,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t } drag_last_pos = mb->get_position(); } - } else if (tile_data && tile_data->get_terrain_set() == terrain_set) { + } else if (tile_data->get_terrain_set() == terrain_set) { if (mb->is_ctrl_pressed()) { // Paint terrain set with rect. drag_type = DRAG_TYPE_PAINT_TERRAIN_BITS_RECT; @@ -2085,10 +2119,11 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t // Save the old terrain_set and terrains bits. Dictionary dict; dict["terrain_set"] = tile_data->get_terrain_set(); + dict["terrain"] = tile_data->get_terrain(); Array array; for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); - array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1); + array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1); } dict["terrain_peering_bits"] = array; drag_modified[cell] = dict; @@ -2097,12 +2132,16 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); 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_polygon(terrain_set); + if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) { + tile_data->set_terrain(terrain); + } 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)) { - Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit); + if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) { + polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit); if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) { - tile_data->set_peering_bit_terrain(bit, terrain); + tile_data->set_terrain_peering_bit(bit, terrain); } } } @@ -2118,7 +2157,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position())); rect = rect.abs(); - Set<TileMapCell> edited; + RBSet<TileMapCell> edited; for (int x = rect.get_position().x; x <= rect.get_end().x; x++) { for (int y = rect.get_position().y; y <= rect.get_end().y; y++) { Vector2i coords = Vector2i(x, y); @@ -2133,15 +2172,16 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t } } undo_redo->create_action(TTR("Painting Terrain Set")); - for (Set<TileMapCell>::Element *E = edited.front(); E; E = E->next()) { - Vector2i coords = E->get().get_atlas_coords(); + for (const TileMapCell &E : edited) { + Vector2i coords = E.get_atlas_coords(); TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0); - undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E->get().alternative_tile), tile_data->get_terrain_set()); - undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E->get().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.alternative_tile), tile_data->get_terrain_set()); + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.alternative_tile), drag_painted_value); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.alternative_tile), tile_data->get_terrain()); for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); - if (tile_data->is_valid_peering_bit_terrain(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->get().alternative_tile), tile_data->get_peering_bit_terrain(bit)); + if (tile_data->is_valid_terrain_peering_bit(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.alternative_tile), tile_data->get_terrain_peering_bit(bit)); } } } @@ -2154,10 +2194,11 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t 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"]); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.key.alternative_tile), dict["terrain"]); 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)) { + if (tile_set->is_valid_terrain_peering_bit(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]); } } @@ -2172,13 +2213,15 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t 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", coords.x, coords.y, E.key.alternative_tile), terrain); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.key.alternative_tile), dict["terrain"]); 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)) { + if (tile_set->is_valid_terrain_peering_bit(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); } - if (tile_set->is_valid_peering_bit_terrain(dict["terrain_set"], bit)) { + if (tile_set->is_valid_terrain_peering_bit(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]); } } @@ -2195,7 +2238,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position())); rect = rect.abs(); - Set<TileMapCell> edited; + RBSet<TileMapCell> edited; for (int x = rect.get_position().x; x <= rect.get_end().x; x++) { for (int y = rect.get_position().y; y <= rect.get_end().y; y++) { Vector2i coords = Vector2i(x, y); @@ -2220,24 +2263,34 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t mouse_pos_rect_polygon.push_back(Vector2(drag_start_pos.x, mb->get_position().y)); undo_redo->create_action(TTR("Painting Terrain")); - for (Set<TileMapCell>::Element *E = edited.front(); E; E = E->next()) { - Vector2i coords = E->get().get_atlas_coords(); + for (const TileMapCell &E : edited) { + Vector2i coords = E.get_atlas_coords(); TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0); + Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); + 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_polygon(terrain_set); + for (int j = 0; j < polygon.size(); j++) { + polygon.write[j] += position; + } + if (!Geometry2D::intersect_polygons(polygon, mouse_pos_rect_polygon).is_empty()) { + // Draw terrain. + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.alternative_tile), terrain); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.alternative_tile), tile_data->get_terrain()); + } + 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)) { - Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); - 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); + if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) { + polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit); for (int j = 0; j < polygon.size(); j++) { polygon.write[j] += position; } if (!Geometry2D::intersect_polygons(polygon, mouse_pos_rect_polygon).is_empty()) { // Draw 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->get().alternative_tile), terrain); - 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->get().alternative_tile), tile_data->get_peering_bit_terrain(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.alternative_tile), terrain); + 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.alternative_tile), tile_data->get_terrain_peering_bit(bit)); } } } @@ -2267,10 +2320,11 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi if (!drag_modified.has(cell)) { Dictionary dict; dict["terrain_set"] = tile_data->get_terrain_set(); + dict["terrain"] = tile_data->get_terrain(); Array array; for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); - array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1); + array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1); } dict["terrain_peering_bits"] = array; drag_modified[cell] = dict; @@ -2300,10 +2354,11 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi if (!drag_modified.has(cell)) { Dictionary dict; dict["terrain_set"] = tile_data->get_terrain_set(); + dict["terrain"] = tile_data->get_terrain(); Array array; for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); - array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1); + array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1); } dict["terrain_peering_bits"] = array; drag_modified[cell] = dict; @@ -2312,12 +2367,18 @@ 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.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile); + + Vector<Vector2> polygon = tile_set->get_terrain_polygon(tile_data->get_terrain_set()); + if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) { + tile_data->set_terrain(terrain); + } + 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)) { - Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(tile_data->get_terrain_set(), bit); + if (tile_data->is_valid_terrain_peering_bit(bit)) { + polygon = tile_set->get_terrain_peering_bit_polygon(tile_data->get_terrain_set(), bit); if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) { - tile_data->set_peering_bit_terrain(bit, terrain); + tile_data->set_terrain_peering_bit(bit, terrain); } } } @@ -2343,12 +2404,18 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi 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); + + Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set); + if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) { + dummy_object->set("terrain", tile_data->get_terrain()); + } + 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)) { - Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit); + if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) { + polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit); if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) { - dummy_object->set("terrain", tile_data->get_peering_bit_terrain(bit)); + dummy_object->set("terrain", tile_data->get_terrain_peering_bit(bit)); } } } @@ -2380,14 +2447,14 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi Array array; for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); - array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1); + array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1); } dict["terrain_peering_bits"] = array; drag_modified[cell] = dict; tile_data->set_terrain_set(drag_painted_value); } drag_last_pos = mb->get_position(); - } else if (tile_data && tile_data->get_terrain_set() == terrain_set) { + } else if (tile_data->get_terrain_set() == terrain_set) { // Paint terrain bits. drag_type = DRAG_TYPE_PAINT_TERRAIN_BITS; drag_modified.clear(); @@ -2405,10 +2472,11 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi // Save the old terrain_set and terrains bits. Dictionary dict; dict["terrain_set"] = tile_data->get_terrain_set(); + dict["terrain"] = tile_data->get_terrain(); Array array; for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); - array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1); + array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1); } dict["terrain_peering_bits"] = array; drag_modified[cell] = dict; @@ -2416,12 +2484,17 @@ 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.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile); + + Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set); + if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) { + tile_data->set_terrain(terrain); + } 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)) { - Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit); + if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) { + polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit); if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) { - tile_data->set_peering_bit_terrain(bit, terrain); + tile_data->set_terrain_peering_bit(bit, terrain); } } } @@ -2437,6 +2510,7 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi 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); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.key.alternative_tile), dict["terrain"]); 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]); @@ -2452,13 +2526,15 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi 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", coords.x, coords.y, E.key.alternative_tile), terrain); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.key.alternative_tile), dict["terrain"]); 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)) { + if (tile_set->is_valid_terrain_peering_bit(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); } - if (tile_set->is_valid_peering_bit_terrain(dict["terrain_set"], bit)) { + if (tile_set->is_valid_terrain_peering_bit(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]); } } @@ -2491,7 +2567,8 @@ TileDataTerrainsEditor::TileDataTerrainsEditor() { undo_redo = EditorNode::get_undo_redo(); label = memnew(Label); - label->set_text("Painting:"); + label->set_text(TTR("Painting:")); + label->set_theme_type_variation("HeaderSmall"); add_child(label); // Toolbar @@ -2571,7 +2648,7 @@ Variant TileDataNavigationEditor::_get_value(TileSetAtlasSource *p_tile_set_atla return tile_data->get_navigation_polygon(navigation_layer); } -void TileDataNavigationEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map<TileMapCell, Variant> p_previous_values, Variant p_new_value) { +void TileDataNavigationEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant 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); diff --git a/editor/plugins/tiles/tile_data_editors.h b/editor/plugins/tiles/tile_data_editors.h index 3ac9eacb05..f9b8948d0a 100644 --- a/editor/plugins/tiles/tile_data_editors.h +++ b/editor/plugins/tiles/tile_data_editors.h @@ -71,7 +71,7 @@ public: class DummyObject : public Object { GDCLASS(DummyObject, Object) private: - Map<String, Variant> properties; + HashMap<String, Variant> properties; protected: bool _set(const StringName &p_name, const Variant &p_value); @@ -108,8 +108,8 @@ private: DRAG_TYPE_PAN, }; DragType drag_type = DRAG_TYPE_NONE; - int drag_polygon_index; - int drag_point_index; + int drag_polygon_index = 0; + int drag_point_index = 0; Vector2 drag_last_pos; PackedVector2Array drag_old_polygon; @@ -132,9 +132,9 @@ private: Ref<Texture2D> background_texture; Rect2 background_region; Vector2 background_offset; - bool background_h_flip; - bool background_v_flip; - bool background_transpose; + bool background_h_flip = false; + bool background_v_flip = false; + bool background_transpose = false; Color background_modulate; Color polygon_color = Color(1.0, 0.0, 0.0); @@ -206,7 +206,7 @@ private: DragType drag_type = DRAG_TYPE_NONE; Vector2 drag_start_pos; Vector2 drag_last_pos; - Map<TileMapCell, Variant> drag_modified; + HashMap<TileMapCell, Variant, TileMapCell> drag_modified; Variant drag_painted_value; void _property_value_changed(StringName p_property, Variant p_value, StringName p_field); @@ -224,7 +224,7 @@ protected: virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile); virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value); virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile); - virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map<TileMapCell, Variant> p_previous_values, Variant p_new_value); + virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value); public: virtual Control *get_toolbar() override { return toolbar; }; @@ -276,7 +276,7 @@ private: virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override; virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) override; virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override; - virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map<TileMapCell, Variant> p_previous_values, Variant p_new_value) override; + virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) override; protected: UndoRedo *undo_redo = nullptr; @@ -301,7 +301,7 @@ class TileDataCollisionEditor : public TileDataDefaultEditor { // UI GenericTilePolygonEditor *polygon_editor = nullptr; DummyObject *dummy_object = memnew(DummyObject); - Map<StringName, EditorProperty *> property_editors; + HashMap<StringName, EditorProperty *> property_editors; void _property_value_changed(StringName p_property, Variant p_value, StringName p_field); void _property_selected(StringName p_path, int p_focusable); @@ -311,7 +311,7 @@ class TileDataCollisionEditor : public TileDataDefaultEditor { virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override; virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) override; virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override; - virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map<TileMapCell, Variant> p_previous_values, Variant p_new_value) override; + virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) override; protected: UndoRedo *undo_redo = nullptr; @@ -348,7 +348,7 @@ private: DragType drag_type = DRAG_TYPE_NONE; Vector2 drag_start_pos; Vector2 drag_last_pos; - Map<TileMapCell, Variant> drag_modified; + HashMap<TileMapCell, Variant, TileMapCell> drag_modified; Variant drag_painted_value; // UI @@ -396,7 +396,7 @@ private: virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override; virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) override; virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override; - virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map<TileMapCell, Variant> p_previous_values, Variant p_new_value) override; + virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) override; protected: UndoRedo *undo_redo = nullptr; diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index c1a95c11f6..77a3c07548 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -76,7 +76,7 @@ void TileMapEditorTilesPlugin::_update_toolbar() { picker_button->show(); erase_button->show(); tools_settings_vsep_2->show(); - random_tile_checkbox->show(); + random_tile_toggle->show(); scatter_label->show(); scatter_spinbox->show(); } else if (tool_buttons_group->get_pressed_button() == line_tool_button) { @@ -84,7 +84,7 @@ void TileMapEditorTilesPlugin::_update_toolbar() { picker_button->show(); erase_button->show(); tools_settings_vsep_2->show(); - random_tile_checkbox->show(); + random_tile_toggle->show(); scatter_label->show(); scatter_spinbox->show(); } else if (tool_buttons_group->get_pressed_button() == rect_tool_button) { @@ -92,7 +92,7 @@ void TileMapEditorTilesPlugin::_update_toolbar() { picker_button->show(); erase_button->show(); tools_settings_vsep_2->show(); - random_tile_checkbox->show(); + random_tile_toggle->show(); scatter_label->show(); scatter_spinbox->show(); } else if (tool_buttons_group->get_pressed_button() == bucket_tool_button) { @@ -101,7 +101,7 @@ void TileMapEditorTilesPlugin::_update_toolbar() { erase_button->show(); tools_settings_vsep_2->show(); bucket_contiguous_checkbox->show(); - random_tile_checkbox->show(); + random_tile_toggle->show(); scatter_label->show(); scatter_spinbox->show(); } @@ -443,7 +443,11 @@ void TileMapEditorTilesPlugin::_scenes_list_multi_selected(int p_index, bool p_s _update_selection_pattern_from_tileset_tiles_selection(); } -void TileMapEditorTilesPlugin::_scenes_list_nothing_selected() { +void TileMapEditorTilesPlugin::_scenes_list_lmb_empty_clicked(const Vector2 &p_pos, MouseButton p_mouse_button_index) { + if (p_mouse_button_index != MouseButton::LEFT) { + return; + } + scene_tiles_list->deselect_all(); tile_set_selection.clear(); tile_map_selection.clear(); @@ -461,6 +465,7 @@ void TileMapEditorTilesPlugin::_update_theme() { picker_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("ColorPick"), SNAME("EditorIcons"))); erase_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("Eraser"), SNAME("EditorIcons"))); + random_tile_toggle->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("RandomNumberGenerator"), SNAME("EditorIcons"))); missing_atlas_texture_icon = tiles_bottom_panel->get_theme_icon(SNAME("TileSet"), SNAME("EditorIcons")); } @@ -496,8 +501,8 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p if (!tile_map_selection.is_empty()) { tile_map_clipboard.instantiate(); TypedArray<Vector2i> coords_array; - for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) { - coords_array.push_back(E->get()); + for (const Vector2i &E : tile_map_selection) { + coords_array.push_back(E); } tile_map_clipboard = tile_map->get_pattern(tile_map_layer, coords_array); } @@ -506,9 +511,9 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p // Delete selected tiles. if (!tile_map_selection.is_empty()) { undo_redo->create_action(TTR("Delete tiles")); - for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) { - undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->get(), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); - undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->get(), tile_map->get_cell_source_id(tile_map_layer, E->get()), tile_map->get_cell_atlas_coords(tile_map_layer, E->get()), tile_map->get_cell_alternative_tile(tile_map_layer, E->get())); + for (const Vector2i &E : tile_map_selection) { + undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); + undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E, tile_map->get_cell_source_id(tile_map_layer, E), tile_map->get_cell_atlas_coords(tile_map_layer, E), tile_map->get_cell_alternative_tile(tile_map_layer, E)); } undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection()); tile_map_selection.clear(); @@ -537,9 +542,9 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p // Delete selected tiles. if (!tile_map_selection.is_empty()) { undo_redo->create_action(TTR("Delete tiles")); - for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) { - undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->get(), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); - undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->get(), tile_map->get_cell_source_id(tile_map_layer, E->get()), tile_map->get_cell_atlas_coords(tile_map_layer, E->get()), tile_map->get_cell_alternative_tile(tile_map_layer, E->get())); + for (const Vector2i &E : tile_map_selection) { + undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); + undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E, tile_map->get_cell_source_id(tile_map_layer, E), tile_map->get_cell_atlas_coords(tile_map_layer, E), tile_map->get_cell_alternative_tile(tile_map_layer, E)); } undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection()); tile_map_selection.clear(); @@ -557,7 +562,7 @@ 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, drag_erasing); + HashMap<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, drag_last_mouse_pos, mpos, drag_erasing); for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) { if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) { continue; @@ -574,7 +579,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p 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_contiguous_checkbox->is_pressed(), drag_erasing); + HashMap<Vector2i, TileMapCell> to_draw = _draw_bucket_fill(line[i], bucket_contiguous_checkbox->is_pressed(), drag_erasing); for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) { if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) { continue; @@ -623,8 +628,8 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p _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()) { - Vector2i coords = E->get(); + for (const Vector2i &E : tile_map_selection) { + Vector2i coords = E; drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords)); tile_map->set_cell(tile_map_layer, coords, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); } @@ -643,7 +648,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p drag_type = DRAG_TYPE_PAINT; drag_start_mouse_pos = mpos; drag_modified.clear(); - Map<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, mpos, mpos, drag_erasing); + HashMap<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, mpos, mpos, drag_erasing); for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) { if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) { continue; @@ -670,7 +675,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p 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_contiguous_checkbox->is_pressed(), drag_erasing); + HashMap<Vector2i, TileMapCell> to_draw = _draw_bucket_fill(line[i], bucket_contiguous_checkbox->is_pressed(), drag_erasing); for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) { if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) { continue; @@ -741,7 +746,7 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over // Handle the preview of the tiles to be placed. if ((tiles_bottom_panel->is_visible_in_tree() || patterns_bottom_panel->is_visible_in_tree()) && has_mouse) { // Only if the tilemap editor is opened and the viewport is hovered. - Map<Vector2i, TileMapCell> preview; + HashMap<Vector2i, TileMapCell> preview; Rect2i drawn_grid_rect; if (drag_type == DRAG_TYPE_PICK) { @@ -763,7 +768,7 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over // Draw the area being selected. Rect2i rect = Rect2i(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(drag_last_mouse_pos) - tile_map->world_to_map(drag_start_mouse_pos)).abs(); rect.size += Vector2i(1, 1); - Set<Vector2i> to_draw; + RBSet<Vector2i> to_draw; for (int x = rect.position.x; x < rect.get_end().x; x++) { for (int y = rect.position.y; y < rect.get_end().y; y++) { Vector2i coords = Vector2i(x, y); @@ -780,8 +785,8 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over if (!tile_map_selection.is_empty()) { top_left = tile_map_selection.front()->get(); } - for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) { - top_left = top_left.min(E->get()); + for (const Vector2i &E : tile_map_selection) { + top_left = top_left.min(E); } Vector2i offset = drag_start_mouse_pos - tile_map->map_to_world(top_left); offset = tile_map->world_to_map(drag_last_mouse_pos - offset) - tile_map->world_to_map(drag_start_mouse_pos - offset); @@ -827,7 +832,7 @@ 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)); + drawn_grid_rect = Rect2i(preview.begin()->key, Vector2i(1, 1)); for (const KeyValue<Vector2i, TileMapCell> &E : preview) { drawn_grid_rect.expand_to(E.key); } @@ -870,7 +875,7 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over Transform2D tile_xform; tile_xform.set_origin(tile_map->map_to_world(E.key)); tile_xform.set_scale(tile_set->get_tile_size()); - if (!(drag_erasing || erase_button->is_pressed()) && random_tile_checkbox->is_pressed()) { + if (!(drag_erasing || erase_button->is_pressed()) && random_tile_toggle->is_pressed()) { 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.value.source_id)) { @@ -981,15 +986,15 @@ TileMapCell TileMapEditorTilesPlugin::_pick_random_tile(Ref<TileMapPattern> p_pa return TileMapCell(); } -Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2 p_to_mouse_pos, bool p_erase) { +HashMap<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2 p_to_mouse_pos, bool p_erase) { TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); if (!tile_map) { - return Map<Vector2i, TileMapCell>(); + return HashMap<Vector2i, TileMapCell>(); } Ref<TileSet> tile_set = tile_map->get_tileset(); if (!tile_set.is_valid()) { - return Map<Vector2i, TileMapCell>(); + return HashMap<Vector2i, TileMapCell>(); } // Get or create the pattern. @@ -998,10 +1003,10 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_line(Vector2 p_start_ erase_pattern->set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); Ref<TileMapPattern> pattern = p_erase ? erase_pattern : selection_pattern; - Map<Vector2i, TileMapCell> output; + HashMap<Vector2i, TileMapCell> output; if (!pattern->is_empty()) { // Paint the tiles on the tile map. - if (!p_erase && random_tile_checkbox->is_pressed()) { + if (!p_erase && random_tile_toggle->is_pressed()) { // Paint a random tile. Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->world_to_map(p_from_mouse_pos), tile_map->world_to_map(p_to_mouse_pos)); for (int i = 0; i < line.size(); i++) { @@ -1030,15 +1035,15 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_line(Vector2 p_start_ return output; } -Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase) { +HashMap<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase) { TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); if (!tile_map) { - return Map<Vector2i, TileMapCell>(); + return HashMap<Vector2i, TileMapCell>(); } Ref<TileSet> tile_set = tile_map->get_tileset(); if (!tile_set.is_valid()) { - return Map<Vector2i, TileMapCell>(); + return HashMap<Vector2i, TileMapCell>(); } // Create the rect to draw. @@ -1051,7 +1056,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_rect(Vector2i p_start erase_pattern->set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); Ref<TileMapPattern> pattern = p_erase ? erase_pattern : selection_pattern; - Map<Vector2i, TileMapCell> err_output; + HashMap<Vector2i, TileMapCell> err_output; ERR_FAIL_COND_V(pattern->is_empty(), err_output); // Compute the offset to align things to the bottom or right. @@ -1059,9 +1064,9 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_rect(Vector2i p_start bool valigned_bottom = p_end_cell.y < p_start_cell.y; Vector2i offset = Vector2i(aligned_right ? -(pattern->get_size().x - (rect.get_size().x % pattern->get_size().x)) : 0, valigned_bottom ? -(pattern->get_size().y - (rect.get_size().y % pattern->get_size().y)) : 0); - Map<Vector2i, TileMapCell> output; + HashMap<Vector2i, TileMapCell> output; if (!pattern->is_empty()) { - if (!p_erase && random_tile_checkbox->is_pressed()) { + if (!p_erase && random_tile_toggle->is_pressed()) { // Paint a random tile. for (int x = 0; x < rect.size.x; x++) { for (int y = 0; y < rect.size.y; y++) { @@ -1089,21 +1094,21 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_rect(Vector2i p_start return output; } -Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase) { +HashMap<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase) { TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); if (!tile_map) { - return Map<Vector2i, TileMapCell>(); + return HashMap<Vector2i, TileMapCell>(); } if (tile_map_layer < 0) { - return Map<Vector2i, TileMapCell>(); + return HashMap<Vector2i, TileMapCell>(); } - Map<Vector2i, TileMapCell> output; + HashMap<Vector2i, TileMapCell> output; ERR_FAIL_INDEX_V(tile_map_layer, tile_map->get_layers_count(), output); Ref<TileSet> tile_set = tile_map->get_tileset(); if (!tile_set.is_valid()) { - return Map<Vector2i, TileMapCell>(); + return HashMap<Vector2i, TileMapCell>(); } // Get or create the pattern. @@ -1123,7 +1128,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i if (p_contiguous) { // Replace continuous tiles like the source. - Set<Vector2i> already_checked; + RBSet<Vector2i> already_checked; List<Vector2i> to_check; to_check.push_back(p_coords); while (!to_check.is_empty()) { @@ -1134,7 +1139,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i source_cell.get_atlas_coords() == tile_map->get_cell_atlas_coords(tile_map_layer, coords) && source_cell.alternative_tile == tile_map->get_cell_alternative_tile(tile_map_layer, coords) && (source_cell.source_id != TileSet::INVALID_SOURCE || boundaries.has_point(coords))) { - if (!p_erase && random_tile_checkbox->is_pressed()) { + if (!p_erase && random_tile_toggle->is_pressed()) { // Paint a random tile. output.insert(coords, _pick_random_tile(pattern)); } else { @@ -1180,7 +1185,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i source_cell.get_atlas_coords() == tile_map->get_cell_atlas_coords(tile_map_layer, coords) && source_cell.alternative_tile == tile_map->get_cell_alternative_tile(tile_map_layer, coords) && (source_cell.source_id != TileSet::INVALID_SOURCE || boundaries.has_point(coords))) { - if (!p_erase && random_tile_checkbox->is_pressed()) { + if (!p_erase && random_tile_toggle->is_pressed()) { // Paint a random tile. output.insert(coords, _pick_random_tile(pattern)); } else { @@ -1273,8 +1278,8 @@ void TileMapEditorTilesPlugin::_stop_dragging() { if (!tile_map_selection.is_empty()) { top_left = tile_map_selection.front()->get(); } - for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) { - top_left = top_left.min(E->get()); + for (const Vector2i &E : tile_map_selection) { + top_left = top_left.min(E); } // Get the offset from the mouse. @@ -1285,7 +1290,7 @@ void TileMapEditorTilesPlugin::_stop_dragging() { // Build the list of cells to undo. Vector2i coords; - Map<Vector2i, TileMapCell> cells_undo; + HashMap<Vector2i, TileMapCell> cells_undo; for (int i = 0; i < selection_used_cells.size(); i++) { coords = tile_map->map_pattern(top_left, selection_used_cells[i], selection_pattern); cells_undo[coords] = TileMapCell(drag_modified[coords].source_id, drag_modified[coords].get_atlas_coords(), drag_modified[coords].alternative_tile); @@ -1294,7 +1299,7 @@ void TileMapEditorTilesPlugin::_stop_dragging() { } // Build the list of cells to do. - Map<Vector2i, TileMapCell> cells_do; + HashMap<Vector2i, TileMapCell> cells_do; for (int i = 0; i < selection_used_cells.size(); i++) { coords = tile_map->map_pattern(top_left, selection_used_cells[i], selection_pattern); cells_do[coords] = TileMapCell(); @@ -1306,11 +1311,11 @@ void TileMapEditorTilesPlugin::_stop_dragging() { // Move the tiles. undo_redo->create_action(TTR("Move 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. @@ -1373,7 +1378,7 @@ void TileMapEditorTilesPlugin::_stop_dragging() { 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, drag_erasing); + HashMap<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, drag_start_mouse_pos, mpos, drag_erasing); undo_redo->create_action(TTR("Paint tiles")); for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) { if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) { @@ -1385,7 +1390,7 @@ void TileMapEditorTilesPlugin::_stop_dragging() { 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), drag_erasing); + HashMap<Vector2i, TileMapCell> to_draw = _draw_rect(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(mpos), drag_erasing); undo_redo->create_action(TTR("Paint tiles")); for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) { if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) { @@ -1471,7 +1476,7 @@ void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() { } // Selection if needed. - for (Set<TileMapCell>::Element *E = tile_set_selection.front(); E; E = E->next()) { + for (RBSet<TileMapCell>::Element *E = tile_set_selection.front(); E; E = E->next()) { const TileMapCell *selected = &(E->get()); if (!tile_set->has_source(selected->source_id) || !tile_set->get_source(selected->source_id)->has_tile(selected->get_atlas_coords()) || @@ -1495,7 +1500,7 @@ void TileMapEditorTilesPlugin::_fix_invalid_tiles_in_tile_map_selection() { return; } - Set<Vector2i> to_remove; + RBSet<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) { @@ -1507,6 +1512,11 @@ void TileMapEditorTilesPlugin::_fix_invalid_tiles_in_tile_map_selection() { tile_map_selection.erase(cell); } } +void TileMapEditorTilesPlugin::patterns_item_list_empty_clicked(const Vector2 &p_pos, MouseButton p_mouse_button_index) { + if (p_mouse_button_index == MouseButton::LEFT) { + _update_selection_pattern_from_tileset_pattern_selection(); + } +} void TileMapEditorTilesPlugin::_update_selection_pattern_from_tilemap_selection() { TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); @@ -1524,8 +1534,8 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tilemap_selection( selection_pattern.instantiate(); TypedArray<Vector2i> coords_array; - for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) { - coords_array.push_back(E->get()); + for (const Vector2i &E : tile_map_selection) { + coords_array.push_back(E); } selection_pattern = tile_map->get_pattern(tile_map_layer, coords_array); } @@ -1548,9 +1558,9 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_tiles_sele selection_pattern.instantiate(); // Group per source. - Map<int, List<const TileMapCell *>> per_source; - for (Set<TileMapCell>::Element *E = tile_set_selection.front(); E; E = E->next()) { - per_source[E->get().source_id].push_back(&(E->get())); + HashMap<int, List<const TileMapCell *>> per_source; + for (const TileMapCell &E : tile_set_selection) { + per_source[E.source_id].push_back(&(E)); } int vertical_offset = 0; @@ -1558,7 +1568,7 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_tiles_sele // Per source. List<const TileMapCell *> unorganized; Rect2i encompassing_rect_coords; - Map<Vector2i, const TileMapCell *> organized_pattern; + HashMap<Vector2i, const TileMapCell *> organized_pattern; TileSetSource *source = *tile_set->get_source(E_source.key); TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); @@ -1573,12 +1583,12 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_tiles_sele } // Compute the encompassing rect for the organized pattern. - Map<Vector2i, const TileMapCell *>::Element *E_cell = organized_pattern.front(); + HashMap<Vector2i, const TileMapCell *>::Iterator E_cell = organized_pattern.begin(); if (E_cell) { - encompassing_rect_coords = Rect2i(E_cell->key(), Vector2i(1, 1)); - for (; E_cell; E_cell = E_cell->next()) { - encompassing_rect_coords.expand_to(E_cell->key() + Vector2i(1, 1)); - encompassing_rect_coords.expand_to(E_cell->key()); + encompassing_rect_coords = Rect2i(E_cell->key, Vector2i(1, 1)); + for (; E_cell; ++E_cell) { + encompassing_rect_coords.expand_to(E_cell->key + Vector2i(1, 1)); + encompassing_rect_coords.expand_to(E_cell->key); } } } else { @@ -1670,14 +1680,14 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_draw() { // Draw the selection. 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); - 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) { - for (int frame = 0; frame < atlas->get_tile_animation_frames_count(E->get().get_atlas_coords()); frame++) { + for (const TileMapCell &E : tile_set_selection) { + if (E.source_id == source_id && E.alternative_tile == 0) { + for (int frame = 0; frame < atlas->get_tile_animation_frames_count(E.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); + tile_atlas_control->draw_rect(atlas->get_tile_texture_region(E.get_atlas_coords(), frame), color, false); } } } @@ -1701,7 +1711,7 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_draw() { Rect2i region = Rect2i(start_tile, end_tile - start_tile).abs(); region.size += Vector2i(1, 1); - Set<Vector2i> to_draw; + RBSet<Vector2i> to_draw; for (int x = region.position.x; x < region.get_end().x; x++) { for (int y = region.position.y; y < region.get_end().y; y++) { Vector2i tile = atlas->get_tile_at_coords(Vector2i(x, y)); @@ -1711,8 +1721,8 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_draw() { } } Color selection_rect_color = selection_color.lightened(0.2); - for (Set<Vector2i>::Element *E = to_draw.front(); E; E = E->next()) { - tile_atlas_control->draw_rect(atlas->get_tile_texture_region(E->get()), selection_rect_color, false); + for (const Vector2i &E : to_draw) { + tile_atlas_control->draw_rect(atlas->get_tile_texture_region(E), selection_rect_color, false); } } } @@ -1858,9 +1868,9 @@ void TileMapEditorTilesPlugin::_tile_alternatives_control_draw() { } // Draw the selection. - for (Set<TileMapCell>::Element *E = tile_set_selection.front(); E; E = E->next()) { - if (E->get().source_id == source_id && E->get().get_atlas_coords() != TileSetSource::INVALID_ATLAS_COORDS && E->get().alternative_tile > 0) { - Rect2i rect = tile_atlas_view->get_alternative_tile_rect(E->get().get_atlas_coords(), E->get().alternative_tile); + for (const TileMapCell &E : tile_set_selection) { + if (E.source_id == source_id && E.get_atlas_coords() != TileSetSource::INVALID_ATLAS_COORDS && E.alternative_tile > 0) { + Rect2i rect = tile_atlas_view->get_alternative_tile_rect(E.get_atlas_coords(), E.alternative_tile); if (rect != Rect2i()) { alternative_tiles_control->draw_rect(rect, Color(0.2, 0.2, 1.0), false); } @@ -1962,8 +1972,8 @@ void TileMapEditorTilesPlugin::_set_tile_map_selection(const TypedArray<Vector2i TypedArray<Vector2i> TileMapEditorTilesPlugin::_get_tile_map_selection() const { TypedArray<Vector2i> output; - for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) { - output.push_back(E->get()); + for (const Vector2i &E : tile_map_selection) { + output.push_back(E); } return output; } @@ -2018,7 +2028,6 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() { // --- Toolbar --- toolbar = memnew(HBoxContainer); - toolbar->set_h_size_flags(Control::SIZE_EXPAND_FILL); HBoxContainer *tilemap_tiles_tools_buttons = memnew(HBoxContainer); @@ -2103,11 +2112,12 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() { tools_settings->add_child(bucket_contiguous_checkbox); // Random tile checkbox. - random_tile_checkbox = memnew(CheckBox); - random_tile_checkbox->set_flat(true); - random_tile_checkbox->set_text(TTR("Place Random Tile")); - random_tile_checkbox->connect("toggled", callable_mp(this, &TileMapEditorTilesPlugin::_on_random_tile_checkbox_toggled)); - tools_settings->add_child(random_tile_checkbox); + random_tile_toggle = memnew(Button); + random_tile_toggle->set_flat(true); + random_tile_toggle->set_toggle_mode(true); + random_tile_toggle->set_tooltip(TTR("Place Random Tile")); + random_tile_toggle->connect("toggled", callable_mp(this, &TileMapEditorTilesPlugin::_on_random_tile_checkbox_toggled)); + tools_settings->add_child(random_tile_toggle); // Random tile scattering. scatter_label = memnew(Label); @@ -2219,7 +2229,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() { scene_tiles_list->set_drag_forwarding(this); scene_tiles_list->set_select_mode(ItemList::SELECT_MULTI); scene_tiles_list->connect("multi_selected", callable_mp(this, &TileMapEditorTilesPlugin::_scenes_list_multi_selected)); - scene_tiles_list->connect("nothing_selected", callable_mp(this, &TileMapEditorTilesPlugin::_scenes_list_nothing_selected)); + scene_tiles_list->connect("empty_clicked", callable_mp(this, &TileMapEditorTilesPlugin::_scenes_list_lmb_empty_clicked)); scene_tiles_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST); atlas_sources_split_container->add_child(scene_tiles_list); @@ -2249,7 +2259,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() { patterns_item_list->connect("gui_input", callable_mp(this, &TileMapEditorTilesPlugin::_patterns_item_list_gui_input)); patterns_item_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_pattern_selection).unbind(1)); patterns_item_list->connect("item_activated", callable_mp(this, &TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_pattern_selection)); - patterns_item_list->connect("nothing_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_pattern_selection)); + patterns_item_list->connect("empty_clicked", callable_mp(this, &TileMapEditorTilesPlugin::patterns_item_list_empty_clicked)); patterns_bottom_panel->add_child(patterns_item_list); patterns_help_label = memnew(Label); @@ -2310,161 +2320,138 @@ Vector<TileMapEditorPlugin::TabData> TileMapEditorTerrainsPlugin::get_tabs() con return tabs; } -Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const Map<Vector2i, TileSet::TerrainsPattern> &p_to_paint, int p_terrain_set) const { +HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrain_path_or_connect(const Vector<Vector2i> &p_to_paint, int p_terrain_set, int p_terrain, bool p_connect) const { TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); if (!tile_map) { - return Map<Vector2i, TileMapCell>(); + return HashMap<Vector2i, TileMapCell>(); } Ref<TileSet> tile_set = tile_map->get_tileset(); if (!tile_set.is_valid()) { - return Map<Vector2i, TileMapCell>(); + return HashMap<Vector2i, TileMapCell>(); } - Map<Vector2i, TileMapCell> output; - - // Add the constraints from the added tiles. - Set<TileMap::TerrainConstraint> added_tiles_constraints_set; - for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) { - Vector2i coords = E_to_paint.key; - TileSet::TerrainsPattern terrains_pattern = E_to_paint.value; - - Set<TileMap::TerrainConstraint> cell_constraints = tile_map->get_terrain_constraints_from_added_tile(coords, p_terrain_set, terrains_pattern); - for (Set<TileMap::TerrainConstraint>::Element *E = cell_constraints.front(); E; E = E->next()) { - added_tiles_constraints_set.insert(E->get()); - } - } - - // Build the list of potential tiles to replace. - Set<Vector2i> potential_to_replace; - for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &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)); - if (!p_to_paint.has(neighbor)) { - potential_to_replace.insert(neighbor); - } - } - } + HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output; + if (p_connect) { + terrain_fill_output = tile_map->terrain_fill_connect(tile_map_layer, p_to_paint, p_terrain_set, p_terrain, false); + } else { + terrain_fill_output = tile_map->terrain_fill_path(tile_map_layer, p_to_paint, p_terrain_set, p_terrain, false); } - // Set of tiles to replace - Set<Vector2i> to_replace; - - // Add the central tiles to the one to replace. - for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) { - to_replace.insert(E_to_paint.key); + // Make the painted path a set for faster lookups + HashSet<Vector2i> painted_set; + for (Vector2i coords : p_to_paint) { + painted_set.insert(coords); } - // Add the constraints from the surroundings of the modified areas. - Set<TileMap::TerrainConstraint> removed_cells_constraints_set; - bool to_replace_modified = true; - while (to_replace_modified) { - // Get the constraints from the removed cells. - removed_cells_constraints_set = tile_map->get_terrain_constraints_from_removed_cells_list(tile_map_layer, to_replace, p_terrain_set, false); - - // Filter the sources to make sure they are in the potential_to_replace. - Map<TileMap::TerrainConstraint, Set<Vector2i>> per_constraint_tiles; - for (Set<TileMap::TerrainConstraint>::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 (const KeyValue<Vector2i, TileSet::CellNeighbor> &E_source_tile_of_constraint : sources_of_constraint) { - if (potential_to_replace.has(E_source_tile_of_constraint.key)) { - per_constraint_tiles[E->get()].insert(E_source_tile_of_constraint.key); - } - } - } - - to_replace_modified = false; - for (Set<TileMap::TerrainConstraint>::Element *E = added_tiles_constraints_set.front(); E; E = E->next()) { - TileMap::TerrainConstraint c = E->get(); - // Check if we have a conflict in constraints. - if (removed_cells_constraints_set.has(c) && removed_cells_constraints_set.find(c)->get().get_terrain() != c.get_terrain()) { - // If we do, we search for a neighbor to remove. - if (per_constraint_tiles.has(c) && !per_constraint_tiles[c].is_empty()) { - // Remove it. - Vector2i to_add_to_remove = per_constraint_tiles[c].front()->get(); - potential_to_replace.erase(to_add_to_remove); - to_replace.insert(to_add_to_remove); - to_replace_modified = true; - for (KeyValue<TileMap::TerrainConstraint, Set<Vector2i>> &E_source_tiles_of_constraint : per_constraint_tiles) { - E_source_tiles_of_constraint.value.erase(to_add_to_remove); + HashMap<Vector2i, TileMapCell> output; + for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E : terrain_fill_output) { + if (painted_set.has(E.key)) { + // Paint a random tile with the correct terrain for the painted path. + output[E.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value); + } else { + // Avoids updating the painted path from the output if the new pattern is the same as before. + bool keep_old = false; + TileMapCell cell = tile_map->get_cell(tile_map_layer, E.key); + if (cell.source_id != TileSet::INVALID_SOURCE) { + TileSetSource *source = *tile_set->get_source(cell.source_id); + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + // Get tile data. + TileData *tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile); + if (tile_data && tile_data->get_terrains_pattern() == E.value) { + keep_old = true; } - break; } } + if (!keep_old) { + output[E.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value); + } } } + return output; +} - // Combine all constraints together. - Set<TileMap::TerrainConstraint> constraints = removed_cells_constraints_set; - for (Set<TileMap::TerrainConstraint>::Element *E = added_tiles_constraints_set.front(); E; E = E->next()) { - constraints.insert(E->get()); +HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrain_pattern(const Vector<Vector2i> &p_to_paint, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const { + TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); + if (!tile_map) { + return HashMap<Vector2i, TileMapCell>(); } - // Remove the central tiles from the ones to replace. - for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) { - to_replace.erase(E_to_paint.key); + Ref<TileSet> tile_set = tile_map->get_tileset(); + if (!tile_set.is_valid()) { + return HashMap<Vector2i, TileMapCell>(); } - // Run WFC to fill the holes with the constraints. - Map<Vector2i, TileSet::TerrainsPattern> wfc_output = tile_map->terrain_wave_function_collapse(to_replace, p_terrain_set, constraints); + HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output = tile_map->terrain_fill_pattern(tile_map_layer, p_to_paint, p_terrain_set, p_terrains_pattern, false); - // Actually paint the tiles. - for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) { - output[E_to_paint.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E_to_paint.value); + // Make the painted path a set for faster lookups + HashSet<Vector2i> painted_set; + for (Vector2i coords : p_to_paint) { + painted_set.insert(coords); } - // Use the WFC run for the output. - for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E : wfc_output) { - output[E.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value); + HashMap<Vector2i, TileMapCell> output; + for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E : terrain_fill_output) { + if (painted_set.has(E.key)) { + // Paint a random tile with the correct terrain for the painted path. + output[E.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value); + } else { + // Avoids updating the painted path from the output if the new pattern is the same as before. + TileMapCell cell = tile_map->get_cell(tile_map_layer, E.key); + if (cell.source_id != TileSet::INVALID_SOURCE) { + TileSetSource *source = *tile_set->get_source(cell.source_id); + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + // Get tile data. + TileData *tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile); + if (tile_data && !(tile_data->get_terrains_pattern() == E.value)) { + output[E.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value); + } + } + } + } } - return output; } -Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_line(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase) { +HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_line(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase) { TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); if (!tile_map) { - return Map<Vector2i, TileMapCell>(); + return HashMap<Vector2i, TileMapCell>(); } Ref<TileSet> tile_set = tile_map->get_tileset(); if (!tile_set.is_valid()) { - return Map<Vector2i, TileMapCell>(); + return HashMap<Vector2i, TileMapCell>(); } - TileSet::TerrainsPattern terrains_pattern; - if (p_erase) { - terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set); - } else { - terrains_pattern = selected_terrains_pattern; - } + if (selected_type == SELECTED_TYPE_CONNECT) { + return _draw_terrain_path_or_connect(TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell), selected_terrain_set, selected_terrain, true); + } else if (selected_type == SELECTED_TYPE_PATH) { + return _draw_terrain_path_or_connect(TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell), selected_terrain_set, selected_terrain, false); + } else { // SELECTED_TYPE_PATTERN + TileSet::TerrainsPattern terrains_pattern; + if (p_erase) { + terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set); + } else { + terrains_pattern = selected_terrains_pattern; + } - Vector<Vector2i> line = TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell); - Map<Vector2i, TileSet::TerrainsPattern> to_draw; - for (int i = 0; i < line.size(); i++) { - to_draw[line[i]] = terrains_pattern; + Vector<Vector2i> line = TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell); + return _draw_terrain_pattern(line, selected_terrain_set, terrains_pattern); } - return _draw_terrains(to_draw, selected_terrain_set); } -Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase) { +HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase) { TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); if (!tile_map) { - return Map<Vector2i, TileMapCell>(); + return HashMap<Vector2i, TileMapCell>(); } Ref<TileSet> tile_set = tile_map->get_tileset(); if (!tile_set.is_valid()) { - return Map<Vector2i, TileMapCell>(); - } - - TileSet::TerrainsPattern terrains_pattern; - if (p_erase) { - terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set); - } else { - terrains_pattern = selected_terrains_pattern; + return HashMap<Vector2i, TileMapCell>(); } Rect2i rect; @@ -2472,24 +2459,35 @@ Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_rect(Vector2i p_st rect.set_end(p_end_cell); rect = rect.abs(); - Map<Vector2i, TileSet::TerrainsPattern> to_draw; + Vector<Vector2i> to_draw; for (int x = rect.position.x; x <= rect.get_end().x; x++) { for (int y = rect.position.y; y <= rect.get_end().y; y++) { - to_draw[Vector2i(x, y)] = terrains_pattern; + to_draw.append(Vector2i(x, y)); + } + } + + if (selected_type == SELECTED_TYPE_CONNECT || selected_type == SELECTED_TYPE_PATH) { + return _draw_terrain_path_or_connect(to_draw, selected_terrain_set, selected_terrain, true); + } else { // SELECTED_TYPE_PATTERN + TileSet::TerrainsPattern terrains_pattern; + if (p_erase) { + terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set); + } else { + terrains_pattern = selected_terrains_pattern; } + return _draw_terrain_pattern(to_draw, selected_terrain_set, terrains_pattern); } - return _draw_terrains(to_draw, selected_terrain_set); } -Set<Vector2i> TileMapEditorTerrainsPlugin::_get_cells_for_bucket_fill(Vector2i p_coords, bool p_contiguous) { +RBSet<Vector2i> TileMapEditorTerrainsPlugin::_get_cells_for_bucket_fill(Vector2i p_coords, bool p_contiguous) { TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); if (!tile_map) { - return Set<Vector2i>(); + return RBSet<Vector2i>(); } Ref<TileSet> tile_set = tile_map->get_tileset(); if (!tile_set.is_valid()) { - return Set<Vector2i>(); + return RBSet<Vector2i>(); } TileMapCell source_cell = tile_map->get_cell(tile_map_layer, p_coords); @@ -2503,7 +2501,7 @@ Set<Vector2i> TileMapEditorTerrainsPlugin::_get_cells_for_bucket_fill(Vector2i p tile_data = atlas_source->get_tile_data(source_cell.get_atlas_coords(), source_cell.alternative_tile); } if (!tile_data) { - return Set<Vector2i>(); + return RBSet<Vector2i>(); } source_pattern = tile_data->get_terrains_pattern(); } @@ -2514,10 +2512,10 @@ Set<Vector2i> TileMapEditorTerrainsPlugin::_get_cells_for_bucket_fill(Vector2i p boundaries = tile_map->get_used_rect(); } - Set<Vector2i> output; + RBSet<Vector2i> output; if (p_contiguous) { // Replace continuous tiles like the source. - Set<Vector2i> already_checked; + RBSet<Vector2i> already_checked; List<Vector2i> to_check; to_check.push_back(p_coords); while (!to_check.is_empty()) { @@ -2592,31 +2590,34 @@ Set<Vector2i> TileMapEditorTerrainsPlugin::_get_cells_for_bucket_fill(Vector2i p return output; } -Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase) { +HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase) { TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); if (!tile_map) { - return Map<Vector2i, TileMapCell>(); + return HashMap<Vector2i, TileMapCell>(); } Ref<TileSet> tile_set = tile_map->get_tileset(); if (!tile_set.is_valid()) { - return Map<Vector2i, TileMapCell>(); + return HashMap<Vector2i, TileMapCell>(); } - TileSet::TerrainsPattern terrains_pattern; - if (p_erase) { - terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set); - } else { - terrains_pattern = selected_terrains_pattern; + RBSet<Vector2i> cells_to_draw = _get_cells_for_bucket_fill(p_coords, p_contiguous); + Vector<Vector2i> cells_to_draw_as_vector; + for (Vector2i cell : cells_to_draw) { + cells_to_draw_as_vector.append(cell); } - Set<Vector2i> cells_to_draw = _get_cells_for_bucket_fill(p_coords, p_contiguous); - Map<Vector2i, TileSet::TerrainsPattern> to_draw; - for (const Vector2i &coords : cells_to_draw) { - to_draw[coords] = terrains_pattern; + if (selected_type == SELECTED_TYPE_CONNECT || selected_type == SELECTED_TYPE_PATH) { + return _draw_terrain_path_or_connect(cells_to_draw_as_vector, selected_terrain_set, selected_terrain, true); + } else { // SELECTED_TYPE_PATTERN + TileSet::TerrainsPattern terrains_pattern; + if (p_erase) { + terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set); + } else { + terrains_pattern = selected_terrains_pattern; + } + return _draw_terrain_pattern(cells_to_draw_as_vector, selected_terrain_set, terrains_pattern); } - - return _draw_terrains(to_draw, selected_terrain_set); } void TileMapEditorTerrainsPlugin::_stop_dragging() { @@ -2685,11 +2686,13 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() { if (tree_item) { for (int i = 0; i < terrains_tile_list->get_item_count(); i++) { Dictionary metadata_dict = terrains_tile_list->get_item_metadata(i); - TileSet::TerrainsPattern in_meta_terrains_pattern(*tile_set, new_terrain_set); - in_meta_terrains_pattern.set_terrains_from_array(metadata_dict["terrains_pattern"]); - if (in_meta_terrains_pattern == terrains_pattern) { - terrains_tile_list->select(i); - break; + if (int(metadata_dict["type"]) == SELECTED_TYPE_PATTERN) { + TileSet::TerrainsPattern in_meta_terrains_pattern(*tile_set, new_terrain_set); + in_meta_terrains_pattern.from_array(metadata_dict["terrains_pattern"]); + if (in_meta_terrains_pattern == terrains_pattern) { + terrains_tile_list->select(i); + break; + } } } } else { @@ -2707,7 +2710,7 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() { undo_redo->commit_action(false); } break; case DRAG_TYPE_LINE: { - Map<Vector2i, TileMapCell> to_draw = _draw_line(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(mpos), drag_erasing); + HashMap<Vector2i, TileMapCell> to_draw = _draw_line(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(mpos), drag_erasing); undo_redo->create_action(TTR("Paint terrain")); for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) { if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) { @@ -2719,7 +2722,7 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() { 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), drag_erasing); + HashMap<Vector2i, TileMapCell> to_draw = _draw_rect(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(mpos), drag_erasing); undo_redo->create_action(TTR("Paint terrain")); for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) { if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) { @@ -2762,22 +2765,33 @@ void TileMapEditorTerrainsPlugin::_update_selection() { } // Get the selected terrain. - selected_terrains_pattern = TileSet::TerrainsPattern(); selected_terrain_set = -1; + selected_terrains_pattern = TileSet::TerrainsPattern(); TreeItem *selected_tree_item = terrains_tree->get_selected(); if (selected_tree_item && selected_tree_item->get_metadata(0)) { Dictionary metadata_dict = selected_tree_item->get_metadata(0); // Selected terrain selected_terrain_set = metadata_dict["terrain_set"]; + selected_terrain = metadata_dict["terrain_id"]; - // Selected tile + // Selected mode/terrain pattern if (erase_button->is_pressed()) { + selected_type = SELECTED_TYPE_PATTERN; selected_terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set); } else if (terrains_tile_list->is_anything_selected()) { metadata_dict = terrains_tile_list->get_item_metadata(terrains_tile_list->get_selected_items()[0]); - selected_terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set); - selected_terrains_pattern.set_terrains_from_array(metadata_dict["terrains_pattern"]); + if (int(metadata_dict["type"]) == SELECTED_TYPE_CONNECT) { + selected_type = SELECTED_TYPE_CONNECT; + } else if (int(metadata_dict["type"]) == SELECTED_TYPE_PATH) { + selected_type = SELECTED_TYPE_PATH; + } else if (int(metadata_dict["type"]) == SELECTED_TYPE_PATTERN) { + selected_type = SELECTED_TYPE_PATTERN; + selected_terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set); + selected_terrains_pattern.from_array(metadata_dict["terrains_pattern"]); + } else { + ERR_FAIL(); + } } } } @@ -2818,7 +2832,7 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent> switch (drag_type) { case DRAG_TYPE_PAINT: { if (selected_terrain_set >= 0) { - Map<Vector2i, TileMapCell> to_draw = _draw_line(tile_map->world_to_map(drag_last_mouse_pos), tile_map->world_to_map(mpos), drag_erasing); + HashMap<Vector2i, TileMapCell> to_draw = _draw_line(tile_map->world_to_map(drag_last_mouse_pos), tile_map->world_to_map(mpos), drag_erasing); for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) { if (!drag_modified.has(E.key)) { drag_modified[E.key] = tile_map->get_cell(tile_map_layer, E.key); @@ -2854,7 +2868,7 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent> } else { // Paint otherwise. if (tool_buttons_group->get_pressed_button() == paint_tool_button && !Input::get_singleton()->is_key_pressed(Key::CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT)) { - if (selected_terrain_set < 0 || !selected_terrains_pattern.is_valid()) { + if (selected_terrain_set < 0 || selected_terrain < 0 || (selected_type == SELECTED_TYPE_PATTERN && !selected_terrains_pattern.is_valid())) { return true; } @@ -2863,27 +2877,27 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent> drag_modified.clear(); Vector2i cell = tile_map->world_to_map(mpos); - Map<Vector2i, TileMapCell> to_draw = _draw_line(cell, cell, drag_erasing); + HashMap<Vector2i, TileMapCell> to_draw = _draw_line(cell, cell, drag_erasing); 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); } } else if (tool_buttons_group->get_pressed_button() == line_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(Key::SHIFT) && !Input::get_singleton()->is_key_pressed(Key::CTRL))) { - if (selected_terrain_set < 0 || !selected_terrains_pattern.is_valid()) { + if (selected_terrain_set < 0 || selected_terrain < 0 || (selected_type == SELECTED_TYPE_PATTERN && !selected_terrains_pattern.is_valid())) { return true; } drag_type = DRAG_TYPE_LINE; drag_start_mouse_pos = mpos; drag_modified.clear(); } else if (tool_buttons_group->get_pressed_button() == rect_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(Key::SHIFT) && Input::get_singleton()->is_key_pressed(Key::CTRL))) { - if (selected_terrain_set < 0 || !selected_terrains_pattern.is_valid()) { + if (selected_terrain_set < 0 || selected_terrain < 0 || (selected_type == SELECTED_TYPE_PATTERN && !selected_terrains_pattern.is_valid())) { return true; } drag_type = DRAG_TYPE_RECT; drag_start_mouse_pos = mpos; drag_modified.clear(); } else if (tool_buttons_group->get_pressed_button() == bucket_tool_button) { - if (selected_terrain_set < 0 || !selected_terrains_pattern.is_valid()) { + if (selected_terrain_set < 0 || selected_terrain < 0 || (selected_type == SELECTED_TYPE_PATTERN && !selected_terrains_pattern.is_valid())) { return true; } drag_type = DRAG_TYPE_BUCKET; @@ -2892,7 +2906,7 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent> 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_contiguous_checkbox->is_pressed(), drag_erasing); + HashMap<Vector2i, TileMapCell> to_draw = _draw_bucket_fill(line[i], bucket_contiguous_checkbox->is_pressed(), drag_erasing); for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) { if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) { continue; @@ -2948,7 +2962,7 @@ void TileMapEditorTerrainsPlugin::forward_canvas_draw_over_viewport(Control *p_o // Handle the preview of the tiles to be placed. if (main_vbox_container->is_visible_in_tree() && has_mouse) { // Only if the tilemap editor is opened and the viewport is hovered. - Set<Vector2i> preview; + RBSet<Vector2i> preview; Rect2i drawn_grid_rect; if (drag_type == DRAG_TYPE_PICK) { @@ -2985,7 +2999,7 @@ void TileMapEditorTerrainsPlugin::forward_canvas_draw_over_viewport(Control *p_o rect.set_end(tile_map->world_to_map(drag_last_mouse_pos)); rect = rect.abs(); - Map<Vector2i, TileSet::TerrainsPattern> to_draw; + HashMap<Vector2i, TileSet::TerrainsPattern> to_draw; for (int x = rect.position.x; x <= rect.get_end().x; x++) { for (int y = rect.position.y; y <= rect.get_end().y; y++) { preview.insert(Vector2i(x, y)); @@ -3094,11 +3108,18 @@ void TileMapEditorTerrainsPlugin::_update_terrains_cache() { cell.alternative_tile = alternative_id; TileSet::TerrainsPattern terrains_pattern = tile_data->get_terrains_pattern(); + + // Terrain center bit + int terrain = terrains_pattern.get_terrain(); + if (terrain >= 0 && terrain < (int)per_terrain_terrains_patterns[terrain_set].size()) { + per_terrain_terrains_patterns[terrain_set][terrain].insert(terrains_pattern); + } + // Terrain bits. 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)) { - int terrain = terrains_pattern.get_terrain(bit); + if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) { + terrain = terrains_pattern.get_terrain_peering_bit(bit); if (terrain >= 0 && terrain < (int)per_terrain_terrains_patterns[terrain_set].size()) { per_terrain_terrains_patterns[terrain_set][terrain].insert(terrains_pattern); } @@ -3180,25 +3201,38 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() { ERR_FAIL_INDEX(selected_terrain_set, tile_set->get_terrain_sets_count()); ERR_FAIL_INDEX(selected_terrain_id, tile_set->get_terrains_count(selected_terrain_set)); + // Add the two first generic modes + int item_index = terrains_tile_list->add_icon_item(main_vbox_container->get_theme_icon(SNAME("TerrainConnect"), SNAME("EditorIcons"))); + terrains_tile_list->set_item_tooltip(item_index, TTR("Connect mode: paints a terrain, then connects it with the surrounding tiles with the same terrain.")); + Dictionary list_metadata_dict; + list_metadata_dict["type"] = SELECTED_TYPE_CONNECT; + terrains_tile_list->set_item_metadata(item_index, list_metadata_dict); + + item_index = terrains_tile_list->add_icon_item(main_vbox_container->get_theme_icon(SNAME("TerrainPath"), SNAME("EditorIcons"))); + terrains_tile_list->set_item_tooltip(item_index, TTR("Path mode: paints a terrain, thens connects it to the previous tile painted withing the same stroke.")); + list_metadata_dict = Dictionary(); + list_metadata_dict["type"] = SELECTED_TYPE_PATH; + terrains_tile_list->set_item_metadata(item_index, list_metadata_dict); + // Sort the items in a map by the number of corresponding terrains. - Map<int, Set<TileSet::TerrainsPattern>> sorted; + RBMap<int, RBSet<TileSet::TerrainsPattern>> sorted; - for (Set<TileSet::TerrainsPattern>::Element *E = per_terrain_terrains_patterns[selected_terrain_set][selected_terrain_id].front(); E; E = E->next()) { + for (const TileSet::TerrainsPattern &E : per_terrain_terrains_patterns[selected_terrain_set][selected_terrain_id]) { // Count the number of matching sides/terrains. int 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(selected_terrain_set, bit) && E->get().get_terrain(bit) == selected_terrain_id) { + if (tile_set->is_valid_terrain_peering_bit(selected_terrain_set, bit) && E.get_terrain_peering_bit(bit) == selected_terrain_id) { count++; } } - sorted[count].insert(E->get()); + sorted[count].insert(E); } - for (Map<int, Set<TileSet::TerrainsPattern>>::Element *E_set = sorted.back(); E_set; E_set = E_set->prev()) { - for (Set<TileSet::TerrainsPattern>::Element *E = E_set->get().front(); E; E = E->next()) { - TileSet::TerrainsPattern terrains_pattern = E->get(); + for (RBMap<int, RBSet<TileSet::TerrainsPattern>>::Element *E_set = sorted.back(); E_set; E_set = E_set->prev()) { + for (const TileSet::TerrainsPattern &E : E_set->get()) { + TileSet::TerrainsPattern terrains_pattern = E; // Get the icon. Ref<Texture2D> icon; @@ -3230,12 +3264,13 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() { } // Create the ItemList's item. - int item_index = terrains_tile_list->add_item(""); + item_index = terrains_tile_list->add_item(""); terrains_tile_list->set_item_icon(item_index, icon); terrains_tile_list->set_item_icon_region(item_index, region); terrains_tile_list->set_item_icon_transposed(item_index, transpose); - Dictionary list_metadata_dict; - list_metadata_dict["terrains_pattern"] = terrains_pattern.get_terrains_as_array(); + list_metadata_dict = Dictionary(); + list_metadata_dict["type"] = SELECTED_TYPE_PATTERN; + list_metadata_dict["terrains_pattern"] = terrains_pattern.as_array(); terrains_tile_list->set_item_metadata(item_index, list_metadata_dict); } } @@ -3253,6 +3288,8 @@ void TileMapEditorTerrainsPlugin::_update_theme() { picker_button->set_icon(main_vbox_container->get_theme_icon(SNAME("ColorPick"), SNAME("EditorIcons"))); erase_button->set_icon(main_vbox_container->get_theme_icon(SNAME("Eraser"), SNAME("EditorIcons"))); + + _update_tiles_list(); } void TileMapEditorTerrainsPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_layer) { @@ -3292,7 +3329,7 @@ TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() { terrains_tile_list->set_h_size_flags(Control::SIZE_EXPAND_FILL); terrains_tile_list->set_max_columns(0); terrains_tile_list->set_same_column_width(true); - terrains_tile_list->set_fixed_icon_size(Size2(30, 30) * EDSCALE); + terrains_tile_list->set_fixed_icon_size(Size2(32, 32) * EDSCALE); terrains_tile_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST); tilemap_tab_terrains->add_child(terrains_tile_list); @@ -3385,7 +3422,7 @@ void TileMapEditor::_notification(int p_what) { advanced_menu_button->set_icon(get_theme_icon(SNAME("Tools"), SNAME("EditorIcons"))); toggle_grid_button->set_icon(get_theme_icon(SNAME("Grid"), SNAME("EditorIcons"))); toggle_grid_button->set_pressed(EditorSettings::get_singleton()->get("editors/tiles_editor/display_grid")); - toogle_highlight_selected_layer_button->set_icon(get_theme_icon(SNAME("TileMapHighlightSelected"), SNAME("EditorIcons"))); + toggle_highlight_selected_layer_button->set_icon(get_theme_icon(SNAME("TileMapHighlightSelected"), SNAME("EditorIcons"))); } break; case NOTIFICATION_INTERNAL_PROCESS: { @@ -3417,66 +3454,16 @@ void TileMapEditor::_notification(int p_what) { void TileMapEditor::_on_grid_toggled(bool p_pressed) { EditorSettings::get_singleton()->set("editors/tiles_editor/display_grid", p_pressed); + CanvasItemEditor::get_singleton()->update_viewport(); } -void TileMapEditor::_layers_selection_button_draw() { - if (!has_theme_icon(SNAME("arrow"), SNAME("OptionButton"))) { +void TileMapEditor::_layers_selection_item_selected(int p_index) { + TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); + if (!tile_map || tile_map->get_layers_count() <= 0) { return; } - RID ci = layers_selection_button->get_canvas_item(); - Ref<Texture2D> arrow = Control::get_theme_icon(SNAME("arrow"), SNAME("OptionButton")); - - Color clr = Color(1, 1, 1); - if (get_theme_constant(SNAME("modulate_arrow"))) { - switch (layers_selection_button->get_draw_mode()) { - case BaseButton::DRAW_PRESSED: - clr = get_theme_color(SNAME("font_pressed_color")); - break; - case BaseButton::DRAW_HOVER: - clr = get_theme_color(SNAME("font_hover_color")); - break; - case BaseButton::DRAW_DISABLED: - clr = get_theme_color(SNAME("font_disabled_color")); - break; - default: - if (layers_selection_button->has_focus()) { - clr = get_theme_color(SNAME("font_focus_color")); - } else { - clr = get_theme_color(SNAME("font_color")); - } - } - } - - Size2 size = layers_selection_button->get_size(); - - Point2 ofs; - if (is_layout_rtl()) { - ofs = Point2(get_theme_constant(SNAME("arrow_margin"), SNAME("OptionButton")), int(Math::abs((size.height - arrow->get_height()) / 2))); - } else { - ofs = Point2(size.width - arrow->get_width() - get_theme_constant(SNAME("arrow_margin"), SNAME("OptionButton")), int(Math::abs((size.height - arrow->get_height()) / 2))); - } - Rect2 dst_rect = Rect2(ofs, arrow->get_size()); - if (!layers_selection_button->is_pressed()) { - dst_rect.size = -dst_rect.size; - } - arrow->draw_rect(ci, dst_rect, false, clr); -} - -void TileMapEditor::_layers_selection_button_pressed() { - if (!layers_selection_popup->is_visible()) { - Size2 size = layers_selection_popup->get_contents_minimum_size(); - size.x = MAX(size.x, layers_selection_button->get_size().x); - layers_selection_popup->set_position(layers_selection_button->get_screen_position() - Size2(0, size.y * get_global_transform().get_scale().y)); - layers_selection_popup->set_size(size); - layers_selection_popup->popup(); - } else { - layers_selection_popup->hide(); - } -} - -void TileMapEditor::_layers_selection_id_pressed(int p_id) { - tile_map_layer = p_id; + tile_map_layer = p_index; _update_layers_selection(); } @@ -3659,8 +3646,6 @@ void TileMapEditor::_layers_select_next_or_previous(bool p_next) { } void TileMapEditor::_update_layers_selection() { - layers_selection_popup->clear(); - TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); if (!tile_map) { return; @@ -3687,32 +3672,23 @@ void TileMapEditor::_update_layers_selection() { } else { tile_map_layer = -1; } - tile_map->set_selected_layer(toogle_highlight_selected_layer_button->is_pressed() ? tile_map_layer : -1); + tile_map->set_selected_layer(toggle_highlight_selected_layer_button->is_pressed() ? tile_map_layer : -1); - // Build the list of layers. - for (int i = 0; i < tile_map->get_layers_count(); i++) { - String name = tile_map->get_layer_name(i); - layers_selection_popup->add_item(name.is_empty() ? vformat(TTR("Layer #%d"), i) : name, i); - layers_selection_popup->set_item_as_radio_checkable(i, true); - layers_selection_popup->set_item_disabled(i, !tile_map->is_layer_enabled(i)); - layers_selection_popup->set_item_checked(i, i == tile_map_layer); - } + layers_selection_button->clear(); + if (tile_map->get_layers_count() > 0) { + // Build the list of layers. + for (int i = 0; i < tile_map->get_layers_count(); i++) { + String name = tile_map->get_layer_name(i); + layers_selection_button->add_item(name.is_empty() ? vformat(TTR("Layer %d"), i) : name, i); + layers_selection_button->set_item_disabled(i, !tile_map->is_layer_enabled(i)); + } - // Update the button label. - if (tile_map_layer >= 0) { - layers_selection_button->set_text(layers_selection_popup->get_item_text(tile_map_layer)); + layers_selection_button->set_disabled(false); + layers_selection_button->select(tile_map_layer); } else { - layers_selection_button->set_text(TTR("Select a layer")); - } - - // Set button minimum width. - Size2 min_button_size = Size2(layers_selection_popup->get_contents_minimum_size().x, 0); - if (has_theme_icon(SNAME("arrow"), SNAME("OptionButton"))) { - Ref<Texture2D> arrow = Control::get_theme_icon(SNAME("arrow"), SNAME("OptionButton")); - min_button_size.x += arrow->get_size().x; + layers_selection_button->set_disabled(true); + layers_selection_button->set_text(TTR("No Layers")); } - layers_selection_button->set_custom_minimum_size(min_button_size); - layers_selection_button->update(); tabs_plugins[tabs_bar->get_current_tab()]->edit(tile_map_id, tile_map_layer); } @@ -3989,7 +3965,6 @@ TileMapEditor::TileMapEditor() { // TabBar. tabs_bar = memnew(TabBar); - tabs_bar->set_tab_alignment(TabBar::ALIGNMENT_CENTER); tabs_bar->set_clip_tabs(false); for (int plugin_index = 0; plugin_index < tile_map_editor_plugins.size(); plugin_index++) { Vector<TileMapEditorPlugin::TabData> tabs_vector = tile_map_editor_plugins[plugin_index]->get_tabs(); @@ -4018,31 +3993,23 @@ TileMapEditor::TileMapEditor() { } // Wide empty separation control. - Control *h_empty_space = memnew(Control); - h_empty_space->set_h_size_flags(SIZE_EXPAND_FILL); - tile_map_toolbar->add_child(h_empty_space); + tile_map_toolbar->add_spacer(); // Layer selector. - layers_selection_popup = memnew(PopupMenu); - layers_selection_popup->connect("id_pressed", callable_mp(this, &TileMapEditor::_layers_selection_id_pressed)); - layers_selection_popup->set_flag(Window::FLAG_POPUP, false); - - layers_selection_button = memnew(Button); - layers_selection_button->set_toggle_mode(true); - layers_selection_button->connect("draw", callable_mp(this, &TileMapEditor::_layers_selection_button_draw)); - layers_selection_button->connect("pressed", callable_mp(this, &TileMapEditor::_layers_selection_button_pressed)); - layers_selection_button->connect("hidden", callable_mp((Window *)layers_selection_popup, &Popup::hide)); - layers_selection_button->set_tooltip(TTR("Tile Map Layer")); - layers_selection_button->add_child(layers_selection_popup); + layers_selection_button = memnew(OptionButton); + layers_selection_button->set_custom_minimum_size(Size2(200, 0)); + layers_selection_button->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS); + layers_selection_button->set_tooltip(TTR("TileMap Layers")); + layers_selection_button->connect("item_selected", callable_mp(this, &TileMapEditor::_layers_selection_item_selected)); tile_map_toolbar->add_child(layers_selection_button); - toogle_highlight_selected_layer_button = memnew(Button); - toogle_highlight_selected_layer_button->set_flat(true); - toogle_highlight_selected_layer_button->set_toggle_mode(true); - toogle_highlight_selected_layer_button->set_pressed(true); - toogle_highlight_selected_layer_button->connect("pressed", callable_mp(this, &TileMapEditor::_update_layers_selection)); - toogle_highlight_selected_layer_button->set_tooltip(TTR("Highlight Selected TileMap Layer")); - tile_map_toolbar->add_child(toogle_highlight_selected_layer_button); + toggle_highlight_selected_layer_button = memnew(Button); + toggle_highlight_selected_layer_button->set_flat(true); + toggle_highlight_selected_layer_button->set_toggle_mode(true); + toggle_highlight_selected_layer_button->set_pressed(true); + toggle_highlight_selected_layer_button->connect("pressed", callable_mp(this, &TileMapEditor::_update_layers_selection)); + toggle_highlight_selected_layer_button->set_tooltip(TTR("Highlight Selected TileMap Layer")); + tile_map_toolbar->add_child(toggle_highlight_selected_layer_button); tile_map_toolbar->add_child(memnew(VSeparator)); diff --git a/editor/plugins/tiles/tile_map_editor.h b/editor/plugins/tiles/tile_map_editor.h index 3a0293f48f..ff586ebbfe 100644 --- a/editor/plugins/tiles/tile_map_editor.h +++ b/editor/plugins/tiles/tile_map_editor.h @@ -40,6 +40,7 @@ #include "scene/gui/check_box.h" #include "scene/gui/item_list.h" #include "scene/gui/menu_button.h" +#include "scene/gui/option_button.h" #include "scene/gui/separator.h" #include "scene/gui/spin_box.h" #include "scene/gui/split_container.h" @@ -92,7 +93,7 @@ private: VSeparator *tools_settings_vsep_2 = nullptr; CheckBox *bucket_contiguous_checkbox = nullptr; - CheckBox *random_tile_checkbox = nullptr; + Button *random_tile_toggle = nullptr; float scattering = 0.0; Label *scatter_label = nullptr; SpinBox *scatter_spinbox = nullptr; @@ -120,22 +121,22 @@ private: bool drag_erasing = false; Vector2 drag_start_mouse_pos; Vector2 drag_last_mouse_pos; - Map<Vector2i, TileMapCell> drag_modified; + HashMap<Vector2i, TileMapCell> drag_modified; TileMapCell _pick_random_tile(Ref<TileMapPattern> p_pattern); - Map<Vector2i, TileMapCell> _draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2 p_to_mouse_pos, bool p_erase); - Map<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase); - Map<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase); + HashMap<Vector2i, TileMapCell> _draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2 p_to_mouse_pos, bool p_erase); + HashMap<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase); + HashMap<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase); void _stop_dragging(); ///// Selection system. ///// - Set<Vector2i> tile_map_selection; + RBSet<Vector2i> tile_map_selection; Ref<TileMapPattern> tile_map_clipboard; Ref<TileMapPattern> selection_pattern; void _set_tile_map_selection(const TypedArray<Vector2i> &p_selection); TypedArray<Vector2i> _get_tile_map_selection() const; - Set<TileMapCell> tile_set_selection; + RBSet<TileMapCell> tile_set_selection; void _update_selection_pattern_from_tilemap_selection(); void _update_selection_pattern_from_tileset_tiles_selection(); @@ -144,6 +145,8 @@ private: void _update_fix_selected_and_hovered(); void _fix_invalid_tiles_in_tile_map_selection(); + void patterns_item_list_empty_clicked(const Vector2 &p_pos, MouseButton p_mouse_button_index); + ///// Bottom panel common //// void _tab_changed(); @@ -187,7 +190,7 @@ private: void _update_scenes_collection_view(); void _scene_thumbnail_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, Variant p_ud); void _scenes_list_multi_selected(int p_index, bool p_selected); - void _scenes_list_nothing_selected(); + void _scenes_list_lmb_empty_clicked(const Vector2 &p_pos, MouseButton p_mouse_button_index); ///// Bottom panel patterns //// VBoxContainer *patterns_bottom_panel = nullptr; @@ -263,17 +266,25 @@ private: bool drag_erasing = false; Vector2 drag_start_mouse_pos; Vector2 drag_last_mouse_pos; - Map<Vector2i, TileMapCell> drag_modified; + HashMap<Vector2i, TileMapCell> drag_modified; // Painting - Map<Vector2i, TileMapCell> _draw_terrains(const Map<Vector2i, TileSet::TerrainsPattern> &p_to_paint, int p_terrain_set) const; - Map<Vector2i, TileMapCell> _draw_line(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase); - Map<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase); - Set<Vector2i> _get_cells_for_bucket_fill(Vector2i p_coords, bool p_contiguous); - Map<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase); + HashMap<Vector2i, TileMapCell> _draw_terrain_path_or_connect(const Vector<Vector2i> &p_to_paint, int p_terrain_set, int p_terrain, bool p_connect) const; + HashMap<Vector2i, TileMapCell> _draw_terrain_pattern(const Vector<Vector2i> &p_to_paint, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const; + HashMap<Vector2i, TileMapCell> _draw_line(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase); + HashMap<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase); + RBSet<Vector2i> _get_cells_for_bucket_fill(Vector2i p_coords, bool p_contiguous); + HashMap<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase); void _stop_dragging(); + enum SelectedType { + SELECTED_TYPE_CONNECT = 0, + SELECTED_TYPE_PATH, + SELECTED_TYPE_PATTERN, + }; + SelectedType selected_type; int selected_terrain_set = -1; + int selected_terrain = -1; TileSet::TerrainsPattern selected_terrains_pattern; void _update_selection(); @@ -282,7 +293,7 @@ private: ItemList *terrains_tile_list = nullptr; // Cache. - LocalVector<LocalVector<Set<TileSet::TerrainsPattern>>> per_terrain_terrains_patterns; + LocalVector<LocalVector<RBSet<TileSet::TerrainsPattern>>> per_terrain_terrains_patterns; // Update functions. void _update_terrains_cache(); @@ -317,12 +328,9 @@ private: // Toolbar. HBoxContainer *tile_map_toolbar = nullptr; - PopupMenu *layers_selection_popup = nullptr; - Button *layers_selection_button = nullptr; - Button *toogle_highlight_selected_layer_button = nullptr; - void _layers_selection_button_draw(); - void _layers_selection_button_pressed(); - void _layers_selection_id_pressed(int p_id); + OptionButton *layers_selection_button = nullptr; + Button *toggle_highlight_selected_layer_button = nullptr; + void _layers_selection_item_selected(int p_index); Button *toggle_grid_button = nullptr; void _on_grid_toggled(bool p_pressed); diff --git a/editor/plugins/tiles/tile_proxies_manager_dialog.cpp b/editor/plugins/tiles/tile_proxies_manager_dialog.cpp index 62f3bd6356..3fe13fd341 100644 --- a/editor/plugins/tiles/tile_proxies_manager_dialog.cpp +++ b/editor/plugins/tiles/tile_proxies_manager_dialog.cpp @@ -33,7 +33,11 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" -void TileProxiesManagerDialog::_right_clicked(int p_item, Vector2 p_local_mouse_pos, Object *p_item_list) { +void TileProxiesManagerDialog::_right_clicked(int p_item, Vector2 p_local_mouse_pos, Object *p_item_list, MouseButton p_mouse_button_index) { + if (p_mouse_button_index != MouseButton::RIGHT) { + return; + } + ItemList *item_list = Object::cast_to<ItemList>(p_item_list); popup_menu->reset_size(); popup_menu->set_position(get_position() + item_list->get_global_mouse_position()); @@ -336,7 +340,7 @@ TileProxiesManagerDialog::TileProxiesManagerDialog() { source_level_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); source_level_list->set_select_mode(ItemList::SELECT_MULTI); source_level_list->set_allow_rmb_select(true); - source_level_list->connect("item_rmb_selected", callable_mp(this, &TileProxiesManagerDialog::_right_clicked), varray(source_level_list)); + source_level_list->connect("item_clicked", callable_mp(this, &TileProxiesManagerDialog::_right_clicked), varray(source_level_list)); vbox_container->add_child(source_level_list); Label *coords_level_label = memnew(Label); @@ -347,7 +351,7 @@ TileProxiesManagerDialog::TileProxiesManagerDialog() { coords_level_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); coords_level_list->set_select_mode(ItemList::SELECT_MULTI); coords_level_list->set_allow_rmb_select(true); - coords_level_list->connect("item_rmb_selected", callable_mp(this, &TileProxiesManagerDialog::_right_clicked), varray(coords_level_list)); + coords_level_list->connect("item_clicked", callable_mp(this, &TileProxiesManagerDialog::_right_clicked), varray(coords_level_list)); vbox_container->add_child(coords_level_list); Label *alternative_level_label = memnew(Label); @@ -358,7 +362,7 @@ TileProxiesManagerDialog::TileProxiesManagerDialog() { alternative_level_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); alternative_level_list->set_select_mode(ItemList::SELECT_MULTI); alternative_level_list->set_allow_rmb_select(true); - alternative_level_list->connect("item_rmb_selected", callable_mp(this, &TileProxiesManagerDialog::_right_clicked), varray(alternative_level_list)); + alternative_level_list->connect("item_clicked", callable_mp(this, &TileProxiesManagerDialog::_right_clicked), varray(alternative_level_list)); vbox_container->add_child(alternative_level_list); popup_menu = memnew(PopupMenu); diff --git a/editor/plugins/tiles/tile_proxies_manager_dialog.h b/editor/plugins/tiles/tile_proxies_manager_dialog.h index 00866544be..44de708898 100644 --- a/editor/plugins/tiles/tile_proxies_manager_dialog.h +++ b/editor/plugins/tiles/tile_proxies_manager_dialog.h @@ -61,7 +61,7 @@ private: EditorPropertyInteger *alternative_to_property_editor = nullptr; PopupMenu *popup_menu = nullptr; - void _right_clicked(int p_item, Vector2 p_local_mouse_pos, Object *p_item_list); + void _right_clicked(int p_item, Vector2 p_local_mouse_pos, Object *p_item_list, MouseButton p_mouse_button_index); void _menu_id_pressed(int p_id); void _delete_selected_bindings(); void _update_lists(); diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index 0c78a0f1c0..37ccc6ad45 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -97,9 +97,9 @@ bool TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::_get(const StringN void TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::STRING, "name", PROPERTY_HINT_NONE, "")); 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, "texture_region_size", PROPERTY_HINT_NONE, "")); + p_list->push_back(PropertyInfo(Variant::VECTOR2I, "margins", PROPERTY_HINT_NONE, "suffix:px")); + p_list->push_back(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE, "suffix:px")); + p_list->push_back(PropertyInfo(Variant::VECTOR2I, "texture_region_size", PROPERTY_HINT_NONE, "suffix:px")); p_list->push_back(PropertyInfo(Variant::BOOL, "use_texture_padding", PROPERTY_HINT_NONE, "")); } @@ -270,9 +270,9 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_set(const StringName &p_na // Other properties. bool any_valid = false; - for (Set<TileSelection>::Element *E = tiles.front(); E; E = E->next()) { - const Vector2i &coords = E->get().tile; - const int &alternative = E->get().alternative; + for (const TileSelection &E : tiles) { + const Vector2i &coords = E.tile; + const int &alternative = E.alternative; bool valid = false; TileData *tile_data = tile_set_atlas_source->get_tile_data(coords, alternative); @@ -354,11 +354,11 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_get(const StringName &p_na } } - for (Set<TileSelection>::Element *E = tiles.front(); E; E = E->next()) { + for (const TileSelection &E : tiles) { // Return the first tile with a property matching the name. // Note: It's a little bit annoying, but the behavior is the same the one in MultiNodeEdit. - const Vector2i &coords = E->get().tile; - const int &alternative = E->get().alternative; + const Vector2i &coords = E.tile; + const int &alternative = E.alternative; TileData *tile_data = tile_set_atlas_source->get_tile_data(coords, alternative); ERR_FAIL_COND_V(!tile_data, false); @@ -401,15 +401,15 @@ void TileSetAtlasSourceEditor::AtlasTileProxyObject::_get_property_list(List<Pro 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::VECTOR2I, "animation_separation", PROPERTY_HINT_NONE, "suffix:px")); 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)); + p_list->push_back(PropertyInfo(Variant::FLOAT, "animation_frame_0/duration", PROPERTY_HINT_NONE, "suffix:s", 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, "")); + p_list->push_back(PropertyInfo(Variant::FLOAT, vformat("animation_frame_%d/duration", i), PROPERTY_HINT_NONE, "suffix:s")); } } } @@ -426,12 +426,12 @@ void TileSetAtlasSourceEditor::AtlasTileProxyObject::_get_property_list(List<Pro int uses = 0; PropertyInfo property_info; }; - Map<PropertyId, PLData> usage; + RBMap<PropertyId, PLData> usage; List<PLData *> data_list; - for (Set<TileSelection>::Element *E = tiles.front(); E; E = E->next()) { - const Vector2i &coords = E->get().tile; - const int &alternative = E->get().alternative; + for (const TileSelection &E : tiles) { + const Vector2i &coords = E.tile; + const int &alternative = E.alternative; TileData *tile_data = tile_set_atlas_source->get_tile_data(coords, alternative); ERR_FAIL_COND(!tile_data); @@ -439,7 +439,7 @@ void TileSetAtlasSourceEditor::AtlasTileProxyObject::_get_property_list(List<Pro List<PropertyInfo> list; tile_data->get_property_list(&list); - Map<String, int> counts; // Counts the number of time a property appears (useful for groups that may appear more than once) + HashMap<String, int> counts; // Counts the number of time a property appears (useful for groups that may appear more than once) for (List<PropertyInfo>::Element *E_property = list.front(); E_property; E_property = E_property->next()) { const String &property_string = E_property->get().name; if (!tile_data->is_allowing_transform() && (property_string == "flip_h" || property_string == "flip_v" || property_string == "transpose")) { @@ -473,18 +473,18 @@ void TileSetAtlasSourceEditor::AtlasTileProxyObject::_get_property_list(List<Pro } } -void TileSetAtlasSourceEditor::AtlasTileProxyObject::edit(TileSetAtlasSource *p_tile_set_atlas_source, Set<TileSelection> p_tiles) { +void TileSetAtlasSourceEditor::AtlasTileProxyObject::edit(TileSetAtlasSource *p_tile_set_atlas_source, RBSet<TileSelection> p_tiles) { ERR_FAIL_COND(!p_tile_set_atlas_source); ERR_FAIL_COND(p_tiles.is_empty()); - for (Set<TileSelection>::Element *E = p_tiles.front(); E; E = E->next()) { - ERR_FAIL_COND(E->get().tile == TileSetSource::INVALID_ATLAS_COORDS); - ERR_FAIL_COND(E->get().alternative < 0); + for (const TileSelection &E : p_tiles) { + ERR_FAIL_COND(E.tile == TileSetSource::INVALID_ATLAS_COORDS); + ERR_FAIL_COND(E.alternative < 0); } // Disconnect to changes. - for (Set<TileSelection>::Element *E = tiles.front(); E; E = E->next()) { - const Vector2i &coords = E->get().tile; - const int &alternative = E->get().alternative; + for (const TileSelection &E : tiles) { + const Vector2i &coords = E.tile; + const int &alternative = E.alternative; if (tile_set_atlas_source && tile_set_atlas_source->has_tile(coords) && tile_set_atlas_source->has_alternative_tile(coords, alternative)) { TileData *tile_data = tile_set_atlas_source->get_tile_data(coords, alternative); @@ -495,12 +495,12 @@ void TileSetAtlasSourceEditor::AtlasTileProxyObject::edit(TileSetAtlasSource *p_ } tile_set_atlas_source = p_tile_set_atlas_source; - tiles = Set<TileSelection>(p_tiles); + tiles = RBSet<TileSelection>(p_tiles); // Connect to changes. - for (Set<TileSelection>::Element *E = p_tiles.front(); E; E = E->next()) { - const Vector2i &coords = E->get().tile; - const int &alternative = E->get().alternative; + for (const TileSelection &E : p_tiles) { + const Vector2i &coords = E.tile; + const int &alternative = E.alternative; if (tile_set_atlas_source->has_tile(coords) && tile_set_atlas_source->has_alternative_tile(coords, alternative)) { TileData *tile_data = tile_set_atlas_source->get_tile_data(coords, alternative); @@ -541,7 +541,7 @@ void TileSetAtlasSourceEditor::_update_source_inspector() { void TileSetAtlasSourceEditor::_update_fix_selected_and_hovered_tiles() { // Fix selected. - for (Set<TileSelection>::Element *E = selection.front(); E; E = E->next()) { + for (RBSet<TileSelection>::Element *E = selection.front(); E; E = E->next()) { TileSelection selected = E->get(); if (!tile_set_atlas_source->has_tile(selected.tile) || !tile_set_atlas_source->has_alternative_tile(selected.tile, selected.alternative)) { selection.erase(E); @@ -610,8 +610,8 @@ void TileSetAtlasSourceEditor::_update_tile_data_editors() { } // Theming. - tile_data_editors_tree->add_theme_constant_override("vseparation", 1); - tile_data_editors_tree->add_theme_constant_override("hseparation", 3); + tile_data_editors_tree->add_theme_constant_override("v_separation", 1); + tile_data_editors_tree->add_theme_constant_override("h_separation", 3); Color group_color = get_theme_color(SNAME("prop_category"), SNAME("Editor")); @@ -1313,9 +1313,9 @@ void TileSetAtlasSourceEditor::_end_dragging() { switch (drag_type) { case DRAG_TYPE_CREATE_TILES: undo_redo->create_action(TTR("Create tiles")); - for (Set<Vector2i>::Element *E = drag_modified_tiles.front(); E; E = E->next()) { - undo_redo->add_do_method(tile_set_atlas_source, "create_tile", E->get()); - undo_redo->add_undo_method(tile_set_atlas_source, "remove_tile", E->get()); + for (const Vector2i &E : drag_modified_tiles) { + undo_redo->add_do_method(tile_set_atlas_source, "create_tile", E); + undo_redo->add_undo_method(tile_set_atlas_source, "remove_tile", E); } undo_redo->commit_action(false); break; @@ -1328,10 +1328,10 @@ void TileSetAtlasSourceEditor::_end_dragging() { case DRAG_TYPE_REMOVE_TILES: { List<PropertyInfo> list; tile_set_atlas_source->get_property_list(&list); - Map<Vector2i, List<const PropertyInfo *>> per_tile = _group_properties_per_tiles(list, tile_set_atlas_source); + HashMap<Vector2i, List<const PropertyInfo *>> per_tile = _group_properties_per_tiles(list, tile_set_atlas_source); undo_redo->create_action(TTR("Remove tiles")); - for (Set<Vector2i>::Element *E = drag_modified_tiles.front(); E; E = E->next()) { - Vector2i coords = E->get(); + for (const Vector2i &E : drag_modified_tiles) { + Vector2i coords = E; undo_redo->add_do_method(tile_set_atlas_source, "remove_tile", coords); undo_redo->add_undo_method(tile_set_atlas_source, "create_tile", coords); if (per_tile.has(coords)) { @@ -1370,9 +1370,9 @@ void TileSetAtlasSourceEditor::_end_dragging() { area.set_end((area.get_end() + Vector2i(1, 1)).min(tile_set_atlas_source->get_atlas_grid_size())); List<PropertyInfo> list; tile_set_atlas_source->get_property_list(&list); - Map<Vector2i, List<const PropertyInfo *>> per_tile = _group_properties_per_tiles(list, tile_set_atlas_source); + HashMap<Vector2i, List<const PropertyInfo *>> per_tile = _group_properties_per_tiles(list, tile_set_atlas_source); - Set<Vector2i> to_delete; + RBSet<Vector2i> to_delete; for (int x = area.get_position().x; x < area.get_end().x; x++) { for (int y = area.get_position().y; y < area.get_end().y; y++) { Vector2i coords = tile_set_atlas_source->get_tile_at_coords(Vector2i(x, y)); @@ -1384,8 +1384,8 @@ void TileSetAtlasSourceEditor::_end_dragging() { undo_redo->create_action(TTR("Remove tiles")); undo_redo->add_do_method(this, "_set_selection_from_array", Array()); - for (Set<Vector2i>::Element *E = to_delete.front(); E; E = E->next()) { - Vector2i coords = E->get(); + for (const Vector2i &E : to_delete) { + Vector2i coords = E; undo_redo->add_do_method(tile_set_atlas_source, "remove_tile", coords); undo_redo->add_undo_method(tile_set_atlas_source, "create_tile", coords); if (per_tile.has(coords)) { @@ -1523,9 +1523,9 @@ void TileSetAtlasSourceEditor::_end_dragging() { tile_atlas_control->set_default_cursor_shape(CURSOR_ARROW); } -Map<Vector2i, List<const PropertyInfo *>> TileSetAtlasSourceEditor::_group_properties_per_tiles(const List<PropertyInfo> &r_list, const TileSetAtlasSource *p_atlas) { +HashMap<Vector2i, List<const PropertyInfo *>> TileSetAtlasSourceEditor::_group_properties_per_tiles(const List<PropertyInfo> &r_list, const TileSetAtlasSource *p_atlas) { // Group properties per tile. - Map<Vector2i, List<const PropertyInfo *>> per_tile; + HashMap<Vector2i, List<const PropertyInfo *>> per_tile; for (const List<PropertyInfo>::Element *E_property = r_list.front(); E_property; E_property = E_property->next()) { Vector<String> components = String(E_property->get().name).split("/", true, 1); if (components.size() >= 1) { @@ -1544,13 +1544,13 @@ void TileSetAtlasSourceEditor::_menu_option(int p_option) { case TILE_DELETE: { List<PropertyInfo> list; tile_set_atlas_source->get_property_list(&list); - Map<Vector2i, List<const PropertyInfo *>> per_tile = _group_properties_per_tiles(list, tile_set_atlas_source); + HashMap<Vector2i, List<const PropertyInfo *>> per_tile = _group_properties_per_tiles(list, tile_set_atlas_source); undo_redo->create_action(TTR("Remove tile")); // Remove tiles - Set<Vector2i> removed; - for (Set<TileSelection>::Element *E = selection.front(); E; E = E->next()) { - TileSelection selected = E->get(); + RBSet<Vector2i> removed; + for (const TileSelection &E : selection) { + TileSelection selected = E; if (selected.alternative == 0) { // Remove a tile. undo_redo->add_do_method(tile_set_atlas_source, "remove_tile", selected.tile); @@ -1569,8 +1569,8 @@ void TileSetAtlasSourceEditor::_menu_option(int p_option) { } // Remove alternatives - for (Set<TileSelection>::Element *E = selection.front(); E; E = E->next()) { - TileSelection selected = E->get(); + for (const TileSelection &E : selection) { + TileSelection selected = E; if (selected.alternative > 0 && !removed.has(selected.tile)) { // Remove an alternative tile. undo_redo->add_do_method(tile_set_atlas_source, "remove_alternative_tile", selected.tile, selected.alternative); @@ -1608,13 +1608,13 @@ void TileSetAtlasSourceEditor::_menu_option(int p_option) { case TILE_CREATE_ALTERNATIVE: { undo_redo->create_action(TTR("Create tile alternatives")); Array array; - for (Set<TileSelection>::Element *E = selection.front(); E; E = E->next()) { - if (E->get().alternative == 0) { - int next_id = tile_set_atlas_source->get_next_alternative_tile_id(E->get().tile); - undo_redo->add_do_method(tile_set_atlas_source, "create_alternative_tile", E->get().tile, next_id); - array.push_back(E->get().tile); + for (const TileSelection &E : selection) { + if (E.alternative == 0) { + int next_id = tile_set_atlas_source->get_next_alternative_tile_id(E.tile); + undo_redo->add_do_method(tile_set_atlas_source, "create_alternative_tile", E.tile, next_id); + array.push_back(E.tile); array.push_back(next_id); - undo_redo->add_undo_method(tile_set_atlas_source, "remove_alternative_tile", E->get().tile, next_id); + undo_redo->add_undo_method(tile_set_atlas_source, "remove_alternative_tile", E.tile, next_id); } } undo_redo->add_do_method(this, "_set_selection_from_array", array); @@ -1658,9 +1658,9 @@ void TileSetAtlasSourceEditor::_set_selection_from_array(Array p_selection) { Array TileSetAtlasSourceEditor::_get_selection_as_array() { Array output; - for (Set<TileSelection>::Element *E = selection.front(); E; E = E->next()) { - output.push_back(E->get().tile); - output.push_back(E->get().alternative); + for (const TileSelection &E : selection) { + output.push_back(E.tile); + output.push_back(E.alternative); } return output; } @@ -1672,8 +1672,8 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() { // Draw the selected tile. if (tools_button_group->get_pressed_button() == tool_select_button) { - for (Set<TileSelection>::Element *E = selection.front(); E; E = E->next()) { - TileSelection selected = E->get(); + for (const TileSelection &E : selection) { + TileSelection selected = E; if (selected.alternative == 0) { // Draw the rect. for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(selected.tile); frame++) { @@ -1695,8 +1695,8 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() { 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) }; + const Vector2i coords[] = { Vector2i(0, 0), Vector2i(1, 0), Vector2i(1, 1), Vector2i(0, 1) }; + const 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); @@ -1722,9 +1722,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()) { - 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); + for (const Vector2i &E : drag_modified_tiles) { + for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(E); frame++) { + tile_atlas_control->draw_rect(tile_set_atlas_source->get_tile_texture_region(E, 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) { @@ -1739,7 +1739,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() { color = selection_color.lightened(0.2); } - Set<Vector2i> to_paint; + RBSet<Vector2i> to_paint; for (int x = area.get_position().x; x < area.get_end().x; x++) { for (int y = area.get_position().y; y < area.get_end().y; y++) { Vector2i coords = tile_set_atlas_source->get_tile_at_coords(Vector2i(x, y)); @@ -1749,8 +1749,8 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() { } } - for (Set<Vector2i>::Element *E = to_paint.front(); E; E = E->next()) { - Vector2i coords = E->get(); + for (const Vector2i &E : to_paint) { + Vector2i coords = E; tile_atlas_control->draw_rect(tile_set_atlas_source->get_tile_texture_region(coords), color, false); } } else if (drag_type == DRAG_TYPE_CREATE_TILES_USING_RECT) { @@ -1837,19 +1837,19 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_unscaled_draw() { // Draw the selection on top of other. if (tools_button_group->get_pressed_button() == tool_select_button) { - for (Set<TileSelection>::Element *E = selection.front(); E; E = E->next()) { - if (E->get().alternative != 0) { + for (const TileSelection &E : selection) { + if (E.alternative != 0) { continue; } - Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(E->get().tile); - Vector2i position = texture_region.get_center() + tile_set_atlas_source->get_tile_effective_texture_offset(E->get().tile, 0); + Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(E.tile); + Vector2i position = texture_region.get_center() + tile_set_atlas_source->get_tile_effective_texture_offset(E.tile, 0); Transform2D xform = tile_atlas_control->get_parent_control()->get_transform(); xform.translate(position); TileMapCell cell; cell.source_id = tile_set_atlas_source_id; - cell.set_atlas_coords(E->get().tile); + cell.set_atlas_coords(E.tile); cell.alternative_tile = 0; current_tile_data_editor->draw_over_tile(tile_atlas_control_unscaled, xform, cell, true); } @@ -1962,8 +1962,8 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_draw() { } // Draw selected tile. - for (Set<TileSelection>::Element *E = selection.front(); E; E = E->next()) { - TileSelection selected = E->get(); + for (const TileSelection &E : selection) { + TileSelection selected = E; if (selected.alternative >= 1) { Rect2i rect = tile_atlas_view->get_alternative_tile_rect(selected.tile, selected.alternative); if (rect != Rect2i()) { @@ -2005,11 +2005,11 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_unscaled_draw() { // Draw the selection on top of other. if (tools_button_group->get_pressed_button() == tool_select_button) { - for (Set<TileSelection>::Element *E = selection.front(); E; E = E->next()) { - if (E->get().alternative == 0) { + for (const TileSelection &E : selection) { + if (E.alternative == 0) { continue; } - Rect2i rect = tile_atlas_view->get_alternative_tile_rect(E->get().tile, E->get().alternative); + Rect2i rect = tile_atlas_view->get_alternative_tile_rect(E.tile, E.alternative); Vector2 position = rect.get_center(); Transform2D xform = alternative_tiles_control->get_parent_control()->get_transform(); @@ -2017,8 +2017,8 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_unscaled_draw() { TileMapCell cell; cell.source_id = tile_set_atlas_source_id; - cell.set_atlas_coords(E->get().tile); - cell.alternative_tile = E->get().alternative; + cell.set_atlas_coords(E.tile); + cell.alternative_tile = E.alternative; current_tile_data_editor->draw_over_tile(alternative_tiles_control_unscaled, xform, cell, true); } } @@ -2071,9 +2071,10 @@ void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo } } else if (p_property == "terrain_set") { int current_terrain_set = tile_data_proxy->get("terrain_set"); + ADD_UNDO(tile_data_proxy, "terrain"); for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); - if (tile_set->is_valid_peering_bit_terrain(current_terrain_set, bit)) { + if (tile_set->is_valid_terrain_peering_bit(current_terrain_set, bit)) { ADD_UNDO(tile_data_proxy, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i])); } } @@ -2222,7 +2223,7 @@ void TileSetAtlasSourceEditor::_auto_remove_tiles() { List<PropertyInfo> list; tile_set_atlas_source->get_property_list(&list); - Map<Vector2i, List<const PropertyInfo *>> per_tile = _group_properties_per_tiles(list, tile_set_atlas_source); + HashMap<Vector2i, List<const PropertyInfo *>> per_tile = _group_properties_per_tiles(list, tile_set_atlas_source); for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) { Vector2i coords = tile_set_atlas_source->get_tile_id(i); @@ -2334,6 +2335,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { // Tile inspector. tile_inspector_label = memnew(Label); tile_inspector_label->set_text(TTR("Tile Properties:")); + tile_inspector_label->set_theme_type_variation("HeaderSmall"); middle_vbox_container->add_child(tile_inspector_label); tile_proxy_object = memnew(AtlasTileProxyObject(this)); @@ -2349,7 +2351,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { tile_inspector_no_tile_selected_label = memnew(Label); tile_inspector_no_tile_selected_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); - tile_inspector_no_tile_selected_label->set_text(TTR("No tile selected.")); + tile_inspector_no_tile_selected_label->set_text(TTR("No tiles selected.")); middle_vbox_container->add_child(tile_inspector_no_tile_selected_label); // Property values palette. @@ -2357,6 +2359,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { tile_data_editors_label = memnew(Label); tile_data_editors_label->set_text(TTR("Paint Properties:")); + tile_data_editors_label->set_theme_type_variation("HeaderSmall"); middle_vbox_container->add_child(tile_data_editors_label); tile_data_editor_dropdown_button = memnew(Button); @@ -2380,6 +2383,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { // Atlas source inspector. atlas_source_inspector_label = memnew(Label); atlas_source_inspector_label->set_text(TTR("Atlas Properties:")); + atlas_source_inspector_label->set_theme_type_variation("HeaderSmall"); middle_vbox_container->add_child(atlas_source_inspector_label); atlas_source_proxy_object = memnew(TileSetAtlasSourceProxyObject()); diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h index b26b7edbfa..738fe1044d 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.h +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h @@ -88,7 +88,7 @@ public: TileSetAtlasSourceEditor *tiles_set_atlas_source_editor = nullptr; TileSetAtlasSource *tile_set_atlas_source = nullptr; - Set<TileSelection> tiles = Set<TileSelection>(); + RBSet<TileSelection> tiles = RBSet<TileSelection>(); protected: bool _set(const StringName &p_name, const Variant &p_value); @@ -99,10 +99,10 @@ public: public: TileSetAtlasSource *get_edited_tile_set_atlas_source() const { return tile_set_atlas_source; }; - Set<TileSelection> get_edited_tiles() const { return tiles; }; + RBSet<TileSelection> get_edited_tiles() const { return tiles; }; // Update the proxyed object. - void edit(TileSetAtlasSource *p_tile_set_atlas_source, Set<TileSelection> p_tiles = Set<TileSelection>()); + void edit(TileSetAtlasSource *p_tile_set_atlas_source, RBSet<TileSelection> p_tiles = RBSet<TileSelection>()); AtlasTileProxyObject(TileSetAtlasSourceEditor *p_tiles_set_atlas_source_editor) { tiles_set_atlas_source_editor = p_tiles_set_atlas_source_editor; @@ -130,7 +130,7 @@ private: // -- Tile data editors -- String current_property; Control *current_tile_data_editor_toolbar = nullptr; - Map<String, TileDataEditor *> tile_data_editors; + HashMap<String, TileDataEditor *> tile_data_editors; TileDataEditor *current_tile_data_editor = nullptr; void _tile_data_editors_tree_selected(); @@ -182,10 +182,10 @@ private: Vector2i drag_current_tile; Rect2i drag_start_tile_shape; - Set<Vector2i> drag_modified_tiles; + RBSet<Vector2i> drag_modified_tiles; void _end_dragging(); - Map<Vector2i, List<const PropertyInfo *>> _group_properties_per_tiles(const List<PropertyInfo> &r_list, const TileSetAtlasSource *p_atlas); + HashMap<Vector2i, List<const PropertyInfo *>> _group_properties_per_tiles(const List<PropertyInfo> &r_list, const TileSetAtlasSource *p_atlas); // Popup functions. enum MenuOptions { @@ -215,7 +215,7 @@ private: MenuButton *tool_advanced_menu_buttom = nullptr; // Selection. - Set<TileSelection> selection; + RBSet<TileSelection> selection; void _set_selection_from_array(Array p_selection); Array _get_selection_as_array(); diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp index fb4a563992..de373e121b 100644 --- a/editor/plugins/tiles/tile_set_editor.cpp +++ b/editor/plugins/tiles/tile_set_editor.cpp @@ -505,7 +505,7 @@ void TileSetEditor::_move_tile_set_array_element(Object *p_undo_redo, Object *p_ 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)) { + if (tile_data->is_valid_terrain_peering_bit(bit)) { ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[l])); } } @@ -513,7 +513,7 @@ void TileSetEditor::_move_tile_set_array_element(Object *p_undo_redo, Object *p_ } 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)) { + if (tile_data->is_valid_terrain_peering_bit(bit)) { ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[terrain_index])); } } @@ -607,9 +607,10 @@ void TileSetEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p 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"); + ADD_UNDO(tile_data, "terrain"); 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)) { + if (tile_data->is_valid_terrain_peering_bit(bit)) { ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[l])); } } diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp index 21ebcbd655..9a4b14616f 100644 --- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp @@ -394,13 +394,12 @@ void TileSetScenesCollectionSourceEditor::_drop_data_fw(const Point2 &p_point, c if (p_from == scene_tiles_list) { // Handle dropping a texture in the list of atlas resources. - int scene_id = -1; Dictionary d = p_data; Vector<String> files = d["files"]; for (int i = 0; i < files.size(); i++) { Ref<PackedScene> resource = ResourceLoader::load(files[i]); if (resource.is_valid()) { - scene_id = tile_set_scenes_collection_source->get_next_scene_tile_id(); + int scene_id = tile_set_scenes_collection_source->get_next_scene_tile_id(); undo_redo->create_action(TTR("Add a Scene Tile")); undo_redo->add_do_method(tile_set_scenes_collection_source, "create_scene_tile", resource, scene_id); undo_redo->add_undo_method(tile_set_scenes_collection_source, "remove_scene_tile", scene_id); diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 1e7648bc43..8c72a886ea 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -119,7 +119,7 @@ void VisualShaderGraphPlugin::register_shader(VisualShader *p_shader) { visual_shader = Ref<VisualShader>(p_shader); } -void VisualShaderGraphPlugin::set_connections(List<VisualShader::Connection> &p_connections) { +void VisualShaderGraphPlugin::set_connections(const List<VisualShader::Connection> &p_connections) { connections = p_connections; } @@ -148,6 +148,7 @@ void VisualShaderGraphPlugin::show_port_preview(VisualShader::Type p_type, int p if (links[p_node_id].preview_pos != -1) { links[p_node_id].graph_node->move_child(vbox, links[p_node_id].preview_pos); } + links[p_node_id].graph_node->set_slot_draw_stylebox(vbox->get_index(), false); Control *offset = memnew(Control); offset->set_custom_minimum_size(Size2(0, 5 * EDSCALE)); @@ -211,6 +212,10 @@ void VisualShaderGraphPlugin::set_input_port_default_value(VisualShader::Type p_ Vector3 v = p_value; button->set_text(String::num(v.x, 3) + "," + String::num(v.y, 3) + "," + String::num(v.z, 3)); } break; + case Variant::QUATERNION: { + Quaternion v = p_value; + button->set_text(String::num(v.x, 3) + "," + String::num(v.y, 3) + "," + String::num(v.z, 3) + "," + String::num(v.w, 3)); + } break; default: { } } @@ -320,7 +325,7 @@ void VisualShaderGraphPlugin::make_dirty(bool p_enabled) { } void VisualShaderGraphPlugin::register_link(VisualShader::Type p_type, int p_id, VisualShaderNode *p_visual_node, GraphNode *p_graph_node) { - links.insert(p_id, { p_type, p_visual_node, p_graph_node, p_visual_node->get_output_port_for_preview() != -1, -1, Map<int, InputPort>(), Map<int, Port>(), nullptr, nullptr, nullptr, { nullptr, nullptr, nullptr } }); + links.insert(p_id, { p_type, p_visual_node, p_graph_node, p_visual_node->get_output_port_for_preview() != -1, -1, HashMap<int, InputPort>(), HashMap<int, Port>(), nullptr, nullptr, nullptr, { nullptr, nullptr, nullptr } }); } void VisualShaderGraphPlugin::register_output_port(int p_node_id, int p_port, TextureButton *p_button) { @@ -339,6 +344,7 @@ void VisualShaderGraphPlugin::update_theme() { vector_expanded_color[0] = editor->get_theme_color(SNAME("axis_x_color"), SNAME("Editor")); // red vector_expanded_color[1] = editor->get_theme_color(SNAME("axis_y_color"), SNAME("Editor")); // green vector_expanded_color[2] = editor->get_theme_color(SNAME("axis_z_color"), SNAME("Editor")); // blue + vector_expanded_color[3] = editor->get_theme_color(SNAME("axis_w_color"), SNAME("Editor")); // alpha } void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) { @@ -363,22 +369,32 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) { static Ref<StyleBoxEmpty> label_style = make_empty_stylebox(2, 1, 2, 1); - static const Color type_color[7] = { + static const Color type_color[] = { Color(0.38, 0.85, 0.96), // scalar (float) Color(0.49, 0.78, 0.94), // scalar (int) Color(0.74, 0.57, 0.95), // vector2 Color(0.84, 0.49, 0.93), // vector3 + Color(1.0, 0.125, 0.95), // vector4 Color(0.55, 0.65, 0.94), // boolean Color(0.96, 0.66, 0.43), // transform Color(1.0, 1.0, 0.0), // sampler }; - static const String vector_expanded_name[3] = { + static const String vector_expanded_name[4] = { "red", "green", - "blue" + "blue", + "alpha" }; + // Visual shader specific theme for MSDF font. + Ref<Theme> vstheme; + vstheme.instantiate(); + Ref<Font> label_font = EditorNode::get_singleton()->get_editor_theme()->get_font("main_msdf", "EditorFonts"); + vstheme->set_font("font", "Label", label_font); + vstheme->set_font("font", "LineEdit", label_font); + vstheme->set_font("font", "Button", label_font); + Ref<VisualShaderNode> vsnode = visual_shader->get_node(p_type, p_id); Ref<VisualShaderNodeResizableBase> resizable_node = Object::cast_to<VisualShaderNodeResizableBase>(vsnode.ptr()); @@ -399,8 +415,10 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) { custom_node->_set_initialized(true); } + // Create graph node. GraphNode *node = memnew(GraphNode); graph->add_child(node); + node->set_theme(vstheme); editor->_update_created_node(node); register_link(p_type, p_id, vsnode.ptr(), node); @@ -610,6 +628,9 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) { case VisualShaderNode::PORT_TYPE_VECTOR_3D: { output_port_count += 3; } break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { + output_port_count += 4; + } break; default: break; } @@ -636,6 +657,13 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) { i -= 3; } } break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { + if (expanded_port_counter >= 4) { + expanded_type = VisualShaderNode::PORT_TYPE_SCALAR; + expanded_port_counter = 0; + i -= 4; + } + } break; default: break; } @@ -710,6 +738,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) { type_box->add_item(TTR("Int")); type_box->add_item(TTR("Vector2")); type_box->add_item(TTR("Vector3")); + type_box->add_item(TTR("Vector4")); type_box->add_item(TTR("Boolean")); type_box->add_item(TTR("Transform")); type_box->add_item(TTR("Sampler")); @@ -772,6 +801,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) { type_box->add_item(TTR("Int")); type_box->add_item(TTR("Vector2")); type_box->add_item(TTR("Vector3")); + type_box->add_item(TTR("Vector4")); type_box->add_item(TTR("Boolean")); type_box->add_item(TTR("Transform")); type_box->select(group_node->get_output_port_type(i)); @@ -880,6 +910,41 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) { expanded_type = VisualShaderNode::PORT_TYPE_VECTOR_3D; } break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { + port_offset++; + valid_left = (i + 1) < vsnode->get_input_port_count(); + port_left = VisualShaderNode::PORT_TYPE_SCALAR; + if (valid_left) { + port_left = vsnode->get_input_port_type(i + 1); + } + node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[0]); + port_offset++; + + valid_left = (i + 2) < vsnode->get_input_port_count(); + port_left = VisualShaderNode::PORT_TYPE_SCALAR; + if (valid_left) { + port_left = vsnode->get_input_port_type(i + 2); + } + node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[1]); + port_offset++; + + valid_left = (i + 3) < vsnode->get_input_port_count(); + port_left = VisualShaderNode::PORT_TYPE_SCALAR; + if (valid_left) { + port_left = vsnode->get_input_port_type(i + 3); + } + node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[2]); + port_offset++; + + valid_left = (i + 4) < vsnode->get_input_port_count(); + port_left = VisualShaderNode::PORT_TYPE_SCALAR; + if (valid_left) { + port_left = vsnode->get_input_port_type(i + 4); + } + node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[3]); + + expanded_type = VisualShaderNode::PORT_TYPE_VECTOR_4D; + } break; default: break; } @@ -888,12 +953,12 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) { if (vsnode->get_output_port_for_preview() >= 0) { show_port_preview(p_type, p_id, vsnode->get_output_port_for_preview()); + } else { + offset = memnew(Control); + offset->set_custom_minimum_size(Size2(0, 4 * EDSCALE)); + node->add_child(offset); } - offset = memnew(Control); - offset->set_custom_minimum_size(Size2(0, 4 * EDSCALE)); - node->add_child(offset); - String error = vsnode->get_warning(mode, p_type); if (!error.is_empty()) { Label *error_label = memnew(Label); @@ -1037,27 +1102,6 @@ void VisualShaderEditor::edit(VisualShader *p_visual_shader) { if (!visual_shader->is_connected("changed", ce)) { visual_shader->connect("changed", ce); } -#ifndef DISABLE_DEPRECATED - Dictionary engine_version = Engine::get_singleton()->get_version_info(); - static Array components; - if (components.is_empty()) { - components.push_back("major"); - components.push_back("minor"); - } - const Dictionary vs_version = visual_shader->get_engine_version(); - if (!vs_version.has_all(components)) { - visual_shader->update_engine_version(engine_version); - print_line(vformat(TTR("The shader (\"%s\") has been updated to correspond Godot %s.%s version."), visual_shader->get_path(), engine_version["major"], engine_version["minor"])); - } else { - for (int i = 0; i < components.size(); i++) { - if (vs_version[components[i]] != engine_version[components[i]]) { - visual_shader->update_engine_version(engine_version); - print_line(vformat(TTR("The shader (\"%s\") has been updated to correspond Godot %s.%s version."), visual_shader->get_path(), engine_version["major"], engine_version["minor"])); - break; - } - } - } -#endif visual_shader->set_graph_offset(graph->get_scroll_ofs() / EDSCALE); _set_mode(visual_shader->get_mode()); } else { @@ -1305,7 +1349,7 @@ void VisualShaderEditor::_update_options_menu() { static bool low_driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name") == "opengl3"; - Map<String, TreeItem *> folders; + HashMap<String, TreeItem *> folders; int current_func = -1; @@ -1467,6 +1511,9 @@ void VisualShaderEditor::_update_options_menu() { case VisualShaderNode::PORT_TYPE_VECTOR_3D: item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector3"), SNAME("EditorIcons"))); break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector4"), SNAME("EditorIcons"))); + break; case VisualShaderNode::PORT_TYPE_BOOLEAN: item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("bool"), SNAME("EditorIcons"))); break; @@ -1568,6 +1615,7 @@ void VisualShaderEditor::_update_uniforms(bool p_update_refs) { Ref<VisualShaderNodeIntUniform> int_uniform = vsnode; Ref<VisualShaderNodeVec2Uniform> vec2_uniform = vsnode; Ref<VisualShaderNodeVec3Uniform> vec3_uniform = vsnode; + Ref<VisualShaderNodeVec4Uniform> vec4_uniform = vsnode; Ref<VisualShaderNodeColorUniform> color_uniform = vsnode; Ref<VisualShaderNodeBooleanUniform> bool_uniform = vsnode; Ref<VisualShaderNodeTransformUniform> transform_uniform = vsnode; @@ -1583,6 +1631,8 @@ void VisualShaderEditor::_update_uniforms(bool p_update_refs) { uniform_type = VisualShaderNodeUniformRef::UniformType::UNIFORM_TYPE_VECTOR2; } else if (vec3_uniform.is_valid()) { uniform_type = VisualShaderNodeUniformRef::UniformType::UNIFORM_TYPE_VECTOR3; + } else if (vec4_uniform.is_valid()) { + uniform_type = VisualShaderNodeUniformRef::UniformType::UNIFORM_TYPE_VECTOR4; } else if (transform_uniform.is_valid()) { uniform_type = VisualShaderNodeUniformRef::UniformType::UNIFORM_TYPE_TRANSFORM; } else if (color_uniform.is_valid()) { @@ -1599,7 +1649,7 @@ void VisualShaderEditor::_update_uniforms(bool p_update_refs) { } } -void VisualShaderEditor::_update_uniform_refs(Set<String> &p_deleted_names) { +void VisualShaderEditor::_update_uniform_refs(HashSet<String> &p_deleted_names) { for (int i = 0; i < VisualShader::TYPE_MAX; i++) { VisualShader::Type type = VisualShader::Type(i); @@ -1674,6 +1724,8 @@ void VisualShaderEditor::_update_graph() { float graph_minimap_opacity = EditorSettings::get_singleton()->get("editors/visual_editors/minimap_opacity"); graph->set_minimap_opacity(graph_minimap_opacity); + float graph_lines_curvature = EditorSettings::get_singleton()->get("editors/visual_editors/lines_curvature"); + graph->set_connection_lines_curvature(graph_lines_curvature); } VisualShader::Type VisualShaderEditor::get_current_shader_type() const { @@ -1825,6 +1877,9 @@ void VisualShaderEditor::_expand_output_port(int p_node, int p_port, bool p_expa case VisualShaderNode::PORT_TYPE_VECTOR_3D: { type_size = 3; } break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { + type_size = 4; + } break; default: break; } @@ -2214,7 +2269,7 @@ void VisualShaderEditor::_uniform_line_edit_changed(const String &p_text, int p_ undo_redo->add_do_method(this, "_update_uniforms", true); undo_redo->add_undo_method(this, "_update_uniforms", true); - Set<String> changed_names; + HashSet<String> changed_names; changed_names.insert(node->get_uniform_name()); _update_uniform_refs(changed_names); @@ -2682,6 +2737,9 @@ void VisualShaderEditor::_add_node(int p_idx, const Vector<Variant> &p_ops, Stri case VisualShaderNode::PORT_TYPE_VECTOR_3D: initial_expression_code = "output0 = vec3(1.0, 1.0, 1.0);"; break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: + initial_expression_code = "output0 = vec4(1.0, 1.0, 1.0, 1.0);"; + break; case VisualShaderNode::PORT_TYPE_BOOLEAN: initial_expression_code = "output0 = true;"; break; @@ -2699,9 +2757,9 @@ void VisualShaderEditor::_add_node(int p_idx, const Vector<Variant> &p_ops, Stri } if (vsnode->get_output_port_count() > 0 || created_expression_port) { int _from_node = id_to_use; - int _from_slot = 0; if (created_expression_port) { + int _from_slot = 0; undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, _from_node, _from_slot, to_node, to_slot); undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, _from_node, _from_slot, to_node, to_slot); undo_redo->add_do_method(graph_plugin.ptr(), "connect_nodes", type, _from_node, _from_slot, to_node, to_slot); @@ -2739,9 +2797,9 @@ void VisualShaderEditor::_add_node(int p_idx, const Vector<Variant> &p_ops, Stri if (vsnode->get_input_port_count() > 0 || created_expression_port) { int _to_node = id_to_use; - int _to_slot = 0; if (created_expression_port) { + int _to_slot = 0; undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_slot, _to_node, _to_slot); undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, from_node, from_slot, _to_node, _to_slot); undo_redo->add_undo_method(graph_plugin.ptr(), "disconnect_nodes", type, from_node, from_slot, _to_node, _to_slot); @@ -3031,7 +3089,7 @@ void VisualShaderEditor::_delete_nodes(int p_type, const List<int> &p_nodes) { } } - Set<String> uniform_names; + HashSet<String> uniform_names; for (const int &F : p_nodes) { Ref<VisualShaderNode> node = visual_shader->get_node(type, F); @@ -3135,11 +3193,11 @@ void VisualShaderEditor::_convert_constants_to_uniforms(bool p_vice_versa) { undo_redo->create_action(TTR("Convert Uniform Node(s) To Constant(s)")); } - const Set<int> ¤t_set = p_vice_versa ? selected_uniforms : selected_constants; - Set<String> deleted_names; + const HashSet<int> ¤t_set = p_vice_versa ? selected_uniforms : selected_constants; + HashSet<String> deleted_names; - for (Set<int>::Element *E = current_set.front(); E; E = E->next()) { - int node_id = E->get(); + for (const int &E : current_set) { + int node_id = E; Ref<VisualShaderNode> node = visual_shader->get_node(type_id, node_id); bool caught = false; Variant var; @@ -3237,6 +3295,25 @@ void VisualShaderEditor::_convert_constants_to_uniforms(bool p_vice_versa) { } } + // vec4 + if (!caught) { + if (!p_vice_versa) { + Ref<VisualShaderNodeVec4Constant> vec4_const = Object::cast_to<VisualShaderNodeVec4Constant>(node.ptr()); + if (vec4_const.is_valid()) { + _replace_node(type_id, node_id, "VisualShaderNodeVec4Constant", "VisualShaderNodeVec4Uniform"); + var = vec4_const->get_constant(); + caught = true; + } + } else { + Ref<VisualShaderNodeVec4Uniform> vec4_uniform = Object::cast_to<VisualShaderNodeVec4Uniform>(node.ptr()); + if (vec4_uniform.is_valid()) { + _replace_node(type_id, node_id, "VisualShaderNodeVec4Uniform", "VisualShaderNodeVec4Constant"); + var = vec4_uniform->get_default_value(); + caught = true; + } + } + } + // color if (!caught) { if (!p_vice_versa) { @@ -3313,16 +3390,23 @@ void VisualShaderEditor::_delete_node_request(int p_type, int p_node) { undo_redo->commit_action(); } -void VisualShaderEditor::_delete_nodes_request() { +void VisualShaderEditor::_delete_nodes_request(const TypedArray<StringName> &p_nodes) { List<int> to_erase; - for (int i = 0; i < graph->get_child_count(); i++) { - GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i)); - if (gn) { - if (gn->is_selected() && gn->is_close_button_visible()) { - to_erase.push_back(gn->get_name().operator String().to_int()); + if (p_nodes.is_empty()) { + // Called from context menu. + for (int i = 0; i < graph->get_child_count(); i++) { + GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i)); + if (gn) { + if (gn->is_selected() && gn->is_close_button_visible()) { + to_erase.push_back(gn->get_name().operator String().to_int()); + } } } + } else { + for (int i = 0; i < p_nodes.size(); i++) { + to_erase.push_back(p_nodes[i].operator String().to_int()); + } } if (to_erase.is_empty()) { @@ -3568,6 +3652,9 @@ void VisualShaderEditor::_notification(int p_what) { case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { graph->get_panner()->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning"))); graph->set_warped_panning(bool(EditorSettings::get_singleton()->get("editors/panning/warped_mouse_panning"))); + graph->set_minimap_opacity(EditorSettings::get_singleton()->get("editors/visual_editors/minimap_opacity")); + graph->set_connection_lines_curvature(EditorSettings::get_singleton()->get("editors/visual_editors/lines_curvature")); + _update_graph(); } break; case NOTIFICATION_ENTER_TREE: { @@ -3686,7 +3773,7 @@ void VisualShaderEditor::_dup_copy_nodes(int p_type, List<CopyItem> &r_items, Li selection_center.x = 0.0f; selection_center.y = 0.0f; - Set<int> nodes; + HashSet<int> nodes; for (int i = 0; i < graph->get_child_count(); i++) { GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i)); @@ -3765,9 +3852,9 @@ void VisualShaderEditor::_dup_paste_nodes(int p_type, List<CopyItem> &r_items, c int base_id = visual_shader->get_valid_node_id(type); int id_from = base_id; - Map<int, int> connection_remap; - Set<int> unsupported_set; - Set<int> added_set; + HashMap<int, int> connection_remap; + HashSet<int> unsupported_set; + HashSet<int> added_set; for (CopyItem &item : r_items) { if (item.disabled) { @@ -3943,7 +4030,10 @@ void VisualShaderEditor::_input_select_item(Ref<VisualShaderNodeInput> p_input, return; } - bool type_changed = p_input->get_input_type_by_name(p_name) != p_input->get_input_type_by_name(prev_name); + VisualShaderNode::PortType next_input_type = p_input->get_input_type_by_name(p_name); + VisualShaderNode::PortType prev_input_type = p_input->get_input_type_by_name(prev_name); + + bool type_changed = next_input_type != prev_input_type; UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); undo_redo->create_action(TTR("Visual Shader Input Type Changed")); @@ -3951,31 +4041,54 @@ void VisualShaderEditor::_input_select_item(Ref<VisualShaderNodeInput> p_input, undo_redo->add_do_method(p_input.ptr(), "set_input_name", p_name); undo_redo->add_undo_method(p_input.ptr(), "set_input_name", prev_name); - // update output port - for (int type_id = 0; type_id < VisualShader::TYPE_MAX; type_id++) { - VisualShader::Type type = VisualShader::Type(type_id); - int id = visual_shader->find_node_id(type, p_input); - if (id != VisualShader::NODE_ID_INVALID) { - if (type_changed) { + if (type_changed) { + for (int type_id = 0; type_id < VisualShader::TYPE_MAX; type_id++) { + VisualShader::Type type = VisualShader::Type(type_id); + + int id = visual_shader->find_node_id(type, p_input); + if (id != VisualShader::NODE_ID_INVALID) { + bool is_expanded = p_input->is_output_port_expandable(0) && p_input->_is_output_port_expanded(0); + + int type_size = 0; + if (is_expanded) { + switch (next_input_type) { + case VisualShaderNode::PORT_TYPE_VECTOR_2D: { + type_size = 2; + } break; + case VisualShaderNode::PORT_TYPE_VECTOR_3D: { + type_size = 3; + } break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { + type_size = 4; + } break; + default: + break; + } + } + List<VisualShader::Connection> conns; visual_shader->get_node_connections(type, &conns); for (const VisualShader::Connection &E : conns) { - if (E.from_node == id) { - if (visual_shader->is_port_types_compatible(p_input->get_input_type_by_name(p_name), visual_shader->get_node(type, E.to_node)->get_input_port_type(E.to_port))) { - undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, E.from_node, E.from_port, E.to_node, E.to_port); - undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", type, E.from_node, E.from_port, E.to_node, E.to_port); - continue; + int from_node = E.from_node; + int from_port = E.from_port; + int to_node = E.to_node; + int to_port = E.to_port; + + if (from_node == id) { + bool is_incompatible_types = !visual_shader->is_port_types_compatible(p_input->get_input_type_by_name(p_name), visual_shader->get_node(type, to_node)->get_input_port_type(to_port)); + + if (is_incompatible_types || from_port > type_size) { + undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port); + undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", type, from_node, from_port, to_node, to_port); + undo_redo->add_do_method(graph_plugin.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port); + undo_redo->add_undo_method(graph_plugin.ptr(), "connect_nodes", type, from_node, from_port, to_node, to_port); } - undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, E.from_node, E.from_port, E.to_node, E.to_port); - undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", type, E.from_node, E.from_port, E.to_node, E.to_port); - undo_redo->add_do_method(graph_plugin.ptr(), "disconnect_nodes", type, E.from_node, E.from_port, E.to_node, E.to_port); - undo_redo->add_undo_method(graph_plugin.ptr(), "connect_nodes", type, E.from_node, E.from_port, E.to_node, E.to_port); } } + + undo_redo->add_do_method(graph_plugin.ptr(), "update_node", type_id, id); + undo_redo->add_undo_method(graph_plugin.ptr(), "update_node", type_id, id); } - undo_redo->add_do_method(graph_plugin.ptr(), "update_node", type_id, id); - undo_redo->add_undo_method(graph_plugin.ptr(), "update_node", type_id, id); - break; } } @@ -4176,6 +4289,9 @@ void VisualShaderEditor::_update_varying_tree() { case VisualShader::VARYING_TYPE_VECTOR_3D: item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector3"), SNAME("EditorIcons"))); break; + case VisualShader::VARYING_TYPE_VECTOR_4D: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector4"), SNAME("EditorIcons"))); + break; case VisualShader::VARYING_TYPE_COLOR: item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Color"), SNAME("EditorIcons"))); break; @@ -4286,7 +4402,7 @@ void VisualShaderEditor::_node_menu_id_pressed(int p_idx) { _paste_nodes(true, menu_point); break; case NodeMenuOptions::DELETE: - _delete_nodes_request(); + _delete_nodes_request(TypedArray<StringName>()); break; case NodeMenuOptions::DUPLICATE: _duplicate_nodes(); @@ -4538,15 +4654,19 @@ VisualShaderEditor::VisualShaderEditor() { graph->get_zoom_hbox()->set_h_size_flags(SIZE_EXPAND_FILL); graph->set_v_size_flags(SIZE_EXPAND_FILL); graph->set_h_size_flags(SIZE_EXPAND_FILL); + graph->set_show_zoom_label(true); add_child(graph); graph->set_drag_forwarding(this); float graph_minimap_opacity = EditorSettings::get_singleton()->get("editors/visual_editors/minimap_opacity"); graph->set_minimap_opacity(graph_minimap_opacity); + float graph_lines_curvature = EditorSettings::get_singleton()->get("editors/visual_editors/lines_curvature"); + graph->set_connection_lines_curvature(graph_lines_curvature); graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_SCALAR); graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_SCALAR_INT); graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_BOOLEAN); graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_VECTOR_2D); graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_VECTOR_3D); + graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_VECTOR_4D); graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_TRANSFORM); graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_SAMPLER); //graph->add_valid_left_disconnect_type(0); @@ -4567,27 +4687,44 @@ VisualShaderEditor::VisualShaderEditor() { graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_SCALAR_INT); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_VECTOR_2D); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_VECTOR_3D); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_VECTOR_4D); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_BOOLEAN); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR_INT, VisualShaderNode::PORT_TYPE_SCALAR_INT); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR_INT, VisualShaderNode::PORT_TYPE_SCALAR); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR_INT, VisualShaderNode::PORT_TYPE_VECTOR_2D); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR_INT, VisualShaderNode::PORT_TYPE_VECTOR_3D); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR_INT, VisualShaderNode::PORT_TYPE_VECTOR_4D); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR_INT, VisualShaderNode::PORT_TYPE_BOOLEAN); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_2D, VisualShaderNode::PORT_TYPE_SCALAR); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_2D, VisualShaderNode::PORT_TYPE_SCALAR_INT); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_2D, VisualShaderNode::PORT_TYPE_VECTOR_2D); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_2D, VisualShaderNode::PORT_TYPE_VECTOR_3D); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_2D, VisualShaderNode::PORT_TYPE_VECTOR_4D); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_2D, VisualShaderNode::PORT_TYPE_BOOLEAN); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_3D, VisualShaderNode::PORT_TYPE_SCALAR); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_3D, VisualShaderNode::PORT_TYPE_SCALAR_INT); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_3D, VisualShaderNode::PORT_TYPE_VECTOR_2D); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_3D, VisualShaderNode::PORT_TYPE_VECTOR_3D); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_3D, VisualShaderNode::PORT_TYPE_VECTOR_4D); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_3D, VisualShaderNode::PORT_TYPE_BOOLEAN); + + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_4D, VisualShaderNode::PORT_TYPE_SCALAR); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_4D, VisualShaderNode::PORT_TYPE_SCALAR_INT); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_4D, VisualShaderNode::PORT_TYPE_VECTOR_2D); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_4D, VisualShaderNode::PORT_TYPE_VECTOR_3D); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_4D, VisualShaderNode::PORT_TYPE_VECTOR_4D); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_4D, VisualShaderNode::PORT_TYPE_BOOLEAN); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_SCALAR); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_SCALAR_INT); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_VECTOR_2D); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_VECTOR_3D); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_VECTOR_4D); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_BOOLEAN); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShaderNode::PORT_TYPE_TRANSFORM); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SAMPLER, VisualShaderNode::PORT_TYPE_SAMPLER); @@ -4669,7 +4806,7 @@ VisualShaderEditor::VisualShaderEditor() { /////////////////////////////////////// preview_window = memnew(Window); - preview_window->set_title(TTR("Generated shader code")); + preview_window->set_title(TTR("Generated Shader Code")); preview_window->set_visible(preview_showed); preview_window->connect("close_requested", callable_mp(this, &VisualShaderEditor::_preview_close_requested)); preview_window->connect("size_changed", callable_mp(this, &VisualShaderEditor::_preview_size_changed)); @@ -4693,7 +4830,7 @@ VisualShaderEditor::VisualShaderEditor() { error_label = memnew(Label); error_panel->add_child(error_label); - error_label->set_autowrap_mode(Label::AUTOWRAP_WORD_SMART); + error_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART); /////////////////////////////////////// // POPUP MENU @@ -4802,6 +4939,7 @@ VisualShaderEditor::VisualShaderEditor() { varying_type->add_item("Float"); varying_type->add_item("Vector2"); varying_type->add_item("Vector3"); + varying_type->add_item("Vector4"); varying_type->add_item("Color"); varying_type->add_item("Transform"); @@ -4849,7 +4987,7 @@ VisualShaderEditor::VisualShaderEditor() { } alert = memnew(AcceptDialog); - alert->get_label()->set_autowrap_mode(Label::AUTOWRAP_WORD); + alert->get_label()->set_autowrap_mode(TextServer::AUTOWRAP_WORD); alert->get_label()->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); alert->get_label()->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER); alert->get_label()->set_custom_minimum_size(Size2(400, 60) * EDSCALE); @@ -4894,8 +5032,8 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("ColorOp", "Color", "Common", "VisualShaderNodeColorOp", TTR("Color operator."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D)); add_options.push_back(AddOption("Grayscale", "Color", "Functions", "VisualShaderNodeColorFunc", TTR("Grayscale function."), { VisualShaderNodeColorFunc::FUNC_GRAYSCALE }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); - add_options.push_back(AddOption("HSV2RGB", "Color", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts HSV vector to RGB equivalent."), { VisualShaderNodeVectorFunc::FUNC_HSV2RGB, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); - add_options.push_back(AddOption("RGB2HSV", "Color", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts RGB vector to HSV equivalent."), { VisualShaderNodeVectorFunc::FUNC_RGB2HSV, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("HSV2RGB", "Color", "Functions", "VisualShaderNodeColorFunc", TTR("Converts HSV vector to RGB equivalent."), { VisualShaderNodeColorFunc::FUNC_HSV2RGB, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("RGB2HSV", "Color", "Functions", "VisualShaderNodeColorFunc", TTR("Converts RGB vector to HSV equivalent."), { VisualShaderNodeColorFunc::FUNC_RGB2HSV, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); add_options.push_back(AddOption("Sepia", "Color", "Functions", "VisualShaderNodeColorFunc", TTR("Sepia function."), { VisualShaderNodeColorFunc::FUNC_SEPIA }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); add_options.push_back(AddOption("Burn", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Burn operator."), { VisualShaderNodeColorOp::OP_BURN }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); @@ -4908,8 +5046,8 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("Screen", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Screen operator."), { VisualShaderNodeColorOp::OP_SCREEN }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); add_options.push_back(AddOption("SoftLight", "Color", "Operators", "VisualShaderNodeColorOp", TTR("SoftLight operator."), { VisualShaderNodeColorOp::OP_SOFT_LIGHT }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); - add_options.push_back(AddOption("ColorConstant", "Color", "Variables", "VisualShaderNodeColorConstant", TTR("Color constant."))); - add_options.push_back(AddOption("ColorUniform", "Color", "Variables", "VisualShaderNodeColorUniform", TTR("Color uniform."))); + add_options.push_back(AddOption("ColorConstant", "Color", "Variables", "VisualShaderNodeColorConstant", TTR("Color constant."), {}, VisualShaderNode::PORT_TYPE_VECTOR_4D)); + add_options.push_back(AddOption("ColorUniform", "Color", "Variables", "VisualShaderNodeColorUniform", TTR("Color uniform."), {}, VisualShaderNode::PORT_TYPE_VECTOR_4D)); // COMMON @@ -4962,8 +5100,7 @@ VisualShaderEditor::VisualShaderEditor() { // CANVASITEM-FOR-ALL - add_options.push_back(AddOption("Alpha", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "alpha", "COLOR.a"), { "alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_CANVAS_ITEM)); - add_options.push_back(AddOption("Color", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "color", "COLOR.rgb"), { "color" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, -1, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("Color", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "color", "COLOR"), { "color" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, -1, Shader::MODE_CANVAS_ITEM)); add_options.push_back(AddOption("TexturePixelSize", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "texture_pixel_size", "TEXTURE_PIXEL_SIZE"), { "texture_pixel_size" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, -1, Shader::MODE_CANVAS_ITEM)); add_options.push_back(AddOption("Time", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "time", "TIME"), { "time" }, VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_CANVAS_ITEM)); add_options.push_back(AddOption("UV", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "uv", "UV"), { "uv" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, -1, Shader::MODE_CANVAS_ITEM)); @@ -4971,11 +5108,9 @@ VisualShaderEditor::VisualShaderEditor() { // PARTICLES-FOR-ALL add_options.push_back(AddOption("Active", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "active", "ACTIVE"), { "active" }, VisualShaderNode::PORT_TYPE_BOOLEAN, -1, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Alpha", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "alpha", "COLOR.a"), { "alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES)); add_options.push_back(AddOption("AttractorForce", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "attractor_force", "ATTRACTOR_FORCE"), { "attractor_force" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, -1, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Color", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "color", "COLOR.rgb"), { "color" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, -1, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Custom", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "custom", "CUSTOM.rgb"), { "custom" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, -1, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("CustomAlpha", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "custom_alpha", "CUSTOM.a"), { "custom_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Color", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "color", "COLOR"), { "color" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, -1, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Custom", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "custom", "CUSTOM"), { "custom" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, -1, Shader::MODE_PARTICLES)); add_options.push_back(AddOption("Delta", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "delta", "DELTA"), { "delta" }, VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES)); add_options.push_back(AddOption("EmissionTransform", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "emission_transform", "EMISSION_TRANSFORM"), { "emission_transform" }, VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_PARTICLES)); add_options.push_back(AddOption("Index", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "index", "INDEX"), { "index" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, -1, Shader::MODE_PARTICLES)); @@ -5005,12 +5140,10 @@ VisualShaderEditor::VisualShaderEditor() { // NODE3D INPUTS - add_options.push_back(AddOption("Alpha", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "alpha", "COLOR.a"), { "alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("Binormal", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "binormal", "BINORMAL"), { "binormal" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); - add_options.push_back(AddOption("Color", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "color", "COLOR.rgb"), { "color" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Color", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "color", "COLOR"), { "color" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("InstanceId", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_id", "INSTANCE_ID"), { "instance_id" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); - add_options.push_back(AddOption("InstanceCustom", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_custom", "INSTANCE_CUSTOM.rgb"), { "instance_custom" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); - add_options.push_back(AddOption("InstanceCustomAlpha", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_custom_alpha", "INSTANCE_CUSTOM.a"), { "instance_custom_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("InstanceCustom", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_custom", "INSTANCE_CUSTOM"), { "instance_custom" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("ModelViewMatrix", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "modelview_matrix", "MODELVIEW_MATRIX"), { "modelview_matrix" }, VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("PointSize", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "point_size", "POINT_SIZE"), { "point_size" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("Tangent", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_mode, "tangent", "TANGENT"), { "tangent" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); @@ -5020,11 +5153,10 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("ViewMonoLeft", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "view_mono_left", "VIEW_MONO_LEFT"), { "view_mono_left" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("ViewRight", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "view_right", "VIEW_RIGHT"), { "view_right" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); - add_options.push_back(AddOption("Alpha", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "alpha", "COLOR.a"), { "alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("Binormal", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "binormal", "BINORMAL"), { "binormal" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); - add_options.push_back(AddOption("Color", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "color", "COLOR.rgb"), { "color" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Color", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "color", "COLOR"), { "color" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("DepthTexture", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "depth_texture", "DEPTH_TEXTURE"), { "depth_texture" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); - add_options.push_back(AddOption("FragCoord", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "fragcoord", "FRAGCOORD.xyz"), { "fragcoord" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("FragCoord", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "fragcoord", "FRAGCOORD"), { "fragcoord" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("FrontFacing", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "front_facing", "FRONT_FACING"), { "front_facing" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("PointCoord", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "point_coord", "POINT_COORD"), { "point_coord" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("ScreenTexture", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "screen_texture", "SCREEN_TEXTURE"), { "screen_texture" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); @@ -5040,7 +5172,7 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("Attenuation", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "attenuation", "ATTENUATION"), { "attenuation" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("Backlight", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "backlight", "BACKLIGHT"), { "backlight" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("Diffuse", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "diffuse", "DIFFUSE_LIGHT"), { "diffuse" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL)); - add_options.push_back(AddOption("FragCoord", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "fragcoord", "FRAGCOORD.xyz"), { "fragcoord" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("FragCoord", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "fragcoord", "FRAGCOORD"), { "fragcoord" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("Light", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light", "LIGHT"), { "light" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("LightColor", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_color", "LIGHT_COLOR"), { "light_color" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("Metallic", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "metallic", "METALLIC"), { "metallic" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL)); @@ -5052,8 +5184,7 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("AtLightPass", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "at_light_pass", "AT_LIGHT_PASS"), { "at_light_pass" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM)); add_options.push_back(AddOption("CanvasMatrix", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "canvas_matrix", "CANVAS_MATRIX"), { "canvas_matrix" }, VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM)); - add_options.push_back(AddOption("InstanceCustom", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_custom", "INSTANCE_CUSTOM.rgb"), { "instance_custom" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM)); - add_options.push_back(AddOption("InstanceCustomAlpha", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_custom_alpha", "INSTANCE_CUSTOM.a"), { "instance_custom_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("InstanceCustom", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_custom", "INSTANCE_CUSTOM"), { "instance_custom" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM)); add_options.push_back(AddOption("InstanceId", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_id", "INSTANCE_ID"), { "instance_id" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM)); add_options.push_back(AddOption("ModelMatrix", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "model_matrix", "MODEL_MATRIX"), { "model_matrix" }, VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM)); add_options.push_back(AddOption("PointSize", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "point_size", "POINT_SIZE"), { "point_size" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM)); @@ -5062,32 +5193,27 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("VertexId", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "vertex_id", "VERTEX_ID"), { "vertex_id" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM)); add_options.push_back(AddOption("AtLightPass", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "at_light_pass", "AT_LIGHT_PASS"), { "at_light_pass" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM)); - add_options.push_back(AddOption("FragCoord", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "fragcoord", "FRAGCOORD.xyz"), { "fragcoord" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("FragCoord", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "fragcoord", "FRAGCOORD"), { "fragcoord" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM)); add_options.push_back(AddOption("NormalTexture", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "normal_texture", "NORMAL_TEXTURE"), { "normal_texture" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM)); add_options.push_back(AddOption("PointCoord", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "point_coord", "POINT_COORD"), { "point_coord" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM)); add_options.push_back(AddOption("ScreenPixelSize", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "screen_pixel_size", "SCREEN_PIXEL_SIZE"), { "screen_pixel_size" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM)); add_options.push_back(AddOption("ScreenTexture", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "screen_texture", "SCREEN_TEXTURE"), { "screen_texture" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM)); add_options.push_back(AddOption("ScreenUV", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "screen_uv", "SCREEN_UV"), { "screen_uv" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM)); - add_options.push_back(AddOption("SpecularShininess", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "specular_shininess", "SPECULAR_SHININESS.rgb"), { "specular_shininess" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM)); - add_options.push_back(AddOption("SpecularShininessAlpha", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "specular_shininess_alpha", "SPECULAR_SHININESS.a"), { "specular_shininess_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("SpecularShininess", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "specular_shininess", "SPECULAR_SHININESS"), { "specular_shininess" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM)); add_options.push_back(AddOption("SpecularShininessTexture", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "specular_shininess_texture", "SPECULAR_SHININESS_TEXTURE"), { "specular_shininess_texture" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM)); add_options.push_back(AddOption("Texture", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "texture", "TEXTURE"), { "texture" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM)); add_options.push_back(AddOption("Vertex", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_mode, "vertex", "VERTEX"), { "vertex" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM)); - add_options.push_back(AddOption("FragCoord", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "fragcoord", "FRAGCOORD.xyz"), { "fragcoord" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM)); - add_options.push_back(AddOption("Light", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light", "LIGHT.rgb"), { "light" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM)); - add_options.push_back(AddOption("LightAlpha", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_alpha", "LIGHT.a"), { "light_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM)); - add_options.push_back(AddOption("LightColor", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_color", "LIGHT_COLOR.rgb"), { "light_color" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM)); - add_options.push_back(AddOption("LightColorAlpha", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_color_alpha", "LIGHT_COLOR.a"), { "light_color_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("FragCoord", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "fragcoord", "FRAGCOORD"), { "fragcoord" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("Light", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light", "LIGHT"), { "light" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("LightColor", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_color", "LIGHT_COLOR"), { "light_color" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM)); add_options.push_back(AddOption("LightPosition", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_position", "LIGHT_POSITION"), { "light_position" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM)); add_options.push_back(AddOption("LightVertex", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "light_vertex", "LIGHT_VERTEX"), { "light_vertex" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM)); add_options.push_back(AddOption("Normal", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "normal", "NORMAL"), { "normal" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM)); add_options.push_back(AddOption("PointCoord", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "point_coord", "POINT_COORD"), { "point_coord" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM)); add_options.push_back(AddOption("ScreenUV", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "screen_uv", "SCREEN_UV"), { "screen_uv" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM)); - add_options.push_back(AddOption("Shadow", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "shadow", "SHADOW_MODULATE.rgb"), { "shadow" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM)); - add_options.push_back(AddOption("ShadowAlpha", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "shadow_alpha", "SHADOW_MODULATE.a"), { "shadow_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM)); - add_options.push_back(AddOption("SpecularShininess", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "specular_shininess", "SPECULAR_SHININESS.rgb"), { "specular_shininess" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM)); - add_options.push_back(AddOption("SpecularShininessAlpha", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "specular_shininess_alpha", "SPECULAR_SHININESS.a"), { "specular_shininess_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("Shadow", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "shadow", "SHADOW_MODULATE"), { "shadow" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("SpecularShininess", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "specular_shininess", "SPECULAR_SHININESS"), { "specular_shininess" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM)); add_options.push_back(AddOption("Texture", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "texture", "TEXTURE"), { "texture" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM)); // SKY INPUTS @@ -5096,8 +5222,7 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("AtHalfResPass", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "at_half_res_pass", "AT_HALF_RES_PASS"), { "at_half_res_pass" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY)); add_options.push_back(AddOption("AtQuarterResPass", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "at_quarter_res_pass", "AT_QUARTER_RES_PASS"), { "at_quarter_res_pass" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY)); add_options.push_back(AddOption("EyeDir", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "eyedir", "EYEDIR"), { "eyedir" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_SKY, Shader::MODE_SKY)); - add_options.push_back(AddOption("HalfResColor", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "half_res_color", "HALF_RES_COLOR.rgb"), { "half_res_color" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_SKY, Shader::MODE_SKY)); - add_options.push_back(AddOption("HalfResAlpha", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "half_res_alpha", "HALF_RES_COLOR.a"), { "half_res_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("HalfResColor", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "half_res_color", "HALF_RES_COLOR"), { "half_res_color" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_SKY, Shader::MODE_SKY)); add_options.push_back(AddOption("Light0Color", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light0_color", "LIGHT0_COLOR"), { "light0_color" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_SKY, Shader::MODE_SKY)); add_options.push_back(AddOption("Light0Direction", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light0_direction", "LIGHT0_DIRECTION"), { "light0_direction" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_SKY, Shader::MODE_SKY)); add_options.push_back(AddOption("Light0Enabled", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light0_enabled", "LIGHT0_ENABLED"), { "light0_enabled" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY)); @@ -5115,10 +5240,11 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("Light3Enabled", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light3_enabled", "LIGHT3_ENABLED"), { "light3_enabled" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY)); add_options.push_back(AddOption("Light3Energy", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light3_energy", "LIGHT3_ENERGY"), { "light3_energy" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); add_options.push_back(AddOption("Position", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "position", "POSITION"), { "position" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_SKY, Shader::MODE_SKY)); - add_options.push_back(AddOption("QuarterResColor", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "quarter_res_color", "QUARTER_RES_COLOR.rgb"), { "quarter_res_color" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_SKY, Shader::MODE_SKY)); - add_options.push_back(AddOption("QuarterResAlpha", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "quarter_res_alpha", "QUARTER_RES_COLOR.a"), { "quarter_res_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("QuarterResColor", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "quarter_res_color", "QUARTER_RES_COLOR"), { "quarter_res_color" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_SKY, Shader::MODE_SKY)); add_options.push_back(AddOption("Radiance", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "radiance", "RADIANCE"), { "radiance" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_SKY, Shader::MODE_SKY)); add_options.push_back(AddOption("ScreenUV", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "screen_uv", "SCREEN_UV"), { "screen_uv" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("FragCoord", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "fragcoord", "FRAGCOORD"), { "fragcoord" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("SkyCoords", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "sky_coords", "SKY_COORDS"), { "sky_coords" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_SKY, Shader::MODE_SKY)); add_options.push_back(AddOption("Time", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "time", "TIME"), { "time" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); @@ -5185,7 +5311,7 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("Exp", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Base-e Exponential."), { VisualShaderNodeFloatFunc::FUNC_EXP }, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Exp2", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Base-2 Exponential."), { VisualShaderNodeFloatFunc::FUNC_EXP2 }, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Floor", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Finds the nearest integer less than or equal to the parameter."), { VisualShaderNodeFloatFunc::FUNC_FLOOR }, VisualShaderNode::PORT_TYPE_SCALAR)); - add_options.push_back(AddOption("Fract", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Computes the fractional part of the argument."), { VisualShaderNodeFloatFunc::FUNC_FRAC }, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Fract", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Computes the fractional part of the argument."), { VisualShaderNodeFloatFunc::FUNC_FRACT }, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("InverseSqrt", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the inverse of the square root of the parameter."), { VisualShaderNodeFloatFunc::FUNC_INVERSE_SQRT }, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Log", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Natural logarithm."), { VisualShaderNodeFloatFunc::FUNC_LOG }, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Log2", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Base-2 logarithm."), { VisualShaderNodeFloatFunc::FUNC_LOG2 }, VisualShaderNode::PORT_TYPE_SCALAR)); @@ -5249,25 +5375,25 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("UVFunc", "Textures", "Common", "VisualShaderNodeUVFunc", TTR("Function to be applied on texture coordinates."), {}, VisualShaderNode::PORT_TYPE_VECTOR_2D)); cubemap_node_option_idx = add_options.size(); - add_options.push_back(AddOption("CubeMap", "Textures", "Functions", "VisualShaderNodeCubemap", TTR("Perform the cubic texture lookup."))); + add_options.push_back(AddOption("CubeMap", "Textures", "Functions", "VisualShaderNodeCubemap", TTR("Perform the cubic texture lookup."), {}, VisualShaderNode::PORT_TYPE_VECTOR_4D)); curve_node_option_idx = add_options.size(); - add_options.push_back(AddOption("CurveTexture", "Textures", "Functions", "VisualShaderNodeCurveTexture", TTR("Perform the curve texture lookup."))); + add_options.push_back(AddOption("CurveTexture", "Textures", "Functions", "VisualShaderNodeCurveTexture", TTR("Perform the curve texture lookup."), {}, VisualShaderNode::PORT_TYPE_SCALAR)); curve_xyz_node_option_idx = add_options.size(); - add_options.push_back(AddOption("CurveXYZTexture", "Textures", "Functions", "VisualShaderNodeCurveXYZTexture", TTR("Perform the three components curve texture lookup."))); + add_options.push_back(AddOption("CurveXYZTexture", "Textures", "Functions", "VisualShaderNodeCurveXYZTexture", TTR("Perform the three components curve texture lookup."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D)); texture2d_node_option_idx = add_options.size(); - add_options.push_back(AddOption("Texture2D", "Textures", "Functions", "VisualShaderNodeTexture", TTR("Perform the 2D texture lookup."))); + add_options.push_back(AddOption("Texture2D", "Textures", "Functions", "VisualShaderNodeTexture", TTR("Perform the 2D texture lookup."), {}, VisualShaderNode::PORT_TYPE_VECTOR_4D)); texture2d_array_node_option_idx = add_options.size(); - add_options.push_back(AddOption("Texture2DArray", "Textures", "Functions", "VisualShaderNodeTexture2DArray", TTR("Perform the 2D-array texture lookup."))); + add_options.push_back(AddOption("Texture2DArray", "Textures", "Functions", "VisualShaderNodeTexture2DArray", TTR("Perform the 2D-array texture lookup."), {}, VisualShaderNode::PORT_TYPE_VECTOR_4D)); texture3d_node_option_idx = add_options.size(); - add_options.push_back(AddOption("Texture3D", "Textures", "Functions", "VisualShaderNodeTexture3D", TTR("Perform the 3D texture lookup."))); + add_options.push_back(AddOption("Texture3D", "Textures", "Functions", "VisualShaderNodeTexture3D", TTR("Perform the 3D texture lookup."), {}, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("UVPanning", "Textures", "Functions", "VisualShaderNodeUVFunc", TTR("Apply panning function on texture coordinates."), { VisualShaderNodeUVFunc::FUNC_PANNING }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("UVScaling", "Textures", "Functions", "VisualShaderNodeUVFunc", TTR("Apply scaling function on texture coordinates."), { VisualShaderNodeUVFunc::FUNC_SCALING }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); - add_options.push_back(AddOption("CubeMapUniform", "Textures", "Variables", "VisualShaderNodeCubemapUniform", TTR("Cubic texture uniform lookup."))); - add_options.push_back(AddOption("TextureUniform", "Textures", "Variables", "VisualShaderNodeTextureUniform", TTR("2D texture uniform lookup."))); + add_options.push_back(AddOption("CubeMapUniform", "Textures", "Variables", "VisualShaderNodeCubemapUniform", TTR("Cubic texture uniform lookup."), {}, VisualShaderNode::PORT_TYPE_SAMPLER)); + add_options.push_back(AddOption("TextureUniform", "Textures", "Variables", "VisualShaderNodeTextureUniform", TTR("2D texture uniform lookup."), {}, VisualShaderNode::PORT_TYPE_SAMPLER)); add_options.push_back(AddOption("TextureUniformTriplanar", "Textures", "Variables", "VisualShaderNodeTextureUniformTriplanar", TTR("2D texture uniform lookup with triplanar."), {}, -1, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL)); - add_options.push_back(AddOption("Texture2DArrayUniform", "Textures", "Variables", "VisualShaderNodeTexture2DArrayUniform", TTR("2D array of textures uniform lookup."))); - add_options.push_back(AddOption("Texture3DUniform", "Textures", "Variables", "VisualShaderNodeTexture3DUniform", TTR("3D texture uniform lookup."))); + add_options.push_back(AddOption("Texture2DArrayUniform", "Textures", "Variables", "VisualShaderNodeTexture2DArrayUniform", TTR("2D array of textures uniform lookup."), {}, VisualShaderNode::PORT_TYPE_SAMPLER)); + add_options.push_back(AddOption("Texture3DUniform", "Textures", "Variables", "VisualShaderNodeTexture3DUniform", TTR("3D texture uniform lookup."), {}, VisualShaderNode::PORT_TYPE_SAMPLER)); // TRANSFORM @@ -5297,137 +5423,196 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("VectorFunc", "Vector", "Common", "VisualShaderNodeVectorFunc", TTR("Vector function."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D)); add_options.push_back(AddOption("VectorOp", "Vector", "Common", "VisualShaderNodeVectorOp", TTR("Vector operator."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D)); - add_options.push_back(AddOption("VectorCompose", "Vector", "Common", "VisualShaderNodeVectorCompose", TTR("Composes vector from three scalars."))); - add_options.push_back(AddOption("VectorDecompose", "Vector", "Common", "VisualShaderNodeVectorDecompose", TTR("Decomposes vector to three scalars."))); + add_options.push_back(AddOption("VectorCompose", "Vector", "Common", "VisualShaderNodeVectorCompose", TTR("Composes vector from scalars."))); + add_options.push_back(AddOption("VectorDecompose", "Vector", "Common", "VisualShaderNodeVectorDecompose", TTR("Decomposes vector to scalars."))); - add_options.push_back(AddOption("Vector2Compose", "Vector", "Composition", "VisualShaderNodeVectorCompose", TTR("Composes 2D vector from three scalars."), { VisualShaderNodeVectorCompose::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); - add_options.push_back(AddOption("Vector2Decompose", "Vector", "Composition", "VisualShaderNodeVectorDecompose", TTR("Decomposes 2D vector to three scalars."), { VisualShaderNodeVectorCompose::OP_TYPE_VECTOR_2D })); + add_options.push_back(AddOption("Vector2Compose", "Vector", "Composition", "VisualShaderNodeVectorCompose", TTR("Composes 2D vector from two scalars."), { VisualShaderNodeVectorCompose::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); + add_options.push_back(AddOption("Vector2Decompose", "Vector", "Composition", "VisualShaderNodeVectorDecompose", TTR("Decomposes 2D vector to two scalars."), { VisualShaderNodeVectorDecompose::OP_TYPE_VECTOR_2D })); add_options.push_back(AddOption("Vector3Compose", "Vector", "Composition", "VisualShaderNodeVectorCompose", TTR("Composes 3D vector from three scalars."), { VisualShaderNodeVectorCompose::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); - add_options.push_back(AddOption("Vector3Decompose", "Vector", "Composition", "VisualShaderNodeVectorDecompose", TTR("Decomposes 3D vector to three scalars."), { VisualShaderNodeVectorCompose::OP_TYPE_VECTOR_3D })); + add_options.push_back(AddOption("Vector3Decompose", "Vector", "Composition", "VisualShaderNodeVectorDecompose", TTR("Decomposes 3D vector to three scalars."), { VisualShaderNodeVectorDecompose::OP_TYPE_VECTOR_3D })); + add_options.push_back(AddOption("Vector4Compose", "Vector", "Composition", "VisualShaderNodeVectorCompose", TTR("Composes 4D vector from four scalars."), { VisualShaderNodeVectorCompose::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); + add_options.push_back(AddOption("Vector4Decompose", "Vector", "Composition", "VisualShaderNodeVectorDecompose", TTR("Decomposes 4D vector to four scalars."), { VisualShaderNodeVectorDecompose::OP_TYPE_VECTOR_4D })); add_options.push_back(AddOption("Abs", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the absolute value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ABS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Abs", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the absolute value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ABS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Abs", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the absolute value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ABS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("ACos", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ACOS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("ACos", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ACOS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("ACos", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ACOS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("ACosH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ACOSH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("ACosH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ACOSH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("ACosH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ACOSH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("ASin", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ASIN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("ASin", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ASIN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("ASin", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ASIN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("ASinH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ASINH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("ASinH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ASINH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("ASinH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ASINH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("ATan", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ATAN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("ATan", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ATAN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("ATan", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ATAN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("ATan2", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the arc-tangent of the parameters."), { VisualShaderNodeVectorOp::OP_ATAN2, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("ATan2", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the arc-tangent of the parameters."), { VisualShaderNodeVectorOp::OP_ATAN2, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("ATan2", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the arc-tangent of the parameters."), { VisualShaderNodeVectorOp::OP_ATAN2, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("ATanH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ATANH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("ATanH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ATANH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("ATanH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ATANH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Ceil", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer that is greater than or equal to the parameter."), { VisualShaderNodeVectorFunc::FUNC_CEIL }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Ceil", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer that is greater than or equal to the parameter."), { VisualShaderNodeVectorFunc::FUNC_CEIL, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Ceil", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer that is greater than or equal to the parameter."), { VisualShaderNodeVectorFunc::FUNC_CEIL, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Clamp", "Vector", "Functions", "VisualShaderNodeClamp", TTR("Constrains a value to lie between two further values."), { VisualShaderNodeClamp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Clamp", "Vector", "Functions", "VisualShaderNodeClamp", TTR("Constrains a value to lie between two further values."), { VisualShaderNodeClamp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Clamp", "Vector", "Functions", "VisualShaderNodeClamp", TTR("Constrains a value to lie between two further values."), { VisualShaderNodeClamp::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Cos", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_COS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Cos", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_COS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Cos", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_COS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("CosH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_COSH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("CosH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_COSH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("CosH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_COSH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Cross", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Calculates the cross product of two vectors."), { VisualShaderNodeVectorOp::OP_CROSS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); add_options.push_back(AddOption("Degrees", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in radians to degrees."), { VisualShaderNodeVectorFunc::FUNC_DEGREES, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Degrees", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in radians to degrees."), { VisualShaderNodeVectorFunc::FUNC_DEGREES, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Degrees", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in radians to degrees."), { VisualShaderNodeVectorFunc::FUNC_DEGREES, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("DFdX", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Derivative in 'x' using local differencing."), { VisualShaderNodeDerivativeFunc::FUNC_X, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true)); add_options.push_back(AddOption("DFdX", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Derivative in 'x' using local differencing."), { VisualShaderNodeDerivativeFunc::FUNC_X, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true)); + add_options.push_back(AddOption("DFdX", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Derivative in 'x' using local differencing."), { VisualShaderNodeDerivativeFunc::FUNC_X, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true)); add_options.push_back(AddOption("DFdY", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Derivative in 'y' using local differencing."), { VisualShaderNodeDerivativeFunc::FUNC_Y, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true)); add_options.push_back(AddOption("DFdY", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Derivative in 'y' using local differencing."), { VisualShaderNodeDerivativeFunc::FUNC_Y, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true)); + add_options.push_back(AddOption("DFdY", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Derivative in 'y' using local differencing."), { VisualShaderNodeDerivativeFunc::FUNC_Y, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true)); add_options.push_back(AddOption("Distance2D", "Vector", "Functions", "VisualShaderNodeVectorDistance", TTR("Returns the distance between two points."), { VisualShaderNodeVectorDistance::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Distance3D", "Vector", "Functions", "VisualShaderNodeVectorDistance", TTR("Returns the distance between two points."), { VisualShaderNodeVectorDistance::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Distance4D", "Vector", "Functions", "VisualShaderNodeVectorDistance", TTR("Returns the distance between two points."), { VisualShaderNodeVectorDistance::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Dot", "Vector", "Functions", "VisualShaderNodeDotProduct", TTR("Calculates the dot product of two vectors."), {}, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Exp", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-e Exponential."), { VisualShaderNodeVectorFunc::FUNC_EXP, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Exp", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-e Exponential."), { VisualShaderNodeVectorFunc::FUNC_EXP, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Exp", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-e Exponential."), { VisualShaderNodeVectorFunc::FUNC_EXP, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Exp2", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-2 Exponential."), { VisualShaderNodeVectorFunc::FUNC_EXP2, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Exp2", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-2 Exponential."), { VisualShaderNodeVectorFunc::FUNC_EXP2, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Exp2", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-2 Exponential."), { VisualShaderNodeVectorFunc::FUNC_EXP2, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("FaceForward", "Vector", "Functions", "VisualShaderNodeFaceForward", TTR("Returns the vector that points in the same direction as a reference vector. The function has three vector parameters : N, the vector to orient, I, the incident vector, and Nref, the reference vector. If the dot product of I and Nref is smaller than zero the return value is N. Otherwise -N is returned."), { VisualShaderNodeFaceForward::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("FaceForward", "Vector", "Functions", "VisualShaderNodeFaceForward", TTR("Returns the vector that points in the same direction as a reference vector. The function has three vector parameters : N, the vector to orient, I, the incident vector, and Nref, the reference vector. If the dot product of I and Nref is smaller than zero the return value is N. Otherwise -N is returned."), { VisualShaderNodeFaceForward::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("FaceForward", "Vector", "Functions", "VisualShaderNodeFaceForward", TTR("Returns the vector that points in the same direction as a reference vector. The function has three vector parameters : N, the vector to orient, I, the incident vector, and Nref, the reference vector. If the dot product of I and Nref is smaller than zero the return value is N. Otherwise -N is returned."), { VisualShaderNodeFaceForward::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Floor", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer less than or equal to the parameter."), { VisualShaderNodeVectorFunc::FUNC_FLOOR, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Floor", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer less than or equal to the parameter."), { VisualShaderNodeVectorFunc::FUNC_FLOOR, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); - add_options.push_back(AddOption("Fract", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Computes the fractional part of the argument."), { VisualShaderNodeVectorFunc::FUNC_FRAC, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); - add_options.push_back(AddOption("Fract", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Computes the fractional part of the argument."), { VisualShaderNodeVectorFunc::FUNC_FRAC, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Floor", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer less than or equal to the parameter."), { VisualShaderNodeVectorFunc::FUNC_FLOOR, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); + add_options.push_back(AddOption("Fract", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Computes the fractional part of the argument."), { VisualShaderNodeVectorFunc::FUNC_FRACT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); + add_options.push_back(AddOption("Fract", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Computes the fractional part of the argument."), { VisualShaderNodeVectorFunc::FUNC_FRACT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Fract", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Computes the fractional part of the argument."), { VisualShaderNodeVectorFunc::FUNC_FRACT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Fresnel", "Vector", "Functions", "VisualShaderNodeFresnel", TTR("Returns falloff based on the dot product of surface normal and view direction of camera (pass associated inputs to it)."), {}, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("InverseSqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse of the square root of the parameter."), { VisualShaderNodeVectorFunc::FUNC_INVERSE_SQRT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("InverseSqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse of the square root of the parameter."), { VisualShaderNodeVectorFunc::FUNC_INVERSE_SQRT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); - add_options.push_back(AddOption("Length", "Vector", "Functions", "VisualShaderNodeVectorLen", TTR("Calculates the length of a vector."), { VisualShaderNodeVectorLen::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_SCALAR)); - add_options.push_back(AddOption("Length", "Vector", "Functions", "VisualShaderNodeVectorLen", TTR("Calculates the length of a vector."), { VisualShaderNodeVectorLen::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("InverseSqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse of the square root of the parameter."), { VisualShaderNodeVectorFunc::FUNC_INVERSE_SQRT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); + add_options.push_back(AddOption("Length2D", "Vector", "Functions", "VisualShaderNodeVectorLen", TTR("Calculates the length of a vector."), { VisualShaderNodeVectorLen::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Length3D", "Vector", "Functions", "VisualShaderNodeVectorLen", TTR("Calculates the length of a vector."), { VisualShaderNodeVectorLen::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Length4D", "Vector", "Functions", "VisualShaderNodeVectorLen", TTR("Calculates the length of a vector."), { VisualShaderNodeVectorLen::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Log", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Natural logarithm."), { VisualShaderNodeVectorFunc::FUNC_LOG, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Log", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Natural logarithm."), { VisualShaderNodeVectorFunc::FUNC_LOG, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Log", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Natural logarithm."), { VisualShaderNodeVectorFunc::FUNC_LOG, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Log2", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-2 logarithm."), { VisualShaderNodeVectorFunc::FUNC_LOG2, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Log2", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-2 logarithm."), { VisualShaderNodeVectorFunc::FUNC_LOG2, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Log2", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-2 logarithm."), { VisualShaderNodeVectorFunc::FUNC_LOG2, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Max", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the greater of two values."), { VisualShaderNodeVectorOp::OP_MAX, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Max", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the greater of two values."), { VisualShaderNodeVectorOp::OP_MAX, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Max", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the greater of two values."), { VisualShaderNodeVectorOp::OP_MAX, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Min", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the lesser of two values."), { VisualShaderNodeVectorOp::OP_MIN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Min", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the lesser of two values."), { VisualShaderNodeVectorOp::OP_MIN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Min", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the lesser of two values."), { VisualShaderNodeVectorOp::OP_MIN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Mix", "Vector", "Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two vectors."), { VisualShaderNodeMix::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Mix", "Vector", "Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two vectors."), { VisualShaderNodeMix::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Mix", "Vector", "Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two vectors."), { VisualShaderNodeMix::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("MixS", "Vector", "Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two vectors using scalar."), { VisualShaderNodeMix::OP_TYPE_VECTOR_2D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("MixS", "Vector", "Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two vectors using scalar."), { VisualShaderNodeMix::OP_TYPE_VECTOR_3D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("MixS", "Vector", "Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two vectors using scalar."), { VisualShaderNodeMix::OP_TYPE_VECTOR_4D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("MultiplyAdd", "Vector", "Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on vectors."), { VisualShaderNodeMultiplyAdd::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("MultiplyAdd", "Vector", "Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on vectors."), { VisualShaderNodeMultiplyAdd::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("MultiplyAdd", "Vector", "Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on vectors."), { VisualShaderNodeMultiplyAdd::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Negate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Negate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Negate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Normalize", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Calculates the normalize product of vector."), { VisualShaderNodeVectorFunc::FUNC_NORMALIZE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Normalize", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Calculates the normalize product of vector."), { VisualShaderNodeVectorFunc::FUNC_NORMALIZE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Normalize", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Calculates the normalize product of vector."), { VisualShaderNodeVectorFunc::FUNC_NORMALIZE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("OneMinus", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 - vector"), { VisualShaderNodeVectorFunc::FUNC_ONEMINUS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("OneMinus", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 - vector"), { VisualShaderNodeVectorFunc::FUNC_ONEMINUS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("OneMinus", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 - vector"), { VisualShaderNodeVectorFunc::FUNC_ONEMINUS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Pow", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the value of the first parameter raised to the power of the second."), { VisualShaderNodeVectorOp::OP_POW, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Pow", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the value of the first parameter raised to the power of the second."), { VisualShaderNodeVectorOp::OP_POW, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Pow", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the value of the first parameter raised to the power of the second."), { VisualShaderNodeVectorOp::OP_POW, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Radians", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in degrees to radians."), { VisualShaderNodeVectorFunc::FUNC_RADIANS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Radians", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in degrees to radians."), { VisualShaderNodeVectorFunc::FUNC_RADIANS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Radians", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in degrees to radians."), { VisualShaderNodeVectorFunc::FUNC_RADIANS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Reciprocal", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 / vector"), { VisualShaderNodeVectorFunc::FUNC_RECIPROCAL, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Reciprocal", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 / vector"), { VisualShaderNodeVectorFunc::FUNC_RECIPROCAL, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Reciprocal", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 / vector"), { VisualShaderNodeVectorFunc::FUNC_RECIPROCAL, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Reflect", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the vector that points in the direction of reflection ( a : incident vector, b : normal vector )."), { VisualShaderNodeVectorOp::OP_REFLECT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); add_options.push_back(AddOption("Refract", "Vector", "Functions", "VisualShaderNodeVectorRefract", TTR("Returns the vector that points in the direction of refraction."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D)); add_options.push_back(AddOption("Round", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer to the parameter."), { VisualShaderNodeVectorFunc::FUNC_ROUND, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Round", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer to the parameter."), { VisualShaderNodeVectorFunc::FUNC_ROUND, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Round", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer to the parameter."), { VisualShaderNodeVectorFunc::FUNC_ROUND, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("RoundEven", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest even integer to the parameter."), { VisualShaderNodeVectorFunc::FUNC_ROUNDEVEN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("RoundEven", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest even integer to the parameter."), { VisualShaderNodeVectorFunc::FUNC_ROUNDEVEN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("RoundEven", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest even integer to the parameter."), { VisualShaderNodeVectorFunc::FUNC_ROUNDEVEN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Saturate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Clamps the value between 0.0 and 1.0."), { VisualShaderNodeVectorFunc::FUNC_SATURATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Saturate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Clamps the value between 0.0 and 1.0."), { VisualShaderNodeVectorFunc::FUNC_SATURATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Saturate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Clamps the value between 0.0 and 1.0."), { VisualShaderNodeVectorFunc::FUNC_SATURATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Sign", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Extracts the sign of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SIGN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Sign", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Extracts the sign of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SIGN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Sign", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Extracts the sign of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SIGN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Sin", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SIN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Sin", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SIN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Sin", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SIN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("SinH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SINH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("SinH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SINH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("SinH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SINH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Sqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the square root of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SQRT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Sqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the square root of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SQRT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Sqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the square root of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SQRT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("SmoothStep", "Vector", "Functions", "VisualShaderNodeSmoothStep", TTR("SmoothStep function( vector(edge0), vector(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), { VisualShaderNodeSmoothStep::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("SmoothStep", "Vector", "Functions", "VisualShaderNodeSmoothStep", TTR("SmoothStep function( vector(edge0), vector(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), { VisualShaderNodeSmoothStep::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("SmoothStep", "Vector", "Functions", "VisualShaderNodeSmoothStep", TTR("SmoothStep function( vector(edge0), vector(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), { VisualShaderNodeSmoothStep::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("SmoothStepS", "Vector", "Functions", "VisualShaderNodeSmoothStep", TTR("SmoothStep function( scalar(edge0), scalar(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), { VisualShaderNodeSmoothStep::OP_TYPE_VECTOR_2D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("SmoothStepS", "Vector", "Functions", "VisualShaderNodeSmoothStep", TTR("SmoothStep function( scalar(edge0), scalar(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), { VisualShaderNodeSmoothStep::OP_TYPE_VECTOR_3D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("SmoothStepS", "Vector", "Functions", "VisualShaderNodeSmoothStep", TTR("SmoothStep function( scalar(edge0), scalar(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), { VisualShaderNodeSmoothStep::OP_TYPE_VECTOR_4D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Step", "Vector", "Functions", "VisualShaderNodeStep", TTR("Step function( vector(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), { VisualShaderNodeStep::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Step", "Vector", "Functions", "VisualShaderNodeStep", TTR("Step function( vector(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), { VisualShaderNodeStep::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); add_options.push_back(AddOption("StepS", "Vector", "Functions", "VisualShaderNodeStep", TTR("Step function( scalar(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), { VisualShaderNodeStep::OP_TYPE_VECTOR_2D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("StepS", "Vector", "Functions", "VisualShaderNodeStep", TTR("Step function( scalar(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), { VisualShaderNodeStep::OP_TYPE_VECTOR_3D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("StepS", "Vector", "Functions", "VisualShaderNodeStep", TTR("Step function( scalar(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), { VisualShaderNodeStep::OP_TYPE_VECTOR_4D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Sum", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Sum of absolute derivative in 'x' and 'y'."), { VisualShaderNodeDerivativeFunc::FUNC_SUM, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true)); add_options.push_back(AddOption("Sum", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Sum of absolute derivative in 'x' and 'y'."), { VisualShaderNodeDerivativeFunc::FUNC_SUM, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true)); + add_options.push_back(AddOption("Sum", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Sum of absolute derivative in 'x' and 'y'."), { VisualShaderNodeDerivativeFunc::FUNC_SUM, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true)); add_options.push_back(AddOption("Tan", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TAN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Tan", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TAN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Tan", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TAN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("TanH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TANH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("TanH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TANH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("TanH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TANH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Trunc", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the truncated value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TRUNC, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Trunc", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the truncated value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TRUNC, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Trunc", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the truncated value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TRUNC, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Add", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Adds 2D vector to 2D vector."), { VisualShaderNodeVectorOp::OP_ADD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Add", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Adds 3D vector to 3D vector."), { VisualShaderNodeVectorOp::OP_ADD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Add", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Adds 4D vector to 4D vector."), { VisualShaderNodeVectorOp::OP_ADD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Divide", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Divides 2D vector by 2D vector."), { VisualShaderNodeVectorOp::OP_DIV, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Divide", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Divides 3D vector by 3D vector."), { VisualShaderNodeVectorOp::OP_DIV, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Divide", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Divides 4D vector by 4D vector."), { VisualShaderNodeVectorOp::OP_DIV, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Multiply", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Multiplies 2D vector by 2D vector."), { VisualShaderNodeVectorOp::OP_MUL, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Multiply", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Multiplies 3D vector by 3D vector."), { VisualShaderNodeVectorOp::OP_MUL, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Multiply", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Multiplies 4D vector by 4D vector."), { VisualShaderNodeVectorOp::OP_MUL, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Remainder", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Returns the remainder of the two 2D vectors."), { VisualShaderNodeVectorOp::OP_MOD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Remainder", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Returns the remainder of the two 3D vectors."), { VisualShaderNodeVectorOp::OP_MOD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Remainder", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Returns the remainder of the two 4D vectors."), { VisualShaderNodeVectorOp::OP_MOD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Subtract", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Subtracts 2D vector from 2D vector."), { VisualShaderNodeVectorOp::OP_SUB, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Subtract", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Subtracts 3D vector from 3D vector."), { VisualShaderNodeVectorOp::OP_SUB, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Subtract", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Subtracts 4D vector from 4D vector."), { VisualShaderNodeVectorOp::OP_SUB, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Vector2Constant", "Vector", "Variables", "VisualShaderNodeVec2Constant", TTR("2D vector constant."), {}, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Vector2Uniform", "Vector", "Variables", "VisualShaderNodeVec2Uniform", TTR("2D vector uniform."), {}, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Vector3Constant", "Vector", "Variables", "VisualShaderNodeVec3Constant", TTR("3D vector constant."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D)); add_options.push_back(AddOption("Vector3Uniform", "Vector", "Variables", "VisualShaderNodeVec3Uniform", TTR("3D vector uniform."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Vector4Constant", "Vector", "Variables", "VisualShaderNodeVec4Constant", TTR("4D vector constant."), {}, VisualShaderNode::PORT_TYPE_VECTOR_4D)); + add_options.push_back(AddOption("Vector4Uniform", "Vector", "Variables", "VisualShaderNodeVec4Uniform", TTR("4D vector uniform."), {}, VisualShaderNode::PORT_TYPE_VECTOR_4D)); // SPECIAL @@ -5460,48 +5645,6 @@ VisualShaderEditor::VisualShaderEditor() { property_editor->connect("variant_changed", callable_mp(this, &VisualShaderEditor::_port_edited)); } -///////////////// - -void VisualShaderEditorPlugin::edit(Object *p_object) { - visual_shader_editor->edit(Object::cast_to<VisualShader>(p_object)); -} - -bool VisualShaderEditorPlugin::handles(Object *p_object) const { - return p_object->is_class("VisualShader"); -} - -void VisualShaderEditorPlugin::make_visible(bool p_visible) { - if (p_visible) { - //editor->hide_animation_player_editors(); - //editor->animation_panel_make_visible(true); - button->show(); - EditorNode::get_singleton()->make_bottom_panel_item_visible(visual_shader_editor); - visual_shader_editor->update_nodes(); - visual_shader_editor->set_process_input(true); - //visual_shader_editor->set_process(true); - } else { - if (visual_shader_editor->is_visible_in_tree()) { - EditorNode::get_singleton()->hide_bottom_panel(); - } - button->hide(); - visual_shader_editor->set_process_input(false); - //visual_shader_editor->set_process(false); - } -} - -VisualShaderEditorPlugin::VisualShaderEditorPlugin() { - visual_shader_editor = memnew(VisualShaderEditor); - visual_shader_editor->set_custom_minimum_size(Size2(0, 300) * EDSCALE); - - button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("VisualShader"), visual_shader_editor); - button->hide(); -} - -VisualShaderEditorPlugin::~VisualShaderEditorPlugin() { -} - -//////////////// - class VisualShaderNodePluginInputEditor : public OptionButton { GDCLASS(VisualShaderNodePluginInputEditor, OptionButton); @@ -5525,11 +5668,12 @@ public: void setup(const Ref<VisualShaderNodeInput> &p_input) { input = p_input; - Ref<Texture2D> type_icon[7] = { + Ref<Texture2D> type_icon[] = { EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("float"), SNAME("EditorIcons")), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("int"), SNAME("EditorIcons")), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector2"), SNAME("EditorIcons")), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector3"), SNAME("EditorIcons")), + EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector4"), SNAME("EditorIcons")), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("bool"), SNAME("EditorIcons")), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Transform3D"), SNAME("EditorIcons")), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("ImageTexture"), SNAME("EditorIcons")), @@ -5578,6 +5722,7 @@ public: EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("float"), SNAME("EditorIcons")), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector2"), SNAME("EditorIcons")), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector3"), SNAME("EditorIcons")), + EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector4"), SNAME("EditorIcons")), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Color"), SNAME("EditorIcons")), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Transform3D"), SNAME("EditorIcons")), }; @@ -5652,12 +5797,13 @@ public: void setup(const Ref<VisualShaderNodeUniformRef> &p_uniform_ref) { uniform_ref = p_uniform_ref; - Ref<Texture2D> type_icon[8] = { + Ref<Texture2D> type_icon[] = { EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("float"), SNAME("EditorIcons")), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("int"), SNAME("EditorIcons")), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("bool"), SNAME("EditorIcons")), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector2"), SNAME("EditorIcons")), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector3"), SNAME("EditorIcons")), + EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector4"), SNAME("EditorIcons")), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Transform3D"), SNAME("EditorIcons")), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Color"), SNAME("EditorIcons")), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("ImageTexture"), SNAME("EditorIcons")), @@ -5683,7 +5829,7 @@ public: class VisualShaderNodePluginDefaultEditor : public VBoxContainer { GDCLASS(VisualShaderNodePluginDefaultEditor, VBoxContainer); Ref<Resource> parent_resource; - int node_id; + int node_id = 0; VisualShader::Type shader_type; public: @@ -5700,18 +5846,18 @@ public: undo_redo->add_undo_property(node.ptr(), p_property, node->get(p_property)); if (p_value.get_type() == Variant::OBJECT) { - RES prev_res = node->get(p_property); - RES curr_res = p_value; + Ref<Resource> prev_res = node->get(p_property); + Ref<Resource> curr_res = p_value; if (curr_res.is_null()) { - undo_redo->add_do_method(this, "_open_inspector", (RES)parent_resource.ptr()); + undo_redo->add_do_method(this, "_open_inspector", (Ref<Resource>)parent_resource.ptr()); } else { - undo_redo->add_do_method(this, "_open_inspector", (RES)curr_res.ptr()); + undo_redo->add_do_method(this, "_open_inspector", (Ref<Resource>)curr_res.ptr()); } if (!prev_res.is_null()) { - undo_redo->add_undo_method(this, "_open_inspector", (RES)prev_res.ptr()); + undo_redo->add_undo_method(this, "_open_inspector", (Ref<Resource>)prev_res.ptr()); } else { - undo_redo->add_undo_method(this, "_open_inspector", (RES)parent_resource.ptr()); + undo_redo->add_undo_method(this, "_open_inspector", (Ref<Resource>)parent_resource.ptr()); } } if (p_property != "constant") { @@ -5738,15 +5884,15 @@ public: } } - void _resource_selected(const String &p_path, RES p_resource) { + void _resource_selected(const String &p_path, Ref<Resource> p_resource) { _open_inspector(p_resource); } - void _open_inspector(RES p_resource) { + void _open_inspector(Ref<Resource> p_resource) { InspectorDock::get_inspector_singleton()->edit(p_resource.ptr()); } - bool updating; + bool updating = false; Ref<VisualShaderNode> node; Vector<EditorProperty *> properties; Vector<Label *> prop_names; @@ -5757,7 +5903,7 @@ public: } } - void setup(Ref<Resource> p_parent_resource, Vector<EditorProperty *> p_properties, const Vector<StringName> &p_names, const Map<StringName, String> &p_overrided_names, Ref<VisualShaderNode> p_node) { + void setup(Ref<Resource> p_parent_resource, Vector<EditorProperty *> p_properties, const Vector<StringName> &p_names, const HashMap<StringName, String> &p_overrided_names, Ref<VisualShaderNode> p_node) { parent_resource = p_parent_resource; updating = false; node = p_node; @@ -5864,6 +6010,8 @@ Control *VisualShaderNodePluginDefault::create_editor(const Ref<Resource> &p_par prop->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); } else if (Object::cast_to<EditorPropertyTransform3D>(prop) || Object::cast_to<EditorPropertyVector3>(prop)) { prop->set_custom_minimum_size(Size2(250 * EDSCALE, 0)); + } else if (Object::cast_to<EditorPropertyQuaternion>(prop)) { + prop->set_custom_minimum_size(Size2(320 * EDSCALE, 0)); } else if (Object::cast_to<EditorPropertyFloat>(prop)) { prop->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); } else if (Object::cast_to<EditorPropertyEnum>(prop)) { @@ -6069,7 +6217,8 @@ void VisualShaderNodePortPreview::setup(const Ref<VisualShader> &p_shader, Visua } Size2 VisualShaderNodePortPreview::get_minimum_size() const { - return Size2(100, 100) * EDSCALE; + int port_preview_size = EditorSettings::get_singleton()->get("editors/visual_editors/visualshader/port_preview_size"); + return Size2(port_preview_size, port_preview_size) * EDSCALE; } void VisualShaderNodePortPreview::_notification(int p_what) { diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index 4087fc583c..b8da266ed7 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -48,7 +48,7 @@ class VisualShaderNodePlugin : public RefCounted { protected: static void _bind_methods(); - GDVIRTUAL2RC(Object *, _create_editor, RES, Ref<VisualShaderNode>) + GDVIRTUAL2RC(Object *, _create_editor, Ref<Resource>, Ref<VisualShaderNode>) public: virtual Control *create_editor(const Ref<Resource> &p_parent_resource, const Ref<VisualShaderNode> &p_node); @@ -72,8 +72,8 @@ private: GraphNode *graph_node = nullptr; bool preview_visible = false; int preview_pos = 0; - Map<int, InputPort> input_ports; - Map<int, Port> output_ports; + HashMap<int, InputPort> input_ports; + HashMap<int, Port> output_ports; VBoxContainer *preview_box = nullptr; LineEdit *uniform_name = nullptr; CodeEdit *expression_edit = nullptr; @@ -81,18 +81,18 @@ private: }; Ref<VisualShader> visual_shader; - Map<int, Link> links; + HashMap<int, Link> links; List<VisualShader::Connection> connections; bool dirty = false; - Color vector_expanded_color[3]; + Color vector_expanded_color[4]; protected: static void _bind_methods(); public: void register_shader(VisualShader *p_visual_shader); - void set_connections(List<VisualShader::Connection> &p_connections); + void set_connections(const List<VisualShader::Connection> &p_connections); void register_link(VisualShader::Type p_type, int p_id, VisualShaderNode *p_visual_node, GraphNode *p_graph_node); void register_output_port(int p_id, int p_port, TextureButton *p_button); void register_uniform_name(int p_id, LineEdit *p_uniform_name); @@ -346,7 +346,7 @@ class VisualShaderEditor : public VBoxContainer { void _delete_nodes(int p_type, const List<int> &p_nodes); void _delete_node_request(int p_type, int p_node); - void _delete_nodes_request(); + void _delete_nodes_request(const TypedArray<StringName> &p_nodes); void _node_changed(int p_id); @@ -358,8 +358,8 @@ class VisualShaderEditor : public VBoxContainer { int from_node = -1; int from_slot = -1; - Set<int> selected_constants; - Set<int> selected_uniforms; + HashSet<int> selected_constants; + HashSet<int> selected_uniforms; int selected_comment = -1; int selected_float_constant = -1; @@ -468,7 +468,7 @@ class VisualShaderEditor : public VBoxContainer { bool _is_available(int p_mode); void _update_created_node(GraphNode *node); void _update_uniforms(bool p_update_refs); - void _update_uniform_refs(Set<String> &p_names); + void _update_uniform_refs(HashSet<String> &p_names); void _update_varyings(); void _visibility_changed(); @@ -493,23 +493,6 @@ public: VisualShaderEditor(); }; -class VisualShaderEditorPlugin : public EditorPlugin { - GDCLASS(VisualShaderEditorPlugin, EditorPlugin); - - VisualShaderEditor *visual_shader_editor = nullptr; - Button *button = nullptr; - -public: - virtual String get_name() const override { return "VisualShader"; } - bool has_main_screen() const override { return false; } - virtual void edit(Object *p_object) override; - virtual bool handles(Object *p_object) const override; - virtual void make_visible(bool p_visible) override; - - VisualShaderEditorPlugin(); - ~VisualShaderEditorPlugin(); -}; - class VisualShaderNodePluginDefault : public VisualShaderNodePlugin { GDCLASS(VisualShaderNodePluginDefault, VisualShaderNodePlugin); |