diff options
70 files changed, 1439 insertions, 598 deletions
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 41a8a569d0..b4826c356e 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -845,27 +845,29 @@ Error ResourceLoaderBinary::load() { } } - if (value.get_type() == Variant::ARRAY) { - Array set_array = value; - bool is_get_valid = false; - Variant get_value = res->get(name, &is_get_valid); - if (is_get_valid && get_value.get_type() == Variant::ARRAY) { - Array get_array = get_value; - if (!set_array.is_same_typed(get_array)) { - value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script()); + if (ClassDB::has_property(res->get_class_name(), name)) { + if (value.get_type() == Variant::ARRAY) { + Array set_array = value; + bool is_get_valid = false; + Variant get_value = res->get(name, &is_get_valid); + if (is_get_valid && get_value.get_type() == Variant::ARRAY) { + Array get_array = get_value; + if (!set_array.is_same_typed(get_array)) { + value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script()); + } } } - } - if (value.get_type() == Variant::DICTIONARY) { - Dictionary set_dict = value; - bool is_get_valid = false; - Variant get_value = res->get(name, &is_get_valid); - if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) { - Dictionary get_dict = get_value; - if (!set_dict.is_same_typed(get_dict)) { - value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(), - get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script()); + if (value.get_type() == Variant::DICTIONARY) { + Dictionary set_dict = value; + bool is_get_valid = false; + Variant get_value = res->get(name, &is_get_valid); + if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) { + Dictionary get_dict = get_value; + if (!set_dict.is_same_typed(get_dict)) { + value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(), + get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script()); + } } } } diff --git a/core/math/expression.cpp b/core/math/expression.cpp index 636c2c16bf..0692ece1e6 100644 --- a/core/math/expression.cpp +++ b/core/math/expression.cpp @@ -30,12 +30,7 @@ #include "expression.h" -#include "core/io/marshalls.h" -#include "core/math/math_funcs.h" #include "core/object/class_db.h" -#include "core/object/ref_counted.h" -#include "core/os/os.h" -#include "core/variant/variant_parser.h" Error Expression::_get_token(Token &r_token) { while (true) { @@ -392,7 +387,6 @@ Error Expression::_get_token(Token &r_token) { if (is_digit(c)) { } else if (c == 'e') { reading = READING_EXP; - } else { reading = READING_DONE; } @@ -419,7 +413,9 @@ Error Expression::_get_token(Token &r_token) { is_first_char = false; } - str_ofs--; + if (c != 0) { + str_ofs--; + } r_token.type = TK_CONSTANT; diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml index f938460c2f..a37dd47914 100644 --- a/doc/classes/LineEdit.xml +++ b/doc/classes/LineEdit.xml @@ -4,7 +4,14 @@ An input field for single-line text. </brief_description> <description> - [LineEdit] provides an input field for editing a single line of text. It features many built-in shortcuts that are always available ([kbd]Ctrl[/kbd] here maps to [kbd]Cmd[/kbd] on macOS): + [LineEdit] provides an input field for editing a single line of text. + - When the [LineEdit] control is focused using the keyboard arrow keys, it will only gain focus and not enter edit mode. + - To enter edit mode, click on the control with the mouse or press the "ui_text_submit" action (default: [kbd]Enter[/kbd] or [kbd]Kp Enter[/kbd]). + - To exit edit mode, press "ui_text_submit" or "ui_cancel" (default: [kbd]Escape[/kbd]) actions. + - Check [method is_editing] and [signal editing_toggled] for more information. + [b]Important:[/b] + - Focusing the [LineEdit] with "ui_focus_next" (default: [kbd]Tab[/kbd]) or "ui_focus_prev" (default: [kbd]Shift + Tab[/kbd]) or [method Control.grab_focus] still enters edit mode (for compatibility). + [LineEdit] features many built-in shortcuts that are always available ([kbd]Ctrl[/kbd] here maps to [kbd]Cmd[/kbd] on macOS): - [kbd]Ctrl + C[/kbd]: Copy - [kbd]Ctrl + X[/kbd]: Cut - [kbd]Ctrl + V[/kbd] or [kbd]Ctrl + Y[/kbd]: Paste/"yank" @@ -139,6 +146,12 @@ Inserts [param text] at the caret. If the resulting value is longer than [member max_length], nothing happens. </description> </method> + <method name="is_editing" qualifiers="const"> + <return type="bool" /> + <description> + Returns whether the [LineEdit] is being edited. + </description> + </method> <method name="is_menu_visible" qualifiers="const"> <return type="bool" /> <description> @@ -301,6 +314,12 @@ </member> </members> <signals> + <signal name="editing_toggled"> + <param index="0" name="toggled_on" type="bool" /> + <description> + Emitted when the [LineEdit] switches in or out of edit mode. + </description> + </signal> <signal name="text_change_rejected"> <param index="0" name="rejected_substring" type="String" /> <description> diff --git a/doc/classes/NavigationServer2D.xml b/doc/classes/NavigationServer2D.xml index a0d03d7a01..7e78006240 100644 --- a/doc/classes/NavigationServer2D.xml +++ b/doc/classes/NavigationServer2D.xml @@ -486,7 +486,7 @@ <param index="0" name="map" type="RID" /> <param index="1" name="to_point" type="Vector2" /> <description> - Returns the point closest to the provided [param to_point] on the navigation mesh surface. + Returns the navigation mesh surface point closest to the provided [param to_point] on the navigation [param map]. </description> </method> <method name="map_get_closest_point_owner" qualifiers="const"> @@ -494,7 +494,7 @@ <param index="0" name="map" type="RID" /> <param index="1" name="to_point" type="Vector2" /> <description> - Returns the owner region RID for the point returned by [method map_get_closest_point]. + Returns the owner region RID for the navigation mesh surface point closest to the provided [param to_point] on the navigation [param map]. </description> </method> <method name="map_get_edge_connection_margin" qualifiers="const"> @@ -768,6 +768,14 @@ Creates a new region. </description> </method> + <method name="region_get_closest_point" qualifiers="const"> + <return type="Vector2" /> + <param index="0" name="region" type="RID" /> + <param index="1" name="to_point" type="Vector2" /> + <description> + Returns the navigation mesh surface point closest to the provided [param to_point] on the navigation [param region]. + </description> + </method> <method name="region_get_connection_pathway_end" qualifiers="const"> <return type="Vector2" /> <param index="0" name="region" type="RID" /> diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml index 42f6235f8b..7e206046d6 100644 --- a/doc/classes/NavigationServer3D.xml +++ b/doc/classes/NavigationServer3D.xml @@ -532,7 +532,7 @@ <param index="0" name="map" type="RID" /> <param index="1" name="to_point" type="Vector3" /> <description> - Returns the point closest to the provided [param to_point] on the navigation mesh surface. + Returns the navigation mesh surface point closest to the provided [param to_point] on the navigation [param map]. </description> </method> <method name="map_get_closest_point_normal" qualifiers="const"> @@ -540,7 +540,7 @@ <param index="0" name="map" type="RID" /> <param index="1" name="to_point" type="Vector3" /> <description> - Returns the normal for the point returned by [method map_get_closest_point]. + Returns the navigation mesh surface normal closest to the provided [param to_point] on the navigation [param map]. </description> </method> <method name="map_get_closest_point_owner" qualifiers="const"> @@ -548,7 +548,7 @@ <param index="0" name="map" type="RID" /> <param index="1" name="to_point" type="Vector3" /> <description> - Returns the owner region RID for the point returned by [method map_get_closest_point]. + Returns the owner region RID for the navigation mesh surface point closest to the provided [param to_point] on the navigation [param map]. </description> </method> <method name="map_get_closest_point_to_segment" qualifiers="const"> @@ -558,7 +558,8 @@ <param index="2" name="end" type="Vector3" /> <param index="3" name="use_collision" type="bool" default="false" /> <description> - Returns the closest point between the navigation surface and the segment. + Returns the navigation mesh surface point closest to the provided [param start] and [param end] segment on the navigation [param map]. + If [param use_collision] is [code]true[/code], a closest point test is only done when the segment intersects with the navigation mesh surface. </description> </method> <method name="map_get_edge_connection_margin" qualifiers="const"> @@ -908,6 +909,33 @@ Creates a new region. </description> </method> + <method name="region_get_closest_point" qualifiers="const"> + <return type="Vector3" /> + <param index="0" name="region" type="RID" /> + <param index="1" name="to_point" type="Vector3" /> + <description> + Returns the navigation mesh surface point closest to the provided [param to_point] on the navigation [param region]. + </description> + </method> + <method name="region_get_closest_point_normal" qualifiers="const"> + <return type="Vector3" /> + <param index="0" name="region" type="RID" /> + <param index="1" name="to_point" type="Vector3" /> + <description> + Returns the navigation mesh surface normal closest to the provided [param to_point] on the navigation [param region]. + </description> + </method> + <method name="region_get_closest_point_to_segment" qualifiers="const"> + <return type="Vector3" /> + <param index="0" name="region" type="RID" /> + <param index="1" name="start" type="Vector3" /> + <param index="2" name="end" type="Vector3" /> + <param index="3" name="use_collision" type="bool" default="false" /> + <description> + Returns the navigation mesh surface point closest to the provided [param start] and [param end] segment on the navigation [param region]. + If [param use_collision] is [code]true[/code], a closest point test is only done when the segment intersects with the navigation mesh surface. + </description> + </method> <method name="region_get_connection_pathway_end" qualifiers="const"> <return type="Vector3" /> <param index="0" name="region" type="RID" /> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 497070fa81..08427ffe83 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -2947,6 +2947,9 @@ <member name="xr/openxr/enabled" type="bool" setter="" getter="" default="false"> If [code]true[/code], Godot will setup and initialize OpenXR on startup. </member> + <member name="xr/openxr/enabled.editor" type="bool" setter="" getter="" default="false"> + If [code]true[/code], Godot will setup and initialize OpenXR on editor startup. + </member> <member name="xr/openxr/environment_blend_mode" type="int" setter="" getter="" default=""0""> Specify how OpenXR should blend in the environment. This is specific to certain AR and passthrough devices where camera images are blended in by the XR compositor. </member> diff --git a/doc/classes/ResourceImporterScene.xml b/doc/classes/ResourceImporterScene.xml index 900e028b25..1565a244fe 100644 --- a/doc/classes/ResourceImporterScene.xml +++ b/doc/classes/ResourceImporterScene.xml @@ -68,6 +68,9 @@ <member name="nodes/root_type" type="String" setter="" getter="" default=""""> Override for the root node type. If empty, the root node will use what the scene specifies, or [Node3D] if the scene does not specify a root type. Using a node type that inherits from [Node3D] is recommended. Otherwise, you'll lose the ability to position the node directly in the 3D editor. </member> + <member name="nodes/use_node_type_suffixes" type="bool" setter="" getter="" default="true"> + If [code]true[/code], use suffixes in the node names to determine the node type, such as [code]-col[/code] for collision shapes. Disabling this makes editor-imported files more similar to the original files, and more similar to importing files at runtime. See [url=$DOCS_URL/tutorials/assets_pipeline/importing_3d_scenes/node_type_customization.html]Node type customization using name suffixes[/url] for more information. + </member> <member name="skins/use_named_skins" type="bool" setter="" getter="" default="true"> If checked, use named [Skin]s for animation. The [MeshInstance3D] node contains 3 properties of relevance here: a skeleton [NodePath] pointing to the [Skeleton3D] node (usually [code]..[/code]), a mesh, and a skin: - The [Skeleton3D] node contains a list of bones with names, their pose and rest, a name and a parent bone. diff --git a/doc/classes/Theme.xml b/doc/classes/Theme.xml index eb3c170583..479456ae66 100644 --- a/doc/classes/Theme.xml +++ b/doc/classes/Theme.xml @@ -551,7 +551,7 @@ </member> <member name="default_font_size" type="int" setter="set_default_font_size" getter="get_default_font_size" default="-1"> The default font size of this theme resource. Used as the default value when trying to fetch a font size value that doesn't exist in this theme or is in invalid state. If the default font size is also missing or invalid, the engine fallback value is used (see [member ThemeDB.fallback_font_size]). - Values below [code]0[/code] are invalid and can be used to unset the property. Use [method has_default_font_size] to check if this value is valid. + Values below [code]1[/code] are invalid and can be used to unset the property. Use [method has_default_font_size] to check if this value is valid. </member> </members> <constants> diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index a37eba3b15..7d5af48384 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -348,7 +348,7 @@ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataType type, int p_ } } -_FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, const Vector<ShaderLanguage::ConstantNode::Value> &value, uint8_t *data) { +_FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, const Vector<ShaderLanguage::Scalar> &value, uint8_t *data) { switch (type) { case ShaderLanguage::TYPE_BOOL: { uint32_t *gui = (uint32_t *)data; @@ -572,7 +572,7 @@ void ShaderData::set_default_texture_parameter(const StringName &p_name, RID p_t Variant ShaderData::get_default_parameter(const StringName &p_parameter) const { if (uniforms.has(p_parameter)) { ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; - Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; + Vector<ShaderLanguage::Scalar> default_value = uniform.default_value; return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint); } return Variant(); diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp index de82d74aff..5058554659 100644 --- a/drivers/gles3/storage/mesh_storage.cpp +++ b/drivers/gles3/storage/mesh_storage.cpp @@ -1769,14 +1769,14 @@ AABB MeshStorage::_multimesh_get_custom_aabb(RID p_multimesh) const { return multimesh->custom_aabb; } -AABB MeshStorage::_multimesh_get_aabb(RID p_multimesh) const { +AABB MeshStorage::_multimesh_get_aabb(RID p_multimesh) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, AABB()); if (multimesh->custom_aabb != AABB()) { return multimesh->custom_aabb; } if (multimesh->aabb_dirty) { - const_cast<MeshStorage *>(this)->_update_dirty_multimeshes(); + _update_dirty_multimeshes(); } return multimesh->aabb; } diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h index a2edbb9c48..31858cd372 100644 --- a/drivers/gles3/storage/mesh_storage.h +++ b/drivers/gles3/storage/mesh_storage.h @@ -510,7 +510,7 @@ public: virtual RID _multimesh_get_mesh(RID p_multimesh) const override; virtual void _multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) override; virtual AABB _multimesh_get_custom_aabb(RID p_multimesh) const override; - virtual AABB _multimesh_get_aabb(RID p_multimesh) const override; + virtual AABB _multimesh_get_aabb(RID p_multimesh) override; virtual Transform3D _multimesh_instance_get_transform(RID p_multimesh, int p_index) const override; virtual Transform2D _multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override; diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index 0243d863f8..9d6aa13332 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -118,11 +118,12 @@ Error FileAccessWindows::open_internal(const String &p_path, int p_mode_flags) { return ERR_INVALID_PARAMETER; } - struct _stat st; - if (_wstat((LPCWSTR)(path.utf16().get_data()), &st) == 0) { - if (!S_ISREG(st.st_mode)) { - return ERR_FILE_CANT_OPEN; - } + if (path.ends_with(":\\") || path.ends_with(":")) { + return ERR_FILE_CANT_OPEN; + } + DWORD file_attr = GetFileAttributesW((LPCWSTR)(path.utf16().get_data())); + if (file_attr != INVALID_FILE_ATTRIBUTES && (file_attr & FILE_ATTRIBUTE_DIRECTORY)) { + return ERR_FILE_CANT_OPEN; } #ifdef TOOLS_ENABLED @@ -412,15 +413,40 @@ uint64_t FileAccessWindows::_get_modified_time(const String &p_file) { file = file.substr(0, file.length() - 1); } - struct _stat st; - int rv = _wstat((LPCWSTR)(file.utf16().get_data()), &st); + HANDLE handle = CreateFileW((LPCWSTR)(file.utf16().get_data()), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); - if (rv == 0) { - return st.st_mtime; - } else { - print_verbose("Failed to get modified time for: " + p_file + ""); - return 0; + if (handle != INVALID_HANDLE_VALUE) { + FILETIME ft_create, ft_write; + + bool status = GetFileTime(handle, &ft_create, nullptr, &ft_write); + + CloseHandle(handle); + + if (status) { + uint64_t ret = 0; + + // If write time is invalid, fallback to creation time. + if (ft_write.dwHighDateTime == 0 && ft_write.dwLowDateTime == 0) { + ret = ft_create.dwHighDateTime; + ret <<= 32; + ret |= ft_create.dwLowDateTime; + } else { + ret = ft_write.dwHighDateTime; + ret <<= 32; + ret |= ft_write.dwLowDateTime; + } + + const uint64_t WINDOWS_TICKS_PER_SECOND = 10000000; + const uint64_t TICKS_TO_UNIX_EPOCH = 116444736000000000LL; + + if (ret >= TICKS_TO_UNIX_EPOCH) { + return (ret - TICKS_TO_UNIX_EPOCH) / WINDOWS_TICKS_PER_SECOND; + } + } } + + print_verbose("Failed to get modified time for: " + p_file); + return 0; } BitField<FileAccess::UnixPermissionFlags> FileAccessWindows::_get_unix_permissions(const String &p_file) { diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 123d903220..2e46068e07 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -81,7 +81,6 @@ void EditorPropertyText::_text_submitted(const String &p_string) { } if (text->has_focus()) { - text->release_focus(); _text_changed(p_string); } } @@ -2703,7 +2702,11 @@ void EditorPropertyNodePath::_update_menu() { void EditorPropertyNodePath::_menu_option(int p_idx) { switch (p_idx) { case ACTION_CLEAR: { - emit_changed(get_edited_property(), NodePath()); + if (editing_node) { + emit_changed(get_edited_property(), Variant()); + } else { + emit_changed(get_edited_property(), NodePath()); + } update_property(); } break; diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index b1a91fa3dd..d0c89c49c4 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -527,15 +527,18 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { // Touchscreen bool has_touchscreen_ui = DisplayServer::get_singleton()->is_touchscreen_available(); - EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/touchscreen/increase_scrollbar_touch_area", has_touchscreen_ui, "") - set_restart_if_changed("interface/touchscreen/increase_scrollbar_touch_area", true); - EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/touchscreen/enable_long_press_as_right_click", has_touchscreen_ui, "") - set_restart_if_changed("interface/touchscreen/enable_long_press_as_right_click", true); EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/touchscreen/enable_pan_and_scale_gestures", has_touchscreen_ui, "") set_restart_if_changed("interface/touchscreen/enable_pan_and_scale_gestures", true); EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "interface/touchscreen/scale_gizmo_handles", has_touchscreen_ui ? 3 : 1, "1,5,1") set_restart_if_changed("interface/touchscreen/scale_gizmo_handles", true); + // Disable some touchscreen settings by default for the XR Editor. + bool is_native_touchscreen = has_touchscreen_ui && !OS::get_singleton()->has_feature("xr_editor"); + EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/touchscreen/enable_long_press_as_right_click", is_native_touchscreen, "") + set_restart_if_changed("interface/touchscreen/enable_long_press_as_right_click", true); + EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/touchscreen/increase_scrollbar_touch_area", is_native_touchscreen, "") + set_restart_if_changed("interface/touchscreen/increase_scrollbar_touch_area", true); + // Scene tabs EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/scene_tabs/display_close_button", 1, "Never,If Tab Active,Always"); // TabBar::CloseButtonDisplayPolicy _initial_set("interface/scene_tabs/show_thumbnail_on_hover", true); diff --git a/editor/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp index 5c28213ca7..cb348f713c 100644 --- a/editor/import/3d/resource_importer_scene.cpp +++ b/editor/import/3d/resource_importer_scene.cpp @@ -637,10 +637,10 @@ void _apply_permanent_scale_to_descendants(Node *p_root_node, Vector3 p_scale) { _apply_scale_to_scalable_node_collection(scalable_node_collection, p_scale); } -Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &r_collision_map, Pair<PackedVector3Array, PackedInt32Array> *r_occluder_arrays, List<Pair<NodePath, Node *>> &r_node_renames) { +Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &r_collision_map, Pair<PackedVector3Array, PackedInt32Array> *r_occluder_arrays, List<Pair<NodePath, Node *>> &r_node_renames, const HashMap<StringName, Variant> &p_options) { // Children first. for (int i = 0; i < p_node->get_child_count(); i++) { - Node *r = _pre_fix_node(p_node->get_child(i), p_root, r_collision_map, r_occluder_arrays, r_node_renames); + Node *r = _pre_fix_node(p_node->get_child(i), p_root, r_collision_map, r_occluder_arrays, r_node_renames, p_options); if (!r) { i--; // Was erased. } @@ -750,6 +750,14 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, HashMap<R } } + bool use_node_type_suffixes = true; + if (p_options.has("nodes/use_node_type_suffixes")) { + use_node_type_suffixes = p_options["nodes/use_node_type_suffixes"]; + } + if (!use_node_type_suffixes) { + return p_node; + } + if (_teststr(name, "colonly") || _teststr(name, "convcolonly")) { if (isroot) { return p_node; @@ -2373,6 +2381,7 @@ void ResourceImporterScene::get_import_options(const String &p_path, List<Import r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "nodes/apply_root_scale"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "nodes/root_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001"), 1.0)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "nodes/import_as_skeleton_bones"), false)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "nodes/use_node_type_suffixes"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/ensure_tangents"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/generate_lods"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/create_shadow_meshes"), true)); @@ -2854,7 +2863,7 @@ Node *ResourceImporterScene::pre_import(const String &p_source_file, const HashM HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> collision_map; List<Pair<NodePath, Node *>> node_renames; - _pre_fix_node(scene, scene, collision_map, nullptr, node_renames); + _pre_fix_node(scene, scene, collision_map, nullptr, node_renames, p_options); return scene; } @@ -2992,7 +3001,7 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p Pair<PackedVector3Array, PackedInt32Array> occluder_arrays; List<Pair<NodePath, Node *>> node_renames; - _pre_fix_node(scene, scene, collision_map, &occluder_arrays, node_renames); + _pre_fix_node(scene, scene, collision_map, &occluder_arrays, node_renames, p_options); for (int i = 0; i < post_importer_plugins.size(); i++) { post_importer_plugins.write[i]->pre_process(scene, p_options); diff --git a/editor/import/3d/resource_importer_scene.h b/editor/import/3d/resource_importer_scene.h index 9759f328d7..fe757dc2a3 100644 --- a/editor/import/3d/resource_importer_scene.h +++ b/editor/import/3d/resource_importer_scene.h @@ -288,7 +288,7 @@ public: virtual int get_import_order() const override { return ResourceImporter::IMPORT_ORDER_SCENE; } void _pre_fix_global(Node *p_scene, const HashMap<StringName, Variant> &p_options) const; - Node *_pre_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &r_collision_map, Pair<PackedVector3Array, PackedInt32Array> *r_occluder_arrays, List<Pair<NodePath, Node *>> &r_node_renames); + Node *_pre_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &r_collision_map, Pair<PackedVector3Array, PackedInt32Array> *r_occluder_arrays, List<Pair<NodePath, Node *>> &r_node_renames, const HashMap<StringName, Variant> &p_options); Node *_pre_fix_animations(Node *p_node, Node *p_root, const Dictionary &p_node_data, const Dictionary &p_animation_data, float p_animation_fps); Node *_post_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Pair<PackedVector3Array, PackedInt32Array> &r_occluder_arrays, HashSet<Ref<ImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps, float p_applied_root_scale); Node *_post_fix_animations(Node *p_node, Node *p_root, const Dictionary &p_node_data, const Dictionary &p_animation_data, float p_animation_fps, bool p_remove_immutable_tracks); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index d9b23875f1..e9a796dae7 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -4012,6 +4012,8 @@ void CanvasItemEditor::_project_settings_changed() { void CanvasItemEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: { + _update_lock_and_group_button(); + EditorRunBar::get_singleton()->connect("play_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(true)); EditorRunBar::get_singleton()->connect("stop_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(false)); ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &CanvasItemEditor::_project_settings_changed)); diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 7ac924571d..a2c36b1f3c 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -76,7 +76,7 @@ void post_process_preview(Ref<Image> p_image) { } bool EditorTexturePreviewPlugin::handles(const String &p_type) const { - return ClassDB::is_parent_class(p_type, "Texture2D"); + return ClassDB::is_parent_class(p_type, "Texture2D") || ClassDB::is_parent_class(p_type, "Texture3D") || ClassDB::is_parent_class(p_type, "TextureLayered"); } bool EditorTexturePreviewPlugin::generate_small_preview_automatically() const { @@ -85,9 +85,13 @@ bool EditorTexturePreviewPlugin::generate_small_preview_automatically() const { Ref<Texture2D> EditorTexturePreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const { Ref<Image> img; - Ref<AtlasTexture> atex = p_from; - if (atex.is_valid()) { - Ref<Texture2D> tex = atex->get_atlas(); + + Ref<AtlasTexture> tex_atlas = p_from; + Ref<Texture3D> tex_3d = p_from; + Ref<TextureLayered> tex_lyr = p_from; + + if (tex_atlas.is_valid()) { + Ref<Texture2D> tex = tex_atlas->get_atlas(); if (!tex.is_valid()) { return Ref<Texture2D>(); } @@ -97,11 +101,36 @@ Ref<Texture2D> EditorTexturePreviewPlugin::generate(const Ref<Resource> &p_from, return Ref<Texture2D>(); } - if (!atex->get_region().has_area()) { + if (!tex_atlas->get_region().has_area()) { + return Ref<Texture2D>(); + } + + img = atlas->get_region(tex_atlas->get_region()); + + } else if (tex_3d.is_valid()) { + if (tex_3d->get_depth() == 0) { + return Ref<Texture2D>(); + } + + const int mid_depth = (tex_3d->get_depth() - 1) / 2; + + Vector<Ref<Image>> data = tex_3d->get_data(); + if (!data.is_empty() && data[mid_depth].is_valid()) { + img = data[mid_depth]->duplicate(); + } + + } else if (tex_lyr.is_valid()) { + if (tex_lyr->get_layers() == 0) { return Ref<Texture2D>(); } - img = atlas->get_region(atex->get_region()); + const int mid_layer = (tex_lyr->get_layers() - 1) / 2; + + Ref<Image> data = tex_lyr->get_layer_data(mid_layer); + if (data.is_valid()) { + img = data->duplicate(); + } + } else { Ref<Texture2D> tex = p_from; if (tex.is_valid()) { @@ -115,6 +144,7 @@ Ref<Texture2D> EditorTexturePreviewPlugin::generate(const Ref<Resource> &p_from, if (img.is_null() || img->is_empty()) { return Ref<Texture2D>(); } + p_metadata["dimensions"] = img->get_size(); img->clear_mipmaps(); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 9168dc812c..f58dfbb5a5 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -811,7 +811,7 @@ ObjectID Node3DEditorViewport::_select_ray(const Point2 &p_pos) const { Vector<Node3D *> nodes_with_gizmos = Node3DEditor::get_singleton()->gizmo_bvh_ray_query(pos, pos + ray * camera->get_far()); for (Node3D *spat : nodes_with_gizmos) { - if (!spat) { + if (!spat || _is_node_locked(spat)) { continue; } @@ -1558,7 +1558,7 @@ void Node3DEditorViewport::_surface_focus_exit() { view_menu->set_disable_shortcuts(true); } -bool Node3DEditorViewport ::_is_node_locked(const Node *p_node) { +bool Node3DEditorViewport::_is_node_locked(const Node *p_node) const { return p_node->get_meta("_edit_lock_", false); } @@ -1936,11 +1936,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { if (after != EditorPlugin::AFTER_GUI_INPUT_CUSTOM) { // Single item selection. - Vector<_RayResult> selection; - _find_items_at_pos(b->get_position(), selection, false); - if (!selection.is_empty()) { - clicked = selection[0].item->get_instance_id(); - } + clicked = _select_ray(b->get_position()); selection_in_progress = true; diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index 2cfe784ca6..c7e6420875 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -460,7 +460,7 @@ private: bool previewing_camera = false; bool previewing_cinema = false; - bool _is_node_locked(const Node *p_node); + bool _is_node_locked(const Node *p_node) const; void _preview_exited_scene(); void _toggle_camera_preview(bool); void _toggle_cinema_preview(bool); diff --git a/main/main.cpp b/main/main.cpp index 18ffedef18..e59f6d03b0 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2541,6 +2541,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph // XR project settings. GLOBAL_DEF_RST_BASIC("xr/openxr/enabled", false); + GLOBAL_DEF_RST_BASIC("xr/openxr/enabled.editor", false); GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "xr/openxr/default_action_map", PROPERTY_HINT_FILE, "*.tres"), "res://openxr_action_map.tres"); GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/form_factor", PROPERTY_HINT_ENUM, "Head Mounted,Handheld"), "0"); GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/view_configuration", PROPERTY_HINT_ENUM, "Mono,Stereo"), "1"); // "Mono,Stereo,Quad,Observer" diff --git a/modules/basis_universal/image_compress_basisu.cpp b/modules/basis_universal/image_compress_basisu.cpp index 8167fe8c73..ab20d00b5b 100644 --- a/modules/basis_universal/image_compress_basisu.cpp +++ b/modules/basis_universal/image_compress_basisu.cpp @@ -84,14 +84,12 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha decompress_format = BASIS_DECOMPRESS_RGBA; } break; case Image::USED_CHANNELS_R: { - decompress_format = BASIS_DECOMPRESS_RGB; + decompress_format = BASIS_DECOMPRESS_R; } break; case Image::USED_CHANNELS_RG: { - // Currently RG textures are compressed as DXT5/ETC2_RGBA8 with a RA -> RG swizzle, - // as BasisUniversal didn't use to support ETC2_RG11 transcoding. params.m_force_alpha = true; image->convert_rg_to_ra_rgba8(); - decompress_format = BASIS_DECOMPRESS_RG_AS_RA; + decompress_format = BASIS_DECOMPRESS_RG; } break; case Image::USED_CHANNELS_RGB: { decompress_format = BASIS_DECOMPRESS_RGB; @@ -219,15 +217,68 @@ Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) { // Get supported compression formats. bool bptc_supported = RS::get_singleton()->has_os_feature("bptc"); bool astc_supported = RS::get_singleton()->has_os_feature("astc"); + bool rgtc_supported = RS::get_singleton()->has_os_feature("rgtc"); bool s3tc_supported = RS::get_singleton()->has_os_feature("s3tc"); bool etc2_supported = RS::get_singleton()->has_os_feature("etc2"); bool needs_ra_rg_swap = false; + bool needs_rg_trim = false; + + BasisDecompressFormat decompress_format = (BasisDecompressFormat)(*(uint32_t *)(src_ptr)); - switch (*(uint32_t *)(src_ptr)) { + switch (decompress_format) { + case BASIS_DECOMPRESS_R: { + if (rgtc_supported) { + basisu_format = basist::transcoder_texture_format::cTFBC4_R; + image_format = Image::FORMAT_RGTC_R; + } else if (s3tc_supported) { + basisu_format = basist::transcoder_texture_format::cTFBC1; + image_format = Image::FORMAT_DXT1; + } else if (etc2_supported) { + basisu_format = basist::transcoder_texture_format::cTFETC2_EAC_R11; + image_format = Image::FORMAT_ETC2_R11; + } else { + // No supported VRAM compression formats, decompress. + basisu_format = basist::transcoder_texture_format::cTFRGBA32; + image_format = Image::FORMAT_RGBA8; + needs_rg_trim = true; + } + + } break; case BASIS_DECOMPRESS_RG: { - // RGTC transcoding is currently performed with RG_AS_RA, fail. - ERR_FAIL_V(image); + if (rgtc_supported) { + basisu_format = basist::transcoder_texture_format::cTFBC5_RG; + image_format = Image::FORMAT_RGTC_RG; + } else if (s3tc_supported) { + basisu_format = basist::transcoder_texture_format::cTFBC3; + image_format = Image::FORMAT_DXT5_RA_AS_RG; + } else if (etc2_supported) { + basisu_format = basist::transcoder_texture_format::cTFETC2_EAC_RG11; + image_format = Image::FORMAT_ETC2_RG11; + } else { + // No supported VRAM compression formats, decompress. + basisu_format = basist::transcoder_texture_format::cTFRGBA32; + image_format = Image::FORMAT_RGBA8; + needs_ra_rg_swap = true; + needs_rg_trim = true; + } + + } break; + case BASIS_DECOMPRESS_RG_AS_RA: { + if (s3tc_supported) { + basisu_format = basist::transcoder_texture_format::cTFBC3; + image_format = Image::FORMAT_DXT5_RA_AS_RG; + } else if (etc2_supported) { + basisu_format = basist::transcoder_texture_format::cTFETC2; + image_format = Image::FORMAT_ETC2_RA_AS_RG; + } else { + // No supported VRAM compression formats, decompress. + basisu_format = basist::transcoder_texture_format::cTFRGBA32; + image_format = Image::FORMAT_RGBA8; + needs_ra_rg_swap = true; + needs_rg_trim = true; + } + } break; case BASIS_DECOMPRESS_RGB: { if (bptc_supported) { @@ -267,20 +318,7 @@ Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) { basisu_format = basist::transcoder_texture_format::cTFRGBA32; image_format = Image::FORMAT_RGBA8; } - } break; - case BASIS_DECOMPRESS_RG_AS_RA: { - if (s3tc_supported) { - basisu_format = basist::transcoder_texture_format::cTFBC3; - image_format = Image::FORMAT_DXT5_RA_AS_RG; - } else if (etc2_supported) { - basisu_format = basist::transcoder_texture_format::cTFETC2; - image_format = Image::FORMAT_ETC2_RA_AS_RG; - } else { - // No supported VRAM compression formats, decompress. - basisu_format = basist::transcoder_texture_format::cTFRGBA32; - image_format = Image::FORMAT_RGBA8; - needs_ra_rg_swap = true; - } + } break; } @@ -324,6 +362,15 @@ Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) { image->convert_ra_rgba8_to_rg(); } + if (needs_rg_trim) { + // Remove unnecessary color channels from uncompressed textures. + if (decompress_format == BASIS_DECOMPRESS_R) { + image->convert(Image::FORMAT_R8); + } else if (decompress_format == BASIS_DECOMPRESS_RG || decompress_format == BASIS_DECOMPRESS_RG_AS_RA) { + image->convert(Image::FORMAT_RG8); + } + } + return image; } diff --git a/modules/basis_universal/image_compress_basisu.h b/modules/basis_universal/image_compress_basisu.h index ac5d62ae73..5e36d448f6 100644 --- a/modules/basis_universal/image_compress_basisu.h +++ b/modules/basis_universal/image_compress_basisu.h @@ -38,6 +38,7 @@ enum BasisDecompressFormat { BASIS_DECOMPRESS_RGB, BASIS_DECOMPRESS_RGBA, BASIS_DECOMPRESS_RG_AS_RA, + BASIS_DECOMPRESS_R, }; void basis_universal_init(); diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 8777651545..8c81c0ce4e 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -513,7 +513,7 @@ Ref<ConcavePolygonShape3D> CSGShape3D::bake_collision_shape() { } bool CSGShape3D::_is_debug_collision_shape_visible() { - return is_inside_tree() && (get_tree()->is_debugging_collisions_hint() || Engine::get_singleton()->is_editor_hint()); + return !Engine::get_singleton()->is_editor_hint() && is_inside_tree() && get_tree()->is_debugging_collisions_hint(); } void CSGShape3D::_update_debug_collision_shape() { @@ -604,11 +604,6 @@ void CSGShape3D::_notification(int p_what) { // Update this node's parent only if its own visibility has changed, not the visibility of parent nodes parent_shape->_make_dirty(); } - if (is_visible()) { - _update_debug_collision_shape(); - } else { - _clear_debug_collision_shape(); - } last_visible = is_visible(); } break; diff --git a/modules/minimp3/audio_stream_mp3.cpp b/modules/minimp3/audio_stream_mp3.cpp index 394213963a..e29fe9295a 100644 --- a/modules/minimp3/audio_stream_mp3.cpp +++ b/modules/minimp3/audio_stream_mp3.cpp @@ -159,6 +159,9 @@ Ref<AudioSamplePlayback> AudioStreamPlaybackMP3::get_sample_playback() const { void AudioStreamPlaybackMP3::set_sample_playback(const Ref<AudioSamplePlayback> &p_playback) { sample_playback = p_playback; + if (sample_playback.is_valid()) { + sample_playback->stream_playback = Ref<AudioStreamPlayback>(this); + } } void AudioStreamPlaybackMP3::set_parameter(const StringName &p_name, const Variant &p_value) { diff --git a/modules/navigation/2d/godot_navigation_server_2d.cpp b/modules/navigation/2d/godot_navigation_server_2d.cpp index bf69adc14c..2af125d434 100644 --- a/modules/navigation/2d/godot_navigation_server_2d.cpp +++ b/modules/navigation/2d/godot_navigation_server_2d.cpp @@ -318,6 +318,11 @@ int FORWARD_1_C(region_get_connections_count, RID, p_region, rid_to_rid); Vector2 FORWARD_2_R_C(v3_to_v2, region_get_connection_pathway_start, RID, p_region, int, p_connection_id, rid_to_rid, int_to_int); Vector2 FORWARD_2_R_C(v3_to_v2, region_get_connection_pathway_end, RID, p_region, int, p_connection_id, rid_to_rid, int_to_int); +Vector2 GodotNavigationServer2D::region_get_closest_point(RID p_region, const Vector2 &p_point) const { + Vector3 result = NavigationServer3D::get_singleton()->region_get_closest_point(p_region, v2_to_v3(p_point)); + return v3_to_v2(result); +} + Vector2 GodotNavigationServer2D::region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const { Vector3 result = NavigationServer3D::get_singleton()->region_get_random_point(p_region, p_navigation_layers, p_uniformly); return v3_to_v2(result); diff --git a/modules/navigation/2d/godot_navigation_server_2d.h b/modules/navigation/2d/godot_navigation_server_2d.h index ea77fa5e6e..1579ca2907 100644 --- a/modules/navigation/2d/godot_navigation_server_2d.h +++ b/modules/navigation/2d/godot_navigation_server_2d.h @@ -101,6 +101,7 @@ public: virtual int region_get_connections_count(RID p_region) const override; virtual Vector2 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override; virtual Vector2 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override; + virtual Vector2 region_get_closest_point(RID p_region, const Vector2 &p_point) const override; virtual Vector2 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override; virtual RID link_create() override; diff --git a/modules/navigation/3d/godot_navigation_server_3d.cpp b/modules/navigation/3d/godot_navigation_server_3d.cpp index 11a5de608b..5dfc39f6f5 100644 --- a/modules/navigation/3d/godot_navigation_server_3d.cpp +++ b/modules/navigation/3d/godot_navigation_server_3d.cpp @@ -536,6 +536,27 @@ Vector3 GodotNavigationServer3D::region_get_connection_pathway_end(RID p_region, return Vector3(); } +Vector3 GodotNavigationServer3D::region_get_closest_point_to_segment(RID p_region, const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision) const { + const NavRegion *region = region_owner.get_or_null(p_region); + ERR_FAIL_NULL_V(region, Vector3()); + + return region->get_closest_point_to_segment(p_from, p_to, p_use_collision); +} + +Vector3 GodotNavigationServer3D::region_get_closest_point(RID p_region, const Vector3 &p_point) const { + const NavRegion *region = region_owner.get_or_null(p_region); + ERR_FAIL_NULL_V(region, Vector3()); + + return region->get_closest_point_info(p_point).point; +} + +Vector3 GodotNavigationServer3D::region_get_closest_point_normal(RID p_region, const Vector3 &p_point) const { + const NavRegion *region = region_owner.get_or_null(p_region); + ERR_FAIL_NULL_V(region, Vector3()); + + return region->get_closest_point_info(p_point).normal; +} + Vector3 GodotNavigationServer3D::region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const { const NavRegion *region = region_owner.get_or_null(p_region); ERR_FAIL_NULL_V(region, Vector3()); diff --git a/modules/navigation/3d/godot_navigation_server_3d.h b/modules/navigation/3d/godot_navigation_server_3d.h index 12a1132f07..eae6ea2860 100644 --- a/modules/navigation/3d/godot_navigation_server_3d.h +++ b/modules/navigation/3d/godot_navigation_server_3d.h @@ -178,6 +178,9 @@ public: virtual int region_get_connections_count(RID p_region) const override; virtual Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override; virtual Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override; + virtual Vector3 region_get_closest_point_to_segment(RID p_region, const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision = false) const override; + virtual Vector3 region_get_closest_point(RID p_region, const Vector3 &p_point) const override; + virtual Vector3 region_get_closest_point_normal(RID p_region, const Vector3 &p_point) const override; virtual Vector3 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override; virtual RID link_create() override; diff --git a/modules/navigation/nav_region.cpp b/modules/navigation/nav_region.cpp index 7a44adecbc..2c91b80af2 100644 --- a/modules/navigation/nav_region.cpp +++ b/modules/navigation/nav_region.cpp @@ -105,7 +105,22 @@ void NavRegion::set_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh) { polygons_dirty = true; } +Vector3 NavRegion::get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision) const { + RWLockRead read_lock(region_rwlock); + + return NavMeshQueries3D::polygons_get_closest_point_to_segment( + get_polygons(), p_from, p_to, p_use_collision); +} + +gd::ClosestPointQueryResult NavRegion::get_closest_point_info(const Vector3 &p_point) const { + RWLockRead read_lock(region_rwlock); + + return NavMeshQueries3D::polygons_get_closest_point_info(get_polygons(), p_point); +} + Vector3 NavRegion::get_random_point(uint32_t p_navigation_layers, bool p_uniformly) const { + RWLockRead read_lock(region_rwlock); + if (!get_enabled()) { return Vector3(); } @@ -114,6 +129,8 @@ Vector3 NavRegion::get_random_point(uint32_t p_navigation_layers, bool p_uniform } bool NavRegion::sync() { + RWLockWrite write_lock(region_rwlock); + bool something_changed = polygons_dirty /* || something_dirty? */; update_polygons(); diff --git a/modules/navigation/nav_region.h b/modules/navigation/nav_region.h index 662a32c47a..c015802b92 100644 --- a/modules/navigation/nav_region.h +++ b/modules/navigation/nav_region.h @@ -38,6 +38,8 @@ #include "scene/resources/navigation_mesh.h" class NavRegion : public NavBase { + RWLock region_rwlock; + NavMap *map = nullptr; Transform3D transform; bool enabled = true; @@ -88,6 +90,8 @@ public: return polygons; } + Vector3 get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision) const; + gd::ClosestPointQueryResult get_closest_point_info(const Vector3 &p_point) const; Vector3 get_random_point(uint32_t p_navigation_layers, bool p_uniformly) const; real_t get_surface_area() const { return surface_area; }; diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp index ff032c88c6..c89534a60c 100644 --- a/modules/vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp @@ -390,6 +390,9 @@ Ref<AudioSamplePlayback> AudioStreamPlaybackOggVorbis::get_sample_playback() con void AudioStreamPlaybackOggVorbis::set_sample_playback(const Ref<AudioSamplePlayback> &p_playback) { sample_playback = p_playback; + if (sample_playback.is_valid()) { + sample_playback->stream_playback = Ref<AudioStreamPlayback>(this); + } } AudioStreamPlaybackOggVorbis::~AudioStreamPlaybackOggVorbis() { diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index f8ac591a78..d78f8db79c 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -2276,6 +2276,11 @@ String EditorExportPlatformAndroid::get_apksigner_path(int p_target_sdk, bool p_ bool failed = false; String version_to_use; + String java_sdk_path = EDITOR_GET("export/android/java_sdk_path"); + if (!java_sdk_path.is_empty()) { + OS::get_singleton()->set_environment("JAVA_HOME", java_sdk_path); + } + List<String> args; args.push_back("--version"); String output; diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotEditor.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotEditor.kt index 50daf44d2a..d296d6ad03 100644 --- a/platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotEditor.kt +++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotEditor.kt @@ -511,4 +511,12 @@ abstract class BaseGodotEditor : GodotActivity() { val godot = godot ?: return Error.ERR_UNCONFIGURED return verifyApk(godot.fileAccessHandler, apkPath) } + + override fun supportsFeature(featureTag: String): Boolean { + if (featureTag == "xr_editor") { + return isNativeXRDevice(); + } + + return false + } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt index 38bd336e2d..5b1d09e749 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt @@ -992,6 +992,10 @@ class Godot(private val context: Context) { */ @Keep private fun hasFeature(feature: String): Boolean { + if (primaryHost?.supportsFeature(feature) ?: false) { + return true; + } + for (plugin in pluginRegistry.allPlugins) { if (plugin.supportsFeature(feature)) { return true diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java b/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java index e0f5744368..d60595c0bb 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java @@ -501,4 +501,12 @@ public class GodotFragment extends Fragment implements IDownloaderClient, GodotH } return Error.ERR_UNAVAILABLE; } + + @Override + public boolean supportsFeature(String featureTag) { + if (parentHost != null) { + return parentHost.supportsFeature(featureTag); + } + return false; + } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java index f1c84e90a7..344b73f799 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java @@ -136,4 +136,13 @@ public interface GodotHost { default Error verifyApk(@NonNull String apkPath) { return Error.ERR_UNAVAILABLE; } + + /** + * Returns whether the given feature tag is supported. + * + * @see <a href="https://docs.godotengine.org/en/stable/tutorials/export/feature_tags.html">Feature tags</a> + */ + default boolean supportsFeature(String featureTag) { + return false; + } } diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 92ac921cee..684a7c34a0 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -666,7 +666,7 @@ def get_ar_version(env): print_warning("Couldn't check version of `ar`.") return ret - match = re.search(r"GNU ar \(GNU Binutils\) (\d+)\.(\d+)(:?\.(\d+))?", output) + match = re.search(r"GNU ar \(GNU Binutils\) (\d+)\.(\d+)(?:\.(\d+))?", output) if match: ret["major"] = int(match[1]) ret["minor"] = int(match[2]) diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index f9c636a4a6..e6e8c6aaef 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -101,7 +101,7 @@ static String fix_path(const String &p_path) { } path = path.simplify_path(); path = path.replace("/", "\\"); - if (!path.is_network_share_path() && !path.begins_with(R"(\\?\)")) { + if (path.size() >= MAX_PATH && !path.is_network_share_path() && !path.begins_with(R"(\\?\)")) { path = R"(\\?\)" + path; } return path; diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index 20d646fe1e..64259a24b0 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -216,24 +216,7 @@ void Path3D::_bind_methods() { ADD_SIGNAL(MethodInfo("curve_changed")); } -// Update transform, in deferred mode by default to avoid superfluity. -void PathFollow3D::update_transform(bool p_immediate) { - transform_dirty = true; - - if (p_immediate) { - _update_transform(); - } else { - callable_mp(this, &PathFollow3D::_update_transform).call_deferred(); - } -} - -// Update transform immediately . -void PathFollow3D::_update_transform() { - if (!transform_dirty) { - return; - } - transform_dirty = false; - +void PathFollow3D::update_transform() { if (!path) { return; } @@ -286,9 +269,7 @@ void PathFollow3D::_notification(int p_what) { Node *parent = get_parent(); if (parent) { path = Object::cast_to<Path3D>(parent); - if (path) { - update_transform(); - } + update_transform(); } } break; @@ -414,6 +395,9 @@ void PathFollow3D::_bind_methods() { void PathFollow3D::set_progress(real_t p_progress) { ERR_FAIL_COND(!isfinite(p_progress)); + if (progress == p_progress) { + return; + } progress = p_progress; if (path) { @@ -435,10 +419,11 @@ void PathFollow3D::set_progress(real_t p_progress) { } void PathFollow3D::set_h_offset(real_t p_h_offset) { - h_offset = p_h_offset; - if (path) { - update_transform(); + if (h_offset == p_h_offset) { + return; } + h_offset = p_h_offset; + update_transform(); } real_t PathFollow3D::get_h_offset() const { @@ -446,10 +431,11 @@ real_t PathFollow3D::get_h_offset() const { } void PathFollow3D::set_v_offset(real_t p_v_offset) { - v_offset = p_v_offset; - if (path) { - update_transform(); + if (v_offset == p_v_offset) { + return; } + v_offset = p_v_offset; + update_transform(); } real_t PathFollow3D::get_v_offset() const { @@ -476,6 +462,9 @@ real_t PathFollow3D::get_progress_ratio() const { } void PathFollow3D::set_rotation_mode(RotationMode p_rotation_mode) { + if (rotation_mode == p_rotation_mode) { + return; + } rotation_mode = p_rotation_mode; update_configuration_warnings(); @@ -487,6 +476,9 @@ PathFollow3D::RotationMode PathFollow3D::get_rotation_mode() const { } void PathFollow3D::set_use_model_front(bool p_use_model_front) { + if (use_model_front == p_use_model_front) { + return; + } use_model_front = p_use_model_front; update_transform(); } @@ -496,6 +488,9 @@ bool PathFollow3D::is_using_model_front() const { } void PathFollow3D::set_loop(bool p_loop) { + if (loop == p_loop) { + return; + } loop = p_loop; update_transform(); } @@ -505,6 +500,9 @@ bool PathFollow3D::has_loop() const { } void PathFollow3D::set_tilt_enabled(bool p_enabled) { + if (tilt_enabled == p_enabled) { + return; + } tilt_enabled = p_enabled; update_transform(); } diff --git a/scene/3d/path_3d.h b/scene/3d/path_3d.h index 0c9111bb8e..fb4f301375 100644 --- a/scene/3d/path_3d.h +++ b/scene/3d/path_3d.h @@ -90,7 +90,6 @@ protected: void _validate_property(PropertyInfo &p_property) const; void _notification(int p_what); - void _update_transform(); static void _bind_methods(); @@ -124,7 +123,7 @@ public: PackedStringArray get_configuration_warnings() const override; - void update_transform(bool p_immediate = false); + void update_transform(); static Transform3D correct_posture(Transform3D p_transform, PathFollow3D::RotationMode p_rotation_mode); diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp index 1c5f40f56e..664302d45e 100644 --- a/scene/animation/animation_mixer.cpp +++ b/scene/animation/animation_mixer.cpp @@ -1636,6 +1636,9 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { } if (t_obj->call(SNAME("get_is_sample"))) { + if (t->audio_stream_playback->get_sample_playback().is_valid()) { + AudioServer::get_singleton()->stop_sample_playback(t->audio_stream_playback->get_sample_playback()); + } Ref<AudioSamplePlayback> sample_playback; sample_playback.instantiate(); sample_playback->stream = stream; diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index c2818edd9c..3b5d4fc33e 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -45,6 +45,70 @@ #include "editor/editor_settings.h" #endif +void LineEdit::_edit() { + if (!is_inside_tree()) { + return; + } + + if (!has_focus()) { + grab_focus(); + } + + if (!editable || editing) { + return; + } + + editing = true; + _validate_caret_can_draw(); + + DisplayServer::WindowID wid = get_window() ? get_window()->get_window_id() : DisplayServer::INVALID_WINDOW_ID; + if (wid != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { + DisplayServer::get_singleton()->window_set_ime_active(true, wid); + Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + theme_cache.font->get_height(theme_cache.font_size)) / 2) + get_global_position(); + if (get_window()->get_embedder()) { + pos += get_viewport()->get_popup_base_transform().get_origin(); + } + DisplayServer::get_singleton()->window_set_ime_position(pos, wid); + } + + show_virtual_keyboard(); + queue_redraw(); + emit_signal(SNAME("editing_toggled"), true); +} + +void LineEdit::_unedit() { + if (!editing) { + return; + } + + editing = false; + _validate_caret_can_draw(); + + DisplayServer::WindowID wid = get_window() ? get_window()->get_window_id() : DisplayServer::INVALID_WINDOW_ID; + if (wid != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { + DisplayServer::get_singleton()->window_set_ime_position(Point2(), wid); + DisplayServer::get_singleton()->window_set_ime_active(false, wid); + } + ime_text = ""; + ime_selection = Point2(); + _shape(); + set_caret_column(caret_column); // Update scroll_offset. + + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { + DisplayServer::get_singleton()->virtual_keyboard_hide(); + } + + if (deselect_on_focus_loss_enabled && !selection.drag_attempt) { + deselect(); + } + + emit_signal(SNAME("editing_toggled"), false); +} + +bool LineEdit::is_editing() const { + return editing; +} + void LineEdit::_swap_current_input_direction() { if (input_direction == TEXT_DIRECTION_LTR) { input_direction = TEXT_DIRECTION_RTL; @@ -52,7 +116,6 @@ void LineEdit::_swap_current_input_direction() { input_direction = TEXT_DIRECTION_LTR; } set_caret_column(get_caret_column()); - queue_redraw(); } void LineEdit::_move_caret_left(bool p_select, bool p_move_by_word) { @@ -240,6 +303,11 @@ void LineEdit::_delete(bool p_word, bool p_all_to_right) { } void LineEdit::unhandled_key_input(const Ref<InputEvent> &p_event) { + // Return to prevent editing if just focused. + if (!editing) { + return; + } + Ref<InputEventKey> k = p_event; if (k.is_valid()) { @@ -265,26 +333,38 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> b = p_event; - if (b.is_valid()) { - if (ime_text.length() != 0) { - // Ignore mouse clicks in IME input mode. - return; - } - if (b->is_pressed() && b->get_button_index() == MouseButton::RIGHT && context_menu_enabled) { - _update_context_menu(); - menu->set_position(get_screen_position() + get_local_mouse_position()); - menu->reset_size(); - menu->popup(); - grab_focus(); + // Ignore mouse clicks in IME input mode. + if (b.is_valid() && ime_text.is_empty()) { + if (b->is_pressed() && b->get_button_index() == MouseButton::RIGHT) { + if (editable && !selection.enabled) { + set_caret_at_pixel_pos(b->get_position().x); + } + + if (context_menu_enabled) { + _update_context_menu(); + menu->set_position(get_screen_position() + get_local_mouse_position()); + menu->reset_size(); + menu->popup(); + } + + if (editable && !editing) { + _edit(); + } + accept_event(); return; } - if (is_middle_mouse_paste_enabled() && b->is_pressed() && b->get_button_index() == MouseButton::MIDDLE && is_editable() && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { + if (editable && is_middle_mouse_paste_enabled() && b->is_pressed() && b->get_button_index() == MouseButton::MIDDLE && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { String paste_buffer = DisplayServer::get_singleton()->clipboard_get_primary().strip_escapes(); deselect(); set_caret_at_pixel_pos(b->get_position().x); + + if (!editing) { + _edit(); + } + if (!paste_buffer.is_empty()) { insert_text_at_caret(paste_buffer); @@ -295,7 +375,6 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { text_changed_dirty = true; } } - grab_focus(); accept_event(); return; } @@ -304,10 +383,13 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { return; } - _reset_caret_blink_timer(); + if (editing) { + _reset_caret_blink_timer(); + } + if (b->is_pressed()) { accept_event(); // Don't pass event further when clicked on text field. - if (!text.is_empty() && is_editable() && _is_over_clear_button(b->get_position())) { + if (editable && !text.is_empty() && _is_over_clear_button(b->get_position())) { clear_button_status.press_attempt = true; clear_button_status.pressing_inside = true; queue_redraw(); @@ -330,7 +412,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { const int triple_click_tolerance = 5; const bool is_triple_click = !b->is_double_click() && (OS::get_singleton()->get_ticks_msec() - last_dblclk) < triple_click_timeout && b->get_position().distance_to(last_dblclk_pos) < triple_click_tolerance; - if (is_triple_click && text.length()) { + if (is_triple_click && !text.is_empty()) { // Triple-click select all. selection.enabled = true; selection.begin = 0; @@ -377,13 +459,17 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { } } + if (editable && !editing) { + _edit(); + return; + } queue_redraw(); } else { if (selection.enabled && !pass && b->get_button_index() == MouseButton::LEFT && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text()); } - if (!text.is_empty() && is_editable() && clear_button_enabled) { + if (editable && !text.is_empty() && clear_button_enabled) { bool press_attempt = clear_button_status.press_attempt; clear_button_status.press_attempt = false; if (press_attempt && clear_button_status.pressing_inside && _is_over_clear_button(b->get_position())) { @@ -416,7 +502,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseMotion> m = p_event; if (m.is_valid()) { - if (!text.is_empty() && is_editable() && clear_button_enabled) { + if (editable && !text.is_empty() && clear_button_enabled) { bool last_press_inside = clear_button_status.pressing_inside; clear_button_status.pressing_inside = clear_button_status.press_attempt && _is_over_clear_button(m->get_position()); if (last_press_inside != clear_button_status.pressing_inside) { @@ -462,221 +548,240 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventKey> k = p_event; - if (k.is_valid()) { - if (!k->is_pressed()) { - if (alt_start && k->get_keycode() == Key::ALT) { - alt_start = false; - if ((alt_code > 0x31 && alt_code < 0xd800) || (alt_code > 0xdfff && alt_code <= 0x10ffff)) { - char32_t ucodestr[2] = { (char32_t)alt_code, 0 }; - insert_text_at_caret(ucodestr); - } - accept_event(); - return; - } - return; - } - - // Alt + Unicode input: - if (k->is_alt_pressed()) { - if (!alt_start) { - if (k->get_keycode() == Key::KP_ADD) { - alt_start = true; - alt_code = 0; - accept_event(); - return; - } - } else { - if (k->get_keycode() >= Key::KEY_0 && k->get_keycode() <= Key::KEY_9) { - alt_code = alt_code << 4; - alt_code += (uint32_t)(k->get_keycode() - Key::KEY_0); - } - if (k->get_keycode() >= Key::KP_0 && k->get_keycode() <= Key::KP_9) { - alt_code = alt_code << 4; - alt_code += (uint32_t)(k->get_keycode() - Key::KP_0); - } - if (k->get_keycode() >= Key::A && k->get_keycode() <= Key::F) { - alt_code = alt_code << 4; - alt_code += (uint32_t)(k->get_keycode() - Key::A) + 10; - } - accept_event(); - return; - } - } + if (k.is_null()) { + return; + } - if (context_menu_enabled) { - if (k->is_action("ui_menu", true)) { - _update_context_menu(); - Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + theme_cache.font->get_height(theme_cache.font_size)) / 2); - menu->set_position(get_screen_position() + pos); - menu->reset_size(); - menu->popup(); - menu->grab_focus(); + if (editable && !editing && k->is_action_pressed("ui_text_submit", false)) { + _edit(); + return; + } - accept_event(); - return; - } - } + if (!editing) { + return; + } - // Default is ENTER and KP_ENTER. Cannot use ui_accept as default includes SPACE. - if (k->is_action("ui_text_submit", false)) { - emit_signal(SNAME("text_submitted"), text); - if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { - DisplayServer::get_singleton()->virtual_keyboard_hide(); + if (!k->is_pressed()) { + if (alt_start && k->get_keycode() == Key::ALT) { + alt_start = false; + if ((alt_code > 0x31 && alt_code < 0xd800) || (alt_code > 0xdfff && alt_code <= 0x10ffff)) { + char32_t ucodestr[2] = { (char32_t)alt_code, 0 }; + insert_text_at_caret(ucodestr); } accept_event(); return; } + return; + } - if (k->is_action("ui_cancel")) { - callable_mp((Control *)this, &Control::release_focus).call_deferred(); - accept_event(); - return; - } - - if (is_shortcut_keys_enabled()) { - if (k->is_action("ui_copy", true)) { - copy_text(); + // Alt + Unicode input: + if (k->is_alt_pressed()) { + if (!alt_start) { + if (k->get_keycode() == Key::KP_ADD) { + alt_start = true; + alt_code = 0; accept_event(); return; } - - if (k->is_action("ui_text_select_all", true)) { - select(); - accept_event(); - return; + } else { + if (k->get_keycode() >= Key::KEY_0 && k->get_keycode() <= Key::KEY_9) { + alt_code = alt_code << 4; + alt_code += (uint32_t)(k->get_keycode() - Key::KEY_0); } - - // Cut / Paste - if (k->is_action("ui_cut", true)) { - cut_text(); - accept_event(); - return; + if (k->get_keycode() >= Key::KP_0 && k->get_keycode() <= Key::KP_9) { + alt_code = alt_code << 4; + alt_code += (uint32_t)(k->get_keycode() - Key::KP_0); } - - if (k->is_action("ui_paste", true)) { - paste_text(); - accept_event(); - return; + if (k->get_keycode() >= Key::A && k->get_keycode() <= Key::F) { + alt_code = alt_code << 4; + alt_code += (uint32_t)(k->get_keycode() - Key::A) + 10; } - - // Undo / Redo - if (k->is_action("ui_undo", true)) { - undo(); - accept_event(); - return; - } - - if (k->is_action("ui_redo", true)) { - redo(); - accept_event(); - return; - } - } - - // BACKSPACE - if (k->is_action("ui_text_backspace_all_to_left", true)) { - _backspace(false, true); - accept_event(); - return; - } - if (k->is_action("ui_text_backspace_word", true)) { - _backspace(true); - accept_event(); - return; - } - if (k->is_action("ui_text_backspace", true)) { - _backspace(); accept_event(); return; } + } + + if (context_menu_enabled) { + if (k->is_action("ui_menu", true)) { + _update_context_menu(); + Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + theme_cache.font->get_height(theme_cache.font_size)) / 2); + menu->set_position(get_screen_position() + pos); + menu->reset_size(); + menu->popup(); + menu->grab_focus(); - // DELETE - if (k->is_action("ui_text_delete_all_to_right", true)) { - _delete(false, true); - accept_event(); - return; - } - if (k->is_action("ui_text_delete_word", true)) { - _delete(true); accept_event(); return; } - if (k->is_action("ui_text_delete", true)) { - _delete(); - accept_event(); - return; + } + + // Default is ENTER and KP_ENTER. Cannot use ui_accept as default includes SPACE. + if (k->is_action_pressed("ui_text_submit")) { + emit_signal(SNAME("text_submitted"), text); + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { + DisplayServer::get_singleton()->virtual_keyboard_hide(); } - // Cursor Movement + if (editing) { + _unedit(); + } - k = k->duplicate(); - bool shift_pressed = k->is_shift_pressed(); - // Remove shift or else actions will not match. Use above variable for selection. - k->set_shift_pressed(false); + accept_event(); + return; + } - if (k->is_action("ui_text_caret_word_left", true)) { - _move_caret_left(shift_pressed, true); - accept_event(); - return; + if (k->is_action("ui_cancel")) { + if (editing) { + _unedit(); } - if (k->is_action("ui_text_caret_left", true)) { - _move_caret_left(shift_pressed); + + accept_event(); + return; + } + + if (is_shortcut_keys_enabled()) { + if (k->is_action("ui_copy", true)) { + copy_text(); accept_event(); return; } - if (k->is_action("ui_text_caret_word_right", true)) { - _move_caret_right(shift_pressed, true); + + if (k->is_action("ui_text_select_all", true)) { + select(); accept_event(); return; } - if (k->is_action("ui_text_caret_right", true)) { - _move_caret_right(shift_pressed, false); + + // Cut / Paste + if (k->is_action("ui_cut", true)) { + cut_text(); accept_event(); return; } - // Up = Home, Down = End - if (k->is_action("ui_text_caret_up", true) || k->is_action("ui_text_caret_line_start", true) || k->is_action("ui_text_caret_page_up", true)) { - _move_caret_start(shift_pressed); + if (k->is_action("ui_paste", true)) { + paste_text(); accept_event(); return; } - if (k->is_action("ui_text_caret_down", true) || k->is_action("ui_text_caret_line_end", true) || k->is_action("ui_text_caret_page_down", true)) { - _move_caret_end(shift_pressed); + + // Undo / Redo + if (k->is_action("ui_undo", true)) { + undo(); accept_event(); return; } - // Misc - if (k->is_action("ui_swap_input_direction", true)) { - _swap_current_input_direction(); + if (k->is_action("ui_redo", true)) { + redo(); accept_event(); return; } + } - _reset_caret_blink_timer(); + // BACKSPACE + if (k->is_action("ui_text_backspace_all_to_left", true)) { + _backspace(false, true); + accept_event(); + return; + } + if (k->is_action("ui_text_backspace_word", true)) { + _backspace(true); + accept_event(); + return; + } + if (k->is_action("ui_text_backspace", true)) { + _backspace(); + accept_event(); + return; + } - // Allow unicode handling if: - // * No Modifiers are pressed (except shift) - bool allow_unicode_handling = !(k->is_ctrl_pressed() || k->is_alt_pressed() || k->is_meta_pressed()); + // DELETE + if (k->is_action("ui_text_delete_all_to_right", true)) { + _delete(false, true); + accept_event(); + return; + } + if (k->is_action("ui_text_delete_word", true)) { + _delete(true); + accept_event(); + return; + } + if (k->is_action("ui_text_delete", true)) { + _delete(); + accept_event(); + return; + } - if (allow_unicode_handling && editable && k->get_unicode() >= 32) { - // Handle Unicode if no modifiers are active. - selection_delete(); - char32_t ucodestr[2] = { (char32_t)k->get_unicode(), 0 }; - int prev_len = text.length(); - insert_text_at_caret(ucodestr); - if (text.length() != prev_len) { - if (!text_changed_dirty) { - if (is_inside_tree()) { - callable_mp(this, &LineEdit::_text_changed).call_deferred(); - } - text_changed_dirty = true; + // Cursor Movement + + k = k->duplicate(); + bool shift_pressed = k->is_shift_pressed(); + // Remove shift or else actions will not match. Use above variable for selection. + k->set_shift_pressed(false); + + if (k->is_action("ui_text_caret_word_left", true)) { + _move_caret_left(shift_pressed, true); + accept_event(); + return; + } + if (k->is_action("ui_text_caret_left", true)) { + _move_caret_left(shift_pressed); + accept_event(); + return; + } + if (k->is_action("ui_text_caret_word_right", true)) { + _move_caret_right(shift_pressed, true); + accept_event(); + return; + } + if (k->is_action("ui_text_caret_right", true)) { + _move_caret_right(shift_pressed, false); + accept_event(); + return; + } + + // Up = Home, Down = End + if (k->is_action("ui_text_caret_up", true) || k->is_action("ui_text_caret_line_start", true) || k->is_action("ui_text_caret_page_up", true)) { + _move_caret_start(shift_pressed); + accept_event(); + return; + } + if (k->is_action("ui_text_caret_down", true) || k->is_action("ui_text_caret_line_end", true) || k->is_action("ui_text_caret_page_down", true)) { + _move_caret_end(shift_pressed); + accept_event(); + return; + } + + // Misc + if (k->is_action("ui_swap_input_direction", true)) { + _swap_current_input_direction(); + accept_event(); + return; + } + + _reset_caret_blink_timer(); + + // Allow unicode handling if: + // * No Modifiers are pressed (except shift) + bool allow_unicode_handling = !(k->is_ctrl_pressed() || k->is_alt_pressed() || k->is_meta_pressed()); + + if (allow_unicode_handling && editable && k->get_unicode() >= 32) { + // Handle Unicode if no modifiers are active. + selection_delete(); + char32_t ucodestr[2] = { (char32_t)k->get_unicode(), 0 }; + int prev_len = text.length(); + insert_text_at_caret(ucodestr); + if (text.length() != prev_len) { + if (!text_changed_dirty) { + if (is_inside_tree()) { + callable_mp(this, &LineEdit::_text_changed).call_deferred(); } + text_changed_dirty = true; } - accept_event(); - return; } + accept_event(); + return; } } @@ -1107,7 +1212,7 @@ void LineEdit::_notification(int p_what) { } } - if (has_focus()) { + if (editing) { DisplayServer::WindowID wid = get_window() ? get_window()->get_window_id() : DisplayServer::INVALID_WINDOW_ID; if (wid != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { DisplayServer::get_singleton()->window_set_ime_active(true, wid); @@ -1121,8 +1226,6 @@ void LineEdit::_notification(int p_what) { } break; case NOTIFICATION_FOCUS_ENTER: { - _validate_caret_can_draw(); - if (select_all_on_focus) { if (Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) { // Select all when the mouse button is up. @@ -1132,43 +1235,20 @@ void LineEdit::_notification(int p_what) { } } - DisplayServer::WindowID wid = get_window() ? get_window()->get_window_id() : DisplayServer::INVALID_WINDOW_ID; - if (wid != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { - DisplayServer::get_singleton()->window_set_ime_active(true, wid); - Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + theme_cache.font->get_height(theme_cache.font_size)) / 2) + get_global_position(); - if (get_window()->get_embedder()) { - pos += get_viewport()->get_popup_base_transform().get_origin(); - } - DisplayServer::get_singleton()->window_set_ime_position(pos, wid); + // Only allow editing if the LineEdit is not focused with arrow keys. + if (!(Input::get_singleton()->is_action_pressed("ui_up") || Input::get_singleton()->is_action_pressed("ui_down") || Input::get_singleton()->is_action_pressed("ui_left") || Input::get_singleton()->is_action_pressed("ui_right"))) { + _edit(); } - - show_virtual_keyboard(); } break; case NOTIFICATION_FOCUS_EXIT: { - _validate_caret_can_draw(); - - DisplayServer::WindowID wid = get_window() ? get_window()->get_window_id() : DisplayServer::INVALID_WINDOW_ID; - if (wid != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { - DisplayServer::get_singleton()->window_set_ime_position(Point2(), wid); - DisplayServer::get_singleton()->window_set_ime_active(false, wid); - } - ime_text = ""; - ime_selection = Point2(); - _shape(); - set_caret_column(caret_column); // Update scroll_offset. - - if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { - DisplayServer::get_singleton()->virtual_keyboard_hide(); - } - - if (deselect_on_focus_loss_enabled && !selection.drag_attempt) { - deselect(); + if (editing) { + _unedit(); } } break; case MainLoop::NOTIFICATION_OS_IME_UPDATE: { - if (has_focus()) { + if (editing) { ime_text = DisplayServer::get_singleton()->ime_get_text(); ime_selection = DisplayServer::get_singleton()->ime_get_selection(); @@ -1178,8 +1258,6 @@ void LineEdit::_notification(int p_what) { _shape(); set_caret_column(caret_column); // Update scroll_offset. - - queue_redraw(); } } break; @@ -1419,7 +1497,7 @@ Vector2 LineEdit::get_caret_pixel_pos() { Vector2 ret; CaretInfo caret; // Get position of the start of caret. - if (ime_text.length() != 0 && ime_selection.x != 0) { + if (!ime_text.is_empty() && ime_selection.x != 0) { caret = TS->shaped_text_get_carets(text_rid, caret_column + ime_selection.x); } else { caret = TS->shaped_text_get_carets(text_rid, caret_column); @@ -1432,7 +1510,7 @@ Vector2 LineEdit::get_caret_pixel_pos() { } // Get position of the end of caret. - if (ime_text.length() != 0) { + if (!ime_text.is_empty()) { if (ime_selection.y != 0) { caret = TS->shaped_text_get_carets(text_rid, caret_column + ime_selection.x + ime_selection.y); } else { @@ -1525,11 +1603,11 @@ void LineEdit::_validate_caret_can_draw() { draw_caret = true; caret_blink_timer = 0.0; } - caret_can_draw = editable && (window_has_focus || (menu && menu->has_focus())) && (has_focus() || caret_force_displayed); + caret_can_draw = editing && (window_has_focus || (menu && menu->has_focus())) && (has_focus() || caret_force_displayed); } void LineEdit::delete_char() { - if ((text.length() <= 0) || (caret_column == 0)) { + if (text.is_empty() || caret_column == 0) { return; } @@ -1661,7 +1739,7 @@ void LineEdit::clear() { _text_changed(); // This should reset virtual keyboard state if needed. - if (has_focus()) { + if (editing) { show_virtual_keyboard(); } } @@ -1834,7 +1912,8 @@ Size2 LineEdit::get_minimum_size() const { Size2 min_size; // Minimum size of text. - float em_space_size = font->get_char_size('M', font_size).x; + // W is wider than M in most fonts, Using M may result in hiding the last digit when using float values in SpinBox, ie. ColorPicker RAW values. + float em_space_size = font->get_char_size('W', font_size).x; min_size.width = theme_cache.minimum_character_width * em_space_size; if (expand_to_text_length) { @@ -1932,7 +2011,8 @@ void LineEdit::select_all() { return; } - if (!text.length()) { + if (text.is_empty()) { + set_caret_column(0); return; } @@ -1948,6 +2028,10 @@ void LineEdit::set_editable(bool p_editable) { } editable = p_editable; + + if (!editable && editing) { + _unedit(); + } _validate_caret_can_draw(); update_minimum_size(); @@ -2328,6 +2412,7 @@ void LineEdit::_emit_text_change() { emit_signal(SceneStringName(text_changed), text); text_changed_dirty = false; } + PackedStringArray LineEdit::get_configuration_warnings() const { PackedStringArray warnings = Control::get_configuration_warnings(); if (secret_character.length() > 1) { @@ -2347,13 +2432,13 @@ void LineEdit::_shape() { TS->shaped_text_clear(text_rid); String t; - if (text.length() == 0 && ime_text.length() == 0) { + if (text.is_empty() && ime_text.is_empty()) { t = placeholder_translated; } else if (pass) { - String s = (secret_character.length() > 0) ? secret_character.left(1) : U"•"; + String s = secret_character.is_empty() ? U"•" : secret_character.left(1); t = s.repeat(text.length() + ime_text.length()); } else { - if (ime_text.length() > 0) { + if (!ime_text.is_empty()) { t = text.substr(0, caret_column) + ime_text + text.substr(caret_column, text.length()); } else { t = text; @@ -2562,6 +2647,7 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &LineEdit::set_horizontal_alignment); ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &LineEdit::get_horizontal_alignment); + ClassDB::bind_method(D_METHOD("is_editing"), &LineEdit::is_editing); ClassDB::bind_method(D_METHOD("clear"), &LineEdit::clear); ClassDB::bind_method(D_METHOD("select", "from", "to"), &LineEdit::select, DEFVAL(0), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("select_all"), &LineEdit::select_all); @@ -2642,6 +2728,7 @@ void LineEdit::_bind_methods() { ADD_SIGNAL(MethodInfo("text_changed", PropertyInfo(Variant::STRING, "new_text"))); ADD_SIGNAL(MethodInfo("text_change_rejected", PropertyInfo(Variant::STRING, "rejected_substring"))); ADD_SIGNAL(MethodInfo("text_submitted", PropertyInfo(Variant::STRING, "new_text"))); + ADD_SIGNAL(MethodInfo("editing_toggled", PropertyInfo(Variant::BOOL, "toggled_on"))); BIND_ENUM_CONSTANT(MENU_CUT); BIND_ENUM_CONSTANT(MENU_COPY); diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index 993bc727e4..984512745a 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -86,6 +86,7 @@ public: private: HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT; + bool editing = false; bool editable = false; bool pass = false; bool text_changed_dirty = false; @@ -205,6 +206,9 @@ private: float base_scale = 1.0; } theme_cache; + void _edit(); + void _unedit(); + void _clear_undo_stack(); void _clear_redo(); void _create_undo_state(); @@ -257,6 +261,8 @@ protected: virtual void gui_input(const Ref<InputEvent> &p_event) override; public: + bool is_editing() const; + void set_horizontal_alignment(HorizontalAlignment p_alignment); HorizontalAlignment get_horizontal_alignment() const; diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index ac81f0de56..01c2b9bffe 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -46,7 +46,7 @@ void SpinBox::_update_text(bool p_keep_line_edit) { value = TS->format_number(value); } - if (!line_edit->has_focus()) { + if (!line_edit->is_editing()) { if (!prefix.is_empty()) { value = prefix + " " + value; } @@ -197,13 +197,13 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) { } } break; case MouseButton::WHEEL_UP: { - if (line_edit->has_focus()) { + if (line_edit->is_editing()) { set_value(get_value() + step * mb->get_factor()); accept_event(); } } break; case MouseButton::WHEEL_DOWN: { - if (line_edit->has_focus()) { + if (line_edit->is_editing()) { set_value(get_value() - step * mb->get_factor()); accept_event(); } @@ -253,34 +253,26 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) { } } -void SpinBox::_line_edit_focus_enter() { - int col = line_edit->get_caret_column(); - _update_text(); - line_edit->set_caret_column(col); +void SpinBox::_line_edit_editing_toggled(bool p_toggled_on) { + if (p_toggled_on) { + int col = line_edit->get_caret_column(); + _update_text(); + line_edit->set_caret_column(col); - // LineEdit text might change and it clears any selection. Have to re-select here. - if (line_edit->is_select_all_on_focus() && !Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) { - line_edit->select_all(); - } -} + // LineEdit text might change and it clears any selection. Have to re-select here. + if (line_edit->is_select_all_on_focus() && !Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) { + line_edit->select_all(); + } + } else { + // Discontinue because the focus_exit was caused by canceling. + if (Input::get_singleton()->is_action_pressed("ui_cancel")) { + _update_text(); + return; + } -void SpinBox::_line_edit_focus_exit() { - // Discontinue because the focus_exit was caused by left-clicking the arrows. - const Viewport *viewport = get_viewport(); - if (!viewport || viewport->gui_get_focus_owner() == get_line_edit()) { - return; - } - // Discontinue because the focus_exit was caused by right-click context menu. - if (line_edit->is_menu_visible()) { - return; - } - // Discontinue because the focus_exit was caused by canceling. - if (Input::get_singleton()->is_action_pressed("ui_cancel")) { - _update_text(); - return; + line_edit->deselect(); + _text_submitted(line_edit->get_text()); } - - _text_submitted(line_edit->get_text()); } inline void SpinBox::_compute_sizes() { @@ -602,8 +594,7 @@ SpinBox::SpinBox() { line_edit->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_LEFT); line_edit->connect("text_submitted", callable_mp(this, &SpinBox::_text_submitted), CONNECT_DEFERRED); - line_edit->connect(SceneStringName(focus_entered), callable_mp(this, &SpinBox::_line_edit_focus_enter), CONNECT_DEFERRED); - line_edit->connect(SceneStringName(focus_exited), callable_mp(this, &SpinBox::_line_edit_focus_exit), CONNECT_DEFERRED); + line_edit->connect("editing_toggled", callable_mp(this, &SpinBox::_line_edit_editing_toggled), CONNECT_DEFERRED); line_edit->connect(SceneStringName(gui_input), callable_mp(this, &SpinBox::_line_edit_input)); range_click_timer = memnew(Timer); diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h index 592805f43a..294dc3e5d5 100644 --- a/scene/gui/spin_box.h +++ b/scene/gui/spin_box.h @@ -86,8 +86,7 @@ class SpinBox : public Range { bool down_button_disabled = false; } state_cache; - void _line_edit_focus_enter(); - void _line_edit_focus_exit(); + void _line_edit_editing_toggled(bool p_toggled_on); inline void _compute_sizes(); inline int _get_widest_button_icon_width(); diff --git a/scene/resources/audio_stream_polyphonic.cpp b/scene/resources/audio_stream_polyphonic.cpp index 999b0c9f0a..c7b8b1c723 100644 --- a/scene/resources/audio_stream_polyphonic.cpp +++ b/scene/resources/audio_stream_polyphonic.cpp @@ -247,6 +247,11 @@ AudioStreamPlaybackPolyphonic::ID AudioStreamPlaybackPolyphonic::play_stream(con sp->volume_vector.write[2] = AudioFrame(linear_volume, linear_volume); sp->volume_vector.write[3] = AudioFrame(linear_volume, linear_volume); sp->bus = p_bus; + + if (streams[i].stream_playback->get_sample_playback().is_valid()) { + AudioServer::get_singleton()->stop_playback_stream(sp); + } + streams[i].stream_playback->set_sample_playback(sp); AudioServer::get_singleton()->start_sample_playback(sp); } @@ -315,6 +320,9 @@ Ref<AudioSamplePlayback> AudioStreamPlaybackPolyphonic::get_sample_playback() co void AudioStreamPlaybackPolyphonic::set_sample_playback(const Ref<AudioSamplePlayback> &p_playback) { sample_playback = p_playback; + if (sample_playback.is_valid()) { + sample_playback->stream_playback = Ref<AudioStreamPlayback>(this); + } } void AudioStreamPlaybackPolyphonic::_bind_methods() { diff --git a/scene/resources/audio_stream_wav.cpp b/scene/resources/audio_stream_wav.cpp index d0e0f0eef3..de67a93bd1 100644 --- a/scene/resources/audio_stream_wav.cpp +++ b/scene/resources/audio_stream_wav.cpp @@ -475,6 +475,9 @@ Ref<AudioSamplePlayback> AudioStreamPlaybackWAV::get_sample_playback() const { void AudioStreamPlaybackWAV::set_sample_playback(const Ref<AudioSamplePlayback> &p_playback) { sample_playback = p_playback; + if (sample_playback.is_valid()) { + sample_playback->stream_playback = Ref<AudioStreamPlayback>(this); + } } AudioStreamPlaybackWAV::AudioStreamPlaybackWAV() {} diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 29f9835ba9..d531eea311 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -612,27 +612,29 @@ Error ResourceLoaderText::load() { } } - if (value.get_type() == Variant::ARRAY) { - Array set_array = value; - bool is_get_valid = false; - Variant get_value = res->get(assign, &is_get_valid); - if (is_get_valid && get_value.get_type() == Variant::ARRAY) { - Array get_array = get_value; - if (!set_array.is_same_typed(get_array)) { - value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script()); + if (ClassDB::has_property(res->get_class_name(), assign)) { + if (value.get_type() == Variant::ARRAY) { + Array set_array = value; + bool is_get_valid = false; + Variant get_value = res->get(assign, &is_get_valid); + if (is_get_valid && get_value.get_type() == Variant::ARRAY) { + Array get_array = get_value; + if (!set_array.is_same_typed(get_array)) { + value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script()); + } } } - } - if (value.get_type() == Variant::DICTIONARY) { - Dictionary set_dict = value; - bool is_get_valid = false; - Variant get_value = res->get(assign, &is_get_valid); - if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) { - Dictionary get_dict = get_value; - if (!set_dict.is_same_typed(get_dict)) { - value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(), - get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script()); + if (value.get_type() == Variant::DICTIONARY) { + Dictionary set_dict = value; + bool is_get_valid = false; + Variant get_value = res->get(assign, &is_get_valid); + if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) { + Dictionary get_dict = get_value; + if (!set_dict.is_same_typed(get_dict)) { + value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(), + get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script()); + } } } } @@ -752,27 +754,29 @@ Error ResourceLoaderText::load() { } } - if (value.get_type() == Variant::ARRAY) { - Array set_array = value; - bool is_get_valid = false; - Variant get_value = resource->get(assign, &is_get_valid); - if (is_get_valid && get_value.get_type() == Variant::ARRAY) { - Array get_array = get_value; - if (!set_array.is_same_typed(get_array)) { - value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script()); + if (ClassDB::has_property(resource->get_class_name(), assign)) { + if (value.get_type() == Variant::ARRAY) { + Array set_array = value; + bool is_get_valid = false; + Variant get_value = resource->get(assign, &is_get_valid); + if (is_get_valid && get_value.get_type() == Variant::ARRAY) { + Array get_array = get_value; + if (!set_array.is_same_typed(get_array)) { + value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script()); + } } } - } - if (value.get_type() == Variant::DICTIONARY) { - Dictionary set_dict = value; - bool is_get_valid = false; - Variant get_value = resource->get(assign, &is_get_valid); - if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) { - Dictionary get_dict = get_value; - if (!set_dict.is_same_typed(get_dict)) { - value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(), - get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script()); + if (value.get_type() == Variant::DICTIONARY) { + Dictionary set_dict = value; + bool is_get_valid = false; + Variant get_value = resource->get(assign, &is_get_valid); + if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) { + Dictionary get_dict = get_value; + if (!set_dict.is_same_typed(get_dict)) { + value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(), + get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script()); + } } } } diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h index c41545aeba..7f2b68a796 100644 --- a/servers/audio/audio_stream.h +++ b/servers/audio/audio_stream.h @@ -48,6 +48,7 @@ class AudioSamplePlayback : public RefCounted { public: Ref<AudioStream> stream; + Ref<AudioStreamPlayback> stream_playback; float offset = 0.0f; float pitch_scale = 1.0; diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp index 332f8984a2..e06079efe8 100644 --- a/servers/audio_server.cpp +++ b/servers/audio_server.cpp @@ -501,12 +501,7 @@ void AudioServer::_mix_step() { switch (playback->state.load()) { case AudioStreamPlaybackListNode::AWAITING_DELETION: case AudioStreamPlaybackListNode::FADE_OUT_TO_DELETION: - playback_list.erase(playback, [](AudioStreamPlaybackListNode *p) { - delete p->prev_bus_details; - delete p->bus_details.load(); - p->stream_playback.unref(); - delete p; - }); + _delete_stream_playback_list_node(playback); break; case AudioStreamPlaybackListNode::FADE_OUT_TO_PAUSE: { // Pause the stream. @@ -697,6 +692,23 @@ AudioServer::AudioStreamPlaybackListNode *AudioServer::_find_playback_list_node( return nullptr; } +void AudioServer::_delete_stream_playback(Ref<AudioStreamPlayback> p_playback) { + ERR_FAIL_COND(p_playback.is_null()); + AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback); + if (playback_node) { + _delete_stream_playback_list_node(playback_node); + } +} + +void AudioServer::_delete_stream_playback_list_node(AudioStreamPlaybackListNode *p_playback_node) { + playback_list.erase(p_playback_node, [](AudioStreamPlaybackListNode *p) { + delete p->prev_bus_details; + delete p->bus_details.load(); + p->stream_playback.unref(); + delete p; + }); +} + bool AudioServer::thread_has_channel_mix_buffer(int p_bus, int p_buffer) const { if (p_bus < 0 || p_bus >= buses.size()) { return false; @@ -1227,8 +1239,12 @@ void AudioServer::stop_playback_stream(Ref<AudioStreamPlayback> p_playback) { ERR_FAIL_COND(p_playback.is_null()); // Handle sample playback. - if (p_playback->get_is_sample() && p_playback->get_sample_playback().is_valid()) { - AudioServer::get_singleton()->stop_sample_playback(p_playback->get_sample_playback()); + if (p_playback->get_is_sample()) { + if (p_playback->get_sample_playback().is_valid()) { + AudioServer::get_singleton()->stop_sample_playback(p_playback->get_sample_playback()); + } else { + _delete_stream_playback(p_playback); + } return; } @@ -1370,8 +1386,12 @@ void AudioServer::set_playback_highshelf_params(Ref<AudioStreamPlayback> p_playb bool AudioServer::is_playback_active(Ref<AudioStreamPlayback> p_playback) { ERR_FAIL_COND_V(p_playback.is_null(), false); - if (p_playback->get_is_sample() && p_playback->get_sample_playback().is_valid()) { - return sample_playback_list.has(p_playback->get_sample_playback()); + if (p_playback->get_is_sample()) { + if (p_playback->get_sample_playback().is_valid()) { + return sample_playback_list.has(p_playback->get_sample_playback()); + } else { + return false; + } } AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback); @@ -1845,8 +1865,12 @@ void AudioServer::start_sample_playback(const Ref<AudioSamplePlayback> &p_playba void AudioServer::stop_sample_playback(const Ref<AudioSamplePlayback> &p_playback) { ERR_FAIL_COND_MSG(p_playback.is_null(), "Parameter p_playback is null."); - AudioDriver::get_singleton()->stop_sample_playback(p_playback); - sample_playback_list.erase(p_playback); + if (sample_playback_list.has(p_playback)) { + sample_playback_list.erase(p_playback); + AudioDriver::get_singleton()->stop_sample_playback(p_playback); + p_playback->stream_playback->set_sample_playback(nullptr); + stop_playback_stream(p_playback->stream_playback); + } } void AudioServer::set_sample_playback_pause(const Ref<AudioSamplePlayback> &p_playback, bool p_paused) { diff --git a/servers/audio_server.h b/servers/audio_server.h index 2d6fc60860..16fcc029b3 100644 --- a/servers/audio_server.h +++ b/servers/audio_server.h @@ -297,6 +297,8 @@ private: SafeList<AudioStreamPlaybackListNode *> playback_list; SafeList<AudioStreamPlaybackBusDetails *> bus_details_graveyard; + void _delete_stream_playback(Ref<AudioStreamPlayback> p_playback); + void _delete_stream_playback_list_node(AudioStreamPlaybackListNode *p_node); // TODO document if this is necessary. SafeList<AudioStreamPlaybackBusDetails *> bus_details_graveyard_frame_old; diff --git a/servers/navigation_server_2d.cpp b/servers/navigation_server_2d.cpp index c5ce82265b..ceb5e909a3 100644 --- a/servers/navigation_server_2d.cpp +++ b/servers/navigation_server_2d.cpp @@ -86,6 +86,7 @@ void NavigationServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("region_get_connections_count", "region"), &NavigationServer2D::region_get_connections_count); ClassDB::bind_method(D_METHOD("region_get_connection_pathway_start", "region", "connection"), &NavigationServer2D::region_get_connection_pathway_start); ClassDB::bind_method(D_METHOD("region_get_connection_pathway_end", "region", "connection"), &NavigationServer2D::region_get_connection_pathway_end); + ClassDB::bind_method(D_METHOD("region_get_closest_point", "region", "to_point"), &NavigationServer2D::region_get_closest_point); ClassDB::bind_method(D_METHOD("region_get_random_point", "region", "navigation_layers", "uniformly"), &NavigationServer2D::region_get_random_point); ClassDB::bind_method(D_METHOD("link_create"), &NavigationServer2D::link_create); diff --git a/servers/navigation_server_2d.h b/servers/navigation_server_2d.h index a8d9678a6f..250183300f 100644 --- a/servers/navigation_server_2d.h +++ b/servers/navigation_server_2d.h @@ -149,6 +149,7 @@ public: virtual Vector2 region_get_connection_pathway_start(RID p_region, int p_connection_id) const = 0; virtual Vector2 region_get_connection_pathway_end(RID p_region, int p_connection_id) const = 0; + virtual Vector2 region_get_closest_point(RID p_region, const Vector2 &p_point) const = 0; virtual Vector2 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const = 0; /// Creates a new link between positions in the nav map. diff --git a/servers/navigation_server_2d_dummy.h b/servers/navigation_server_2d_dummy.h index 465cfcca98..0664b37ef0 100644 --- a/servers/navigation_server_2d_dummy.h +++ b/servers/navigation_server_2d_dummy.h @@ -83,6 +83,7 @@ public: int region_get_connections_count(RID p_region) const override { return 0; } Vector2 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override { return Vector2(); } Vector2 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override { return Vector2(); } + Vector2 region_get_closest_point(RID p_region, const Vector2 &p_point) const override { return Vector2(); } Vector2 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override { return Vector2(); }; RID link_create() override { return RID(); } diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp index f4ffcf5a3e..572309c429 100644 --- a/servers/navigation_server_3d.cpp +++ b/servers/navigation_server_3d.cpp @@ -99,6 +99,9 @@ void NavigationServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("region_get_connections_count", "region"), &NavigationServer3D::region_get_connections_count); ClassDB::bind_method(D_METHOD("region_get_connection_pathway_start", "region", "connection"), &NavigationServer3D::region_get_connection_pathway_start); ClassDB::bind_method(D_METHOD("region_get_connection_pathway_end", "region", "connection"), &NavigationServer3D::region_get_connection_pathway_end); + ClassDB::bind_method(D_METHOD("region_get_closest_point_to_segment", "region", "start", "end", "use_collision"), &NavigationServer3D::region_get_closest_point_to_segment, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("region_get_closest_point", "region", "to_point"), &NavigationServer3D::region_get_closest_point); + ClassDB::bind_method(D_METHOD("region_get_closest_point_normal", "region", "to_point"), &NavigationServer3D::region_get_closest_point_normal); ClassDB::bind_method(D_METHOD("region_get_random_point", "region", "navigation_layers", "uniformly"), &NavigationServer3D::region_get_random_point); ClassDB::bind_method(D_METHOD("link_create"), &NavigationServer3D::link_create); diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h index cdacf8e7e4..6dbbd35648 100644 --- a/servers/navigation_server_3d.h +++ b/servers/navigation_server_3d.h @@ -168,6 +168,9 @@ public: virtual Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const = 0; virtual Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const = 0; + virtual Vector3 region_get_closest_point_to_segment(RID p_region, const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision = false) const = 0; + virtual Vector3 region_get_closest_point(RID p_region, const Vector3 &p_point) const = 0; + virtual Vector3 region_get_closest_point_normal(RID p_region, const Vector3 &p_point) const = 0; virtual Vector3 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const = 0; /// Creates a new link between positions in the nav map. diff --git a/servers/navigation_server_3d_dummy.h b/servers/navigation_server_3d_dummy.h index 5c9e97d226..210c404365 100644 --- a/servers/navigation_server_3d_dummy.h +++ b/servers/navigation_server_3d_dummy.h @@ -93,6 +93,9 @@ public: int region_get_connections_count(RID p_region) const override { return 0; } Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override { return Vector3(); } Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override { return Vector3(); } + Vector3 region_get_closest_point_to_segment(RID p_region, const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision = false) const override { return Vector3(); } + Vector3 region_get_closest_point(RID p_region, const Vector3 &p_point) const override { return Vector3(); } + Vector3 region_get_closest_point_normal(RID p_region, const Vector3 &p_point) const override { return Vector3(); } Vector3 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override { return Vector3(); } RID link_create() override { return RID(); } diff --git a/servers/rendering/dummy/storage/mesh_storage.h b/servers/rendering/dummy/storage/mesh_storage.h index ec19562147..b0953b5dce 100644 --- a/servers/rendering/dummy/storage/mesh_storage.h +++ b/servers/rendering/dummy/storage/mesh_storage.h @@ -163,7 +163,7 @@ public: virtual AABB _multimesh_get_custom_aabb(RID p_multimesh) const override { return AABB(); } virtual RID _multimesh_get_mesh(RID p_multimesh) const override { return RID(); } - virtual AABB _multimesh_get_aabb(RID p_multimesh) const override { return AABB(); } + virtual AABB _multimesh_get_aabb(RID p_multimesh) override { return AABB(); } virtual Transform3D _multimesh_instance_get_transform(RID p_multimesh, int p_index) const override { return Transform3D(); } virtual Transform2D _multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override { return Transform2D(); } diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp index 63dc54e24c..9f390c99f9 100644 --- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp @@ -342,7 +342,7 @@ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataType type, int p_ } } -_FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, const Vector<ShaderLanguage::ConstantNode::Value> &value, uint8_t *data) { +_FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, const Vector<ShaderLanguage::Scalar> &value, uint8_t *data) { switch (type) { case ShaderLanguage::TYPE_BOOL: { uint32_t *gui = (uint32_t *)data; @@ -566,7 +566,7 @@ void MaterialStorage::ShaderData::set_default_texture_parameter(const StringName Variant MaterialStorage::ShaderData::get_default_parameter(const StringName &p_parameter) const { if (uniforms.has(p_parameter)) { ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; - Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; + Vector<ShaderLanguage::Scalar> default_value = uniform.default_value; return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint); } return Variant(); diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp index 9bd62ba065..9ae39691dc 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp @@ -2041,7 +2041,7 @@ AABB MeshStorage::_multimesh_get_custom_aabb(RID p_multimesh) const { return multimesh->custom_aabb; } -AABB MeshStorage::_multimesh_get_aabb(RID p_multimesh) const { +AABB MeshStorage::_multimesh_get_aabb(RID p_multimesh) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, AABB()); if (multimesh->custom_aabb != AABB()) { @@ -2049,7 +2049,7 @@ AABB MeshStorage::_multimesh_get_aabb(RID p_multimesh) const { } if (multimesh->aabb_dirty) { - const_cast<MeshStorage *>(this)->_update_dirty_multimeshes(); + _update_dirty_multimeshes(); } return multimesh->aabb; } diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h index 4344db783d..f811314fb6 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h @@ -652,7 +652,7 @@ public: virtual void _multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) override; virtual AABB _multimesh_get_custom_aabb(RID p_multimesh) const override; - virtual AABB _multimesh_get_aabb(RID p_multimesh) const override; + virtual AABB _multimesh_get_aabb(RID p_multimesh) override; virtual MultiMeshInterpolator *_multimesh_get_interpolator(RID p_multimesh) const override; diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index f0f267c246..276cb4b210 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -1641,8 +1641,8 @@ Vector<uint8_t> RenderingDevice::texture_get_data(RID p_texture, uint32_t p_laye copy_region.texture_region_size.z = d; command_buffer_texture_copy_regions_vector.push_back(copy_region); - w = (w >> 1); - h = (h >> 1); + w = MAX(1u, w >> 1); + h = MAX(1u, h >> 1); d = MAX(1u, d >> 1); } diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp index 49e005ca96..43703f8656 100644 --- a/servers/rendering/shader_compiler.cpp +++ b/servers/rendering/shader_compiler.cpp @@ -185,7 +185,7 @@ static String f2sp0(float p_float) { return num; } -static String get_constant_text(SL::DataType p_type, const Vector<SL::ConstantNode::Value> &p_values) { +static String get_constant_text(SL::DataType p_type, const Vector<SL::Scalar> &p_values) { switch (p_type) { case SL::TYPE_BOOL: return p_values[0].boolean ? "true" : "false"; diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 4eaf7fcb55..2249cd2010 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -1362,7 +1362,7 @@ void ShaderLanguage::_parse_used_identifier(const StringName &p_identifier, Iden } #endif // DEBUG_ENABLED -bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type, IdentifierType *r_type, bool *r_is_const, int *r_array_size, StringName *r_struct_name, ConstantNode::Value *r_constant_value) { +bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type, IdentifierType *r_type, bool *r_is_const, int *r_array_size, StringName *r_struct_name, Vector<Scalar> *r_constant_values) { if (is_shader_inc) { for (int i = 0; i < RenderingServer::SHADER_MAX; i++) { for (const KeyValue<StringName, FunctionInfo> &E : ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(i))) { @@ -1424,8 +1424,8 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea if (r_struct_name) { *r_struct_name = p_block->variables[p_identifier].struct_name; } - if (r_constant_value) { - *r_constant_value = p_block->variables[p_identifier].value; + if (r_constant_values && !p_block->variables[p_identifier].values.is_empty()) { + *r_constant_values = p_block->variables[p_identifier].values; } if (r_type) { *r_type = IDENTIFIER_LOCAL_VAR; @@ -1507,13 +1507,9 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea if (r_struct_name) { *r_struct_name = shader->constants[p_identifier].struct_name; } - if (r_constant_value) { - if (shader->constants[p_identifier].initializer && shader->constants[p_identifier].initializer->type == Node::NODE_TYPE_CONSTANT) { - ConstantNode *cnode = static_cast<ConstantNode *>(shader->constants[p_identifier].initializer); - - if (cnode->values.size() == 1) { - *r_constant_value = cnode->values[0]; - } + if (r_constant_values) { + if (shader->constants[p_identifier].initializer && !shader->constants[p_identifier].initializer->get_values().is_empty()) { + *r_constant_values = shader->constants[p_identifier].initializer->get_values(); } } if (r_type) { @@ -1544,7 +1540,7 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea return false; } -bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type, int *r_ret_size) { +bool ShaderLanguage::_validate_operator(const BlockNode *p_block, OperatorNode *p_op, DataType *r_ret_type, int *r_ret_size) { bool valid = false; DataType ret_type = TYPE_VOID; int ret_size = 0; @@ -2007,9 +2003,384 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type if (r_ret_size) { *r_ret_size = ret_size; } + + if (valid && (!p_block || p_block->use_op_eval)) { + // Need to be placed here and not in the `_reduce_expression` because otherwise expressions like `1 + 2 / 2` will not work correctly. + valid = _eval_operator(p_block, p_op); + } + return valid; } +Vector<ShaderLanguage::Scalar> ShaderLanguage::_get_node_values(const BlockNode *p_block, Node *p_node) { + Vector<Scalar> result; + + switch (p_node->type) { + case Node::NODE_TYPE_VARIABLE: { + _find_identifier(p_block, false, FunctionInfo(), static_cast<VariableNode *>(p_node)->name, nullptr, nullptr, nullptr, nullptr, nullptr, &result); + } break; + default: { + result = p_node->get_values(); + } break; + } + + return result; +} + +bool ShaderLanguage::_eval_operator(const BlockNode *p_block, OperatorNode *p_op) { + bool is_valid = true; + + switch (p_op->op) { + case OP_EQUAL: + case OP_NOT_EQUAL: + case OP_LESS: + case OP_LESS_EQUAL: + case OP_GREATER: + case OP_GREATER_EQUAL: + case OP_AND: + case OP_OR: + case OP_ADD: + case OP_SUB: + case OP_MUL: + case OP_DIV: + case OP_MOD: + case OP_SHIFT_LEFT: + case OP_SHIFT_RIGHT: + case OP_BIT_AND: + case OP_BIT_OR: + case OP_BIT_XOR: { + DataType a = p_op->arguments[0]->get_datatype(); + DataType b = p_op->arguments[1]->get_datatype(); + + bool is_op_vec_transform = false; + if (p_op->op == OP_MUL) { + DataType ta = a; + DataType tb = b; + + if (ta > tb) { + SWAP(ta, tb); + } + if (ta == TYPE_VEC2 && tb == TYPE_MAT2) { + is_op_vec_transform = true; + } else if (ta == TYPE_VEC3 && tb == TYPE_MAT3) { + is_op_vec_transform = true; + } else if (ta == TYPE_VEC4 && tb == TYPE_MAT4) { + is_op_vec_transform = true; + } + } + + Vector<Scalar> va = _get_node_values(p_block, p_op->arguments[0]); + Vector<Scalar> vb = _get_node_values(p_block, p_op->arguments[1]); + + if (is_op_vec_transform) { + p_op->values = _eval_vector_transform(va, vb, a, b, p_op->get_datatype()); + } else { + p_op->values = _eval_vector(va, vb, a, b, p_op->get_datatype(), p_op->op, is_valid); + } + } break; + case OP_NOT: + case OP_NEGATE: + case OP_BIT_INVERT: { + p_op->values = _eval_unary_vector(_get_node_values(p_block, p_op->arguments[0]), p_op->get_datatype(), p_op->op); + } break; + default: { + } break; + } + + return is_valid; +} + +ShaderLanguage::Scalar ShaderLanguage::_eval_unary_scalar(const Scalar &p_a, Operator p_op, DataType p_ret_type) { + Scalar scalar; + + switch (p_op) { + case OP_NOT: { + scalar.boolean = !p_a.boolean; + } break; + case OP_NEGATE: { + if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) { + scalar.sint = -p_a.sint; + } else if (p_ret_type >= TYPE_UINT && p_ret_type <= TYPE_UVEC4) { + // Intentionally wrap the unsigned int value, because GLSL does. + scalar.uint = 0 - p_a.uint; + } else { // float types + scalar.real = -scalar.real; + } + } break; + case OP_BIT_INVERT: { + if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) { + scalar.sint = ~p_a.sint; + } else { // uint types + scalar.uint = ~p_a.uint; + } + } break; + default: { + } break; + } + + return scalar; +} + +ShaderLanguage::Scalar ShaderLanguage::_eval_scalar(const Scalar &p_a, const Scalar &p_b, Operator p_op, DataType p_ret_type, bool &r_is_valid) { + Scalar scalar; + + switch (p_op) { + case OP_EQUAL: { + scalar.boolean = p_a.boolean == p_b.boolean; + } break; + case OP_NOT_EQUAL: { + scalar.boolean = p_a.boolean != p_b.boolean; + } break; + case OP_LESS: { + if (p_ret_type == TYPE_INT) { + scalar.boolean = p_a.sint < p_b.sint; + } else if (p_ret_type == TYPE_UINT) { + scalar.boolean = p_a.uint < p_b.uint; + } else { // float type + scalar.boolean = p_a.real < p_b.real; + } + } break; + case OP_LESS_EQUAL: { + if (p_ret_type == TYPE_INT) { + scalar.boolean = p_a.sint <= p_b.sint; + } else if (p_ret_type == TYPE_UINT) { + scalar.boolean = p_a.uint <= p_b.uint; + } else { // float type + scalar.boolean = p_a.real <= p_b.real; + } + } break; + case OP_GREATER: { + if (p_ret_type == TYPE_INT) { + scalar.boolean = p_a.sint > p_b.sint; + } else if (p_ret_type == TYPE_UINT) { + scalar.boolean = p_a.uint > p_b.uint; + } else { // float type + scalar.boolean = p_a.real > p_b.real; + } + } break; + case OP_GREATER_EQUAL: { + if (p_ret_type == TYPE_INT) { + scalar.boolean = p_a.sint >= p_b.sint; + } else if (p_ret_type == TYPE_UINT) { + scalar.boolean = p_a.uint >= p_b.uint; + } else { // float type + scalar.boolean = p_a.real >= p_b.real; + } + } break; + case OP_AND: { + scalar.boolean = p_a.boolean && p_b.boolean; + } break; + case OP_OR: { + scalar.boolean = p_a.boolean || p_b.boolean; + } break; + case OP_ADD: { + if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) { + scalar.sint = p_a.sint + p_b.sint; + } else if (p_ret_type >= TYPE_UINT && p_ret_type <= TYPE_UVEC4) { + scalar.uint = p_a.uint + p_b.uint; + } else { // float + matrix types + scalar.real = p_a.real + p_b.real; + } + } break; + case OP_SUB: { + if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) { + scalar.sint = p_a.sint - p_b.sint; + } else if (p_ret_type >= TYPE_UINT && p_ret_type <= TYPE_UVEC4) { + scalar.uint = p_a.uint - p_b.uint; + } else { // float + matrix types + scalar.real = p_a.real - p_b.real; + } + } break; + case OP_MUL: { + if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) { + scalar.sint = p_a.sint * p_b.sint; + } else if (p_ret_type >= TYPE_UINT && p_ret_type <= TYPE_UVEC4) { + scalar.uint = p_a.uint * p_b.uint; + } else { // float + matrix types + scalar.real = p_a.real * p_b.real; + } + } break; + case OP_DIV: { + if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) { + if (p_b.sint == 0) { + _set_error(RTR("Division by zero error.")); + r_is_valid = false; + break; + } + scalar.sint = p_a.sint / p_b.sint; + } else if (p_ret_type == TYPE_UINT && p_ret_type <= TYPE_UVEC4) { + if (p_b.uint == 0U) { + _set_error(RTR("Division by zero error.")); + r_is_valid = false; + break; + } + scalar.uint = p_a.uint / p_b.uint; + } else { // float + matrix types + scalar.real = p_a.real / p_b.real; + } + } break; + case OP_MOD: { + if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) { + if (p_b.sint == 0) { + _set_error(RTR("Modulo by zero error.")); + r_is_valid = false; + break; + } + scalar.sint = p_a.sint % p_b.sint; + } else { // uint types + if (p_b.uint == 0U) { + _set_error(RTR("Modulo by zero error.")); + r_is_valid = false; + break; + } + scalar.uint = p_a.uint % p_b.uint; + } + } break; + case OP_SHIFT_LEFT: { + if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) { + scalar.sint = p_a.sint << p_b.sint; + } else { // uint types + scalar.uint = p_a.uint << p_b.uint; + } + } break; + case OP_SHIFT_RIGHT: { + if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) { + scalar.sint = p_a.sint >> p_b.sint; + } else { // uint types + scalar.uint = p_a.uint >> p_b.uint; + } + } break; + case OP_BIT_AND: { + if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) { + scalar.sint = p_a.sint & p_b.sint; + } else { // uint types + scalar.uint = p_a.uint & p_b.uint; + } + } break; + case OP_BIT_OR: { + if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) { + scalar.sint = p_a.sint | p_b.sint; + } else { // uint types + scalar.uint = p_a.uint | p_b.uint; + } + } break; + case OP_BIT_XOR: { + if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) { + scalar.sint = p_a.sint ^ p_b.sint; + } else { // uint types + scalar.uint = p_a.uint ^ p_b.uint; + } + } break; + default: { + } break; + } + + return scalar; +} + +Vector<ShaderLanguage::Scalar> ShaderLanguage::_eval_unary_vector(const Vector<Scalar> &p_va, DataType p_ret_type, Operator p_op) { + uint32_t size = get_datatype_component_count(p_ret_type); + if (p_va.size() != p_ret_type) { + return Vector<Scalar>(); // Non-evaluable values should not be parsed further. + } + Vector<Scalar> value; + value.resize(size); + + Scalar *w = value.ptrw(); + for (uint32_t i = 0U; i < size; i++) { + w[i] = _eval_unary_scalar(p_va[i], p_op, p_ret_type); + } + return value; +} + +Vector<ShaderLanguage::Scalar> ShaderLanguage::_eval_vector(const Vector<Scalar> &p_va, const Vector<Scalar> &p_vb, DataType p_left_type, DataType p_right_type, DataType p_ret_type, Operator p_op, bool &r_is_valid) { + uint32_t left_size = get_datatype_component_count(p_left_type); + uint32_t right_size = get_datatype_component_count(p_right_type); + + if (p_va.size() != left_size || p_vb.size() != right_size) { + return Vector<Scalar>(); // Non-evaluable values should not be parsed further. + } + + uint32_t ret_size = get_datatype_component_count(p_ret_type); + Vector<Scalar> value; + value.resize(ret_size); + + Scalar *w = value.ptrw(); + for (uint32_t i = 0U; i < ret_size; i++) { + w[i] = _eval_scalar(p_va[MIN(i, left_size - 1)], p_vb[MIN(i, right_size - 1)], p_op, p_ret_type, r_is_valid); + if (!r_is_valid) { + return value; + } + } + return value; +} + +Vector<ShaderLanguage::Scalar> ShaderLanguage::_eval_vector_transform(const Vector<Scalar> &p_va, const Vector<Scalar> &p_vb, DataType p_left_type, DataType p_right_type, DataType p_ret_type) { + uint32_t left_size = get_datatype_component_count(p_left_type); + uint32_t right_size = get_datatype_component_count(p_right_type); + + if (p_va.size() != left_size || p_vb.size() != right_size) { + return Vector<Scalar>(); // Non-evaluable values should not be parsed further. + } + + uint32_t ret_size = get_datatype_component_count(p_ret_type); + Vector<Scalar> value; + value.resize_zeroed(ret_size); + + Scalar *w = value.ptrw(); + switch (p_ret_type) { + case TYPE_VEC2: { + if (left_size == 2) { // v * m + Vector2 v = Vector2(p_va[0].real, p_va[1].real); + + w[0].real = (p_vb[0].real * v.x + p_vb[1].real * v.y); + w[1].real = (p_vb[2].real * v.x + p_vb[3].real * v.y); + } else { // m * v + Vector2 v = Vector2(p_vb[0].real, p_vb[1].real); + + w[0].real = (p_va[0].real * v.x + p_va[2].real * v.y); + w[1].real = (p_va[1].real * v.x + p_va[3].real * v.y); + } + } break; + case TYPE_VEC3: { + if (left_size == 3) { // v * m + Vector3 v = Vector3(p_va[0].real, p_va[1].real, p_va[2].real); + + w[0].real = (p_vb[0].real * v.x + p_vb[1].real * v.y + p_vb[2].real * v.z); + w[1].real = (p_vb[3].real * v.x + p_vb[4].real * v.y + p_vb[5].real * v.z); + w[2].real = (p_vb[6].real * v.x + p_vb[7].real * v.y + p_vb[8].real * v.z); + } else { // m * v + Vector3 v = Vector3(p_vb[0].real, p_vb[1].real, p_vb[2].real); + + w[0].real = (p_va[0].real * v.x + p_va[3].real * v.y + p_va[6].real * v.z); + w[1].real = (p_va[1].real * v.x + p_va[4].real * v.y + p_va[7].real * v.z); + w[2].real = (p_va[2].real * v.x + p_va[5].real * v.y + p_va[8].real * v.z); + } + } break; + case TYPE_VEC4: { + if (left_size == 4) { // v * m + Vector4 v = Vector4(p_va[0].real, p_va[1].real, p_va[2].real, p_va[3].real); + + w[0].real = (p_vb[0].real * v.x + p_vb[1].real * v.y + p_vb[2].real * v.z + p_vb[3].real * v.w); + w[1].real = (p_vb[4].real * v.x + p_vb[5].real * v.y + p_vb[6].real * v.z + p_vb[7].real * v.w); + w[2].real = (p_vb[8].real * v.x + p_vb[9].real * v.y + p_vb[10].real * v.z + p_vb[11].real * v.w); + w[3].real = (p_vb[12].real * v.x + p_vb[13].real * v.y + p_vb[14].real * v.z + p_vb[15].real * v.w); + } else { // m * v + Vector4 v = Vector4(p_vb[0].real, p_vb[1].real, p_vb[2].real, p_vb[3].real); + + w[0].real = (p_vb[0].real * v.x + p_vb[4].real * v.y + p_vb[8].real * v.z + p_vb[12].real * v.w); + w[1].real = (p_vb[1].real * v.x + p_vb[5].real * v.y + p_vb[9].real * v.z + p_vb[13].real * v.w); + w[2].real = (p_vb[2].real * v.x + p_vb[6].real * v.y + p_vb[10].real * v.z + p_vb[14].real * v.w); + w[3].real = (p_vb[3].real * v.x + p_vb[7].real * v.y + p_vb[11].real * v.z + p_vb[15].real * v.w); + } + } break; + default: { + } break; + } + + return value; +} + const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { // Constructors. @@ -3271,34 +3642,15 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI int max = builtin_func_const_args[constarg_idx].max; bool error = false; - if (p_func->arguments[arg]->type == Node::NODE_TYPE_VARIABLE) { - const VariableNode *vn = static_cast<VariableNode *>(p_func->arguments[arg]); - - bool is_const = false; - ConstantNode::Value value; - value.sint = -1; - - _find_identifier(p_block, false, p_function_info, vn->name, nullptr, nullptr, &is_const, nullptr, nullptr, &value); - if (!is_const || value.sint < min || value.sint > max) { + Vector<Scalar> values = _get_node_values(p_block, p_func->arguments[arg]); + if (p_func->arguments[arg]->get_datatype() == TYPE_INT && !values.is_empty()) { + if (values[0].sint < min || values[0].sint > max) { error = true; } } else { - if (p_func->arguments[arg]->type == Node::NODE_TYPE_CONSTANT) { - const ConstantNode *cn = static_cast<ConstantNode *>(p_func->arguments[arg]); - - if (cn->get_datatype() == TYPE_INT && cn->values.size() == 1) { - int value = cn->values[0].sint; - - if (value < min || value > max) { - error = true; - } - } else { - error = true; - } - } else { - error = true; - } + error = true; } + if (error) { _set_error(vformat(RTR("Expected integer constant within [%d..%d] range."), min, max)); return false; @@ -3760,7 +4112,7 @@ bool ShaderLanguage::is_token_hint(TokenType p_type) { return int(p_type) > int(TK_RENDER_MODE) && int(p_type) < int(TK_SHADER_TYPE); } -bool ShaderLanguage::convert_constant(ConstantNode *p_constant, DataType p_to_type, ConstantNode::Value *p_value) { +bool ShaderLanguage::convert_constant(ConstantNode *p_constant, DataType p_to_type, Scalar *p_value) { if (p_constant->datatype == p_to_type) { if (p_value) { for (int i = 0; i < p_constant->values.size(); i++) { @@ -3828,7 +4180,7 @@ bool ShaderLanguage::is_sampler_type(DataType p_type) { return p_type > TYPE_MAT4 && p_type < TYPE_STRUCT; } -Variant ShaderLanguage::constant_value_to_variant(const Vector<ShaderLanguage::ConstantNode::Value> &p_value, DataType p_type, int p_array_size, ShaderLanguage::ShaderNode::Uniform::Hint p_hint) { +Variant ShaderLanguage::constant_value_to_variant(const Vector<Scalar> &p_value, DataType p_type, int p_array_size, ShaderLanguage::ShaderNode::Uniform::Hint p_hint) { int array_size = p_array_size; if (p_value.size() > 0) { @@ -4437,6 +4789,52 @@ uint32_t ShaderLanguage::get_datatype_size(ShaderLanguage::DataType p_type) { ERR_FAIL_V(0); } +uint32_t ShaderLanguage::get_datatype_component_count(ShaderLanguage::DataType p_type) { + switch (p_type) { + case TYPE_BOOL: + return 1U; + case TYPE_BVEC2: + return 2U; + case TYPE_BVEC3: + return 3U; + case TYPE_BVEC4: + return 4U; + case TYPE_INT: + return 1U; + case TYPE_IVEC2: + return 2U; + case TYPE_IVEC3: + return 3U; + case TYPE_IVEC4: + return 4U; + case TYPE_UINT: + return 1U; + case TYPE_UVEC2: + return 2U; + case TYPE_UVEC3: + return 3U; + case TYPE_UVEC4: + return 4U; + case TYPE_FLOAT: + return 1U; + case TYPE_VEC2: + return 2U; + case TYPE_VEC3: + return 3U; + case TYPE_VEC4: + return 4U; + case TYPE_MAT2: + return 4U; + case TYPE_MAT3: + return 9U; + case TYPE_MAT4: + return 16U; + default: + break; + } + return 0U; +} + void ShaderLanguage::get_keyword_list(List<String> *r_keywords) { HashSet<String> kws; @@ -4929,45 +5327,30 @@ Error ShaderLanguage::_parse_array_size(BlockNode *p_block, const FunctionInfo & *r_unknown_size = true; } } else { - int array_size = 0; + _set_tkpos(pos); - if (!tk.is_integer_constant() || ((int)tk.constant) <= 0) { - _set_tkpos(pos); - Node *n = _parse_and_reduce_expression(p_block, p_function_info); - if (n) { - if (n->type == Node::NODE_TYPE_VARIABLE) { - VariableNode *vn = static_cast<VariableNode *>(n); - if (vn) { - ConstantNode::Value v; - DataType data_type; - bool is_const = false; + int array_size = 0; + Node *expr = _parse_and_reduce_expression(p_block, p_function_info); - _find_identifier(p_block, false, p_function_info, vn->name, &data_type, nullptr, &is_const, nullptr, nullptr, &v); + if (expr) { + Vector<Scalar> values = _get_node_values(p_block, expr); - if (is_const) { - if (data_type == TYPE_INT) { - int32_t value = v.sint; - if (value > 0) { - array_size = value; - } - } else if (data_type == TYPE_UINT) { - uint32_t value = v.uint; - if (value > 0U) { - array_size = value; - } - } - } - } - } else if (n->type == Node::NODE_TYPE_OPERATOR) { - _set_error(vformat(RTR("Array size expressions are not supported."))); - return ERR_PARSE_ERROR; - } - if (r_size_expression != nullptr) { - *r_size_expression = n; + if (!values.is_empty()) { + switch (expr->get_datatype()) { + case TYPE_INT: { + array_size = values[0].sint; + } break; + case TYPE_UINT: { + array_size = (int)values[0].uint; + } break; + default: { + } break; } } - } else if (((int)tk.constant) > 0) { - array_size = (uint32_t)tk.constant; + + if (r_size_expression != nullptr) { + *r_size_expression = expr; + } } if (array_size <= 0) { @@ -5064,7 +5447,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_bloc idx++; } if (!auto_size && !undefined_size && an->initializer.size() != array_size) { - _set_error(RTR("Array size mismatch.")); + _set_error(vformat(RTR("Array size mismatch. Expected %d elements (found %d)."), array_size, an->initializer.size())); return nullptr; } } else { @@ -5185,7 +5568,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_bloc } } if (an->initializer.size() != p_array_size) { - _set_error(RTR("Array size mismatch.")); + _set_error(vformat(RTR("Array size mismatch. Expected %d elements (found %d)."), p_array_size, an->initializer.size())); return nullptr; } } else { @@ -5230,7 +5613,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } else if (tk.type == TK_FLOAT_CONSTANT) { ConstantNode *constant = alloc_node<ConstantNode>(); - ConstantNode::Value v; + Scalar v; v.real = tk.constant; constant->values.push_back(v); constant->datatype = TYPE_FLOAT; @@ -5238,7 +5621,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } else if (tk.type == TK_INT_CONSTANT) { ConstantNode *constant = alloc_node<ConstantNode>(); - ConstantNode::Value v; + Scalar v; v.sint = tk.constant; constant->values.push_back(v); constant->datatype = TYPE_INT; @@ -5246,7 +5629,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } else if (tk.type == TK_UINT_CONSTANT) { ConstantNode *constant = alloc_node<ConstantNode>(); - ConstantNode::Value v; + Scalar v; v.uint = tk.constant; constant->values.push_back(v); constant->datatype = TYPE_UINT; @@ -5255,7 +5638,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } else if (tk.type == TK_TRUE) { //handle true constant ConstantNode *constant = alloc_node<ConstantNode>(); - ConstantNode::Value v; + Scalar v; v.boolean = true; constant->values.push_back(v); constant->datatype = TYPE_BOOL; @@ -5264,7 +5647,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } else if (tk.type == TK_FALSE) { //handle false constant ConstantNode *constant = alloc_node<ConstantNode>(); - ConstantNode::Value v; + Scalar v; v.boolean = false; constant->values.push_back(v); constant->datatype = TYPE_BOOL; @@ -6527,7 +6910,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons op->op = tk.type == TK_OP_DECREMENT ? OP_POST_DECREMENT : OP_POST_INCREMENT; op->arguments.push_back(expr); - if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) { + if (!_validate_operator(p_block, op, &op->return_cache, &op->return_array_size)) { _set_error(RTR("Invalid base type for increment/decrement operator.")); return nullptr; } @@ -6876,7 +7259,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons expression.write[i].is_op = false; expression.write[i].node = op; - if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) { + if (!_validate_operator(p_block, op, &op->return_cache, &op->return_array_size)) { + if (error_set) { + return nullptr; + } + String at; for (int j = 0; j < op->arguments.size(); j++) { if (j > 0) { @@ -6914,7 +7301,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons expression.write[next_op - 1].is_op = false; expression.write[next_op - 1].node = op; - if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) { + if (!_validate_operator(p_block, op, &op->return_cache, &op->return_array_size)) { + if (error_set) { + return nullptr; + } + String at; for (int i = 0; i < op->arguments.size(); i++) { if (i > 0) { @@ -6950,6 +7341,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } if (_is_operator_assign(op->op)) { + if (p_block && expression[next_op - 1].node->type == Node::NODE_TYPE_VARIABLE) { + VariableNode *vn = static_cast<VariableNode *>(expression[next_op - 1].node); + p_block->use_op_eval = vn->is_const; + } + String assign_message; if (!_validate_assign(expression[next_op - 1].node, p_function_info, &assign_message)) { _set_error(assign_message); @@ -6972,7 +7368,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons //replace all 3 nodes by this operator and make it an expression - if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) { + if (!_validate_operator(p_block, op, &op->return_cache, &op->return_array_size)) { + if (error_set) { + return nullptr; + } + String at; for (int i = 0; i < op->arguments.size(); i++) { if (i > 0) { @@ -6998,6 +7398,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } } + if (p_block) { + p_block->use_op_eval = true; + } + if (p_previous_expression_info != nullptr) { p_previous_expression_info->expression->push_back(expression[0]); } @@ -7020,7 +7424,7 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha DataType base = get_scalar_type(type); int cardinality = get_cardinality(type); - Vector<ConstantNode::Value> values; + Vector<Scalar> values; for (int i = 1; i < op->arguments.size(); i++) { op->arguments.write[i] = _reduce_expression(p_block, op->arguments[i]); @@ -7032,7 +7436,7 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha values.push_back(cn->values[j]); } } else if (get_scalar_type(cn->datatype) == cn->datatype) { - ConstantNode::Value v; + Scalar v; if (!convert_constant(cn, base, &v)) { return p_node; } @@ -7048,8 +7452,8 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha if (values.size() == 1) { if (type >= TYPE_MAT2 && type <= TYPE_MAT4) { - ConstantNode::Value value = values[0]; - ConstantNode::Value zero; + Scalar value = values[0]; + Scalar zero; zero.real = 0.0f; int size = 2 + (type - TYPE_MAT2); @@ -7060,7 +7464,7 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha } } } else { - ConstantNode::Value value = values[0]; + Scalar value = values[0]; for (int i = 1; i < cardinality; i++) { values.push_back(value); } @@ -7081,10 +7485,10 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha DataType base = get_scalar_type(cn->datatype); - Vector<ConstantNode::Value> values; + Vector<Scalar> values; for (int i = 0; i < cn->values.size(); i++) { - ConstantNode::Value nv; + Scalar nv; switch (base) { case TYPE_BOOL: { nv.boolean = !cn->values[i].boolean; @@ -7515,7 +7919,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun decl.size = decl.initializer.size(); var.array_size = decl.initializer.size(); } else if (decl.initializer.size() != var.array_size) { - _set_error(RTR("Array size mismatch.")); + _set_error(vformat(RTR("Array size mismatch. Expected %d elements (found %d)."), var.array_size, decl.initializer.size())); return ERR_PARSE_ERROR; } tk = _get_token(); @@ -7537,7 +7941,9 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun array_size = var.array_size; } else if (tk.type == TK_OP_ASSIGN) { - //variable created with assignment! must parse an expression + p_block->use_op_eval = is_const; + + // Variable created with assignment! Must parse an expression. Node *n = _parse_and_reduce_expression(p_block, p_function_info); if (!n) { return ERR_PARSE_ERROR; @@ -7552,11 +7958,8 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun } } - if (n->type == Node::NODE_TYPE_CONSTANT) { - ConstantNode *const_node = static_cast<ConstantNode *>(n); - if (const_node && const_node->values.size() == 1) { - var.value = const_node->values[0]; - } + if (is_const) { + var.values = n->get_values(); } if (!_compare_datatypes(var.type, var.struct_name, var.array_size, n->get_datatype(), n->get_datatype_name(), n->get_array_size())) { @@ -7734,13 +8137,13 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun if (!vn) { return ERR_PARSE_ERROR; } - ConstantNode::Value v; + Vector<Scalar> v = { Scalar() }; _find_identifier(p_block, false, p_function_info, vn->name, nullptr, nullptr, nullptr, nullptr, nullptr, &v); - if (constants.has(v.sint)) { - _set_error(vformat(RTR("Duplicated case label: %d."), v.sint)); + if (constants.has(v[0].sint)) { + _set_error(vformat(RTR("Duplicated case label: %d."), v[0].sint)); return ERR_PARSE_ERROR; } - constants.insert(v.sint); + constants.insert(v[0].sint); } } else if (flow->flow_op == FLOW_OP_DEFAULT) { continue; @@ -7801,7 +8204,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun vn->name = tk.text; n = vn; } else { - ConstantNode::Value v; + Scalar v; if (tk.type == TK_UINT_CONSTANT) { v.uint = (uint32_t)tk.constant; } else { @@ -9678,7 +10081,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f decl.size = decl.initializer.size(); constant.array_size = decl.initializer.size(); } else if (decl.initializer.size() != constant.array_size) { - _set_error(RTR("Array size mismatch.")); + _set_error(vformat(RTR("Array size mismatch. Expected %d elements (found %d)."), constant.array_size, decl.initializer.size())); return ERR_PARSE_ERROR; } } else { diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index 63dca99654..ba02e181b9 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -355,6 +355,13 @@ public: } }; + union Scalar { + bool boolean = false; + float real; + int32_t sint; + uint32_t uint; + }; + struct Node { Node *next = nullptr; @@ -379,6 +386,7 @@ public: virtual String get_datatype_name() const { return ""; } virtual int get_array_size() const { return 0; } virtual bool is_indexed() const { return false; } + virtual Vector<Scalar> get_values() const { return Vector<Scalar>(); } Node(Type t) : type(t) {} @@ -402,11 +410,13 @@ public: Operator op = OP_EQUAL; StringName struct_name; Vector<Node *> arguments; + Vector<Scalar> values; virtual DataType get_datatype() const override { return return_cache; } virtual String get_datatype_name() const override { return String(struct_name); } virtual int get_array_size() const override { return return_array_size; } virtual bool is_indexed() const override { return op == OP_INDEX; } + virtual Vector<Scalar> get_values() const override { return values; } OperatorNode() : Node(NODE_TYPE_OPERATOR) {} @@ -485,19 +495,15 @@ public: String struct_name = ""; int array_size = 0; - union Value { - bool boolean = false; - float real; - int32_t sint; - uint32_t uint; - }; - - Vector<Value> values; + Vector<Scalar> values; Vector<VariableDeclarationNode::Declaration> array_declarations; virtual DataType get_datatype() const override { return datatype; } virtual String get_datatype_name() const override { return struct_name; } virtual int get_array_size() const override { return array_size; } + virtual Vector<Scalar> get_values() const override { + return values; + } ConstantNode() : Node(NODE_TYPE_CONSTANT) {} @@ -529,13 +535,14 @@ public: int line; //for completion int array_size; bool is_const; - ConstantNode::Value value; + Vector<Scalar> values; }; HashMap<StringName, Variable> variables; List<Node *> statements; bool single_statement = false; bool use_comma_between_statements = false; + bool use_op_eval = true; BlockNode() : Node(NODE_TYPE_BLOCK) {} @@ -657,7 +664,7 @@ public: DataType type = TYPE_VOID; DataPrecision precision = PRECISION_DEFAULT; int array_size = 0; - Vector<ConstantNode::Value> default_value; + Vector<Scalar> default_value; Scope scope = SCOPE_LOCAL; Hint hint = HINT_NONE; bool use_color = false; @@ -803,15 +810,16 @@ public: static bool is_token_operator_assign(TokenType p_type); static bool is_token_hint(TokenType p_type); - static bool convert_constant(ConstantNode *p_constant, DataType p_to_type, ConstantNode::Value *p_value = nullptr); + static bool convert_constant(ConstantNode *p_constant, DataType p_to_type, Scalar *p_value = nullptr); static DataType get_scalar_type(DataType p_type); static int get_cardinality(DataType p_type); static bool is_scalar_type(DataType p_type); static bool is_float_type(DataType p_type); static bool is_sampler_type(DataType p_type); - static Variant constant_value_to_variant(const Vector<ShaderLanguage::ConstantNode::Value> &p_value, DataType p_type, int p_array_size, ShaderLanguage::ShaderNode::Uniform::Hint p_hint = ShaderLanguage::ShaderNode::Uniform::HINT_NONE); + static Variant constant_value_to_variant(const Vector<Scalar> &p_value, DataType p_type, int p_array_size, ShaderLanguage::ShaderNode::Uniform::Hint p_hint = ShaderLanguage::ShaderNode::Uniform::HINT_NONE); static PropertyInfo uniform_to_property_info(const ShaderNode::Uniform &p_uniform); static uint32_t get_datatype_size(DataType p_type); + static uint32_t get_datatype_component_count(DataType p_type); static void get_keyword_list(List<String> *r_keywords); static bool is_control_flow_keyword(String p_keyword); @@ -1070,13 +1078,21 @@ private: IdentifierType last_type = IDENTIFIER_MAX; - bool _find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type = nullptr, IdentifierType *r_type = nullptr, bool *r_is_const = nullptr, int *r_array_size = nullptr, StringName *r_struct_name = nullptr, ConstantNode::Value *r_constant_value = nullptr); + bool _find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type = nullptr, IdentifierType *r_type = nullptr, bool *r_is_const = nullptr, int *r_array_size = nullptr, StringName *r_struct_name = nullptr, Vector<Scalar> *r_constant_values = nullptr); #ifdef DEBUG_ENABLED void _parse_used_identifier(const StringName &p_identifier, IdentifierType p_type, const StringName &p_function); #endif // DEBUG_ENABLED bool _is_operator_assign(Operator p_op) const; bool _validate_assign(Node *p_node, const FunctionInfo &p_function_info, String *r_message = nullptr); - bool _validate_operator(OperatorNode *p_op, DataType *r_ret_type = nullptr, int *r_ret_size = nullptr); + bool _validate_operator(const BlockNode *p_block, OperatorNode *p_op, DataType *r_ret_type = nullptr, int *r_ret_size = nullptr); + + Vector<Scalar> _get_node_values(const BlockNode *p_block, Node *p_node); + bool _eval_operator(const BlockNode *p_block, OperatorNode *p_op); + Scalar _eval_unary_scalar(const Scalar &p_a, Operator p_op, DataType p_ret_type); + Scalar _eval_scalar(const Scalar &p_a, const Scalar &p_b, Operator p_op, DataType p_ret_type, bool &r_is_valid); + Vector<Scalar> _eval_unary_vector(const Vector<Scalar> &p_va, DataType p_ret_type, Operator p_op); + Vector<Scalar> _eval_vector(const Vector<Scalar> &p_va, const Vector<Scalar> &p_vb, DataType p_left_type, DataType p_right_type, DataType p_ret_type, Operator p_op, bool &r_is_valid); + Vector<Scalar> _eval_vector_transform(const Vector<Scalar> &p_va, const Vector<Scalar> &p_vb, DataType p_left_type, DataType p_right_type, DataType p_ret_type); struct BuiltinEntry { const char *name; diff --git a/servers/rendering/storage/mesh_storage.cpp b/servers/rendering/storage/mesh_storage.cpp index 221ebaa277..6680920c98 100644 --- a/servers/rendering/storage/mesh_storage.cpp +++ b/servers/rendering/storage/mesh_storage.cpp @@ -285,7 +285,7 @@ int RendererMeshStorage::multimesh_get_visible_instances(RID p_multimesh) const return _multimesh_get_visible_instances(p_multimesh); } -AABB RendererMeshStorage::multimesh_get_aabb(RID p_multimesh) const { +AABB RendererMeshStorage::multimesh_get_aabb(RID p_multimesh) { return _multimesh_get_aabb(p_multimesh); } diff --git a/servers/rendering/storage/mesh_storage.h b/servers/rendering/storage/mesh_storage.h index ecd2a967d0..5e3a4738e6 100644 --- a/servers/rendering/storage/mesh_storage.h +++ b/servers/rendering/storage/mesh_storage.h @@ -151,7 +151,7 @@ public: virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible); virtual int multimesh_get_visible_instances(RID p_multimesh) const; - virtual AABB multimesh_get_aabb(RID p_multimesh) const; + virtual AABB multimesh_get_aabb(RID p_multimesh); virtual RID _multimesh_allocate() = 0; virtual void _multimesh_initialize(RID p_rid) = 0; @@ -183,7 +183,7 @@ public: virtual void _multimesh_set_visible_instances(RID p_multimesh, int p_visible) = 0; virtual int _multimesh_get_visible_instances(RID p_multimesh) const = 0; - virtual AABB _multimesh_get_aabb(RID p_multimesh) const = 0; + virtual AABB _multimesh_get_aabb(RID p_multimesh) = 0; // Multimesh is responsible for allocating / destroying a MultiMeshInterpolator object. // This allows shared functionality for interpolation across backends. diff --git a/tests/core/math/test_expression.h b/tests/core/math/test_expression.h index 512d7932f9..c3e4280491 100644 --- a/tests/core/math/test_expression.h +++ b/tests/core/math/test_expression.h @@ -122,6 +122,59 @@ TEST_CASE("[Expression] Floating-point arithmetic") { "Float multiplication-addition-subtraction-division should return the expected result."); } +TEST_CASE("[Expression] Floating-point notation") { + Expression expression; + + CHECK_MESSAGE( + expression.parse("2.") == OK, + "The expression should parse successfully."); + CHECK_MESSAGE( + double(expression.execute()) == doctest::Approx(2.0), + "The expression should return the expected result."); + + CHECK_MESSAGE( + expression.parse("(2.)") == OK, + "The expression should parse successfully."); + CHECK_MESSAGE( + double(expression.execute()) == doctest::Approx(2.0), + "The expression should return the expected result."); + + CHECK_MESSAGE( + expression.parse(".3") == OK, + "The expression should parse successfully."); + CHECK_MESSAGE( + double(expression.execute()) == doctest::Approx(0.3), + "The expression should return the expected result."); + + CHECK_MESSAGE( + expression.parse("2.+5.") == OK, + "The expression should parse successfully."); + CHECK_MESSAGE( + double(expression.execute()) == doctest::Approx(7.0), + "The expression should return the expected result."); + + CHECK_MESSAGE( + expression.parse(".3-.8") == OK, + "The expression should parse successfully."); + CHECK_MESSAGE( + double(expression.execute()) == doctest::Approx(-0.5), + "The expression should return the expected result."); + + CHECK_MESSAGE( + expression.parse("2.+.2") == OK, + "The expression should parse successfully."); + CHECK_MESSAGE( + double(expression.execute()) == doctest::Approx(2.2), + "The expression should return the expected result."); + + CHECK_MESSAGE( + expression.parse(".0*0.") == OK, + "The expression should parse successfully."); + CHECK_MESSAGE( + double(expression.execute()) == doctest::Approx(0.0), + "The expression should return the expected result."); +} + TEST_CASE("[Expression] Scientific notation") { Expression expression; diff --git a/tests/scene/test_path_follow_3d.h b/tests/scene/test_path_follow_3d.h index d08af3a70c..6a384bec2b 100644 --- a/tests/scene/test_path_follow_3d.h +++ b/tests/scene/test_path_follow_3d.h @@ -60,39 +60,30 @@ TEST_CASE("[SceneTree][PathFollow3D] Sampling with progress ratio") { SceneTree::get_singleton()->get_root()->add_child(path); path_follow_3d->set_progress_ratio(0); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(0, 0, 0), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress_ratio(0.125); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(50, 0, 0), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress_ratio(0.25); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 0, 0), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress_ratio(0.375); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 50, 0), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress_ratio(0.5); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 100, 0), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress_ratio(0.625); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 100, 50), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress_ratio(0.75); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 100, 100), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress_ratio(0.875); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 50, 100), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress_ratio(1); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 0, 100), path_follow_3d->get_transform().get_origin())); memdelete(path); @@ -113,39 +104,30 @@ TEST_CASE("[SceneTree][PathFollow3D] Sampling with progress") { SceneTree::get_singleton()->get_root()->add_child(path); path_follow_3d->set_progress(0); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(0, 0, 0), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress(50); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(50, 0, 0), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress(100); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 0, 0), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress(150); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 50, 0), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress(200); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 100, 0), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress(250); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 100, 50), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress(300); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 100, 100), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress(350); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 50, 100), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress(400); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 0, 100), path_follow_3d->get_transform().get_origin())); memdelete(path); @@ -163,13 +145,11 @@ TEST_CASE("[SceneTree][PathFollow3D] Removal of a point in curve") { SceneTree::get_singleton()->get_root()->add_child(path); path_follow_3d->set_progress_ratio(0.5); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 0, 0), path_follow_3d->get_transform().get_origin())); curve->remove_point(1); path_follow_3d->set_progress_ratio(0.5); - path_follow_3d->update_transform(true); CHECK_MESSAGE( is_equal_approx(Vector3(50, 50, 0), path_follow_3d->get_transform().get_origin()), "Path follow's position should be updated after removing a point from the curve"); @@ -270,47 +250,36 @@ TEST_CASE("[SceneTree][PathFollow3D] Calculate forward vector") { path_follow_3d->set_rotation_mode(PathFollow3D::RotationMode::ROTATION_ORIENTED); path_follow_3d->set_progress(-50); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(-1, 0, 0), path_follow_3d->get_transform().get_basis().get_column(2))); path_follow_3d->set_progress(0); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(-1, 0, 0), path_follow_3d->get_transform().get_basis().get_column(2))); path_follow_3d->set_progress(50); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(-1, 0, 0), path_follow_3d->get_transform().get_basis().get_column(2))); path_follow_3d->set_progress(100); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(-1, 0, 0), path_follow_3d->get_transform().get_basis().get_column(2))); path_follow_3d->set_progress(100 + dist_cube_100 / 2); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(-0.577348, -0.577348, 0.577348), path_follow_3d->get_transform().get_basis().get_column(2))); path_follow_3d->set_progress(100 + dist_cube_100 - 0.01); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(-0.577348, -0.577348, 0.577348), path_follow_3d->get_transform().get_basis().get_column(2))); path_follow_3d->set_progress(250 + dist_cube_100); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(0, 0, -1), path_follow_3d->get_transform().get_basis().get_column(2))); path_follow_3d->set_progress(400 + dist_cube_100 - 0.01); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(0, 0, -1), path_follow_3d->get_transform().get_basis().get_column(2))); path_follow_3d->set_progress(400 + 1.5 * dist_cube_100); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(0.577348, 0.577348, 0.577348), path_follow_3d->get_transform().get_basis().get_column(2))); path_follow_3d->set_progress(400 + 2 * dist_cube_100 - 0.01); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(0.577348, 0.577348, 0.577348), path_follow_3d->get_transform().get_basis().get_column(2))); path_follow_3d->set_progress(500 + 2 * dist_cube_100); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(1, 0, 0), path_follow_3d->get_transform().get_basis().get_column(2))); memdelete(path); |