diff options
27 files changed, 483 insertions, 169 deletions
diff --git a/doc/classes/CompressedCubemap.xml b/doc/classes/CompressedCubemap.xml index 745e9c87e3..f98dfc61a3 100644 --- a/doc/classes/CompressedCubemap.xml +++ b/doc/classes/CompressedCubemap.xml @@ -5,10 +5,11 @@ </brief_description> <description> A cubemap that is loaded from a [code].ccube[/code] file. This file format is internal to Godot; it is created by importing other image formats with the import system. [CompressedCubemap] can use one of 4 compresson methods: - - Uncompressed (uncompressed on the GPU) - Lossless (WebP or PNG, uncompressed on the GPU) - Lossy (WebP, uncompressed on the GPU) - VRAM Compressed (compressed on the GPU) + - VRAM Uncompressed (uncompressed on the GPU) + - Basis Universal (compressed on the GPU. Lower file sizes than VRAM Compressed, but slower to compress and lower quality than VRAM Compressed) Only [b]VRAM Compressed[/b] actually reduces the memory usage on the GPU. The [b]Lossless[/b] and [b]Lossy[/b] compression methods will reduce the required storage on disk, but they will not reduce memory usage on the GPU as the texture is sent to the GPU uncompressed. Using [b]VRAM Compressed[/b] also improves loading times, as VRAM-compressed textures are faster to load compared to textures using lossless or lossy compression. VRAM compression can exhibit noticeable artifacts and is intended to be used for 3D rendering, not 2D. See [Cubemap] for a general description of cubemaps. diff --git a/doc/classes/CompressedCubemapArray.xml b/doc/classes/CompressedCubemapArray.xml index 8606027242..75e9b3d513 100644 --- a/doc/classes/CompressedCubemapArray.xml +++ b/doc/classes/CompressedCubemapArray.xml @@ -5,10 +5,11 @@ </brief_description> <description> A cubemap array that is loaded from a [code].ccubearray[/code] file. This file format is internal to Godot; it is created by importing other image formats with the import system. [CompressedCubemapArray] can use one of 4 compresson methods: - - Uncompressed (uncompressed on the GPU) - Lossless (WebP or PNG, uncompressed on the GPU) - Lossy (WebP, uncompressed on the GPU) - VRAM Compressed (compressed on the GPU) + - VRAM Uncompressed (uncompressed on the GPU) + - Basis Universal (compressed on the GPU. Lower file sizes than VRAM Compressed, but slower to compress and lower quality than VRAM Compressed) Only [b]VRAM Compressed[/b] actually reduces the memory usage on the GPU. The [b]Lossless[/b] and [b]Lossy[/b] compression methods will reduce the required storage on disk, but they will not reduce memory usage on the GPU as the texture is sent to the GPU uncompressed. Using [b]VRAM Compressed[/b] also improves loading times, as VRAM-compressed textures are faster to load compared to textures using lossless or lossy compression. VRAM compression can exhibit noticeable artifacts and is intended to be used for 3D rendering, not 2D. See [CubemapArray] for a general description of cubemap arrays. diff --git a/doc/classes/CompressedTexture2D.xml b/doc/classes/CompressedTexture2D.xml index f4e7398d6e..9fde9ebeea 100644 --- a/doc/classes/CompressedTexture2D.xml +++ b/doc/classes/CompressedTexture2D.xml @@ -5,10 +5,11 @@ </brief_description> <description> A texture that is loaded from a [code].ctex[/code] file. This file format is internal to Godot; it is created by importing other image formats with the import system. [CompressedTexture2D] can use one of 4 compression methods (including a lack of any compression): - - Uncompressed (uncompressed on the GPU) - Lossless (WebP or PNG, uncompressed on the GPU) - Lossy (WebP, uncompressed on the GPU) - VRAM Compressed (compressed on the GPU) + - VRAM Uncompressed (uncompressed on the GPU) + - Basis Universal (compressed on the GPU. Lower file sizes than VRAM Compressed, but slower to compress and lower quality than VRAM Compressed) Only [b]VRAM Compressed[/b] actually reduces the memory usage on the GPU. The [b]Lossless[/b] and [b]Lossy[/b] compression methods will reduce the required storage on disk, but they will not reduce memory usage on the GPU as the texture is sent to the GPU uncompressed. Using [b]VRAM Compressed[/b] also improves loading times, as VRAM-compressed textures are faster to load compared to textures using lossless or lossy compression. VRAM compression can exhibit noticeable artifacts and is intended to be used for 3D rendering, not 2D. </description> diff --git a/doc/classes/CompressedTexture2DArray.xml b/doc/classes/CompressedTexture2DArray.xml index bfb3cabe05..71888bf824 100644 --- a/doc/classes/CompressedTexture2DArray.xml +++ b/doc/classes/CompressedTexture2DArray.xml @@ -5,10 +5,11 @@ </brief_description> <description> A texture array that is loaded from a [code].ctexarray[/code] file. This file format is internal to Godot; it is created by importing other image formats with the import system. [CompressedTexture2DArray] can use one of 4 compresson methods: - - Uncompressed (uncompressed on the GPU) - Lossless (WebP or PNG, uncompressed on the GPU) - Lossy (WebP, uncompressed on the GPU) - VRAM Compressed (compressed on the GPU) + - VRAM Uncompressed (uncompressed on the GPU) + - Basis Universal (compressed on the GPU. Lower file sizes than VRAM Compressed, but slower to compress and lower quality than VRAM Compressed) Only [b]VRAM Compressed[/b] actually reduces the memory usage on the GPU. The [b]Lossless[/b] and [b]Lossy[/b] compression methods will reduce the required storage on disk, but they will not reduce memory usage on the GPU as the texture is sent to the GPU uncompressed. Using [b]VRAM Compressed[/b] also improves loading times, as VRAM-compressed textures are faster to load compared to textures using lossless or lossy compression. VRAM compression can exhibit noticeable artifacts and is intended to be used for 3D rendering, not 2D. See [Texture2DArray] for a general description of texture arrays. diff --git a/doc/classes/CompressedTextureLayered.xml b/doc/classes/CompressedTextureLayered.xml index 43ce8f5df7..da34180c93 100644 --- a/doc/classes/CompressedTextureLayered.xml +++ b/doc/classes/CompressedTextureLayered.xml @@ -4,13 +4,7 @@ Base class for texture arrays that can optionally be compressed. </brief_description> <description> - A texture array that is loaded from a [code].ctexarray[/code] file. This file format is internal to Godot; it is created by importing other image formats with the import system. [CompressedTexture2D] can use one of 4 compresson methods: - - Uncompressed (uncompressed on the GPU) - - Lossless (WebP or PNG, uncompressed on the GPU) - - Lossy (WebP, uncompressed on the GPU) - - VRAM Compressed (compressed on the GPU) - Only [b]VRAM Compressed[/b] actually reduces the memory usage on the GPU. The [b]Lossless[/b] and [b]Lossy[/b] compression methods will reduce the required storage on disk, but they will not reduce memory usage on the GPU as the texture is sent to the GPU uncompressed. - Using [b]VRAM Compressed[/b] also improves loading times, as VRAM-compressed textures are faster to load compared to textures using lossless or lossy compression. VRAM compression can exhibit noticeable artifacts and is intended to be used for 3D rendering, not 2D. + Base class for [CompressedTexture2DArray] and [CompressedTexture3D]. Cannot be used directly, but contains all the functions necessary for accessing the derived resource types. See also [TextureLayered]. </description> <tutorials> </tutorials> diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index cb829ae183..42418b3e8d 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -830,6 +830,11 @@ When this signal is received, the child [param node] is still in the tree and valid. This signal is emitted [i]after[/i] the child node's own [signal tree_exiting] and [constant NOTIFICATION_EXIT_TREE]. </description> </signal> + <signal name="child_order_changed"> + <description> + Emitted when the list of children is changed. This happens when child nodes are added, moved or removed. + </description> + </signal> <signal name="ready"> <description> Emitted when the node is ready. Comes after [method _ready] callback and follows the same rules. @@ -867,8 +872,8 @@ Notification received when the node is about to exit a [SceneTree]. This notification is emitted [i]after[/i] the related [signal tree_exiting]. </constant> - <constant name="NOTIFICATION_MOVED_IN_PARENT" value="12"> - Notification received when the node is moved in the parent. + <constant name="NOTIFICATION_MOVED_IN_PARENT" value="12" is_deprecated="true"> + This notification is deprecated and is no longer emitted. Use [constant NOTIFICATION_CHILD_ORDER_CHANGED] instead. </constant> <constant name="NOTIFICATION_READY" value="13"> Notification received when the node is ready. See [method _ready]. @@ -907,6 +912,9 @@ <constant name="NOTIFICATION_PATH_RENAMED" value="23"> Notification received when the node's name or one of its parents' name is changed. This notification is [i]not[/i] received when the node is removed from the scene tree to be added to another parent later on. </constant> + <constant name="NOTIFICATION_CHILD_ORDER_CHANGED" value="24"> + Notification received when the list of children is changed. This happens when child nodes are added, moved or removed. + </constant> <constant name="NOTIFICATION_INTERNAL_PROCESS" value="25"> Notification received every frame when the internal process flag is set (see [method set_process_internal]). </constant> diff --git a/doc/classes/TextureLayered.xml b/doc/classes/TextureLayered.xml index adf391841f..52f3b9aca2 100644 --- a/doc/classes/TextureLayered.xml +++ b/doc/classes/TextureLayered.xml @@ -4,7 +4,7 @@ Base class for texture types which contain the data of multiple [Image]s. Each image is of the same size and format. </brief_description> <description> - Base class for [ImageTextureLayered]. Cannot be used directly, but contains all the functions necessary for accessing the derived resource types. See also [Texture3D]. + Base class for [ImageTextureLayered] and [CompressedTextureLayered]. Cannot be used directly, but contains all the functions necessary for accessing the derived resource types. See also [Texture3D]. Data is set on a per-layer basis. For [Texture2DArray]s, the layer specifies the array layer. All images need to have the same width, height and number of mipmap levels. A [TextureLayered] can be loaded with [method ResourceLoader.load]. diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index 20b3cf3618..8d1aadd095 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -42,6 +42,7 @@ #include "editor/scene_tree_dock.h" #include "scene/gui/center_container.h" #include "scene/gui/margin_container.h" +#include "scene/gui/option_button.h" #include "scene/gui/panel_container.h" #include "scene/gui/separator.h" @@ -131,8 +132,14 @@ void SpriteFramesEditor::_sheet_preview_draw() { Color accent = get_theme_color("accent_color", "Editor"); - for (const int &E : frames_selected) { - const int idx = E; + _sheet_sort_frames(); + + Ref<Font> font = get_theme_font(SNAME("bold"), SNAME("EditorFonts")); + int font_size = get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts")); + + for (int i = 0; i < frames_ordered.size(); ++i) { + const int idx = frames_ordered[i].second; + 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); @@ -143,6 +150,15 @@ void SpriteFramesEditor::_sheet_preview_draw() { split_sheet_preview->draw_rect(Rect2(pos + Size2(3, 3), draw_frame_size - Size2(6, 6)), accent, false); split_sheet_preview->draw_rect(Rect2(pos + Size2(4, 4), draw_frame_size - Size2(8, 8)), Color(0, 0, 0, 1), false); split_sheet_preview->draw_rect(Rect2(pos + Size2(5, 5), draw_frame_size - Size2(10, 10)), Color(0, 0, 0, 1), false); + + const String text = itos(i); + const Vector2 string_size = font->get_string_size(text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size); + + // Stop rendering text if too large. + if (string_size.x + 6 < draw_frame_size.x && string_size.y / 2 + 10 < draw_frame_size.y) { + split_sheet_preview->draw_string_outline(font, pos + Size2(5, 7) + Size2(0, string_size.y / 2), text, HORIZONTAL_ALIGNMENT_LEFT, string_size.x, font_size, 1, Color(0, 0, 0, 1)); + split_sheet_preview->draw_string(font, pos + Size2(5, 7) + Size2(0, string_size.y / 2), text, HORIZONTAL_ALIGNMENT_LEFT, string_size.x, font_size, Color(1, 1, 1)); + } } split_sheet_dialog->get_ok_button()->set_disabled(false); @@ -156,21 +172,24 @@ void SpriteFramesEditor::_sheet_preview_input(const Ref<InputEvent> &p_event) { if (idx != -1) { if (mb->is_shift_pressed() && last_frame_selected >= 0) { - //select multiple - int from = idx; - int to = last_frame_selected; - if (from > to) { - SWAP(from, to); - } + // Select multiple frames. + const int from = last_frame_selected; + const int to = idx; + + const int diff = ABS(to - from); + const int dir = SIGN(to - from); + + for (int i = 0; i <= diff; i++) { + const int this_idx = from + i * dir; - for (int i = from; i <= to; i++) { // Prevent double-toggling the same frame when moving the mouse when the mouse button is still held. - frames_toggled_by_mouse_hover.insert(idx); + frames_toggled_by_mouse_hover.insert(this_idx); if (mb->is_ctrl_pressed()) { - frames_selected.erase(i); - } else { - frames_selected.insert(i); + frames_selected.erase(this_idx); + } else if (!frames_selected.has(this_idx)) { + frames_selected.insert(this_idx, selected_count); + selected_count++; } } } else { @@ -180,13 +199,15 @@ void SpriteFramesEditor::_sheet_preview_input(const Ref<InputEvent> &p_event) { if (frames_selected.has(idx)) { frames_selected.erase(idx); } else { - frames_selected.insert(idx); + frames_selected.insert(idx, selected_count); + selected_count++; } } } if (last_frame_selected != idx || idx != -1) { last_frame_selected = idx; + frames_need_sort = true; split_sheet_preview->queue_redraw(); } } @@ -209,13 +230,19 @@ void SpriteFramesEditor::_sheet_preview_input(const Ref<InputEvent> &p_event) { if (frames_selected.has(idx)) { frames_selected.erase(idx); } else { - frames_selected.insert(idx); + frames_selected.insert(idx, selected_count); + selected_count++; } last_frame_selected = idx; + frames_need_sort = true; split_sheet_preview->queue_redraw(); } } + + if (frames_selected.is_empty()) { + selected_count = 0; + } } void SpriteFramesEditor::_sheet_scroll_input(const Ref<InputEvent> &p_event) { @@ -254,8 +281,11 @@ void SpriteFramesEditor::_sheet_add_frames() { undo_redo->create_action(TTR("Add Frame"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); int fc = frames->get_frame_count(edited_anim); - for (const int &E : frames_selected) { - int idx = E; + _sheet_sort_frames(); + + for (const Pair<int, int> &pair : frames_ordered) { + const int idx = pair.second; + const Point2 frame_coords(idx % frame_count.x, idx / frame_count.x); Ref<AtlasTexture> at; @@ -300,21 +330,103 @@ void SpriteFramesEditor::_sheet_zoom_reset() { split_sheet_preview->set_custom_minimum_size(texture_size * sheet_zoom); } -void SpriteFramesEditor::_sheet_select_clear_all_frames() { - bool should_clear = true; +void SpriteFramesEditor::_sheet_order_selected(int p_option) { + frames_need_sort = true; + split_sheet_preview->queue_redraw(); +} + +void SpriteFramesEditor::_sheet_select_all_frames() { for (int i = 0; i < split_sheet_h->get_value() * split_sheet_v->get_value(); i++) { if (!frames_selected.has(i)) { - frames_selected.insert(i); - should_clear = false; + frames_selected.insert(i, selected_count); + selected_count++; + frames_need_sort = true; } } - if (should_clear) { - frames_selected.clear(); - } split_sheet_preview->queue_redraw(); } +void SpriteFramesEditor::_sheet_clear_all_frames() { + frames_selected.clear(); + selected_count = 0; + + split_sheet_preview->queue_redraw(); +} + +void SpriteFramesEditor::_sheet_sort_frames() { + if (!frames_need_sort) { + return; + } + frames_need_sort = false; + frames_ordered.resize(frames_selected.size()); + if (frames_selected.is_empty()) { + return; + } + + const Size2i frame_count = _get_frame_count(); + const int frame_order = split_sheet_order->get_selected_id(); + int index = 0; + + // Fill based on order. + for (const KeyValue<int, int> &from_pair : frames_selected) { + const int idx = from_pair.key; + + const int selection_order = from_pair.value; + + // Default to using selection order. + int order_by = selection_order; + + // Extract coordinates for sorting. + const int pos_frame_x = idx % frame_count.x; + const int pos_frame_y = idx / frame_count.x; + + const int neg_frame_x = frame_count.x - (pos_frame_x + 1); + const int neg_frame_y = frame_count.y - (pos_frame_y + 1); + + switch (frame_order) { + case FRAME_ORDER_LEFT_RIGHT_TOP_BOTTOM: { + order_by = frame_count.x * pos_frame_y + pos_frame_x; + } break; + + case FRAME_ORDER_LEFT_RIGHT_BOTTOM_TOP: { + order_by = frame_count.x * neg_frame_y + pos_frame_x; + } break; + + case FRAME_ORDER_RIGHT_LEFT_TOP_BOTTOM: { + order_by = frame_count.x * pos_frame_y + neg_frame_x; + } break; + + case FRAME_ORDER_RIGHT_LEFT_BOTTOM_TOP: { + order_by = frame_count.x * neg_frame_y + neg_frame_x; + } break; + + case FRAME_ORDER_TOP_BOTTOM_LEFT_RIGHT: { + order_by = pos_frame_y + frame_count.y * pos_frame_x; + } break; + + case FRAME_ORDER_TOP_BOTTOM_RIGHT_LEFT: { + order_by = pos_frame_y + frame_count.y * neg_frame_x; + } break; + + case FRAME_ORDER_BOTTOM_TOP_LEFT_RIGHT: { + order_by = neg_frame_y + frame_count.y * pos_frame_x; + } break; + + case FRAME_ORDER_BOTTOM_TOP_RIGHT_LEFT: { + order_by = neg_frame_y + frame_count.y * neg_frame_x; + } break; + } + + // Assign in vector. + frames_ordered.set(index, Pair<int, int>(order_by, idx)); + index++; + } + + // Sort frames. + frames_ordered.sort_custom<PairSort<int, int>>(); +} + void SpriteFramesEditor::_sheet_spin_changed(double p_value, int p_dominant_param) { if (updating_split_settings) { return; @@ -367,10 +479,25 @@ void SpriteFramesEditor::_sheet_spin_changed(double p_value, int p_dominant_para updating_split_settings = false; frames_selected.clear(); + selected_count = 0; last_frame_selected = -1; split_sheet_preview->queue_redraw(); } +void SpriteFramesEditor::_toggle_show_settings() { + split_sheet_settings_vb->set_visible(!split_sheet_settings_vb->is_visible()); + + _update_show_settings(); +} + +void SpriteFramesEditor::_update_show_settings() { + if (is_layout_rtl()) { + toggle_settings_button->set_icon(get_theme_icon(split_sheet_settings_vb->is_visible() ? SNAME("Back") : SNAME("Forward"), SNAME("EditorIcons"))); + } else { + toggle_settings_button->set_icon(get_theme_icon(split_sheet_settings_vb->is_visible() ? SNAME("Forward") : SNAME("Back"), SNAME("EditorIcons"))); + } +} + void SpriteFramesEditor::_prepare_sprite_sheet(const String &p_file) { Ref<Texture2D> texture = ResourceLoader::load(p_file); if (texture.is_null()) { @@ -378,6 +505,7 @@ void SpriteFramesEditor::_prepare_sprite_sheet(const String &p_file) { ERR_FAIL_COND(texture.is_null()); } frames_selected.clear(); + selected_count = 0; last_frame_selected = -1; bool new_texture = texture != split_sheet_preview->get_texture(); @@ -408,6 +536,7 @@ void SpriteFramesEditor::_prepare_sprite_sheet(const String &p_file) { // Reset zoom. _sheet_zoom_reset(); } + split_sheet_dialog->popup_centered_ratio(0.65); } @@ -450,6 +579,8 @@ void SpriteFramesEditor::_notification(int p_what) { split_sheet_zoom_reset->set_icon(get_theme_icon(SNAME("ZoomReset"), SNAME("EditorIcons"))); split_sheet_zoom_in->set_icon(get_theme_icon(SNAME("ZoomMore"), SNAME("EditorIcons"))); split_sheet_scroll->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree"))); + + _update_show_settings(); } break; case NOTIFICATION_READY: { @@ -1769,82 +1900,58 @@ SpriteFramesEditor::SpriteFramesEditor() { split_sheet_dialog = memnew(ConfirmationDialog); add_child(split_sheet_dialog); - VBoxContainer *split_sheet_vb = memnew(VBoxContainer); - split_sheet_dialog->add_child(split_sheet_vb); split_sheet_dialog->set_title(TTR("Select Frames")); split_sheet_dialog->connect("confirmed", callable_mp(this, &SpriteFramesEditor::_sheet_add_frames)); HBoxContainer *split_sheet_hb = memnew(HBoxContainer); + split_sheet_dialog->add_child(split_sheet_hb); + split_sheet_hb->set_h_size_flags(SIZE_EXPAND_FILL); + split_sheet_hb->set_v_size_flags(SIZE_EXPAND_FILL); - split_sheet_hb->add_child(memnew(Label(TTR("Horizontal:")))); - split_sheet_h = memnew(SpinBox); - split_sheet_h->set_min(1); - split_sheet_h->set_max(128); - split_sheet_h->set_step(1); - split_sheet_hb->add_child(split_sheet_h); - split_sheet_h->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_FRAME_COUNT)); - - split_sheet_hb->add_child(memnew(Label(TTR("Vertical:")))); - split_sheet_v = memnew(SpinBox); - split_sheet_v->set_min(1); - split_sheet_v->set_max(128); - split_sheet_v->set_step(1); - split_sheet_hb->add_child(split_sheet_v); - split_sheet_v->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_FRAME_COUNT)); - - split_sheet_hb->add_child(memnew(VSeparator)); - split_sheet_hb->add_child(memnew(Label(TTR("Size:")))); - split_sheet_size_x = memnew(SpinBox); - split_sheet_size_x->set_min(1); - split_sheet_size_x->set_step(1); - split_sheet_size_x->set_suffix("px"); - split_sheet_size_x->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_SIZE)); - split_sheet_hb->add_child(split_sheet_size_x); - split_sheet_size_y = memnew(SpinBox); - split_sheet_size_y->set_min(1); - split_sheet_size_y->set_step(1); - split_sheet_size_y->set_suffix("px"); - split_sheet_size_y->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_SIZE)); - split_sheet_hb->add_child(split_sheet_size_y); - - split_sheet_hb->add_child(memnew(VSeparator)); - split_sheet_hb->add_child(memnew(Label(TTR("Separation:")))); - split_sheet_sep_x = memnew(SpinBox); - split_sheet_sep_x->set_min(0); - split_sheet_sep_x->set_step(1); - split_sheet_sep_x->set_suffix("px"); - split_sheet_sep_x->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_USE_CURRENT)); - split_sheet_hb->add_child(split_sheet_sep_x); - split_sheet_sep_y = memnew(SpinBox); - split_sheet_sep_y->set_min(0); - split_sheet_sep_y->set_step(1); - split_sheet_sep_y->set_suffix("px"); - split_sheet_sep_y->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_USE_CURRENT)); - split_sheet_hb->add_child(split_sheet_sep_y); - - split_sheet_hb->add_child(memnew(VSeparator)); - split_sheet_hb->add_child(memnew(Label(TTR("Offset:")))); - split_sheet_offset_x = memnew(SpinBox); - split_sheet_offset_x->set_min(0); - split_sheet_offset_x->set_step(1); - split_sheet_offset_x->set_suffix("px"); - split_sheet_offset_x->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_USE_CURRENT)); - split_sheet_hb->add_child(split_sheet_offset_x); - split_sheet_offset_y = memnew(SpinBox); - split_sheet_offset_y->set_min(0); - split_sheet_offset_y->set_step(1); - split_sheet_offset_y->set_suffix("px"); - split_sheet_offset_y->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_USE_CURRENT)); - split_sheet_hb->add_child(split_sheet_offset_y); - - split_sheet_hb->add_spacer(); - - Button *select_clear_all = memnew(Button); - select_clear_all->set_text(TTR("Select/Clear All Frames")); - select_clear_all->connect("pressed", callable_mp(this, &SpriteFramesEditor::_sheet_select_clear_all_frames)); - split_sheet_hb->add_child(select_clear_all); - - split_sheet_vb->add_child(split_sheet_hb); + VBoxContainer *split_sheet_vb = memnew(VBoxContainer); + split_sheet_hb->add_child(split_sheet_vb); + split_sheet_vb->set_h_size_flags(SIZE_EXPAND_FILL); + split_sheet_vb->set_v_size_flags(SIZE_EXPAND_FILL); + + HBoxContainer *split_sheet_menu_hb = memnew(HBoxContainer); + + split_sheet_menu_hb->add_child(memnew(Label(TTR("Frame Order")))); + + split_sheet_order = memnew(OptionButton); + split_sheet_order->add_item(TTR("As Selected"), FRAME_ORDER_SELECTION); + split_sheet_order->add_separator(TTR("By Row")); + split_sheet_order->add_item(TTR("Left to Right, Top to Bottom"), FRAME_ORDER_LEFT_RIGHT_TOP_BOTTOM); + split_sheet_order->add_item(TTR("Left to Right, Bottom to Top"), FRAME_ORDER_LEFT_RIGHT_BOTTOM_TOP); + split_sheet_order->add_item(TTR("Right to Left, Top to Bottom"), FRAME_ORDER_RIGHT_LEFT_TOP_BOTTOM); + split_sheet_order->add_item(TTR("Right to Left, Bottom to Top"), FRAME_ORDER_RIGHT_LEFT_BOTTOM_TOP); + split_sheet_order->add_separator(TTR("By Column")); + split_sheet_order->add_item(TTR("Top to Bottom, Left to Right"), FRAME_ORDER_TOP_BOTTOM_LEFT_RIGHT); + split_sheet_order->add_item(TTR("Top to Bottom, Right to Left"), FRAME_ORDER_TOP_BOTTOM_RIGHT_LEFT); + split_sheet_order->add_item(TTR("Bottom to Top, Left to Right"), FRAME_ORDER_BOTTOM_TOP_LEFT_RIGHT); + split_sheet_order->add_item(TTR("Bottom to Top, Right to Left"), FRAME_ORDER_BOTTOM_TOP_RIGHT_LEFT); + split_sheet_order->connect("item_selected", callable_mp(this, &SpriteFramesEditor::_sheet_order_selected)); + split_sheet_menu_hb->add_child(split_sheet_order); + + Button *select_all = memnew(Button); + select_all->set_text(TTR("Select All")); + select_all->connect("pressed", callable_mp(this, &SpriteFramesEditor::_sheet_select_all_frames)); + split_sheet_menu_hb->add_child(select_all); + + Button *clear_all = memnew(Button); + clear_all->set_text(TTR("Select None")); + clear_all->connect("pressed", callable_mp(this, &SpriteFramesEditor::_sheet_clear_all_frames)); + split_sheet_menu_hb->add_child(clear_all); + + split_sheet_menu_hb->add_spacer(); + + toggle_settings_button = memnew(Button); + toggle_settings_button->set_h_size_flags(SIZE_SHRINK_END); + toggle_settings_button->set_flat(true); + toggle_settings_button->connect("pressed", callable_mp(this, &SpriteFramesEditor::_toggle_show_settings)); + toggle_settings_button->set_tooltip_text(TTR("Toggle Settings Panel")); + split_sheet_menu_hb->add_child(toggle_settings_button); + + split_sheet_vb->add_child(split_sheet_menu_hb); PanelContainer *split_sheet_panel = memnew(PanelContainer); split_sheet_panel->set_h_size_flags(SIZE_EXPAND_FILL); @@ -1896,6 +2003,120 @@ SpriteFramesEditor::SpriteFramesEditor() { split_sheet_zoom_in->connect("pressed", callable_mp(this, &SpriteFramesEditor::_sheet_zoom_in)); split_sheet_zoom_hb->add_child(split_sheet_zoom_in); + split_sheet_settings_vb = memnew(VBoxContainer); + split_sheet_settings_vb->set_v_size_flags(SIZE_EXPAND_FILL); + + HBoxContainer *split_sheet_h_hb = memnew(HBoxContainer); + split_sheet_h_hb->set_h_size_flags(SIZE_EXPAND_FILL); + + Label *split_sheet_h_label = memnew(Label(TTR("Horizontal"))); + split_sheet_h_label->set_h_size_flags(SIZE_EXPAND_FILL); + split_sheet_h_hb->add_child(split_sheet_h_label); + + split_sheet_h = memnew(SpinBox); + split_sheet_h->set_h_size_flags(SIZE_EXPAND_FILL); + split_sheet_h->set_min(1); + split_sheet_h->set_max(128); + split_sheet_h->set_step(1); + split_sheet_h_hb->add_child(split_sheet_h); + split_sheet_h->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_FRAME_COUNT)); + split_sheet_settings_vb->add_child(split_sheet_h_hb); + + HBoxContainer *split_sheet_v_hb = memnew(HBoxContainer); + split_sheet_v_hb->set_h_size_flags(SIZE_EXPAND_FILL); + + Label *split_sheet_v_label = memnew(Label(TTR("Vertical"))); + split_sheet_v_label->set_h_size_flags(SIZE_EXPAND_FILL); + split_sheet_v_hb->add_child(split_sheet_v_label); + + split_sheet_v = memnew(SpinBox); + split_sheet_v->set_h_size_flags(SIZE_EXPAND_FILL); + split_sheet_v->set_min(1); + split_sheet_v->set_max(128); + split_sheet_v->set_step(1); + split_sheet_v_hb->add_child(split_sheet_v); + split_sheet_v->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_FRAME_COUNT)); + split_sheet_settings_vb->add_child(split_sheet_v_hb); + + HBoxContainer *split_sheet_size_hb = memnew(HBoxContainer); + split_sheet_size_hb->set_h_size_flags(SIZE_EXPAND_FILL); + + Label *split_sheet_size_label = memnew(Label(TTR("Size"))); + split_sheet_size_label->set_h_size_flags(SIZE_EXPAND_FILL); + split_sheet_size_label->set_v_size_flags(SIZE_SHRINK_BEGIN); + split_sheet_size_hb->add_child(split_sheet_size_label); + + VBoxContainer *split_sheet_size_vb = memnew(VBoxContainer); + split_sheet_size_vb->set_h_size_flags(SIZE_EXPAND_FILL); + split_sheet_size_x = memnew(SpinBox); + split_sheet_size_x->set_h_size_flags(SIZE_EXPAND_FILL); + split_sheet_size_x->set_min(1); + split_sheet_size_x->set_step(1); + split_sheet_size_x->set_suffix("px"); + split_sheet_size_x->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_SIZE)); + split_sheet_size_vb->add_child(split_sheet_size_x); + split_sheet_size_y = memnew(SpinBox); + split_sheet_size_y->set_h_size_flags(SIZE_EXPAND_FILL); + split_sheet_size_y->set_min(1); + split_sheet_size_y->set_step(1); + split_sheet_size_y->set_suffix("px"); + split_sheet_size_y->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_SIZE)); + split_sheet_size_vb->add_child(split_sheet_size_y); + split_sheet_size_hb->add_child(split_sheet_size_vb); + split_sheet_settings_vb->add_child(split_sheet_size_hb); + + HBoxContainer *split_sheet_sep_hb = memnew(HBoxContainer); + split_sheet_sep_hb->set_h_size_flags(SIZE_EXPAND_FILL); + + Label *split_sheet_sep_label = memnew(Label(TTR("Separation"))); + split_sheet_sep_label->set_h_size_flags(SIZE_EXPAND_FILL); + split_sheet_sep_label->set_v_size_flags(SIZE_SHRINK_BEGIN); + split_sheet_sep_hb->add_child(split_sheet_sep_label); + + VBoxContainer *split_sheet_sep_vb = memnew(VBoxContainer); + split_sheet_sep_vb->set_h_size_flags(SIZE_EXPAND_FILL); + split_sheet_sep_x = memnew(SpinBox); + split_sheet_sep_x->set_min(0); + split_sheet_sep_x->set_step(1); + split_sheet_sep_x->set_suffix("px"); + split_sheet_sep_x->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_USE_CURRENT)); + split_sheet_sep_vb->add_child(split_sheet_sep_x); + split_sheet_sep_y = memnew(SpinBox); + split_sheet_sep_y->set_min(0); + split_sheet_sep_y->set_step(1); + split_sheet_sep_y->set_suffix("px"); + split_sheet_sep_y->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_USE_CURRENT)); + split_sheet_sep_vb->add_child(split_sheet_sep_y); + split_sheet_sep_hb->add_child(split_sheet_sep_vb); + split_sheet_settings_vb->add_child(split_sheet_sep_hb); + + HBoxContainer *split_sheet_offset_hb = memnew(HBoxContainer); + split_sheet_offset_hb->set_h_size_flags(SIZE_EXPAND_FILL); + + Label *split_sheet_offset_label = memnew(Label(TTR("Offset"))); + split_sheet_offset_label->set_h_size_flags(SIZE_EXPAND_FILL); + split_sheet_offset_label->set_v_size_flags(SIZE_SHRINK_BEGIN); + split_sheet_offset_hb->add_child(split_sheet_offset_label); + + VBoxContainer *split_sheet_offset_vb = memnew(VBoxContainer); + split_sheet_offset_vb->set_h_size_flags(SIZE_EXPAND_FILL); + split_sheet_offset_x = memnew(SpinBox); + split_sheet_offset_x->set_min(0); + split_sheet_offset_x->set_step(1); + split_sheet_offset_x->set_suffix("px"); + split_sheet_offset_x->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_USE_CURRENT)); + split_sheet_offset_vb->add_child(split_sheet_offset_x); + split_sheet_offset_y = memnew(SpinBox); + split_sheet_offset_y->set_min(0); + split_sheet_offset_y->set_step(1); + split_sheet_offset_y->set_suffix("px"); + split_sheet_offset_y->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_USE_CURRENT)); + split_sheet_offset_vb->add_child(split_sheet_offset_y); + split_sheet_offset_hb->add_child(split_sheet_offset_vb); + split_sheet_settings_vb->add_child(split_sheet_offset_hb); + + split_sheet_hb->add_child(split_sheet_settings_vb); + file_split_sheet = memnew(EditorFileDialog); file_split_sheet->set_title(TTR("Create Frames from Sprite Sheet")); file_split_sheet->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE); diff --git a/editor/plugins/sprite_frames_editor_plugin.h b/editor/plugins/sprite_frames_editor_plugin.h index 1dfb909388..9317e94be0 100644 --- a/editor/plugins/sprite_frames_editor_plugin.h +++ b/editor/plugins/sprite_frames_editor_plugin.h @@ -45,6 +45,7 @@ #include "scene/gui/texture_rect.h" #include "scene/gui/tree.h" +class OptionButton; class EditorFileDialog; class EditorSpriteFramesFrame : public Resource { @@ -68,6 +69,22 @@ class SpriteFramesEditor : public HSplitContainer { }; int dominant_param = PARAM_FRAME_COUNT; + enum { + FRAME_ORDER_SELECTION, // Order frames were selected in. + + // By Row. + FRAME_ORDER_LEFT_RIGHT_TOP_BOTTOM, + FRAME_ORDER_LEFT_RIGHT_BOTTOM_TOP, + FRAME_ORDER_RIGHT_LEFT_TOP_BOTTOM, + FRAME_ORDER_RIGHT_LEFT_BOTTOM_TOP, + + // By Column. + FRAME_ORDER_TOP_BOTTOM_LEFT_RIGHT, + FRAME_ORDER_TOP_BOTTOM_RIGHT_LEFT, + FRAME_ORDER_BOTTOM_TOP_LEFT_RIGHT, + FRAME_ORDER_BOTTOM_TOP_RIGHT_LEFT, + }; + bool read_only = false; Ref<Texture2D> autoplay_icon; @@ -121,6 +138,7 @@ class SpriteFramesEditor : public HSplitContainer { ConfirmationDialog *split_sheet_dialog = nullptr; ScrollContainer *split_sheet_scroll = nullptr; TextureRect *split_sheet_preview = nullptr; + VBoxContainer *split_sheet_settings_vb = nullptr; SpinBox *split_sheet_h = nullptr; SpinBox *split_sheet_v = nullptr; SpinBox *split_sheet_size_x = nullptr; @@ -132,9 +150,14 @@ class SpriteFramesEditor : public HSplitContainer { Button *split_sheet_zoom_out = nullptr; Button *split_sheet_zoom_reset = nullptr; Button *split_sheet_zoom_in = nullptr; + Button *toggle_settings_button = nullptr; + OptionButton *split_sheet_order = nullptr; EditorFileDialog *file_split_sheet = nullptr; - HashSet<int> frames_selected; + HashMap<int, int> frames_selected; // Key is frame index. Value is selection order. HashSet<int> frames_toggled_by_mouse_hover; + Vector<Pair<int, int>> frames_ordered; // First is the index to be ordered by. Second is the actual frame index. + int selected_count = 0; + bool frames_need_sort = false; int last_frame_selected = 0; float scale_ratio; @@ -206,7 +229,12 @@ class SpriteFramesEditor : public HSplitContainer { void _sheet_zoom_in(); void _sheet_zoom_out(); void _sheet_zoom_reset(); - void _sheet_select_clear_all_frames(); + void _sheet_order_selected(int p_option); + void _sheet_select_all_frames(); + void _sheet_clear_all_frames(); + void _sheet_sort_frames(); + void _toggle_show_settings(); + void _update_show_settings(); void _edit(); void _regist_scene_undo(EditorUndoRedoManager *undo_redo); diff --git a/platform/web/dom_keys.inc b/platform/web/dom_keys.inc index ae3b2fc1a5..cd94b779c0 100644 --- a/platform/web/dom_keys.inc +++ b/platform/web/dom_keys.inc @@ -33,7 +33,7 @@ // See https://w3c.github.io/uievents-code/#code-value-tables Key dom_code2godot_scancode(EM_UTF8 const p_code[32], EM_UTF8 const p_key[32], bool p_physical) { #define DOM2GODOT(p_str, p_godot_code) \ - if (memcmp((const void *)p_str, (void *)(p_physical ? p_key : p_code), strlen(p_str) + 1) == 0) { \ + if (memcmp((const void *)p_str, (void *)(p_physical ? p_code : p_key), strlen(p_str) + 1) == 0) { \ return Key::p_godot_code; \ } diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index c175edb6cb..466ffad951 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -68,7 +68,8 @@ void AudioStreamPlayer2D::_notification(int p_what) { case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { // Update anything related to position first, if possible of course. - if (setplay.get() > 0 || (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count())) { + if (setplay.get() > 0 || (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count()) || force_update_panning) { + force_update_panning = false; _update_panning(); } @@ -109,6 +110,7 @@ void AudioStreamPlayer2D::_notification(int p_what) { } } +// Interacts with PhysicsServer2D, so can only be called during _physics_process StringName AudioStreamPlayer2D::_get_actual_bus() { Vector2 global_pos = get_global_position(); @@ -117,6 +119,7 @@ StringName AudioStreamPlayer2D::_get_actual_bus() { ERR_FAIL_COND_V(world_2d.is_null(), SNAME("Master")); PhysicsDirectSpaceState2D *space_state = PhysicsServer2D::get_singleton()->space_get_direct_state(world_2d->get_space()); + ERR_FAIL_COND_V(space_state == nullptr, SNAME("Master")); PhysicsDirectSpaceState2D::ShapeResult sr[MAX_INTERSECT_AREAS]; PhysicsDirectSpaceState2D::PointParameters point_params; @@ -142,6 +145,7 @@ StringName AudioStreamPlayer2D::_get_actual_bus() { return default_bus; } +// Interacts with PhysicsServer2D, so can only be called during _physics_process void AudioStreamPlayer2D::_update_panning() { if (!active.is_set() || stream.is_null()) { return; diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h index 79a026fed2..9b23fd3943 100644 --- a/scene/2d/audio_stream_player_2d.h +++ b/scene/2d/audio_stream_player_2d.h @@ -61,6 +61,7 @@ private: Vector<AudioFrame> volume_vector; uint64_t last_mix_count = -1; + bool force_update_panning = false; float volume_db = 0.0; float pitch_scale = 1.0; @@ -75,7 +76,7 @@ private: void _update_panning(); void _bus_layout_changed(); - static void _listener_changed_cb(void *self) { reinterpret_cast<AudioStreamPlayer2D *>(self)->_update_panning(); } + static void _listener_changed_cb(void *self) { reinterpret_cast<AudioStreamPlayer2D *>(self)->force_update_panning = true; } uint32_t area_mask = 1; diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp index 1c0efe773f..43a2f62163 100644 --- a/scene/2d/node_2d.cpp +++ b/scene/2d/node_2d.cpp @@ -385,9 +385,14 @@ Point2 Node2D::to_global(Point2 p_local) const { void Node2D::_notification(int p_notification) { switch (p_notification) { - case NOTIFICATION_MOVED_IN_PARENT: { + case NOTIFICATION_ENTER_TREE: { if (get_viewport()) { - get_viewport()->gui_set_root_order_dirty(); + get_parent()->connect(SNAME("child_order_changed"), callable_mp(get_viewport(), &Viewport::gui_set_root_order_dirty), CONNECT_REFERENCE_COUNTED); + } + } break; + case NOTIFICATION_EXIT_TREE: { + if (get_viewport()) { + get_parent()->disconnect(SNAME("child_order_changed"), callable_mp(get_viewport(), &Viewport::gui_set_root_order_dirty)); } } break; } diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 4fdc7b3584..f2270d2b18 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -115,6 +115,7 @@ void Bone2D::_notification(int p_what) { bone.bone = this; skeleton->bones.push_back(bone); skeleton->_make_bone_setup_dirty(); + get_parent()->connect(SNAME("child_order_changed"), callable_mp(skeleton, &Skeleton2D::_make_bone_setup_dirty), CONNECT_REFERENCE_COUNTED); } cache_transform = get_transform(); @@ -154,15 +155,6 @@ void Bone2D::_notification(int p_what) { #endif // TOOLS_ENABLED } break; - case NOTIFICATION_MOVED_IN_PARENT: { - if (skeleton) { - skeleton->_make_bone_setup_dirty(); - } - if (copy_transform_to_cache) { - cache_transform = get_transform(); - } - } break; - case NOTIFICATION_EXIT_TREE: { if (skeleton) { for (int i = 0; i < skeleton->bones.size(); i++) { @@ -172,7 +164,7 @@ void Bone2D::_notification(int p_what) { } } skeleton->_make_bone_setup_dirty(); - skeleton = nullptr; + get_parent()->disconnect(SNAME("child_order_changed"), callable_mp(skeleton, &Skeleton2D::_make_bone_setup_dirty)); } parent_bone = nullptr; set_transform(cache_transform); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 569b89f688..8957a9492e 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -3021,6 +3021,8 @@ void Control::_notification(int p_notification) { Viewport *viewport = get_viewport(); ERR_FAIL_COND(!viewport); data.RI = viewport->_gui_add_root_control(this); + + get_parent()->connect(SNAME("child_order_changed"), callable_mp(get_viewport(), &Viewport::gui_set_root_order_dirty), CONNECT_REFERENCE_COUNTED); } data.parent_canvas_item = get_parent_item(); @@ -3049,24 +3051,17 @@ void Control::_notification(int p_notification) { if (data.RI) { get_viewport()->_gui_remove_root_control(data.RI); data.RI = nullptr; + get_parent()->disconnect(SNAME("child_order_changed"), callable_mp(get_viewport(), &Viewport::gui_set_root_order_dirty)); } data.parent_canvas_item = nullptr; data.is_rtl_dirty = true; } break; - case NOTIFICATION_MOVED_IN_PARENT: { + case NOTIFICATION_CHILD_ORDER_CHANGED: { // Some parents need to know the order of the children to draw (like TabContainer), // so we update them just in case. - Control *parent_control = get_parent_control(); - if (parent_control) { - parent_control->queue_redraw(); - } queue_redraw(); - - if (data.RI) { - get_viewport()->gui_set_root_order_dirty(); - } } break; case NOTIFICATION_RESIZED: { diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 72fb838732..4eabc4916c 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -188,10 +188,13 @@ void CanvasItem::_enter_canvas() { // Resolves to nullptr if the node is top_level. CanvasItem *parent_item = get_parent_item(); + if (get_parent()) { + get_viewport()->canvas_parent_mark_dirty(get_parent()); + } + if (parent_item) { canvas_layer = parent_item->canvas_layer; RenderingServer::get_singleton()->canvas_item_set_parent(canvas_item, parent_item->get_canvas_item()); - RenderingServer::get_singleton()->canvas_item_set_draw_index(canvas_item, get_index()); RenderingServer::get_singleton()->canvas_item_set_visibility_layer(canvas_item, visibility_layer); } else { Node *n = this; @@ -227,8 +230,6 @@ void CanvasItem::_enter_canvas() { } else { get_viewport()->gui_reset_canvas_sort_index(); } - - get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE | SceneTree::GROUP_CALL_DEFERRED, canvas_group, SNAME("_top_level_raise_self")); } pending_update = false; @@ -302,21 +303,12 @@ void CanvasItem::_notification(int p_what) { if (!block_transform_notify && !xform_change.in_list()) { get_tree()->xform_change_list.add(&xform_change); } - } break; - case NOTIFICATION_MOVED_IN_PARENT: { - if (!is_inside_tree()) { - break; + if (get_viewport()) { + get_parent()->connect(SNAME("child_order_changed"), callable_mp(get_viewport(), &Viewport::canvas_parent_mark_dirty).bind(get_parent()), CONNECT_REFERENCE_COUNTED); } - if (canvas_group != StringName()) { - get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE | SceneTree::GROUP_CALL_DEFERRED, canvas_group, "_top_level_raise_self"); - } else { - ERR_FAIL_COND_MSG(!get_parent_item(), "Moved child is in incorrect state (no canvas group, no canvas item parent)."); - RenderingServer::get_singleton()->canvas_item_set_draw_index(canvas_item, get_index()); - } } break; - case NOTIFICATION_EXIT_TREE: { if (xform_change.in_list()) { get_tree()->xform_change_list.remove(&xform_change); @@ -332,6 +324,10 @@ void CanvasItem::_notification(int p_what) { } global_invalid = true; parent_visible_in_tree = false; + + if (get_viewport()) { + get_parent()->disconnect(SNAME("child_order_changed"), callable_mp(get_viewport(), &Viewport::canvas_parent_mark_dirty).bind(get_parent())); + } } break; case NOTIFICATION_VISIBILITY_CHANGED: { @@ -340,6 +336,19 @@ void CanvasItem::_notification(int p_what) { } } +void CanvasItem::update_draw_order() { + if (!is_inside_tree()) { + return; + } + + if (canvas_group != StringName()) { + get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE | SceneTree::GROUP_CALL_DEFERRED, canvas_group, "_top_level_raise_self"); + } else { + ERR_FAIL_COND_MSG(!get_parent_item(), "Moved child is in incorrect state (no canvas group, no canvas item parent)."); + RenderingServer::get_singleton()->canvas_item_set_draw_index(canvas_item, get_index()); + } +} + void CanvasItem::_window_visibility_changed() { if (visible) { _propagate_visibility_changed(window->is_visible()); diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h index 5fbf043159..ea1e666b08 100644 --- a/scene/main/canvas_item.h +++ b/scene/main/canvas_item.h @@ -212,6 +212,8 @@ public: virtual Transform2D _edit_get_transform() const; #endif + void update_draw_order(); + /* VISIBILITY */ void set_visible(bool p_visible); diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp index 76cc17922a..274bd1ad68 100644 --- a/scene/main/canvas_layer.cpp +++ b/scene/main/canvas_layer.cpp @@ -180,25 +180,30 @@ void CanvasLayer::_notification(int p_what) { viewport = vp->get_viewport_rid(); RenderingServer::get_singleton()->viewport_attach_canvas(viewport, canvas); - RenderingServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_index()); RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform); _update_follow_viewport(); + + if (vp) { + get_parent()->connect(SNAME("child_order_changed"), callable_mp(vp, &Viewport::canvas_parent_mark_dirty).bind(get_parent()), CONNECT_REFERENCE_COUNTED); + vp->canvas_parent_mark_dirty(get_parent()); + } } break; case NOTIFICATION_EXIT_TREE: { ERR_FAIL_NULL_MSG(vp, "Viewport is not initialized."); + get_parent()->disconnect(SNAME("child_order_changed"), callable_mp(vp, &Viewport::canvas_parent_mark_dirty).bind(get_parent())); vp->_canvas_layer_remove(this); RenderingServer::get_singleton()->viewport_remove_canvas(viewport, canvas); viewport = RID(); _update_follow_viewport(false); } break; + } +} - case NOTIFICATION_MOVED_IN_PARENT: { - if (is_inside_tree()) { - RenderingServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_index()); - } - } break; +void CanvasLayer::update_draw_order() { + if (is_inside_tree()) { + RenderingServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_index()); } } diff --git a/scene/main/canvas_layer.h b/scene/main/canvas_layer.h index 47077dc7fd..be1a377a14 100644 --- a/scene/main/canvas_layer.h +++ b/scene/main/canvas_layer.h @@ -67,6 +67,8 @@ protected: void _validate_property(PropertyInfo &p_property) const; public: + void update_draw_order(); + void set_layer(int p_xform); int get_layer() const; diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 22bcfc947b..352c8eb77f 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -393,9 +393,9 @@ void Node::_move_child(Node *p_child, int p_index, bool p_ignore_end) { } // notification second move_child_notify(p_child); - for (int i = motion_from; i <= motion_to; i++) { - data.children[i]->notification(NOTIFICATION_MOVED_IN_PARENT); - } + notification(NOTIFICATION_CHILD_ORDER_CHANGED); + emit_signal(SNAME("child_order_changed")); + p_child->_propagate_groups_dirty(); data.blocked--; @@ -1124,6 +1124,8 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) { //recognize children created in this node constructor p_child->data.parent_owned = data.in_constructor; add_child_notify(p_child); + notification(NOTIFICATION_CHILD_ORDER_CHANGED); + emit_signal(SNAME("child_order_changed")); } void Node::add_child(Node *p_child, bool p_force_readable_name, InternalMode p_internal) { @@ -1215,9 +1217,11 @@ void Node::remove_child(Node *p_child) { for (int i = idx; i < child_count; i++) { children[i]->data.index = i; - children[i]->notification(NOTIFICATION_MOVED_IN_PARENT); } + notification(NOTIFICATION_CHILD_ORDER_CHANGED); + emit_signal(SNAME("child_order_changed")); + p_child->data.parent = nullptr; p_child->data.index = -1; @@ -2974,6 +2978,7 @@ void Node::_bind_methods() { BIND_CONSTANT(NOTIFICATION_DRAG_BEGIN); BIND_CONSTANT(NOTIFICATION_DRAG_END); BIND_CONSTANT(NOTIFICATION_PATH_RENAMED); + BIND_CONSTANT(NOTIFICATION_CHILD_ORDER_CHANGED); BIND_CONSTANT(NOTIFICATION_INTERNAL_PROCESS); BIND_CONSTANT(NOTIFICATION_INTERNAL_PHYSICS_PROCESS); BIND_CONSTANT(NOTIFICATION_POST_ENTER_TREE); @@ -3027,6 +3032,7 @@ void Node::_bind_methods() { ADD_SIGNAL(MethodInfo("tree_exited")); ADD_SIGNAL(MethodInfo("child_entered_tree", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT, "Node"))); ADD_SIGNAL(MethodInfo("child_exiting_tree", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT, "Node"))); + ADD_SIGNAL(MethodInfo("child_order_changed")); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_name", "get_name"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "unique_name_in_owner", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_unique_name_in_owner", "is_unique_name_in_owner"); diff --git a/scene/main/node.h b/scene/main/node.h index 8ce42d04bd..939632770e 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -264,7 +264,7 @@ public: NOTIFICATION_DRAG_BEGIN = 21, NOTIFICATION_DRAG_END = 22, NOTIFICATION_PATH_RENAMED = 23, - //NOTIFICATION_TRANSLATION_CHANGED = 24, moved below + NOTIFICATION_CHILD_ORDER_CHANGED = 24, NOTIFICATION_INTERNAL_PROCESS = 25, NOTIFICATION_INTERNAL_PHYSICS_PROCESS = 26, NOTIFICATION_POST_ENTER_TREE = 27, diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index feddb1e779..9f9a2eeb18 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -165,6 +165,31 @@ ViewportTexture::~ViewportTexture() { } } +void Viewport::_process_dirty_canvas_parent_orders() { + for (const ObjectID &id : gui.canvas_parents_with_dirty_order) { + Object *obj = ObjectDB::get_instance(id); + if (!obj) { + continue; // May have been deleted. + } + + Node *n = static_cast<Node *>(obj); + for (int i = 0; i < n->get_child_count(); i++) { + Node *c = n->get_child(i); + CanvasItem *ci = Object::cast_to<CanvasItem>(c); + if (ci) { + ci->update_draw_order(); + continue; + } + CanvasLayer *cl = Object::cast_to<CanvasLayer>(c); + if (cl) { + cl->update_draw_order(); + } + } + } + + gui.canvas_parents_with_dirty_order.clear(); +} + void Viewport::_sub_window_update_order() { if (gui.sub_windows.size() < 2) { return; @@ -929,6 +954,14 @@ Rect2 Viewport::get_visible_rect() const { return r; } +void Viewport::canvas_parent_mark_dirty(Node *p_node) { + bool request_update = gui.canvas_parents_with_dirty_order.is_empty(); + gui.canvas_parents_with_dirty_order.insert(p_node->get_instance_id()); + if (request_update) { + MessageQueue::get_singleton()->push_callable(callable_mp(this, &Viewport::_process_dirty_canvas_parent_orders)); + } +} + void Viewport::_update_audio_listener_2d() { if (AudioServer::get_singleton()) { AudioServer::get_singleton()->notify_listener_changed(); diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 5213c0db01..f12e7921a3 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -376,6 +376,7 @@ private: double tooltip_delay = 0.0; bool roots_order_dirty = false; List<Control *> roots; + HashSet<ObjectID> canvas_parents_with_dirty_order; int canvas_sort_index = 0; //for sorting items with canvas as root bool dragging = false; bool drag_successful = false; @@ -468,6 +469,8 @@ private: virtual bool _can_consume_input_events() const { return true; } uint64_t event_count = 0; + void _process_dirty_canvas_parent_orders(); + protected: void _set_size(const Size2i &p_size, const Size2i &p_size_2d_override, bool p_allocated); @@ -480,6 +483,8 @@ protected: static void _bind_methods(); public: + void canvas_parent_mark_dirty(Node *p_node); + uint64_t get_processed_events_count() const { return event_count; } AudioListener2D *get_audio_listener_2d() const; diff --git a/servers/physics_server_2d.cpp b/servers/physics_server_2d.cpp index 4ad83ef327..fb5092e498 100644 --- a/servers/physics_server_2d.cpp +++ b/servers/physics_server_2d.cpp @@ -893,7 +893,7 @@ PhysicsServer2D::PhysicsServer2D() { GLOBAL_DEF(PropertyInfo(Variant::INT, "physics/2d/solver/solver_iterations", PROPERTY_HINT_RANGE, "1,32,1,or_greater"), 16); GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/contact_recycle_radius", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater"), 1.0); GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/contact_max_separation", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater"), 1.5); - GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/contact_max_allowed_penetration", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater"), 0.3); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/contact_max_allowed_penetration", PROPERTY_HINT_RANGE, "0.01,10,0.01,or_greater"), 0.3); GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/default_contact_bias", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.8); GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/default_constraint_bias", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.2); } diff --git a/servers/physics_server_3d.cpp b/servers/physics_server_3d.cpp index 9495ce2262..ee749de1c6 100644 --- a/servers/physics_server_3d.cpp +++ b/servers/physics_server_3d.cpp @@ -1059,9 +1059,9 @@ PhysicsServer3D::PhysicsServer3D() { GLOBAL_DEF("physics/3d/sleep_threshold_angular", Math::deg_to_rad(8.0)); GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/time_before_sleep", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater"), 0.5); GLOBAL_DEF(PropertyInfo(Variant::INT, "physics/3d/solver/solver_iterations", PROPERTY_HINT_RANGE, "1,32,1,or_greater"), 16); - GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/contact_recycle_radius", PROPERTY_HINT_RANGE, "0,0.1,0.01,or_greater"), 0.01); - GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/contact_max_separation", PROPERTY_HINT_RANGE, "0,0.1,0.01,or_greater"), 0.05); - GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/contact_max_allowed_penetration", PROPERTY_HINT_RANGE, "0,0.1,0.01,or_greater"), 0.01); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/contact_recycle_radius", PROPERTY_HINT_RANGE, "0,0.1,0.001,or_greater"), 0.01); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/contact_max_separation", PROPERTY_HINT_RANGE, "0,0.1,0.001,or_greater"), 0.05); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/contact_max_allowed_penetration", PROPERTY_HINT_RANGE, "0.001,0.1,0.001,or_greater"), 0.01); GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/default_contact_bias", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.8); } diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index f49d8be37a..5c6f630355 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -1866,7 +1866,7 @@ void RendererCanvasRenderRD::occluder_polygon_set_shape(RID p_occluder, const Ve } } - if (oc->line_point_count != lines.size() && oc->vertex_array.is_valid()) { + if ((oc->line_point_count != lines.size() || lines.size() == 0) && oc->vertex_array.is_valid()) { RD::get_singleton()->free(oc->vertex_array); RD::get_singleton()->free(oc->vertex_buffer); RD::get_singleton()->free(oc->index_array); @@ -1881,6 +1881,7 @@ void RendererCanvasRenderRD::occluder_polygon_set_shape(RID p_occluder, const Ve } if (lines.size()) { + oc->line_point_count = lines.size(); Vector<uint8_t> geometry; Vector<uint8_t> indices; int lc = lines.size(); @@ -1971,7 +1972,7 @@ void RendererCanvasRenderRD::occluder_polygon_set_shape(RID p_occluder, const Ve } } - if (oc->sdf_index_count != sdf_indices.size() && oc->sdf_point_count != p_points.size() && oc->sdf_vertex_array.is_valid()) { + if (((oc->sdf_index_count != sdf_indices.size() && oc->sdf_point_count != p_points.size()) || p_points.size() == 0) && oc->sdf_vertex_array.is_valid()) { RD::get_singleton()->free(oc->sdf_vertex_array); RD::get_singleton()->free(oc->sdf_vertex_buffer); RD::get_singleton()->free(oc->sdf_index_array); diff --git a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl index 631d1968b0..1626244b0a 100644 --- a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl @@ -263,9 +263,8 @@ void main() { // Schlick term. float metallic = texelFetch(source_metallic, ssC << 1, 0).w; - float f0 = mix(0.04, 1.0, metallic); // Assume a "specular" amount of 0.5 - normal.y = -normal.y; - float m = clamp(1.0 - dot(normalize(normal), -view_dir), 0.0, 1.0); + float f0 = mix(0.04, 0.37, metallic); // The default value of R0 is 0.04 and the maximum value is considered to be Germanium with R0 value of 0.37 + float m = clamp(1.0 - dot(normal, -view_dir), 0.0, 1.0); float m2 = m * m; m = m2 * m2 * m; // pow(m,5) final_color.a *= f0 + (1.0 - f0) * m; // Fresnel Schlick term. |
