diff options
30 files changed, 156 insertions, 81 deletions
diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml index 89b9e7d6c2..c3ea440aea 100644 --- a/doc/classes/EditorPlugin.xml +++ b/doc/classes/EditorPlugin.xml @@ -444,7 +444,7 @@ <param index="2" name="script" type="Script" /> <param index="3" name="icon" type="Texture2D" /> <description> - Adds a custom type, which will appear in the list of nodes or resources. An icon can be optionally passed. + Adds a custom type, which will appear in the list of nodes or resources. When a given node or resource is selected, the base type will be instantiated (e.g. "Node3D", "Control", "Resource"), then the script will be loaded and set to this object. [b]Note:[/b] The base type is the base engine class which this type's class hierarchy inherits, not any custom type parent classes. You can use the virtual method [method _handles] to check if your custom object is being edited by checking the script or using the [code]is[/code] keyword. diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index b4c1647dde..c97ae197d9 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -1044,7 +1044,7 @@ [b]Note:[/b] The [url=$DOCS_URL/tutorials/scripting/gdscript/gdscript_styleguide.html]GDScript style guide[/url] recommends using tabs for indentation. It is advised to change this setting only if you need to work on a project that currently uses spaces for indentation. </member> <member name="text_editor/behavior/navigation/custom_word_separators" type="String" setter="" getter=""> - The characters to consider as word delimiters if [member text_editor/behavior/navigation/use_custom_word_separators] is [code]true[/code]. The characters should be defined without separation, for example [code]#_![/code]. + The characters to consider as word delimiters if [member text_editor/behavior/navigation/use_custom_word_separators] is [code]true[/code]. This is in addition to default characters if [member text_editor/behavior/navigation/use_default_word_separators] is [code]true[/code]. The characters should be defined without separation, for example [code]_♥=[/code]. </member> <member name="text_editor/behavior/navigation/drag_and_drop_selection" type="bool" setter="" getter=""> If [code]true[/code], allows drag-and-dropping text in the script editor to move text. Disable this if you find yourself accidentally drag-and-dropping text in the script editor. @@ -1066,10 +1066,10 @@ If [code]true[/code], prevents automatically switching between the Script and 2D/3D screens when selecting a node in the Scene tree dock. </member> <member name="text_editor/behavior/navigation/use_custom_word_separators" type="bool" setter="" getter=""> - If [code]false[/code], using [kbd]Ctrl + Left[/kbd] or [kbd]Ctrl + Right[/kbd] ([kbd]Cmd + Left[/kbd] or [kbd]Cmd + Right[/kbd] on macOS) bindings will use the behavior of [member text_editor/behavior/navigation/use_default_word_separators]. If [code]true[/code], it will also stop the caret if a character within [member text_editor/behavior/navigation/custom_word_separators] is detected. Useful for subword moving. This behavior also will be applied to the behavior of text selection. + If [code]true[/code], uses the characters in [member text_editor/behavior/navigation/custom_word_separators] as word separators for word navigation and operations. This is in addition to the default characters if [member text_editor/behavior/navigation/use_default_word_separators] is also enabled. Word navigation and operations include double-clicking on a word or holding [kbd]Ctrl[/kbd] ([kbd]Cmd[/kbd] on macOS) while pressing [kbd]left[/kbd], [kbd]right[/kbd], [kbd]backspace[/kbd], or [kbd]delete[/kbd]. </member> <member name="text_editor/behavior/navigation/use_default_word_separators" type="bool" setter="" getter=""> - If [code]false[/code], using [kbd]Ctrl + Left[/kbd] or [kbd]Ctrl + Right[/kbd] ([kbd]Cmd + Left[/kbd] or [kbd]Cmd + Right[/kbd] on macOS) bindings will stop moving caret only if a space or punctuation is detected. If [code]true[/code], it will also stop the caret if a character is part of [code]`!"#$%&'()*+,-./:;<=>?@[\]^`{|}~[/code], the Unicode General Punctuation table, or the Unicode CJK Punctuation table. Useful for subword moving. This behavior also will be applied to the behavior of text selection. + If [code]true[/code], uses the characters in [code]`!"#$%&'()*+,-./:;<=>?@[\]^`{|}~[/code], the Unicode General Punctuation table, and the Unicode CJK Punctuation table as word separators for word navigation and operations. If [code]false[/code], a subset of these characters are used and does not include the characters [code]<>$~^=+|[/code]. This is in addition to custom characters if [member text_editor/behavior/navigation/use_custom_word_separators] is also enabled. These characters are used to determine where a word stops. Word navigation and operations include double-clicking on a word or holding [kbd]Ctrl[/kbd] ([kbd]Cmd[/kbd] on macOS) while pressing [kbd]left[/kbd], [kbd]right[/kbd], [kbd]backspace[/kbd], or [kbd]delete[/kbd]. </member> <member name="text_editor/behavior/navigation/v_scroll_speed" type="int" setter="" getter=""> The number of pixels to scroll with every mouse wheel increment. Higher values make the script scroll by faster when using the mouse wheel. @@ -1091,7 +1091,7 @@ The delay in seconds after which autocompletion suggestions should be displayed when the user stops typing. </member> <member name="text_editor/completion/code_complete_enabled" type="bool" setter="" getter=""> - If [code]true[/code], code completion will be triggered automatically after [member text_editor/completion/code_complete_delay]. If [code]false[/code], you can still trigger completion manually by pressing [kbd]Ctrl + Space[/kbd] ([kbd]Cmd + Space[/kbd] on macOS). + If [code]true[/code], code completion will be triggered automatically after [member text_editor/completion/code_complete_delay]. Even if [code]false[/code], code completion can be triggered manually with the [code]ui_text_completion_query[/code] action (by default [kbd]Ctrl + Space[/kbd] or [kbd]Cmd + Space[/kbd] on macOS). </member> <member name="text_editor/completion/colorize_suggestions" type="bool" setter="" getter=""> If [code]true[/code] enables the coloring for some items in the autocompletion suggestions, like vector components. diff --git a/doc/classes/GraphEdit.xml b/doc/classes/GraphEdit.xml index 001839d745..670df10a89 100644 --- a/doc/classes/GraphEdit.xml +++ b/doc/classes/GraphEdit.xml @@ -7,6 +7,7 @@ [GraphEdit] provides tools for creation, manipulation, and display of various graphs. Its main purpose in the engine is to power the visual programming systems, such as visual shaders, but it is also available for use in user projects. [GraphEdit] by itself is only an empty container, representing an infinite grid where [GraphNode]s can be placed. Each [GraphNode] represents a node in the graph, a single unit of data in the connected scheme. [GraphEdit], in turn, helps to control various interactions with nodes and between nodes. When the user attempts to connect, disconnect, or delete a [GraphNode], a signal is emitted in the [GraphEdit], but no action is taken by default. It is the responsibility of the programmer utilizing this control to implement the necessary logic to determine how each request should be handled. [b]Performance:[/b] It is greatly advised to enable low-processor usage mode (see [member OS.low_processor_usage_mode]) when using GraphEdits. + [b]Note:[/b] Keep in mind that [method Node.get_children] will also return the connection layer node named [code]_connection_layer[/code] due to technical limitations. This behavior may change in future releases. </description> <tutorials> </tutorials> diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 4323025770..71603e6190 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -6809,7 +6809,7 @@ EditorNode::EditorNode() { distraction_free = memnew(Button); distraction_free->set_theme_type_variation("FlatMenuButton"); ED_SHORTCUT_AND_COMMAND("editor/distraction_free_mode", TTR("Distraction Free Mode"), KeyModifierMask::CTRL | KeyModifierMask::SHIFT | Key::F11); - ED_SHORTCUT_OVERRIDE("editor/distraction_free_mode", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::D); + ED_SHORTCUT_OVERRIDE("editor/distraction_free_mode", "macos", KeyModifierMask::META | KeyModifierMask::SHIFT | Key::D); ED_SHORTCUT_AND_COMMAND("editor/toggle_last_opened_bottom_panel", TTR("Toggle Last Opened Bottom Panel"), KeyModifierMask::CMD_OR_CTRL | Key::J); distraction_free->set_shortcut(ED_GET_SHORTCUT("editor/distraction_free_mode")); distraction_free->set_tooltip_text(TTR("Toggle distraction-free mode.")); diff --git a/editor/editor_quick_open.cpp b/editor/editor_quick_open.cpp index 0621e5f8d1..dfb87f43da 100644 --- a/editor/editor_quick_open.cpp +++ b/editor/editor_quick_open.cpp @@ -32,6 +32,7 @@ #include "core/os/keyboard.h" #include "editor/editor_node.h" +#include "editor/editor_string_names.h" #include "editor/themes/editor_scale.h" Rect2i EditorQuickOpen::prev_rect = Rect2i(); @@ -119,10 +120,12 @@ void EditorQuickOpen::_update_search() { sorter.sort(entries.ptrw(), entries.size()); } + const int class_icon_size = search_options->get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor)); const int entry_limit = MIN(entries.size(), 300); for (int i = 0; i < entry_limit; i++) { TreeItem *ti = search_options->create_item(root); ti->set_text(0, entries[i].path); + ti->set_icon_max_width(0, class_icon_size); ti->set_icon(0, *icons.lookup_ptr(entries[i].path.get_extension())); } diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp index 97f628c4a4..f73c494b25 100644 --- a/editor/plugins/abstract_polygon_2d_editor.cpp +++ b/editor/plugins/abstract_polygon_2d_editor.cpp @@ -570,7 +570,7 @@ void AbstractPolygon2DEditor::forward_canvas_draw_over_viewport(Control *p_overl const Vector2 p = (vertex == edited_point) ? edited_point.pos : (points[i] + offset); const Vector2 point = xform.xform(p); - const Color overlay_modulate = vertex == active_point ? Color(0.5, 1, 2) : Color(1, 1, 1); + const Color overlay_modulate = vertex == active_point ? Color(0.4, 1, 1) : Color(1, 1, 1); p_overlay->draw_texture(handle, point - handle->get_size() * 0.5, overlay_modulate); if (vertex == hover_point) { diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 5b4341bbe1..4e2940a8cb 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -780,19 +780,24 @@ bool CanvasItemEditor::_select_click_on_item(CanvasItem *item, Point2 p_click_po return still_selected; } -List<CanvasItem *> CanvasItemEditor::_get_edited_canvas_items(bool retrieve_locked, bool remove_canvas_item_if_parent_in_selection) const { +List<CanvasItem *> CanvasItemEditor::_get_edited_canvas_items(bool p_retrieve_locked, bool p_remove_canvas_item_if_parent_in_selection, bool *r_has_locked_items) const { List<CanvasItem *> selection; for (const KeyValue<Node *, Object *> &E : editor_selection->get_selection()) { CanvasItem *ci = Object::cast_to<CanvasItem>(E.key); - if (ci && ci->is_visible_in_tree() && ci->get_viewport() == EditorNode::get_singleton()->get_scene_root() && (retrieve_locked || !_is_node_locked(ci))) { - CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(ci); - if (se) { - selection.push_back(ci); + if (ci) { + if (ci->is_visible_in_tree() && ci->get_viewport() == EditorNode::get_singleton()->get_scene_root() && (p_retrieve_locked || !_is_node_locked(ci))) { + CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(ci); + if (se) { + selection.push_back(ci); + } + } else if (r_has_locked_items) { + // CanvasItem is selected, but can't be interacted with. + *r_has_locked_items = true; } } } - if (remove_canvas_item_if_parent_in_selection) { + if (p_remove_canvas_item_if_parent_in_selection) { List<CanvasItem *> filtered_selection; for (CanvasItem *E : selection) { if (!selection.find(E->get_parent())) { @@ -1454,7 +1459,8 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) { if (drag_type == DRAG_NONE) { if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed()) { if ((b->is_command_or_control_pressed() && !b->is_alt_pressed() && tool == TOOL_SELECT) || tool == TOOL_ROTATE) { - List<CanvasItem *> selection = _get_edited_canvas_items(); + bool has_locked_items = false; + List<CanvasItem *> selection = _get_edited_canvas_items(false, true, &has_locked_items); // Remove not movable nodes for (CanvasItem *E : selection) { @@ -1477,6 +1483,11 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) { } _save_canvas_item_state(drag_selection); return true; + } else { + if (has_locked_items) { + EditorToaster::get_singleton()->popup_str(TTR(locked_transform_warning), EditorToaster::SEVERITY_WARNING); + } + return has_locked_items; } } } @@ -1917,7 +1928,8 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) { // Drag resize handles if (drag_type == DRAG_NONE) { if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed() && ((b->is_alt_pressed() && b->is_command_or_control_pressed()) || tool == TOOL_SCALE)) { - List<CanvasItem *> selection = _get_edited_canvas_items(); + bool has_locked_items = false; + List<CanvasItem *> selection = _get_edited_canvas_items(false, true, &has_locked_items); if (selection.size() == 1) { CanvasItem *ci = selection.front()->get(); @@ -1946,6 +1958,11 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) { _save_canvas_item_state(drag_selection); return true; } + } else { + if (has_locked_items) { + EditorToaster::get_singleton()->popup_str(TTR(locked_transform_warning), EditorToaster::SEVERITY_WARNING); + } + return has_locked_items; } } } @@ -2056,7 +2073,8 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { //Start moving the nodes if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed()) { if ((b->is_alt_pressed() && !b->is_command_or_control_pressed()) || tool == TOOL_MOVE) { - List<CanvasItem *> selection = _get_edited_canvas_items(); + bool has_locked_items = false; + List<CanvasItem *> selection = _get_edited_canvas_items(false, true, &has_locked_items); if (selection.size() > 0) { drag_selection.clear(); @@ -2089,6 +2107,11 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { _save_canvas_item_state(drag_selection); return true; + } else { + if (has_locked_items) { + EditorToaster::get_singleton()->popup_str(TTR(locked_transform_warning), EditorToaster::SEVERITY_WARNING); + } + return has_locked_items; } } } diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index a9de5e9a0b..bae9efebc9 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -188,6 +188,8 @@ private: GRID_VISIBILITY_HIDE, }; + const String locked_transform_warning = TTRC("All selected CanvasItems are either invisible or locked in some way and can't be transformed."); + bool selection_menu_additive_selection = false; Tool tool = TOOL_SELECT; @@ -430,7 +432,7 @@ private: ThemePreviewMode theme_preview = THEME_PREVIEW_PROJECT; void _switch_theme_preview(int p_mode); - List<CanvasItem *> _get_edited_canvas_items(bool retrieve_locked = false, bool remove_canvas_item_if_parent_in_selection = true) const; + List<CanvasItem *> _get_edited_canvas_items(bool p_retrieve_locked = false, bool p_remove_canvas_item_if_parent_in_selection = true, bool *r_has_locked_items = nullptr) const; Rect2 _get_encompassing_rect_from_list(const List<CanvasItem *> &p_list); void _expand_encompassing_rect_using_children(Rect2 &r_rect, const Node *p_node, bool &r_first, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D(), bool include_locked_nodes = true); Rect2 _get_encompassing_rect(const Node *p_node); diff --git a/editor/plugins/gradient_texture_2d_editor_plugin.cpp b/editor/plugins/gradient_texture_2d_editor_plugin.cpp index 9ccbea4c3c..7e22e1209c 100644 --- a/editor/plugins/gradient_texture_2d_editor_plugin.cpp +++ b/editor/plugins/gradient_texture_2d_editor_plugin.cpp @@ -213,7 +213,7 @@ void GradientTexture2DEdit::_draw() { } // Draw handles. - const Color focus_modulate = Color(0.5, 1, 2); + const Color focus_modulate = Color(0.4, 1, 1); bool modulate_handle_from = grabbed == HANDLE_FROM || hovered == HANDLE_FROM; bool modulate_handle_to = grabbed == HANDLE_TO || hovered == HANDLE_TO; draw_texture(fill_from_icon, (_get_handle_pos(HANDLE_FROM) - handle_size / 2).round(), modulate_handle_from ? focus_modulate : Color(1, 1, 1)); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 54526d28df..c6a0dfb888 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -2903,8 +2903,8 @@ void Node3DEditorViewport::_notification(int p_what) { } bool show_info = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_INFORMATION)); - if (show_info != info_label->is_visible()) { - info_label->set_visible(show_info); + if (show_info != info_panel->is_visible()) { + info_panel->set_visible(show_info); } Camera3D *current_camera; @@ -3087,7 +3087,7 @@ void Node3DEditorViewport::_notification(int p_what) { frame_time_gradient->set_color(1, get_theme_color(SNAME("warning_color"), EditorStringName(Editor))); frame_time_gradient->set_color(2, get_theme_color(SNAME("error_color"), EditorStringName(Editor))); - info_label->add_theme_style_override(CoreStringName(normal), information_3d_stylebox); + info_panel->add_theme_style_override(SceneStringName(panel), information_3d_stylebox); frame_time_panel->add_theme_style_override(SceneStringName(panel), information_3d_stylebox); // Set a minimum width to prevent the width from changing all the time @@ -5382,15 +5382,19 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p bottom_center_vbox->set_v_grow_direction(GROW_DIRECTION_BEGIN); surface->add_child(bottom_center_vbox); + info_panel = memnew(PanelContainer); + info_panel->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -90 * EDSCALE); + info_panel->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -90 * EDSCALE); + info_panel->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, -10 * EDSCALE); + info_panel->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -10 * EDSCALE); + info_panel->set_h_grow_direction(GROW_DIRECTION_BEGIN); + info_panel->set_v_grow_direction(GROW_DIRECTION_BEGIN); + info_panel->set_mouse_filter(MOUSE_FILTER_IGNORE); + surface->add_child(info_panel); + info_panel->hide(); + info_label = memnew(Label); - info_label->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -90 * EDSCALE); - info_label->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -90 * EDSCALE); - info_label->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, -10 * EDSCALE); - info_label->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -10 * EDSCALE); - info_label->set_h_grow_direction(GROW_DIRECTION_BEGIN); - info_label->set_v_grow_direction(GROW_DIRECTION_BEGIN); - surface->add_child(info_label); - info_label->hide(); + info_panel->add_child(info_label); cinema_label = memnew(Label); cinema_label->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, 10 * EDSCALE); @@ -5475,6 +5479,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p top_right_vbox->add_child(rotation_control); frame_time_panel = memnew(PanelContainer); + frame_time_panel->set_mouse_filter(MOUSE_FILTER_IGNORE); top_right_vbox->add_child(frame_time_panel); frame_time_panel->hide(); diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index 580c001238..5bd14748c0 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -241,6 +241,7 @@ private: real_t freelook_speed; Vector2 previous_mouse_position; + PanelContainer *info_panel = nullptr; Label *info_label = nullptr; Label *cinema_label = nullptr; Label *locked_label = nullptr; diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index 7fe22f7a5e..e8a7b3b514 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -249,7 +249,7 @@ void GenericTilePolygonEditor::_base_control_draw() { for (int i = 0; i < (int)polygons.size(); i++) { const Vector<Vector2> &polygon = polygons[i]; for (int j = 0; j < polygon.size(); j++) { - const Color poly_modulate = (tinted_polygon_index == i && tinted_point_index == j) ? Color(0.5, 1, 2) : Color(1, 1, 1); + const Color poly_modulate = (tinted_polygon_index == i && tinted_point_index == j) ? Color(0.4, 1, 1) : Color(1, 1, 1); base_control->draw_texture(handle, xform.xform(polygon[j]) - handle->get_size() / 2, poly_modulate); } } @@ -265,7 +265,7 @@ void GenericTilePolygonEditor::_base_control_draw() { } if (drag_type == DRAG_TYPE_CREATE_POINT) { - base_control->draw_texture(handle, xform.xform(in_creation_point) - handle->get_size() / 2, Color(0.5, 1, 2)); + base_control->draw_texture(handle, xform.xform(in_creation_point) - handle->get_size() / 2, Color(0.4, 1, 1)); } // Draw the point creation preview in edit mode. diff --git a/editor/plugins/tiles/tile_map_layer_editor.cpp b/editor/plugins/tiles/tile_map_layer_editor.cpp index 41b8b88f73..f6e4c6f951 100644 --- a/editor/plugins/tiles/tile_map_layer_editor.cpp +++ b/editor/plugins/tiles/tile_map_layer_editor.cpp @@ -763,9 +763,13 @@ bool TileMapLayerEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEven } } else { - // Released - _stop_dragging(); + // Released. drag_erasing = false; + if (drag_type == DRAG_TYPE_NONE) { + return false; + } else { + _stop_dragging(); + } } CanvasItemEditor::get_singleton()->update_viewport(); diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 32fe37d9af..e0bdd4cf33 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -5785,15 +5785,17 @@ struct SceneFormatImporterGLTFInterpolate { return 0.5f * ((2.0f * p1) + (-p0 + p2) * t + (2.0f * p0 - 5.0f * p1 + 4.0f * p2 - p3) * t2 + (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3); } - T bezier(T start, T control_1, T control_2, T end, float t) { - /* Formula from Wikipedia article on Bezier curves. */ - const real_t omt = (1.0 - t); - const real_t omt2 = omt * omt; - const real_t omt3 = omt2 * omt; + T hermite(T start, T tan_start, T end, T tan_end, float t) { + /* Formula from the glTF 2.0 specification. */ const real_t t2 = t * t; const real_t t3 = t2 * t; - return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3; + const real_t h00 = 2.0 * t3 - 3.0 * t2 + 1.0; + const real_t h10 = t3 - 2.0 * t2 + t; + const real_t h01 = -2.0 * t3 + 3.0 * t2; + const real_t h11 = t3 - t2; + + return start * h00 + tan_start * h10 + end * h01 + tan_end * h11; } }; @@ -5814,7 +5816,7 @@ struct SceneFormatImporterGLTFInterpolate<Quaternion> { return p1.slerp(p2, c).normalized(); } - Quaternion bezier(const Quaternion start, const Quaternion control_1, const Quaternion control_2, const Quaternion end, const float t) { + Quaternion hermite(const Quaternion start, const Quaternion tan_start, const Quaternion end, const Quaternion tan_end, const float t) { ERR_FAIL_COND_V_MSG(!start.is_normalized(), Quaternion(), vformat("The start quaternion %s must be normalized.", start)); ERR_FAIL_COND_V_MSG(!end.is_normalized(), Quaternion(), vformat("The end quaternion %s must be normalized.", end)); @@ -5879,14 +5881,15 @@ T GLTFDocument::_interpolate_track(const Vector<real_t> &p_times, const Vector<T return p_values[(p_times.size() - 1) * 3 + 1]; } - const float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]); + const float td = (p_times[idx + 1] - p_times[idx]); + const float c = (p_time - p_times[idx]) / td; const T &from = p_values[idx * 3 + 1]; - const T c1 = from + p_values[idx * 3 + 2]; + const T tan_from = td * p_values[idx * 3 + 2]; const T &to = p_values[idx * 3 + 4]; - const T c2 = to + p_values[idx * 3 + 3]; + const T tan_to = td * p_values[idx * 3 + 3]; - return interp.bezier(from, c1, c2, to, c); + return interp.hermite(from, tan_from, to, tan_to, c); } break; } diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp index 8a7da64eb5..f917c988ea 100644 --- a/modules/navigation/nav_map.cpp +++ b/modules/navigation/nav_map.cpp @@ -644,6 +644,26 @@ Vector3 NavMap::get_closest_point_to_segment(const Vector3 &p_from, const Vector } } } + // Finally, check for a case when shortest distance is between some point located on a face's edge and some point located on a line segment. + if (!use_collision) { + for (size_t point_id = 0; point_id < p.points.size(); point_id += 1) { + Vector3 a, b; + + Geometry3D::get_closest_points_between_segments( + p_from, + p_to, + p.points[point_id].pos, + p.points[(point_id + 1) % p.points.size()].pos, + a, + b); + + const real_t d = a.distance_to(b); + if (d < closest_point_d) { + closest_point_d = d; + closest_point = b; + } + } + } } return closest_point; diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp index 3506a0df2b..b3f735e044 100644 --- a/scene/2d/animated_sprite_2d.cpp +++ b/scene/2d/animated_sprite_2d.cpp @@ -267,7 +267,7 @@ void AnimatedSprite2D::_notification(int p_what) { } if (get_viewport() && get_viewport()->is_snap_2d_transforms_to_pixel_enabled()) { - ofs = ofs.round(); + ofs = (ofs + Point2(0.5, 0.5)).floor(); } Rect2 dst_rect(ofs, s); diff --git a/scene/2d/sprite_2d.cpp b/scene/2d/sprite_2d.cpp index efb5029ac4..2b5c40f212 100644 --- a/scene/2d/sprite_2d.cpp +++ b/scene/2d/sprite_2d.cpp @@ -98,7 +98,7 @@ void Sprite2D::_get_rects(Rect2 &r_src_rect, Rect2 &r_dst_rect, bool &r_filter_c } if (get_viewport() && get_viewport()->is_snap_2d_transforms_to_pixel_enabled()) { - dest_offset = dest_offset.round(); + dest_offset = (dest_offset + Point2(0.5, 0.5)).floor(); } r_dst_rect = Rect2(dest_offset, frame_size); @@ -400,7 +400,7 @@ Rect2 Sprite2D::get_rect() const { } if (get_viewport() && get_viewport()->is_snap_2d_transforms_to_pixel_enabled()) { - ofs = ofs.round(); + ofs = (ofs + Point2(0.5, 0.5)).floor(); } if (s == Size2(0, 0)) { diff --git a/scene/2d/tile_map_layer.cpp b/scene/2d/tile_map_layer.cpp index 171ff7f039..437790bb99 100644 --- a/scene/2d/tile_map_layer.cpp +++ b/scene/2d/tile_map_layer.cpp @@ -1889,7 +1889,7 @@ void TileMapLayer::_update_self_texture_repeat(RS::CanvasItemTextureRepeat p_tex #ifdef TOOLS_ENABLED bool TileMapLayer::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { - return get_cell_source_id(local_to_map(p_point)) != TileSet::INVALID_SOURCE; + return tile_set.is_valid() && get_cell_source_id(local_to_map(p_point)) != TileSet::INVALID_SOURCE; } #endif diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp index c74348c2e7..e7626b3c2d 100644 --- a/scene/animation/animation_mixer.cpp +++ b/scene/animation/animation_mixer.cpp @@ -691,7 +691,9 @@ bool AnimationMixer::_update_caches() { track = track_value; - track_value->init_value = anim->track_get_key_value(i, 0); + bool is_value = track_src_type == Animation::TYPE_VALUE; + + track_value->init_value = is_value ? anim->track_get_key_value(i, 0) : (anim->track_get_key_value(i, 0).operator Array())[0]; track_value->init_value.zero(); track_value->is_init = false; @@ -703,7 +705,7 @@ bool AnimationMixer::_update_caches() { if (has_reset_anim) { int rt = reset_anim->find_track(path, track_src_type); if (rt >= 0) { - if (track_src_type == Animation::TYPE_VALUE) { + if (is_value) { if (reset_anim->track_get_key_count(rt) > 0) { track_value->init_value = reset_anim->track_get_key_value(rt, 0); } diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 416a7fafb6..8ffa0f8c63 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -1249,7 +1249,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } if (is_inside_tree() && get_viewport()->is_snap_2d_transforms_to_pixel_enabled()) { - fx_offset = fx_offset.round(); + fx_offset = (fx_offset + Point2(0.5, 0.5)).floor(); } Vector2 char_off = char_xform.get_origin(); diff --git a/servers/display_server_headless.h b/servers/display_server_headless.h index 7e4a517c1d..60422c16cc 100644 --- a/servers/display_server_headless.h +++ b/servers/display_server_headless.h @@ -51,7 +51,18 @@ private: return memnew(DisplayServerHeadless()); } + static void _dispatch_input_events(const Ref<InputEvent> &p_event) { + static_cast<DisplayServerHeadless *>(get_singleton())->_dispatch_input_event(p_event); + } + + void _dispatch_input_event(const Ref<InputEvent> &p_event) { + if (input_event_callback.is_valid()) { + input_event_callback.call(p_event); + } + } + NativeMenu *native_menu = nullptr; + Callable input_event_callback; public: bool has_feature(Feature p_feature) const override { return false; } @@ -86,7 +97,11 @@ public: void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override {} void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override {} - void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override {} + + void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override { + input_event_callback = p_callable; + } + void window_set_input_text_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override {} void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override {} @@ -137,7 +152,9 @@ public: int64_t window_get_native_handle(HandleType p_handle_type, WindowID p_window = MAIN_WINDOW_ID) const override { return 0; } - void process_events() override {} + void process_events() override { + Input::get_singleton()->flush_buffered_events(); + } void set_native_icon(const String &p_filename) override {} void set_icon(const Ref<Image> &p_icon) override {} @@ -179,7 +196,9 @@ public: DisplayServerHeadless() { native_menu = memnew(NativeMenu); + Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events); } + ~DisplayServerHeadless() { if (native_menu) { memdelete(native_menu); diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp index e92050a323..c4286dcc0c 100644 --- a/servers/rendering/renderer_canvas_cull.cpp +++ b/servers/rendering/renderer_canvas_cull.cpp @@ -284,8 +284,8 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2 } if (snapping_2d_transforms_to_pixel) { - final_xform.columns[2] = final_xform.columns[2].round(); - parent_xform.columns[2] = parent_xform.columns[2].round(); + final_xform.columns[2] = (final_xform.columns[2] + Point2(0.5, 0.5)).floor(); + parent_xform.columns[2] = (parent_xform.columns[2] + Point2(0.5, 0.5)).floor(); } final_xform = parent_xform * final_xform; diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 5b4931edec..5f1643229c 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -1547,7 +1547,7 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type } DataType na = p_op->arguments[0]->get_datatype(); - valid = na > TYPE_BOOL && na < TYPE_MAT2; + valid = na > TYPE_BVEC4 && na < TYPE_MAT2; ret_type = na; } break; case OP_ADD: @@ -1567,7 +1567,7 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type } if (na == nb) { - valid = (na > TYPE_BOOL && na <= TYPE_MAT4); + valid = (na > TYPE_BVEC4 && na <= TYPE_MAT4); ret_type = na; } else if (na == TYPE_INT && nb == TYPE_IVEC2) { valid = true; @@ -1776,7 +1776,7 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type DataType nb = p_op->arguments[1]->get_datatype(); if (na == nb) { - valid = (na > TYPE_BOOL && na <= TYPE_MAT4); + valid = (na > TYPE_BVEC4 && na <= TYPE_MAT4); ret_type = na; } else if (na == TYPE_IVEC2 && nb == TYPE_INT) { valid = true; diff --git a/tests/core/math/test_vector2.h b/tests/core/math/test_vector2.h index fc3fd6a87d..7bd494ec80 100644 --- a/tests/core/math/test_vector2.h +++ b/tests/core/math/test_vector2.h @@ -353,7 +353,6 @@ TEST_CASE("[Vector2] Plane methods") { const Vector2 vector = Vector2(1.2, 3.4); const Vector2 vector_y = Vector2(0, 1); const Vector2 vector_normal = Vector2(0.95879811270838721622267, 0.2840883296913739899919); - const Vector2 vector_non_normal = Vector2(5.4, 1.6); const real_t p_d = 99.1; CHECK_MESSAGE( vector.bounce(vector_y) == Vector2(1.2, -3.4), @@ -383,6 +382,8 @@ TEST_CASE("[Vector2] Plane methods") { vector.slide(vector_normal).is_equal_approx(Vector2(-0.8292559899117276166456, 2.798738965952080706179)), "Vector2 slide with normal should return expected value."); // There's probably a better way to test these ones? +#ifdef MATH_CHECKS + const Vector2 vector_non_normal = Vector2(5.4, 1.6); ERR_PRINT_OFF; CHECK_MESSAGE( vector.bounce(vector_non_normal).is_equal_approx(Vector2()), @@ -394,6 +395,7 @@ TEST_CASE("[Vector2] Plane methods") { vector.slide(vector_non_normal).is_equal_approx(Vector2()), "Vector2 slide should return empty Vector2 with non-normalized input."); ERR_PRINT_ON; +#endif // MATH_CHECKS } TEST_CASE("[Vector2] Rounding methods") { diff --git a/tests/core/math/test_vector3.h b/tests/core/math/test_vector3.h index ca0aa02882..4cab753d6f 100644 --- a/tests/core/math/test_vector3.h +++ b/tests/core/math/test_vector3.h @@ -368,7 +368,6 @@ TEST_CASE("[Vector3] Plane methods") { const Vector3 vector = Vector3(1.2, 3.4, 5.6); const Vector3 vector_y = Vector3(0, 1, 0); const Vector3 vector_normal = Vector3(0.88763458893247992491, 0.26300284116517923701, 0.37806658417494515320); - const Vector3 vector_non_normal = Vector3(5.4, 1.6, 2.3); CHECK_MESSAGE( vector.bounce(vector_y) == Vector3(1.2, -3.4, 5.6), "Vector3 bounce on a plane with normal of the Y axis should."); @@ -394,6 +393,8 @@ TEST_CASE("[Vector3] Plane methods") { vector.slide(vector_normal).is_equal_approx(Vector3(-2.41848149148878681437, 2.32785733585517427722237, 4.0587949202918130235)), "Vector3 slide with normal should return expected value."); // There's probably a better way to test these ones? +#ifdef MATH_CHECKS + const Vector3 vector_non_normal = Vector3(5.4, 1.6, 2.3); ERR_PRINT_OFF; CHECK_MESSAGE( vector.bounce(vector_non_normal).is_equal_approx(Vector3()), @@ -405,6 +406,7 @@ TEST_CASE("[Vector3] Plane methods") { vector.slide(vector_non_normal).is_equal_approx(Vector3()), "Vector3 slide should return empty Vector3 with non-normalized input."); ERR_PRINT_ON; +#endif // MATH_CHECKS } TEST_CASE("[Vector3] Rounding methods") { diff --git a/tests/core/object/test_class_db.h b/tests/core/object/test_class_db.h index 381d759e5b..358bbc08a3 100644 --- a/tests/core/object/test_class_db.h +++ b/tests/core/object/test_class_db.h @@ -375,8 +375,10 @@ void validate_property(const Context &p_context, const ExposedClass &p_class, co } void validate_argument(const Context &p_context, const ExposedClass &p_class, const String &p_owner_name, const String &p_owner_type, const ArgumentData &p_arg) { +#ifdef DEBUG_METHODS_ENABLED TEST_COND((p_arg.name.is_empty() || p_arg.name.begins_with("_unnamed_arg")), vformat("Unnamed argument in position %d of %s '%s.%s'.", p_arg.position, p_owner_type, p_class.name, p_owner_name)); +#endif // DEBUG_METHODS_ENABLED const ExposedClass *arg_class = p_context.find_exposed_class(p_arg.type); if (arg_class) { diff --git a/tests/core/os/test_os.h b/tests/core/os/test_os.h index 6ee0ff82e7..1e2f5e222b 100644 --- a/tests/core/os/test_os.h +++ b/tests/core/os/test_os.h @@ -163,12 +163,14 @@ TEST_CASE("[OS] Processor count and memory information") { CHECK_MESSAGE( OS::get_singleton()->get_processor_count() >= 1, "The returned processor count should be greater than zero."); +#ifdef DEBUG_ENABLED CHECK_MESSAGE( OS::get_singleton()->get_static_memory_usage() >= 1, "The returned static memory usage should be greater than zero."); CHECK_MESSAGE( OS::get_singleton()->get_static_memory_peak_usage() >= 1, "The returned static memory peak usage should be greater than zero."); +#endif // DEBUG_ENABLED } TEST_CASE("[OS] Execute") { diff --git a/tests/display_server_mock.h b/tests/display_server_mock.h index e4946995a7..b44ff06b35 100644 --- a/tests/display_server_mock.h +++ b/tests/display_server_mock.h @@ -36,7 +36,7 @@ #include "servers/rendering/dummy/rasterizer_dummy.h" // Specialized DisplayServer for unittests based on DisplayServerHeadless, that -// additionally supports rudimentary InputEvent handling and mouse position. +// additionally supports things like mouse enter/exit events and clipboard. class DisplayServerMock : public DisplayServerHeadless { private: friend class DisplayServer; @@ -45,7 +45,6 @@ private: CursorShape cursor_shape = CursorShape::CURSOR_ARROW; bool window_over = false; Callable event_callback; - Callable input_event_callback; String clipboard_text; String primary_clipboard_text; @@ -62,16 +61,6 @@ private: return memnew(DisplayServerMock()); } - static void _dispatch_input_events(const Ref<InputEvent> &p_event) { - static_cast<DisplayServerMock *>(get_singleton())->_dispatch_input_event(p_event); - } - - void _dispatch_input_event(const Ref<InputEvent> &p_event) { - if (input_event_callback.is_valid()) { - input_event_callback.call(p_event); - } - } - void _set_mouse_position(const Point2i &p_position) { if (mouse_position == p_position) { return; @@ -153,18 +142,9 @@ public: event_callback = p_callable; } - virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override { - input_event_callback = p_callable; - } - static void register_mock_driver() { register_create_function("mock", create_func, get_rendering_drivers_func); } - - DisplayServerMock() { - Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events); - } - ~DisplayServerMock() {} }; #endif // DISPLAY_SERVER_MOCK_H diff --git a/tests/scene/test_instance_placeholder.h b/tests/scene/test_instance_placeholder.h index d915c5d961..17f2151d54 100644 --- a/tests/scene/test_instance_placeholder.h +++ b/tests/scene/test_instance_placeholder.h @@ -333,6 +333,7 @@ TEST_CASE("[SceneTree][InstancePlaceholder] Instantiate from placeholder with ov } } +#ifdef TOOLS_ENABLED TEST_CASE("[SceneTree][InstancePlaceholder] Instance a PackedScene containing an InstancePlaceholder with no overrides") { GDREGISTER_CLASS(_TestInstancePlaceholderNode); @@ -526,6 +527,7 @@ TEST_CASE("[SceneTree][InstancePlaceholder] Instance a PackedScene containing an DirAccess::remove_file_or_error(internal_path); DirAccess::remove_file_or_error(main_path); } +#endif // TOOLS_ENABLED } //namespace TestInstancePlaceholder diff --git a/tests/scene/test_node.h b/tests/scene/test_node.h index 05764d8f29..e387c73f9f 100644 --- a/tests/scene/test_node.h +++ b/tests/scene/test_node.h @@ -529,6 +529,7 @@ TEST_CASE("[SceneTree][Node]Exported node checks") { memdelete(dup); } +#ifdef TOOLS_ENABLED SUBCASE("Saving instance with exported nodes should not store the unchanged property") { Ref<PackedScene> ps; ps.instantiate(); @@ -602,6 +603,7 @@ TEST_CASE("[SceneTree][Node]Exported node checks") { } CHECK_EQ(stored_properties, 2); } +#endif // TOOLS_ENABLED memdelete(node); } |
