summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--SConstruct14
-rw-r--r--core/extension/gdextension.cpp41
-rw-r--r--core/extension/gdextension.h7
-rw-r--r--core/string/ustring.cpp17
-rw-r--r--core/string/ustring.h1
-rw-r--r--core/variant/variant_call.cpp1
-rw-r--r--doc/classes/AudioStreamRandomizer.xml4
-rw-r--r--doc/classes/BackBufferCopy.xml4
-rw-r--r--doc/classes/BaseMaterial3D.xml6
-rw-r--r--doc/classes/CPUParticles2D.xml2
-rw-r--r--doc/classes/CPUParticles3D.xml2
-rw-r--r--doc/classes/CompressedCubemap.xml2
-rw-r--r--doc/classes/CompressedCubemapArray.xml2
-rw-r--r--doc/classes/Cubemap.xml2
-rw-r--r--doc/classes/CubemapArray.xml10
-rw-r--r--doc/classes/Curve.xml4
-rw-r--r--doc/classes/CurveTexture.xml4
-rw-r--r--doc/classes/CurveXYZTexture.xml4
-rw-r--r--doc/classes/DisplayServer.xml4
-rw-r--r--doc/classes/FogMaterial.xml2
-rw-r--r--doc/classes/FogVolume.xml2
-rw-r--r--doc/classes/GPUParticles2D.xml4
-rw-r--r--doc/classes/GPUParticles3D.xml2
-rw-r--r--doc/classes/GPUParticlesAttractor3D.xml4
-rw-r--r--doc/classes/GPUParticlesAttractorBox3D.xml5
-rw-r--r--doc/classes/GPUParticlesAttractorSphere3D.xml5
-rw-r--r--doc/classes/GPUParticlesAttractorVectorField3D.xml5
-rw-r--r--doc/classes/GPUParticlesCollision3D.xml4
-rw-r--r--doc/classes/GPUParticlesCollisionBox3D.xml5
-rw-r--r--doc/classes/GPUParticlesCollisionHeightField3D.xml6
-rw-r--r--doc/classes/GPUParticlesCollisionSDF3D.xml4
-rw-r--r--doc/classes/GPUParticlesCollisionSphere3D.xml5
-rw-r--r--doc/classes/Gradient.xml8
-rw-r--r--doc/classes/GradientTexture1D.xml6
-rw-r--r--doc/classes/GradientTexture2D.xml4
-rw-r--r--doc/classes/Material.xml4
-rw-r--r--doc/classes/NavigationServer3D.xml9
-rw-r--r--doc/classes/ORMMaterial3D.xml2
-rw-r--r--doc/classes/OS.xml2
-rw-r--r--doc/classes/OptionButton.xml7
-rw-r--r--doc/classes/PanoramaSkyMaterial.xml4
-rw-r--r--doc/classes/ParticleProcessMaterial.xml6
-rw-r--r--doc/classes/PhysicalSkyMaterial.xml2
-rw-r--r--doc/classes/PlaceholderCubemap.xml10
-rw-r--r--doc/classes/PlaceholderCubemapArray.xml10
-rw-r--r--doc/classes/PopupMenu.xml4
-rw-r--r--doc/classes/ProceduralSkyMaterial.xml8
-rw-r--r--doc/classes/ProjectSettings.xml6
-rw-r--r--doc/classes/RenderingServer.xml1
-rw-r--r--doc/classes/SceneTree.xml1
-rw-r--r--doc/classes/ShaderGlobalsOverride.xml3
-rw-r--r--doc/classes/ShaderMaterial.xml7
-rw-r--r--doc/classes/Sky.xml4
-rw-r--r--doc/classes/StandardMaterial3D.xml2
-rw-r--r--doc/classes/String.xml6
-rw-r--r--doc/classes/StringName.xml6
-rw-r--r--doc/classes/Tree.xml3
-rw-r--r--doc/classes/Viewport.xml7
-rw-r--r--doc/classes/VisualShader.xml4
-rw-r--r--doc/classes/VisualShaderNode.xml6
-rw-r--r--doc/classes/VisualShaderNodeIf.xml4
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp20
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.h2
-rw-r--r--editor/editor_node.cpp19
-rw-r--r--editor/export/editor_export.cpp17
-rw-r--r--editor/export/editor_export_platform.h2
-rw-r--r--editor/export/project_export.cpp72
-rw-r--r--editor/export/project_export.h20
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp13
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp33
-rw-r--r--editor/plugins/tiles/tile_map_editor.h2
-rw-r--r--editor/scene_tree_dock.cpp48
-rw-r--r--main/main.cpp7
-rw-r--r--misc/extension_api_validation/4.0-stable.expected8
-rw-r--r--modules/denoise/SCsub9
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp21
-rw-r--r--modules/gdscript/gdscript_parser.cpp2
-rw-r--r--modules/gdscript/gdscript_parser.h2
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp18
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_in_static_function.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_in_static_function.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/.editorconfig2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/mixed_indentation_on_blank_lines.gd20
-rw-r--r--modules/gdscript/tests/scripts/parser/features/mixed_indentation_on_blank_lines.out4
-rw-r--r--modules/gridmap/editor/grid_map_editor_plugin.cpp63
-rw-r--r--modules/navigation/godot_navigation_server.cpp34
-rw-r--r--modules/navigation/godot_navigation_server.h5
-rw-r--r--modules/navigation/nav_mesh_generator_3d.cpp125
-rw-r--r--modules/navigation/nav_mesh_generator_3d.h32
-rw-r--r--modules/noise/doc_classes/FastNoiseLite.xml2
-rw-r--r--modules/noise/doc_classes/Noise.xml2
-rw-r--r--modules/noise/doc_classes/NoiseTexture2D.xml4
-rw-r--r--modules/noise/doc_classes/NoiseTexture3D.xml4
-rw-r--r--modules/openxr/SCsub5
-rw-r--r--modules/zip/doc_classes/ZIPReader.xml9
-rw-r--r--modules/zip/zip_reader.cpp16
-rw-r--r--modules/zip/zip_reader.h1
-rw-r--r--platform/android/detect.py4
-rw-r--r--platform/android/export/export_plugin.cpp5
-rw-r--r--platform/ios/detect.py6
-rw-r--r--platform/ios/export/export_plugin.cpp5
-rw-r--r--platform/linuxbsd/freedesktop_portal_desktop.cpp350
-rw-r--r--platform/linuxbsd/freedesktop_portal_desktop.h30
-rw-r--r--platform/linuxbsd/joypad_linux.cpp24
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp21
-rw-r--r--platform/linuxbsd/os_linuxbsd.h2
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp40
-rw-r--r--platform/linuxbsd/x11/display_server_x11.h2
-rw-r--r--platform/macos/export/export_plugin.cpp9
-rw-r--r--platform/web/SCsub3
-rw-r--r--platform/web/detect.py11
-rw-r--r--platform/web/export/export_plugin.cpp5
-rw-r--r--scene/3d/navigation_region_3d.cpp53
-rw-r--r--scene/3d/navigation_region_3d.h2
-rw-r--r--scene/animation/animation_blend_tree.cpp2
-rw-r--r--scene/gui/item_list.cpp25
-rw-r--r--scene/gui/menu_bar.cpp2
-rw-r--r--scene/gui/menu_button.cpp2
-rw-r--r--scene/gui/option_button.cpp22
-rw-r--r--scene/gui/option_button.h4
-rw-r--r--scene/gui/popup_menu.compat.inc46
-rw-r--r--scene/gui/popup_menu.cpp28
-rw-r--r--scene/gui/popup_menu.h11
-rw-r--r--scene/gui/tree.cpp5
-rw-r--r--scene/main/node.cpp4
-rw-r--r--scene/main/node.h2
-rw-r--r--scene/main/viewport.cpp11
-rw-r--r--scene/main/viewport.h1
-rw-r--r--scene/resources/default_theme/default_theme.cpp1
-rw-r--r--servers/audio/audio_stream.h4
-rw-r--r--servers/navigation_server_3d.cpp4
-rw-r--r--servers/navigation_server_3d.h5
-rw-r--r--servers/navigation_server_3d_dummy.h5
-rw-r--r--tests/SCsub6
-rw-r--r--tests/core/io/test_image.h6
-rw-r--r--tests/core/string/test_string.h5
-rw-r--r--tests/scene/test_viewport.h2
137 files changed, 1369 insertions, 390 deletions
diff --git a/SConstruct b/SConstruct
index 6968967380..7ea056f839 100644
--- a/SConstruct
+++ b/SConstruct
@@ -193,6 +193,7 @@ opts.Add(BoolVariable("vulkan", "Enable the vulkan rendering driver", True))
opts.Add(BoolVariable("opengl3", "Enable the OpenGL/GLES3 rendering driver", True))
opts.Add(BoolVariable("openxr", "Enable the OpenXR driver", True))
opts.Add(BoolVariable("use_volk", "Use the volk library to load the Vulkan loader dynamically", True))
+opts.Add(BoolVariable("disable_exceptions", "Force disabling exception handling code", True))
opts.Add("custom_modules", "A list of comma-separated directory paths containing custom modules to build.", "")
opts.Add(BoolVariable("custom_modules_recursive", "Detect custom modules recursively for each specified path.", True))
@@ -710,6 +711,16 @@ if selected_platform in platform_list:
)
Exit(255)
+ # Disable exception handling. Godot doesn't use exceptions anywhere, and this
+ # saves around 20% of binary size and very significant build time (GH-80513).
+ if env["disable_exceptions"]:
+ if env.msvc:
+ env.Append(CPPDEFINES=[("_HAS_EXCEPTIONS", 0)])
+ else:
+ env.Append(CCFLAGS=["-fno-exceptions"])
+ elif env.msvc:
+ env.Append(CCFLAGS=["/EHsc"])
+
# Configure compiler warnings
if env.msvc: # MSVC
if env["warnings"] == "no":
@@ -739,9 +750,6 @@ if selected_platform in platform_list:
]
)
- # Set exception handling model to avoid warnings caused by Windows system headers.
- env.Append(CCFLAGS=["/EHsc"])
-
if env["werror"]:
env.Append(CCFLAGS=["/WX"])
else: # GCC, Clang
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp
index 9a735f5aa6..1e4cd81034 100644
--- a/core/extension/gdextension.cpp
+++ b/core/extension/gdextension.cpp
@@ -485,6 +485,13 @@ void GDExtension::close_library() {
ERR_FAIL_COND(library == nullptr);
OS::get_singleton()->close_dynamic_library(library);
+#if defined(TOOLS_ENABLED) && defined(WINDOWS_ENABLED)
+ // Delete temporary copy of library if it exists.
+ if (!temp_lib_path.is_empty() && Engine::get_singleton()->is_editor_hint()) {
+ DirAccess::remove_absolute(temp_lib_path);
+ }
+#endif
+
library = nullptr;
}
@@ -641,6 +648,40 @@ Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String
Ref<GDExtension> lib;
lib.instantiate();
String abs_path = ProjectSettings::get_singleton()->globalize_path(library_path);
+
+#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
+ // If running on the editor on Windows, we copy the library and open the copy.
+ // This is so the original file isn't locked and can be updated by a compiler.
+ if (Engine::get_singleton()->is_editor_hint()) {
+ if (!FileAccess::exists(abs_path)) {
+ if (r_error) {
+ *r_error = ERR_FILE_NOT_FOUND;
+ }
+ ERR_PRINT("GDExtension library not found: " + library_path);
+ return Ref<Resource>();
+ }
+
+ // Copy the file to the same directory as the original with a prefix in the name.
+ // This is so relative path to dependencies are satisfied.
+ String copy_path = abs_path.get_base_dir().path_join("~" + abs_path.get_file());
+
+ Error copy_err = DirAccess::copy_absolute(abs_path, copy_path);
+ if (copy_err) {
+ if (r_error) {
+ *r_error = ERR_CANT_CREATE;
+ }
+ ERR_PRINT("Error copying GDExtension library: " + library_path);
+ return Ref<Resource>();
+ }
+ FileAccess::set_hidden_attribute(copy_path, true);
+
+ // Save the copied path so it can be deleted later.
+ lib->set_temp_library_path(copy_path);
+
+ // Use the copy to open the library.
+ abs_path = copy_path;
+ }
+#endif
err = lib->open_library(abs_path, entry_symbol);
if (r_error) {
diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h
index b935f8706f..5a4dd3d5f5 100644
--- a/core/extension/gdextension.h
+++ b/core/extension/gdextension.h
@@ -43,6 +43,9 @@ class GDExtension : public Resource {
void *library = nullptr; // pointer if valid,
String library_path;
+#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
+ String temp_lib_path;
+#endif
struct Extension {
ObjectGDExtension gdextension;
@@ -76,6 +79,10 @@ public:
Error open_library(const String &p_path, const String &p_entry_symbol);
void close_library();
+#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
+ void set_temp_library_path(const String &p_path) { temp_lib_path = p_path; }
+#endif
+
enum InitializationLevel {
INITIALIZATION_LEVEL_CORE = GDEXTENSION_INITIALIZATION_CORE,
INITIALIZATION_LEVEL_SERVERS = GDEXTENSION_INITIALIZATION_SERVERS,
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 376d0832d4..80ca51573c 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -3635,6 +3635,23 @@ String String::repeat(int p_count) const {
return new_string;
}
+String String::reverse() const {
+ int len = length();
+ if (len <= 1) {
+ return *this;
+ }
+ String new_string;
+ new_string.resize(len + 1);
+
+ const char32_t *src = ptr();
+ char32_t *dst = new_string.ptrw();
+ for (int i = 0; i < len; i++) {
+ dst[i] = src[len - i - 1];
+ }
+ dst[len] = _null;
+ return new_string;
+}
+
String String::left(int p_len) const {
if (p_len < 0) {
p_len = length() + p_len;
diff --git a/core/string/ustring.h b/core/string/ustring.h
index 295625395d..f45392eee1 100644
--- a/core/string/ustring.h
+++ b/core/string/ustring.h
@@ -305,6 +305,7 @@ public:
String replace(const char *p_key, const char *p_with) const;
String replacen(const String &p_key, const String &p_with) const;
String repeat(int p_count) const;
+ String reverse() const;
String insert(int p_at_pos, const String &p_string) const;
String erase(int p_pos, int p_chars = 1) const;
String pad_decimals(int p_digits) const;
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index dad9183216..ccf9b82022 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1659,6 +1659,7 @@ static void _register_variant_builtin_methods() {
bind_string_methodv(replace, static_cast<String (String::*)(const String &, const String &) const>(&String::replace), sarray("what", "forwhat"), varray());
bind_string_method(replacen, sarray("what", "forwhat"), varray());
bind_string_method(repeat, sarray("count"), varray());
+ bind_string_method(reverse, sarray(), varray());
bind_string_method(insert, sarray("position", "what"), varray());
bind_string_method(erase, sarray("position", "chars"), varray(1));
bind_string_method(capitalize, sarray(), varray());
diff --git a/doc/classes/AudioStreamRandomizer.xml b/doc/classes/AudioStreamRandomizer.xml
index 384c316000..12514fe03d 100644
--- a/doc/classes/AudioStreamRandomizer.xml
+++ b/doc/classes/AudioStreamRandomizer.xml
@@ -68,10 +68,10 @@
<member name="playback_mode" type="int" setter="set_playback_mode" getter="get_playback_mode" enum="AudioStreamRandomizer.PlaybackMode" default="0">
Controls how this AudioStreamRandomizer picks which AudioStream to play next.
</member>
- <member name="random_pitch" type="float" setter="set_random_pitch" getter="get_random_pitch" default="1.1">
+ <member name="random_pitch" type="float" setter="set_random_pitch" getter="get_random_pitch" default="1.0">
The intensity of random pitch variation. A value of 1 means no variation.
</member>
- <member name="random_volume_offset_db" type="float" setter="set_random_volume_offset_db" getter="get_random_volume_offset_db" default="5.0">
+ <member name="random_volume_offset_db" type="float" setter="set_random_volume_offset_db" getter="get_random_volume_offset_db" default="0.0">
The intensity of random volume variation. A value of 0 means no variation.
</member>
<member name="streams_count" type="int" setter="set_streams_count" getter="get_streams_count" default="0">
diff --git a/doc/classes/BackBufferCopy.xml b/doc/classes/BackBufferCopy.xml
index e4e6f1d76d..23d6bc3851 100644
--- a/doc/classes/BackBufferCopy.xml
+++ b/doc/classes/BackBufferCopy.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="BackBufferCopy" inherits="Node2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Copies a region of the screen (or the whole screen) to a buffer so it can be accessed in your shader scripts using the screen texture (i.e. a uniform sampler with [code]hint_screen_texture[/code]).
+ A node that copies a region of the screen to a buffer for access in shader code.
</brief_description>
<description>
- Node for back-buffering the currently-displayed screen. The region defined in the [BackBufferCopy] node is buffered with the content of the screen it covers, or the entire screen according to the copy mode set. Use the screen texture in your shader scripts to access the buffer.
+ Node for back-buffering the currently-displayed screen. The region defined in the [BackBufferCopy] node is buffered with the content of the screen it covers, or the entire screen according to the [member copy_mode]. It can be accessed in shader scripts using the screen texture (i.e. a uniform sampler with [code]hint_screen_texture[/code]).
[b]Note:[/b] Since this node inherits from [Node2D] (and not [Control]), anchors and margins won't apply to child [Control]-derived nodes. This can be problematic when resizing the window. To avoid this, add [Control]-derived nodes as [i]siblings[/i] to the [BackBufferCopy] node instead of adding them as children.
</description>
<tutorials>
diff --git a/doc/classes/BaseMaterial3D.xml b/doc/classes/BaseMaterial3D.xml
index 949dcc24d0..1cc2976c81 100644
--- a/doc/classes/BaseMaterial3D.xml
+++ b/doc/classes/BaseMaterial3D.xml
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="BaseMaterial3D" inherits="Material" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Default 3D rendering material.
+ Abstract base class for defining the 3D rendering properties of meshes.
</brief_description>
<description>
- This provides a default material with a wide variety of rendering features and properties without the need to write shader code. See the tutorial below for details.
+ This class serves as a default material with a wide variety of rendering features and properties without the need to write shader code. See the tutorial below for details.
</description>
<tutorials>
- <link title="Standard Material 3D">$DOCS_URL/tutorials/3d/standard_material_3d.html</link>
+ <link title="Standard Material 3D and ORM Material 3D">$DOCS_URL/tutorials/3d/standard_material_3d.html</link>
</tutorials>
<methods>
<method name="get_feature" qualifiers="const">
diff --git a/doc/classes/CPUParticles2D.xml b/doc/classes/CPUParticles2D.xml
index eaacdd590d..051635cb48 100644
--- a/doc/classes/CPUParticles2D.xml
+++ b/doc/classes/CPUParticles2D.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CPUParticles2D" inherits="Node2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- CPU-based 2D particle emitter.
+ A CPU-based 2D particle emitter.
</brief_description>
<description>
CPU-based 2D particle node used to create a variety of particle systems and effects.
diff --git a/doc/classes/CPUParticles3D.xml b/doc/classes/CPUParticles3D.xml
index f03c463049..bd096dc9c6 100644
--- a/doc/classes/CPUParticles3D.xml
+++ b/doc/classes/CPUParticles3D.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CPUParticles3D" inherits="GeometryInstance3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- CPU-based 3D particle emitter.
+ A CPU-based 3D particle emitter.
</brief_description>
<description>
CPU-based 3D particle node used to create a variety of particle systems and effects.
diff --git a/doc/classes/CompressedCubemap.xml b/doc/classes/CompressedCubemap.xml
index e72d727d7d..6ab0cc5d88 100644
--- a/doc/classes/CompressedCubemap.xml
+++ b/doc/classes/CompressedCubemap.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CompressedCubemap" inherits="CompressedTextureLayered" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- 6-sided texture typically used in 3D rendering, optionally compressed.
+ An optionally compressed [Cubemap].
</brief_description>
<description>
A cubemap that is loaded from a [code].ccube[/code] file. This file format is internal to Godot; it is created by importing other image formats with the import system. [CompressedCubemap] can use one of 4 compresson methods:
diff --git a/doc/classes/CompressedCubemapArray.xml b/doc/classes/CompressedCubemapArray.xml
index f5829e4e3e..32687229ed 100644
--- a/doc/classes/CompressedCubemapArray.xml
+++ b/doc/classes/CompressedCubemapArray.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CompressedCubemapArray" inherits="CompressedTextureLayered" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Array of 6-sided textures typically used in 3D rendering, optionally compressed.
+ An optionally compressed [CubemapArray].
</brief_description>
<description>
A cubemap array that is loaded from a [code].ccubearray[/code] file. This file format is internal to Godot; it is created by importing other image formats with the import system. [CompressedCubemapArray] can use one of 4 compresson methods:
diff --git a/doc/classes/Cubemap.xml b/doc/classes/Cubemap.xml
index 0d3b52ddfc..b7da3c4ec6 100644
--- a/doc/classes/Cubemap.xml
+++ b/doc/classes/Cubemap.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Cubemap" inherits="ImageTextureLayered" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- 6-sided texture typically used in 3D rendering.
+ Six square textures representing the faces of a cube. Commonly used as a skybox.
</brief_description>
<description>
A cubemap is made of 6 textures organized in layers. They are typically used for faking reflections in 3D rendering (see [ReflectionProbe]). It can be used to make an object look as if it's reflecting its surroundings. This usually delivers much better performance than other reflection methods.
diff --git a/doc/classes/CubemapArray.xml b/doc/classes/CubemapArray.xml
index ee4ec239f3..7ee497385e 100644
--- a/doc/classes/CubemapArray.xml
+++ b/doc/classes/CubemapArray.xml
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CubemapArray" inherits="ImageTextureLayered" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A single composite texture resource which consists of multiple [Cubemap]s.
+ An array of [Cubemap]s, stored together and with a single reference.
</brief_description>
<description>
- [CubemapArray]s are made of an array of [Cubemap]s. Accordingly, like [Cubemap]s they are made of multiple textures the amount of which must be divisible by 6 (one image for each face of the cube). The primary benefit of [CubemapArray]s is that they can be accessed in shader code using a single texture reference. In other words, you can pass multiple [Cubemap]s into a shader using a single [CubemapArray].
- Generally, [CubemapArray]s provide a more efficient way for storing multiple [Cubemap]s compared to storing multiple [Cubemap]s themselves in an array.
- Internally, Godot uses [CubemapArray]s for many effects including the [Sky], if you set [member ProjectSettings.rendering/reflections/sky_reflections/texture_array_reflections] to [code]true[/code].
- To create such a texture file yourself, reimport your image files using the Godot Editor import presets.
+ [CubemapArray]s are made of an array of [Cubemap]s. Like [Cubemap]s, they are made of multiple textures, the amount of which must be divisible by 6 (one for each face of the cube). The primary benefit of [CubemapArray]s is that they can be accessed in shader code using a single texture reference. In other words, you can pass multiple [Cubemap]s into a shader using a single [CubemapArray].
+ Moreover, [Cubemap]s are allocated in adjacent cache regions on the GPU. This makes [CubemapArray]s the most efficient way to store multiple [Cubemap]s.
+ Internally, Godot uses [CubemapArray]s for many effects, including the [Sky] if you set [member ProjectSettings.rendering/reflections/sky_reflections/texture_array_reflections] to [code]true[/code].
+ To create such a texture file yourself, reimport your image files using the import presets of the File System dock.
[b]Note:[/b] [CubemapArray] is not supported in the OpenGL 3 rendering backend.
</description>
<tutorials>
diff --git a/doc/classes/Curve.xml b/doc/classes/Curve.xml
index 8f5dc4e945..25b06f1063 100644
--- a/doc/classes/Curve.xml
+++ b/doc/classes/Curve.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Curve" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A mathematic curve.
+ A mathematical curve.
</brief_description>
<description>
- A curve that can be saved and re-used for other objects. By default, it ranges between [code]0[/code] and [code]1[/code] on the Y axis and positions points relative to the [code]0.5[/code] Y position.
+ This resource describes a mathematical curve by defining a set of points and tangents at each point. By default, it ranges between [code]0[/code] and [code]1[/code] on the Y axis and positions points relative to the [code]0.5[/code] Y position.
See also [Gradient] which is designed for color interpolation. See also [Curve2D] and [Curve3D].
</description>
<tutorials>
diff --git a/doc/classes/CurveTexture.xml b/doc/classes/CurveTexture.xml
index 4767c18d5a..8cb2384da3 100644
--- a/doc/classes/CurveTexture.xml
+++ b/doc/classes/CurveTexture.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CurveTexture" inherits="Texture2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A texture that shows a curve.
+ A 1D texture where pixel brightness corresponds to points on a curve.
</brief_description>
<description>
- Renders a given [Curve] provided to it. Simplifies the task of drawing curves and/or saving them as image files.
+ A 1D texture where pixel brightness corresponds to points on a [Curve] resource, either in grayscale or in red. This visual representation simplifies the task of saving curves as image files.
If you need to store up to 3 curves within a single texture, use [CurveXYZTexture] instead. See also [GradientTexture1D] and [GradientTexture2D].
</description>
<tutorials>
diff --git a/doc/classes/CurveXYZTexture.xml b/doc/classes/CurveXYZTexture.xml
index e0ab17a35c..8353ed9092 100644
--- a/doc/classes/CurveXYZTexture.xml
+++ b/doc/classes/CurveXYZTexture.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CurveXYZTexture" inherits="Texture2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A texture that shows 3 different curves (stored on the red, green and blue color channels).
+ A 1D texture where the red, green, and blue color channels correspond to points on 3 curves.
</brief_description>
<description>
- Renders 3 given [Curve]s provided to it, on the red, green and blue channels respectively. Compared to using separate [CurveTexture]s, this further simplifies the task of drawing curves and/or saving them as image files.
+ A 1D texture where the red, green, and blue color channels correspond to points on 3 [Curve] resources. Compared to using separate [CurveTexture]s, this further simplifies the task of saving curves as image files.
If you only need to store one curve within a single texture, use [CurveTexture] instead. See also [GradientTexture1D] and [GradientTexture2D].
</description>
<tutorials>
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index 465855cc92..c2f81ba109 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -121,7 +121,9 @@
Displays OS native dialog for selecting files or directories in the file system.
Callbacks have the following arguments: [code]bool status, PackedStringArray selected_paths[/code].
[b]Note:[/b] This method is implemented if the display server has the [code]FEATURE_NATIVE_DIALOG[/code] feature.
- [b]Note:[/b] This method is implemented on Windows and macOS.
+ [b]Note:[/b] This method is implemented on Linux, Windows and macOS.
+ [b]Note:[/b] [param current_directory] might be ignored.
+ [b]Note:[/b] On Linux, [param show_hidden] is ignored.
[b]Note:[/b] On macOS, native file dialogs have no title.
[b]Note:[/b] On macOS, sandboxed apps will save security-scoped bookmarks to retain access to the opened folders across multiple sessions. Use [method OS.get_granted_permissions] to get a list of saved bookmarks.
</description>
diff --git a/doc/classes/FogMaterial.xml b/doc/classes/FogMaterial.xml
index 13366f1813..a6acaaef00 100644
--- a/doc/classes/FogMaterial.xml
+++ b/doc/classes/FogMaterial.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="FogMaterial" inherits="Material" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- [Material] used with a [FogVolume] to draw things with the volumetric fog effect.
+ A material that controls how volumetric fog is rendered, to be assigned to a [FogVolume].
</brief_description>
<description>
A [Material] resource that can be used by [FogVolume]s to draw volumetric effects.
diff --git a/doc/classes/FogVolume.xml b/doc/classes/FogVolume.xml
index 66aeb580c2..538411016e 100644
--- a/doc/classes/FogVolume.xml
+++ b/doc/classes/FogVolume.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="FogVolume" inherits="VisualInstance3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A node used to add local fog with the volumetric fog effect.
+ A region that contributes to the default volumetric fog from the world environment.
</brief_description>
<description>
[FogVolume]s are used to add localized fog into the global volumetric fog effect. [FogVolume]s can also remove volumetric fog from specific areas if using a [FogMaterial] with a negative [member FogMaterial.density].
diff --git a/doc/classes/GPUParticles2D.xml b/doc/classes/GPUParticles2D.xml
index 2928215c3e..d5a4c146e0 100644
--- a/doc/classes/GPUParticles2D.xml
+++ b/doc/classes/GPUParticles2D.xml
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GPUParticles2D" inherits="Node2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- 2D particle emitter.
+ A 2D particle emitter.
</brief_description>
<description>
2D particle node used to create a variety of particle systems and effects. [GPUParticles2D] features an emitter that generates some number of particles at a given rate.
Use the [member process_material] property to add a [ParticleProcessMaterial] to configure particle appearance and behavior. Alternatively, you can add a [ShaderMaterial] which will be applied to all particles.
- 2D particles can optionally collide with [LightOccluder2D] nodes (note: they don't collide with [PhysicsBody2D] nodes).
+ 2D particles can optionally collide with [LightOccluder2D], but they don't collide with [PhysicsBody2D] nodes.
</description>
<tutorials>
<link title="Particle systems (2D)">$DOCS_URL/tutorials/2d/particle_systems_2d.html</link>
diff --git a/doc/classes/GPUParticles3D.xml b/doc/classes/GPUParticles3D.xml
index 4c0b2d12ed..31f1f9e66e 100644
--- a/doc/classes/GPUParticles3D.xml
+++ b/doc/classes/GPUParticles3D.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GPUParticles3D" inherits="GeometryInstance3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- 3D particle emitter.
+ A 3D particle emitter.
</brief_description>
<description>
3D particle node used to create a variety of particle systems and effects. [GPUParticles3D] features an emitter that generates some number of particles at a given rate.
diff --git a/doc/classes/GPUParticlesAttractor3D.xml b/doc/classes/GPUParticlesAttractor3D.xml
index b3ed3b4701..0d219e4bc9 100644
--- a/doc/classes/GPUParticlesAttractor3D.xml
+++ b/doc/classes/GPUParticlesAttractor3D.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GPUParticlesAttractor3D" inherits="VisualInstance3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Abstract class for 3D particle attractors affecting [GPUParticles3D] nodes.
+ Abstract base class for 3D particle attractors.
</brief_description>
<description>
Particle attractors can be used to attract particles towards the attractor's origin, or to push them away from the attractor's origin.
@@ -25,7 +25,7 @@
[b]Note:[/b] If [member directionality] is greater than [code]0.0[/code], the direction in which particles are pushed can be changed by rotating the [GPUParticlesAttractor3D] node.
</member>
<member name="strength" type="float" setter="set_strength" getter="get_strength" default="1.0">
- If [member strength] is negative, particles will be pushed in the reverse direction. Particles will be pushed [i]away[/i] from the attractor's origin if [member directionality] is [code]0.0[/code], or towards local +Z if [member directionality] is greater than [code]0.0[/code].
+ Adjusts the strength of the attractor. If [member strength] is negative, particles will be pushed in the opposite direction. Particles will be pushed [i]away[/i] from the attractor's origin if [member directionality] is [code]0.0[/code], or towards local +Z if [member directionality] is greater than [code]0.0[/code].
</member>
</members>
</class>
diff --git a/doc/classes/GPUParticlesAttractorBox3D.xml b/doc/classes/GPUParticlesAttractorBox3D.xml
index d43efc73bf..410d504428 100644
--- a/doc/classes/GPUParticlesAttractorBox3D.xml
+++ b/doc/classes/GPUParticlesAttractorBox3D.xml
@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GPUParticlesAttractorBox3D" inherits="GPUParticlesAttractor3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Box-shaped 3D particle attractor affecting [GPUParticles3D] nodes.
+ A box-shaped attractor that influences particles from [GPUParticles3D] nodes.
</brief_description>
<description>
- Box-shaped 3D particle attractor affecting [GPUParticles3D] nodes.
+ A box-shaped attractor that influences particles from [GPUParticles3D] nodes. Can be used to attract particles towards its origin, or to push them away from its origin.
+ Particle attractors work in real-time and can be moved, rotated and scaled during gameplay. Unlike collision shapes, non-uniform scaling of attractors is also supported.
[b]Note:[/b] Particle attractors only affect [GPUParticles3D], not [CPUParticles3D].
</description>
<tutorials>
diff --git a/doc/classes/GPUParticlesAttractorSphere3D.xml b/doc/classes/GPUParticlesAttractorSphere3D.xml
index 05c1e60d9a..599d7e4e2a 100644
--- a/doc/classes/GPUParticlesAttractorSphere3D.xml
+++ b/doc/classes/GPUParticlesAttractorSphere3D.xml
@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GPUParticlesAttractorSphere3D" inherits="GPUParticlesAttractor3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Ellipse-shaped 3D particle attractor affecting [GPUParticles3D] nodes.
+ A spheroid-shaped attractor that influences particles from [GPUParticles3D] nodes.
</brief_description>
<description>
- Ellipse-shaped 3D particle attractor affecting [GPUParticles3D] nodes.
+ A spheroid-shaped attractor that influences particles from [GPUParticles3D] nodes. Can be used to attract particles towards its origin, or to push them away from its origin.
+ Particle attractors work in real-time and can be moved, rotated and scaled during gameplay. Unlike collision shapes, non-uniform scaling of attractors is also supported.
[b]Note:[/b] Particle attractors only affect [GPUParticles3D], not [CPUParticles3D].
</description>
<tutorials>
diff --git a/doc/classes/GPUParticlesAttractorVectorField3D.xml b/doc/classes/GPUParticlesAttractorVectorField3D.xml
index ef9a11e57c..7cb5524a1b 100644
--- a/doc/classes/GPUParticlesAttractorVectorField3D.xml
+++ b/doc/classes/GPUParticlesAttractorVectorField3D.xml
@@ -1,11 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GPUParticlesAttractorVectorField3D" inherits="GPUParticlesAttractor3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Box-shaped 3D particle attractor with strength varying within the box, affecting [GPUParticles3D] nodes.
+ A box-shaped attractor with varying directions and strengths defined in it that influences particles from [GPUParticles3D] nodes.
</brief_description>
<description>
- Box-shaped 3D particle attractor with strength varying within the box, affecting [GPUParticles3D] nodes.
+ A box-shaped attractor with varying directions and strengths defined in it that influences particles from [GPUParticles3D] nodes.
Unlike [GPUParticlesAttractorBox3D], [GPUParticlesAttractorVectorField3D] uses a [member texture] to affect attraction strength within the box. This can be used to create complex attraction scenarios where particles travel in different directions depending on their location. This can be useful for weather effects such as sandstorms.
+ Particle attractors work in real-time and can be moved, rotated and scaled during gameplay. Unlike collision shapes, non-uniform scaling of attractors is also supported.
[b]Note:[/b] Particle attractors only affect [GPUParticles3D], not [CPUParticles3D].
</description>
<tutorials>
diff --git a/doc/classes/GPUParticlesCollision3D.xml b/doc/classes/GPUParticlesCollision3D.xml
index 9471a7caf2..089747b7ee 100644
--- a/doc/classes/GPUParticlesCollision3D.xml
+++ b/doc/classes/GPUParticlesCollision3D.xml
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GPUParticlesCollision3D" inherits="VisualInstance3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Abstract class for 3D particle collision shapes affecting [GPUParticles3D] nodes.
+ Abstract base class for 3D particle collision shapes affecting [GPUParticles3D] nodes.
</brief_description>
<description>
Particle collision shapes can be used to make particles stop or bounce against them.
- Particle collision shapes in real-time and can be moved, rotated and scaled during gameplay. Unlike attractors, non-uniform scaling of collision shapes is [i]not[/i] supported.
+ Particle collision shapes work in real-time and can be moved, rotated and scaled during gameplay. Unlike attractors, non-uniform scaling of collision shapes is [i]not[/i] supported.
Particle collision shapes can be temporarily disabled by hiding them.
[b]Note:[/b] [member ParticleProcessMaterial.collision_mode] must be [constant ParticleProcessMaterial.COLLISION_RIGID] or [constant ParticleProcessMaterial.COLLISION_HIDE_ON_CONTACT] on the [GPUParticles3D]'s process material for collision to work.
[b]Note:[/b] Particle collision only affects [GPUParticles3D], not [CPUParticles3D].
diff --git a/doc/classes/GPUParticlesCollisionBox3D.xml b/doc/classes/GPUParticlesCollisionBox3D.xml
index 4bb947a82d..fc225e460f 100644
--- a/doc/classes/GPUParticlesCollisionBox3D.xml
+++ b/doc/classes/GPUParticlesCollisionBox3D.xml
@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GPUParticlesCollisionBox3D" inherits="GPUParticlesCollision3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Box-shaped 3D particle collision shape affecting [GPUParticles3D] nodes.
+ A box-shaped 3D particle collision shape affecting [GPUParticles3D] nodes.
</brief_description>
<description>
- Box-shaped 3D particle collision shape affecting [GPUParticles3D] nodes.
+ A box-shaped 3D particle collision shape affecting [GPUParticles3D] nodes.
+ Particle collision shapes work in real-time and can be moved, rotated and scaled during gameplay. Unlike attractors, non-uniform scaling of collision shapes is [i]not[/i] supported.
[b]Note:[/b] [member ParticleProcessMaterial.collision_mode] must be [constant ParticleProcessMaterial.COLLISION_RIGID] or [constant ParticleProcessMaterial.COLLISION_HIDE_ON_CONTACT] on the [GPUParticles3D]'s process material for collision to work.
[b]Note:[/b] Particle collision only affects [GPUParticles3D], not [CPUParticles3D].
</description>
diff --git a/doc/classes/GPUParticlesCollisionHeightField3D.xml b/doc/classes/GPUParticlesCollisionHeightField3D.xml
index 58219aa0b3..0b9a94af63 100644
--- a/doc/classes/GPUParticlesCollisionHeightField3D.xml
+++ b/doc/classes/GPUParticlesCollisionHeightField3D.xml
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GPUParticlesCollisionHeightField3D" inherits="GPUParticlesCollision3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Real-time heightmap-shaped 3D particle attractor affecting [GPUParticles3D] nodes.
+ A real-time heightmap-shaped 3D particle collision shape affecting [GPUParticles3D] nodes.
</brief_description>
<description>
- Real-time heightmap-shaped 3D particle attractor affecting [GPUParticles3D] nodes.
+ A real-time heightmap-shaped 3D particle collision shape affecting [GPUParticles3D] nodes.
Heightmap shapes allow for efficiently representing collisions for convex and concave objects with a single "floor" (such as terrain). This is less flexible than [GPUParticlesCollisionSDF3D], but it doesn't require a baking step.
- [GPUParticlesCollisionHeightField3D] can also be regenerated in real-time when it is moved, when the camera moves, or even continuously. This makes [GPUParticlesCollisionHeightField3D] a good choice for weather effects such as rain and snow and games with highly dynamic geometry. However, since heightmaps cannot represent overhangs, [GPUParticlesCollisionHeightField3D] is not suited for indoor particle collision.
+ [GPUParticlesCollisionHeightField3D] can also be regenerated in real-time when it is moved, when the camera moves, or even continuously. This makes [GPUParticlesCollisionHeightField3D] a good choice for weather effects such as rain and snow and games with highly dynamic geometry. However, this class is limited since heightmaps cannot represent overhangs (e.g. indoors or caves).
[b]Note:[/b] [member ParticleProcessMaterial.collision_mode] must be [code]true[/code] on the [GPUParticles3D]'s process material for collision to work.
[b]Note:[/b] Particle collision only affects [GPUParticles3D], not [CPUParticles3D].
</description>
diff --git a/doc/classes/GPUParticlesCollisionSDF3D.xml b/doc/classes/GPUParticlesCollisionSDF3D.xml
index b61be49619..11978e0737 100644
--- a/doc/classes/GPUParticlesCollisionSDF3D.xml
+++ b/doc/classes/GPUParticlesCollisionSDF3D.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GPUParticlesCollisionSDF3D" inherits="GPUParticlesCollision3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Baked signed distance field 3D particle attractor affecting [GPUParticles3D] nodes.
+ A baked signed distance field 3D particle collision shape affecting [GPUParticles3D] nodes.
</brief_description>
<description>
- Baked signed distance field 3D particle attractor affecting [GPUParticles3D] nodes.
+ A baked signed distance field 3D particle collision shape affecting [GPUParticles3D] nodes.
Signed distance fields (SDF) allow for efficiently representing approximate collision shapes for convex and concave objects of any shape. This is more flexible than [GPUParticlesCollisionHeightField3D], but it requires a baking step.
[b]Baking:[/b] The signed distance field texture can be baked by selecting the [GPUParticlesCollisionSDF3D] node in the editor, then clicking [b]Bake SDF[/b] at the top of the 3D viewport. Any [i]visible[/i] [MeshInstance3D]s within the [member size] will be taken into account for baking, regardless of their [member GeometryInstance3D.gi_mode].
[b]Note:[/b] Baking a [GPUParticlesCollisionSDF3D]'s [member texture] is only possible within the editor, as there is no bake method exposed for use in exported projects. However, it's still possible to load pre-baked [Texture3D]s into its [member texture] property in an exported project.
diff --git a/doc/classes/GPUParticlesCollisionSphere3D.xml b/doc/classes/GPUParticlesCollisionSphere3D.xml
index cade0eaeef..fe2fe3d414 100644
--- a/doc/classes/GPUParticlesCollisionSphere3D.xml
+++ b/doc/classes/GPUParticlesCollisionSphere3D.xml
@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GPUParticlesCollisionSphere3D" inherits="GPUParticlesCollision3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Sphere-shaped 3D particle collision shape affecting [GPUParticles3D] nodes.
+ A sphere-shaped 3D particle collision shape affecting [GPUParticles3D] nodes.
</brief_description>
<description>
- Sphere-shaped 3D particle collision shape affecting [GPUParticles3D] nodes.
+ A sphere-shaped 3D particle collision shape affecting [GPUParticles3D] nodes.
+ Particle collision shapes work in real-time and can be moved, rotated and scaled during gameplay. Unlike attractors, non-uniform scaling of collision shapes is [i]not[/i] supported.
[b]Note:[/b] [member ParticleProcessMaterial.collision_mode] must be [constant ParticleProcessMaterial.COLLISION_RIGID] or [constant ParticleProcessMaterial.COLLISION_HIDE_ON_CONTACT] on the [GPUParticles3D]'s process material for collision to work.
[b]Note:[/b] Particle collision only affects [GPUParticles3D], not [CPUParticles3D].
</description>
diff --git a/doc/classes/Gradient.xml b/doc/classes/Gradient.xml
index f2b894d612..c7fa1f40e0 100644
--- a/doc/classes/Gradient.xml
+++ b/doc/classes/Gradient.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Gradient" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A color interpolator resource which can be used to generate colors between user-defined color points.
+ A color transition.
</brief_description>
<description>
- Given a set of colors, this resource will interpolate them in order. This means that if you have color 1, color 2 and color 3, the gradient will interpolate from color 1 to color 2 and from color 2 to color 3. The gradient will initially have 2 colors (black and white), one (black) at gradient lower offset 0 and the other (white) at the gradient higher offset 1.
+ This resource describes a color transition by defining a set of colored points and how to interpolate between them.
See also [Curve] which supports more complex easing methods, but does not support colors.
</description>
<tutorials>
@@ -15,7 +15,7 @@
<param index="0" name="offset" type="float" />
<param index="1" name="color" type="Color" />
<description>
- Adds the specified color to the end of the gradient, with the specified offset.
+ Adds the specified color to the gradient, with the specified offset.
</description>
</method>
<method name="get_color">
@@ -42,7 +42,7 @@
<return type="void" />
<param index="0" name="point" type="int" />
<description>
- Removes the color at the index [param point].
+ Removes the color at index [param point].
</description>
</method>
<method name="reverse">
diff --git a/doc/classes/GradientTexture1D.xml b/doc/classes/GradientTexture1D.xml
index 7207de57fd..4e6b76c0a6 100644
--- a/doc/classes/GradientTexture1D.xml
+++ b/doc/classes/GradientTexture1D.xml
@@ -1,16 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GradientTexture1D" inherits="Texture2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Gradient-filled texture.
+ A 1D texture that uses colors obtained from a [Gradient].
</brief_description>
<description>
- GradientTexture1D uses a [Gradient] to fill the texture data. The gradient will be filled from left to right using colors obtained from the gradient. This means the texture does not necessarily represent an exact copy of the gradient, but instead an interpolation of samples obtained from the gradient at fixed steps (see [member width]). See also [GradientTexture2D], [CurveTexture] and [CurveXYZTexture].
+ A 1D texture that obtains colors from a [Gradient] to fill the texture data. The texture is filled by sampling the gradient for each pixel. Therefore, the texture does not necessarily represent an exact copy of the gradient, as it may miss some colors if there are not enough pixels. See also [GradientTexture2D], [CurveTexture] and [CurveXYZTexture].
</description>
<tutorials>
</tutorials>
<members>
<member name="gradient" type="Gradient" setter="set_gradient" getter="get_gradient">
- The [Gradient] that will be used to fill the texture.
+ The [Gradient] used to fill the texture.
</member>
<member name="resource_local_to_scene" type="bool" setter="set_local_to_scene" getter="is_local_to_scene" overrides="Resource" default="false" />
<member name="use_hdr" type="bool" setter="set_use_hdr" getter="is_using_hdr" default="false">
diff --git a/doc/classes/GradientTexture2D.xml b/doc/classes/GradientTexture2D.xml
index 63e511173a..f6588ef791 100644
--- a/doc/classes/GradientTexture2D.xml
+++ b/doc/classes/GradientTexture2D.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GradientTexture2D" inherits="Texture2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Gradient-filled 2D texture.
+ A 2D texture that creates a pattern with colors obtained from a [Gradient].
</brief_description>
<description>
- The texture uses a [Gradient] to fill the texture data in 2D space. The gradient is filled according to the specified [member fill] and [member repeat] types using colors obtained from the gradient. The texture does not necessarily represent an exact copy of the gradient, but instead an interpolation of samples obtained from the gradient at fixed steps (see [member width] and [member height]). See also [GradientTexture1D], [CurveTexture] and [CurveXYZTexture].
+ A 2D texture that obtains colors from a [Gradient] to fill the texture data. This texture is able to transform a color transition into different patterns such as a linear or a radial gradient. The gradient is sampled individually for each pixel so it does not necessarily represent an exact copy of the gradient(see [member width] and [member height]). See also [GradientTexture1D], [CurveTexture] and [CurveXYZTexture].
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/Material.xml b/doc/classes/Material.xml
index 505a1465bb..1118461445 100644
--- a/doc/classes/Material.xml
+++ b/doc/classes/Material.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Material" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Abstract base [Resource] for coloring and shading geometry.
+ Abstract base class for applying visual properties to an object, such as color and roughness.
</brief_description>
<description>
- Material is a base [Resource] used for coloring and shading geometry. All materials inherit from it and almost all [VisualInstance3D] derived nodes carry a Material. A few flags and parameters are shared between all material types and are configured here.
+ [Material] is a base resource used for coloring and shading geometry. All materials inherit from it and almost all [VisualInstance3D] derived nodes carry a [Material]. A few flags and parameters are shared between all material types and are configured here.
</description>
<tutorials>
<link title="3D Material Testers Demo">https://godotengine.org/asset-library/asset/123</link>
diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml
index 86c605b8a3..b56b86f435 100644
--- a/doc/classes/NavigationServer3D.xml
+++ b/doc/classes/NavigationServer3D.xml
@@ -216,6 +216,15 @@
Bakes the provided [param navigation_mesh] with the data from the provided [param source_geometry_data]. After the process is finished the optional [param callback] will be called.
</description>
</method>
+ <method name="bake_from_source_geometry_data_async">
+ <return type="void" />
+ <param index="0" name="navigation_mesh" type="NavigationMesh" />
+ <param index="1" name="source_geometry_data" type="NavigationMeshSourceGeometryData3D" />
+ <param index="2" name="callback" type="Callable" default="Callable()" />
+ <description>
+ Bakes the provided [param navigation_mesh] with the data from the provided [param source_geometry_data] as an async task running on a background thread. After the process is finished the optional [param callback] will be called.
+ </description>
+ </method>
<method name="free_rid">
<return type="void" />
<param index="0" name="rid" type="RID" />
diff --git a/doc/classes/ORMMaterial3D.xml b/doc/classes/ORMMaterial3D.xml
index d134107618..72620573da 100644
--- a/doc/classes/ORMMaterial3D.xml
+++ b/doc/classes/ORMMaterial3D.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ORMMaterial3D" inherits="BaseMaterial3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Physically based rendering (PBR) material that can be applied to 3D objects, can use an ORM texture.
+ A PBR (Physically Based Rendering) material to be used on 3D objects. Uses an ORM texture.
</brief_description>
<description>
ORMMaterial3D's properties are inherited from [BaseMaterial3D]. Unlike [StandardMaterial3D], ORMMaterial3D uses a single texture for ambient occlusion, roughness and metallic maps, known as an ORM texture.
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index 03169d390a..d4a6b57c58 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -538,7 +538,7 @@
<return type="bool" />
<description>
Returns [code]true[/code] if application is running in the sandbox.
- [b]Note:[/b] This method is implemented on macOS.
+ [b]Note:[/b] This method is implemented on macOS and Linux.
</description>
</method>
<method name="is_stdout_verbose" qualifiers="const">
diff --git a/doc/classes/OptionButton.xml b/doc/classes/OptionButton.xml
index 7737754fc6..d2302fc27f 100644
--- a/doc/classes/OptionButton.xml
+++ b/doc/classes/OptionButton.xml
@@ -148,6 +148,13 @@
Passing [code]-1[/code] as the index deselects any currently selected item.
</description>
</method>
+ <method name="set_disable_shortcuts">
+ <return type="void" />
+ <param index="0" name="disabled" type="bool" />
+ <description>
+ If [code]true[/code], shortcuts are disabled and cannot be used to trigger the button.
+ </description>
+ </method>
<method name="set_item_disabled">
<return type="void" />
<param index="0" name="idx" type="int" />
diff --git a/doc/classes/PanoramaSkyMaterial.xml b/doc/classes/PanoramaSkyMaterial.xml
index ab32ca48dd..0d041b9b90 100644
--- a/doc/classes/PanoramaSkyMaterial.xml
+++ b/doc/classes/PanoramaSkyMaterial.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="PanoramaSkyMaterial" inherits="Material" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A [Material] used with [Sky] to draw a background texture.
+ A material that provides a special texture to a [Sky], usually an HDR panorama.
</brief_description>
<description>
- A resource referenced in a [Sky] that is used to draw a background. The Panorama sky material functions similar to skyboxes in other engines, except it uses an equirectangular sky map instead of a cubemap.
+ A resource referenced in a [Sky] that is used to draw a background. [PanoramaSkyMaterial] functions similar to skyboxes in other engines, except it uses an equirectangular sky map instead of a [Cubemap].
Using an HDR panorama is strongly recommended for accurate, high-quality reflections. Godot supports the Radiance HDR ([code].hdr[/code]) and OpenEXR ([code].exr[/code]) image formats for this purpose.
You can use [url=https://danilw.github.io/GLSL-howto/cubemap_to_panorama_js/cubemap_to_panorama.html]this tool[/url] to convert a cubemap to an equirectangular sky map.
</description>
diff --git a/doc/classes/ParticleProcessMaterial.xml b/doc/classes/ParticleProcessMaterial.xml
index 69d3351733..355fec3713 100644
--- a/doc/classes/ParticleProcessMaterial.xml
+++ b/doc/classes/ParticleProcessMaterial.xml
@@ -1,12 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ParticleProcessMaterial" inherits="Material" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Particle properties for [GPUParticles3D] and [GPUParticles2D] nodes.
+ Holds a particle configuration for [GPUParticles2D] or [GPUParticles3D] nodes.
</brief_description>
<description>
- ParticleProcessMaterial defines particle properties and behavior. It is used in the [code]process_material[/code] of [GPUParticles3D] and [GPUParticles2D] emitter nodes.
- Some of this material's properties are applied to each particle when emitted, while others can have a [CurveTexture] applied to vary values over the lifetime of the particle.
- Particle animation is available only in [GPUParticles2D]. To use it, attach a [CanvasItemMaterial], with [member CanvasItemMaterial.particles_animation] enabled, to the particles node.
+ [ParticleProcessMaterial] defines particle properties and behavior. It is used in the [code]process_material[/code] of the [GPUParticles2D] and [GPUParticles3D] nodes. Some of this material's properties are applied to each particle when emitted, while others can have a [CurveTexture] or a [GradientTexture1D] applied to vary numerical or color values over the lifetime of the particle.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/PhysicalSkyMaterial.xml b/doc/classes/PhysicalSkyMaterial.xml
index e6a9980e19..13f9d93e66 100644
--- a/doc/classes/PhysicalSkyMaterial.xml
+++ b/doc/classes/PhysicalSkyMaterial.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="PhysicalSkyMaterial" inherits="Material" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- [Sky] [Material] used for a physically based sky.
+ A material that defines a sky for a [Sky] resource by a set of physical properties.
</brief_description>
<description>
The [PhysicalSkyMaterial] uses the Preetham analytic daylight model to draw a sky based on physical properties. This results in a substantially more realistic sky than the [ProceduralSkyMaterial], but it is slightly slower and less flexible.
diff --git a/doc/classes/PlaceholderCubemap.xml b/doc/classes/PlaceholderCubemap.xml
index b760bf359b..9cd0c26926 100644
--- a/doc/classes/PlaceholderCubemap.xml
+++ b/doc/classes/PlaceholderCubemap.xml
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="PlaceholderCubemap" inherits="PlaceholderTextureLayered" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Placeholder class for a cubemap texture.
+ A [Cubemap] without image data.
</brief_description>
<description>
- This class is used when loading a project that uses a [Cubemap] subclass in 2 conditions:
- - When running the project exported in dedicated server mode, only the texture's dimensions are kept (as they may be relied upon for gameplay purposes or positioning of other elements). This allows reducing the exported PCK's size significantly.
- - When this subclass is missing due to using a different engine version or build (e.g. modules disabled).
- [b]Note:[/b] This is not intended to be used as an actual texture for rendering. It is not guaranteed to work like one in shaders or materials (for example when calculating UV).
+ This class replaces a [Cubemap] or a [Cubemap]-derived class in 2 conditions:
+ - In dedicated server mode, where the image data shouldn't affect game logic. This allows reducing the exported PCK's size significantly.
+ - When the [Cubemap]-derived class is missing, for example when using a different engine version.
+ [b]Note:[/b] This class is not intended for rendering or for use in shaders. Operations like calculating UV are not guaranteed to work.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/PlaceholderCubemapArray.xml b/doc/classes/PlaceholderCubemapArray.xml
index 1074824c0f..9ffbc316a8 100644
--- a/doc/classes/PlaceholderCubemapArray.xml
+++ b/doc/classes/PlaceholderCubemapArray.xml
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="PlaceholderCubemapArray" inherits="PlaceholderTextureLayered" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Placeholder class for a cubemap texture array.
+ A [CubemapArray] without image data.
</brief_description>
<description>
- This class is used when loading a project that uses a [CubemapArray] subclass in 2 conditions:
- - When running the project exported in dedicated server mode, only the texture's dimensions are kept (as they may be relied upon for gameplay purposes or positioning of other elements). This allows reducing the exported PCK's size significantly.
- - When this subclass is missing due to using a different engine version or build (e.g. modules disabled).
- [b]Note:[/b] This is not intended to be used as an actual texture for rendering. It is not guaranteed to work like one in shaders or materials (for example when calculating UV).
+ This class replaces a [CubemapArray] or a [CubemapArray]-derived class in 2 conditions:
+ - In dedicated server mode, where the image data shouldn't affect game logic. This allows reducing the exported PCK's size significantly.
+ - When the [CubemapArray]-derived class is missing, for example when using a different engine version.
+ [b]Note:[/b] This class is not intended for rendering or for use in shaders. Operations like calculating UV are not guaranteed to work.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml
index daf3163842..b72e65e63a 100644
--- a/doc/classes/PopupMenu.xml
+++ b/doc/classes/PopupMenu.xml
@@ -96,9 +96,11 @@
<param index="1" name="shortcut" type="Shortcut" />
<param index="2" name="id" type="int" default="-1" />
<param index="3" name="global" type="bool" default="false" />
+ <param index="4" name="allow_echo" type="bool" default="false" />
<description>
Adds a new item and assigns the specified [Shortcut] and icon [param texture] to it. Sets the label of the checkbox to the [Shortcut]'s name.
An [param id] can optionally be provided. If no [param id] is provided, one will be created from the index.
+ If [param allow_echo] is [code]true[/code], the shortcut can be activated with echo events.
</description>
</method>
<method name="add_item">
@@ -161,9 +163,11 @@
<param index="0" name="shortcut" type="Shortcut" />
<param index="1" name="id" type="int" default="-1" />
<param index="2" name="global" type="bool" default="false" />
+ <param index="3" name="allow_echo" type="bool" default="false" />
<description>
Adds a [Shortcut].
An [param id] can optionally be provided. If no [param id] is provided, one will be created from the index.
+ If [param allow_echo] is [code]true[/code], the shortcut can be activated with echo events.
</description>
</method>
<method name="add_submenu_item">
diff --git a/doc/classes/ProceduralSkyMaterial.xml b/doc/classes/ProceduralSkyMaterial.xml
index e3a1177cfa..54bffd009f 100644
--- a/doc/classes/ProceduralSkyMaterial.xml
+++ b/doc/classes/ProceduralSkyMaterial.xml
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ProceduralSkyMaterial" inherits="Material" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A [Material] used with [Sky] to generate a background based on user input parameters.
+ A material that defines a simple sky for a [Sky] resource.
</brief_description>
<description>
- ProceduralSkyMaterial provides a way to create an effective background quickly by defining procedural parameters for the sun, the sky and the ground. The sky and ground are very similar, they are defined by a color at the horizon, another color, and finally an easing curve to interpolate between these two colors. Similarly, the sun is described by a position in the sky, a color, and an easing curve. However, the sun also defines a minimum and maximum angle, these two values define at what distance the easing curve begins and ends from the sun, and thus end up defining the size of the sun in the sky.
- The [ProceduralSkyMaterial] uses a lightweight shader to draw the sky and is thus suited for real time updates. When you do not need a quick sky that is not realistic, this is a good option. If you need a more realistic option, try using [PhysicalSkyMaterial] instead.
- The [ProceduralSkyMaterial] supports up to 4 suns. Each sun takes its color, energy, and direction from the corresponding [DirectionalLight3D] in the scene.
+ [ProceduralSkyMaterial] provides a way to create an effective background quickly by defining procedural parameters for the sun, the sky and the ground. The sky and ground are defined by a main color, a color at the horizon, and an easing curve to interpolate between them. Suns are described by a position in the sky, a color, and a max angle from the sun at which the easing curve ends. The max angle therefore defines the size of the sun in the sky.
+ [ProceduralSkyMaterial] supports up to 4 suns, using the color, and energy, direction, and angular distance of the first four [DirectionalLight3D] nodes in the scene. This means that the suns are defined individually by the properties of their corresponding [DirectionalLight3D]s and globally by [member sun_angle_max] and [member sun_curve].
+ [ProceduralSkyMaterial] uses a lightweight shader to draw the sky and is therefore suited for real time updates. This makes it a great option for a sky that is simple and computationally cheap, but unrealistic. If you need a more realistic procedural option, use [PhysicalSkyMaterial].
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 1be69052e4..4407176fd2 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -1995,6 +1995,12 @@
<member name="navigation/avoidance/thread_model/avoidance_use_multiple_threads" type="bool" setter="" getter="" default="true">
If enabled the avoidance calculations use multiple threads.
</member>
+ <member name="navigation/baking/thread_model/baking_use_high_priority_threads" type="bool" setter="" getter="" default="true">
+ If enabled and async navmesh baking uses multiple threads the threads run with high priority.
+ </member>
+ <member name="navigation/baking/thread_model/baking_use_multiple_threads" type="bool" setter="" getter="" default="true">
+ If enabled the async navmesh baking uses multiple threads.
+ </member>
<member name="network/limits/debugger/max_chars_per_second" type="int" setter="" getter="" default="32768">
Maximum number of characters allowed to send as output from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection.
</member>
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index bb4a0f8a16..d972a2214a 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -1450,6 +1450,7 @@
<description>
Returns the name of the video adapter (e.g. "GeForce GTX 1080/PCIe/SSE2").
[b]Note:[/b] When running a headless or server binary, this function returns an empty string.
+ [b]Note:[/b] On the web platform, some browsers such as Firefox may report a different, fixed GPU name such as "GeForce GTX 980" (regardless of the user's actual GPU model). This is done to make fingerprinting more difficult.
</description>
</method>
<method name="get_video_adapter_type" qualifiers="const">
diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml
index 7aed63a2fc..fcc5925f8d 100644
--- a/doc/classes/SceneTree.xml
+++ b/doc/classes/SceneTree.xml
@@ -137,6 +137,7 @@
<param index="0" name="name" type="StringName" />
<description>
Returns [code]true[/code] if the given group exists.
+ A group exists if any [Node] in the tree belongs to it (see [method Node.add_to_group]). Groups without nodes are removed automatically.
</description>
</method>
<method name="notify_group">
diff --git a/doc/classes/ShaderGlobalsOverride.xml b/doc/classes/ShaderGlobalsOverride.xml
index afd6ee527e..c5fe2a3e8a 100644
--- a/doc/classes/ShaderGlobalsOverride.xml
+++ b/doc/classes/ShaderGlobalsOverride.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ShaderGlobalsOverride" inherits="Node" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Overrides global shader parameters' values in a specific scene.
+ A node used to override global shader parameters' values in a scene.
</brief_description>
<description>
Similar to how a [WorldEnvironment] node can be used to override the environment while a specific scene is loaded, [ShaderGlobalsOverride] can be used to override global shader parameters temporarily. Once the node is removed, the project-wide values for the global shader parameters are restored. See the [RenderingServer] [code]global_shader_parameter_*[/code] methods for more information.
@@ -9,5 +9,6 @@
[b]Note:[/b] All [ShaderGlobalsOverride] nodes are made part of a [code]"shader_overrides_group"[/code] group when they are added to the scene tree. The currently active [ShaderGlobalsOverride] node also has a [code]"shader_overrides_group_active"[/code] group added to it. You can use this to check which [ShaderGlobalsOverride] node is currently active.
</description>
<tutorials>
+ <link title="Shading language">$DOCS_URL/tutorials/shaders/shader_reference/shading_language.html</link>
</tutorials>
</class>
diff --git a/doc/classes/ShaderMaterial.xml b/doc/classes/ShaderMaterial.xml
index e2d0696a4b..d0752b4b25 100644
--- a/doc/classes/ShaderMaterial.xml
+++ b/doc/classes/ShaderMaterial.xml
@@ -1,11 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ShaderMaterial" inherits="Material" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A material that uses a custom [Shader] program.
+ A material defined by a custom [Shader] program and the values of its shader parameters.
</brief_description>
<description>
- A material that uses a custom [Shader] program to render either items to screen or process particles. You can create multiple materials for the same shader but configure different values for the uniforms defined in the shader.
- [b]Note:[/b] For performance reasons the [signal Resource.changed] signal is only emitted when the [member Resource.resource_name] is changed. Only in editor, is also emitted for [member shader] changes.
+ A material that uses a custom [Shader] program to render visual items (canvas items, meshes, skies, fog), or to process particles. Compared to other materials, [ShaderMaterial] gives deeper control over the generated shader code. For more information, see the shaders documentation index below.
+ Multiple [ShaderMaterial]s can use the same shader and configure different values for the shader uniforms.
+ [b]Note:[/b] For performance reasons, the [signal Resource.changed] signal is only emitted when the [member Resource.resource_name] changes. Only in editor, it is also emitted for [member shader] changes.
</description>
<tutorials>
<link title="Shaders documentation index">$DOCS_URL/tutorials/shaders/index.html</link>
diff --git a/doc/classes/Sky.xml b/doc/classes/Sky.xml
index d52fd6ce40..d92319b390 100644
--- a/doc/classes/Sky.xml
+++ b/doc/classes/Sky.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Sky" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Background that uses a [Material] to draw a sky.
+ Defines a 3D environment's background by using a [Material].
</brief_description>
<description>
- The [Sky] class uses a [Material] to draw the background and update the reflection/radiance cubemaps.
+ The [Sky] class uses a [Material] to render a 3D environment's background and the light it emits by updating the reflection/radiance cubemaps.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/StandardMaterial3D.xml b/doc/classes/StandardMaterial3D.xml
index 8814169d1d..55247678cb 100644
--- a/doc/classes/StandardMaterial3D.xml
+++ b/doc/classes/StandardMaterial3D.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="StandardMaterial3D" inherits="BaseMaterial3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Physically based rendering (PBR) material that can be applied to 3D objects.
+ A PBR (Physically Based Rendering) material to be used on 3D objects.
</brief_description>
<description>
[StandardMaterial3D]'s properties are inherited from [BaseMaterial3D]. [StandardMaterial3D] uses separate textures for ambient occlusion, roughness and metallic maps. To use a single ORM map for all 3 textures, use an [ORMMaterial3D] instead.
diff --git a/doc/classes/String.xml b/doc/classes/String.xml
index 913f2f2654..9c31576b72 100644
--- a/doc/classes/String.xml
+++ b/doc/classes/String.xml
@@ -720,6 +720,12 @@
Replaces all [b]case-insensitive[/b] occurrences of [param what] inside the string with the given [param forwhat].
</description>
</method>
+ <method name="reverse" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns the copy of this string in reverse order.
+ </description>
+ </method>
<method name="rfind" qualifiers="const">
<return type="int" />
<param index="0" name="what" type="String" />
diff --git a/doc/classes/StringName.xml b/doc/classes/StringName.xml
index 0814a30a8d..1c5032be9b 100644
--- a/doc/classes/StringName.xml
+++ b/doc/classes/StringName.xml
@@ -627,6 +627,12 @@
Replaces all [b]case-insensitive[/b] occurrences of [param what] inside the string with the given [param forwhat].
</description>
</method>
+ <method name="reverse" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns the copy of this string in reverse order.
+ </description>
+ </method>
<method name="rfind" qualifiers="const">
<return type="int" />
<param index="0" name="what" type="String" />
diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml
index a1932ba5b6..004e93ac04 100644
--- a/doc/classes/Tree.xml
+++ b/doc/classes/Tree.xml
@@ -606,6 +606,9 @@
<theme_item name="font_size" data_type="font_size" type="int">
Font size of the item's text.
</theme_item>
+ <theme_item name="title_button_font_size" data_type="font_size" type="int">
+ Font size of the title button's text.
+ </theme_item>
<theme_item name="arrow" data_type="icon" type="Texture2D">
The arrow icon used when a foldable item is not collapsed.
</theme_item>
diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml
index 98836db157..101717966e 100644
--- a/doc/classes/Viewport.xml
+++ b/doc/classes/Viewport.xml
@@ -52,6 +52,13 @@
Returns an individual bit on the rendering layer mask.
</description>
</method>
+ <method name="get_embedded_subwindows" qualifiers="const">
+ <return type="Window[]" />
+ <description>
+ Returns a list of the visible embedded [Window]s inside the viewport.
+ [b]Note:[/b] [Window]s inside other viewports will not be listed.
+ </description>
+ </method>
<method name="get_final_transform" qualifiers="const">
<return type="Transform2D" />
<description>
diff --git a/doc/classes/VisualShader.xml b/doc/classes/VisualShader.xml
index bf48d80c66..f424a27a8f 100644
--- a/doc/classes/VisualShader.xml
+++ b/doc/classes/VisualShader.xml
@@ -4,10 +4,10 @@
A custom shader program with a visual editor.
</brief_description>
<description>
- This class allows you to define a custom shader program that can be used for various materials to render objects.
- The visual shader editor creates the shader.
+ This class provides a graph-like visual editor for creating a [Shader]. Although [VisualShader]s do not require coding, they share the same logic with script shaders. They use [VisualShaderNode]s that can be connected to each other to control the flow of the shader. The visual shader graph is converted to a script shader behind the scenes.
</description>
<tutorials>
+ <link title="Using VisualShaders">$DOCS_URL/tutorials/shaders/visual_shaders.html</link>
</tutorials>
<methods>
<method name="add_node">
diff --git a/doc/classes/VisualShaderNode.xml b/doc/classes/VisualShaderNode.xml
index 5d147bd542..baf9eded56 100644
--- a/doc/classes/VisualShaderNode.xml
+++ b/doc/classes/VisualShaderNode.xml
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNode" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Base class for nodes in a visual shader graph.
+ Base class for [VisualShader] nodes. Not related to scene nodes.
</brief_description>
<description>
- Visual shader graphs consist of various nodes. Each node in the graph is a separate object and they are represented as a rectangular boxes with title and a set of properties. Each node has also connection ports that allow to connect it to another nodes and control the flow of the shader.
+ Visual shader graphs consist of various nodes. Each node in the graph is a separate object and they are represented as a rectangular boxes with title and a set of properties. Each node also has connection ports that allow to connect it to another nodes and control the flow of the shader.
</description>
<tutorials>
- <link title="VisualShaders">$DOCS_URL/tutorials/shaders/visual_shaders.html</link>
+ <link title="Using VisualShaders">$DOCS_URL/tutorials/shaders/visual_shaders.html</link>
</tutorials>
<methods>
<method name="clear_default_input_values">
diff --git a/doc/classes/VisualShaderNodeIf.xml b/doc/classes/VisualShaderNodeIf.xml
index 5fc9a0cdf4..82799bb5fd 100644
--- a/doc/classes/VisualShaderNodeIf.xml
+++ b/doc/classes/VisualShaderNodeIf.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeIf" inherits="VisualShaderNode" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Compares two floating-point numbers in order to return a required vector within the visual shader graph.
+ Outputs a 3D vector based on the result of a floating point comparison within the visual shader graph.
</brief_description>
<description>
- First two ports are scalar floating-point numbers to compare, third is tolerance comparison amount and last three ports represents a vectors returned if [code]a == b[/code], [code]a &gt; b[/code] and [code]a &lt; b[/code] respectively.
+ This visual shader node has six input ports. Port 1 and 2 provide the two floating point numbers [code]a[/code] and [code]b[/code] that will be compared. Port 3 is the tolerance, which allows similar floating point number to be considered equal. Ports 4 to 6 are the possible outputs, returned if [code]a == b[/code], [code]a &gt; b[/code], or [code]a &lt; b[/code] respectively.
</description>
<tutorials>
</tutorials>
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index 86405fa3f0..9f8d9164ce 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -2048,16 +2048,26 @@ void RasterizerCanvasGLES3::occluder_polygon_set_cull_mode(RID p_occluder, RS::C
void RasterizerCanvasGLES3::set_shadow_texture_size(int p_size) {
GLES3::Config *config = GLES3::Config::get_singleton();
p_size = nearest_power_of_2_templated(p_size);
- if (p_size == state.shadow_texture_size) {
- return;
- }
if (p_size > config->max_texture_size) {
p_size = config->max_texture_size;
WARN_PRINT("Attempting to set CanvasItem shadow atlas size to " + itos(p_size) + " which is beyond limit of " + itos(config->max_texture_size) + "supported by hardware.");
}
+ if (p_size == state.shadow_texture_size) {
+ return;
+ }
state.shadow_texture_size = p_size;
+
+ if (state.shadow_fb != 0) {
+ glDeleteFramebuffers(1, &state.shadow_fb);
+ GLES3::Utilities::get_singleton()->texture_free_data(state.shadow_texture);
+ glDeleteRenderbuffers(1, &state.shadow_depth_buffer);
+ state.shadow_fb = 0;
+ state.shadow_texture = 0;
+ state.shadow_depth_buffer = 0;
+ }
+ _update_shadow_atlas();
}
bool RasterizerCanvasGLES3::free(RID p_rid) {
@@ -2442,6 +2452,7 @@ RendererCanvasRender::PolygonID RasterizerCanvasGLES3::request_polygon(const Vec
return id;
}
+
void RasterizerCanvasGLES3::free_polygon(PolygonID p_polygon) {
PolygonBuffers *pb_ptr = polygon_buffers.polygons.getptr(p_polygon);
ERR_FAIL_COND(!pb_ptr);
@@ -2521,6 +2532,8 @@ RasterizerCanvasGLES3::RasterizerCanvasGLES3() {
GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
GLES3::Config *config = GLES3::Config::get_singleton();
+ glVertexAttrib4f(RS::ARRAY_COLOR, 1.0, 1.0, 1.0, 1.0);
+
polygon_buffers.last_id = 1;
// quad buffer
{
@@ -2700,6 +2713,7 @@ RasterizerCanvasGLES3::RasterizerCanvasGLES3() {
GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.initialize(global_defines, 1);
data.canvas_shader_default_version = GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_create();
+ state.shadow_texture_size = GLOBAL_GET("rendering/2d/shadow_atlas/size");
shadow_render.shader.initialize();
shadow_render.shader_version = shadow_render.shader.version_create();
diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h
index 2eac5f57e1..c1b3e20e33 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.h
+++ b/drivers/gles3/rasterizer_canvas_gles3.h
@@ -192,7 +192,7 @@ public:
GLuint index_buffer = 0;
int count = 0;
bool color_disabled = false;
- Color color;
+ Color color = Color(1.0, 1.0, 1.0, 1.0);
};
struct {
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index f207418f71..d9e392e852 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -5681,12 +5681,13 @@ void EditorNode::_scene_tab_exit() {
}
void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) {
+ int tab_id = scene_tabs->get_hovered_tab();
Ref<InputEventMouseButton> mb = p_input;
if (mb.is_valid()) {
- if (scene_tabs->get_hovered_tab() >= 0) {
+ if (tab_id >= 0) {
if (mb->get_button_index() == MouseButton::MIDDLE && mb->is_pressed()) {
- _scene_tab_closed(scene_tabs->get_hovered_tab());
+ _scene_tab_closed(tab_id);
}
} else if (mb->get_button_index() == MouseButton::LEFT && mb->is_double_click()) {
int tab_buttons = 0;
@@ -5705,12 +5706,12 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) {
scene_tabs_context_menu->reset_size();
scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/new_scene"), FILE_NEW_SCENE);
- if (scene_tabs->get_hovered_tab() >= 0) {
+ if (tab_id >= 0) {
scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_scene"), FILE_SAVE_SCENE);
scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_scene_as"), FILE_SAVE_AS_SCENE);
}
scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_all_scenes"), FILE_SAVE_ALL_SCENES);
- if (scene_tabs->get_hovered_tab() >= 0) {
+ if (tab_id >= 0) {
scene_tabs_context_menu->add_separator();
scene_tabs_context_menu->add_item(TTR("Show in FileSystem"), FILE_SHOW_IN_FILESYSTEM);
scene_tabs_context_menu->add_item(TTR("Play This Scene"), FILE_RUN_SCENE);
@@ -5724,7 +5725,13 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) {
scene_tabs_context_menu->set_item_disabled(scene_tabs_context_menu->get_item_index(FILE_OPEN_PREV), true);
}
scene_tabs_context_menu->add_item(TTR("Close Other Tabs"), FILE_CLOSE_OTHERS);
+ if (editor_data.get_edited_scene_count() <= 1) {
+ scene_tabs_context_menu->set_item_disabled(file_menu->get_item_index(FILE_CLOSE_OTHERS), true);
+ }
scene_tabs_context_menu->add_item(TTR("Close Tabs to the Right"), FILE_CLOSE_RIGHT);
+ if (editor_data.get_edited_scene_count() == tab_id + 1) {
+ scene_tabs_context_menu->set_item_disabled(file_menu->get_item_index(FILE_CLOSE_RIGHT), true);
+ }
scene_tabs_context_menu->add_item(TTR("Close All Tabs"), FILE_CLOSE_ALL);
}
scene_tabs_context_menu->set_position(scene_tabs->get_screen_position() + mb->get_position());
@@ -7472,8 +7479,8 @@ EditorNode::EditorNode() {
export_as_menu->connect("index_pressed", callable_mp(this, &EditorNode::_export_as_menu_option));
file_menu->add_separator();
- file_menu->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO, true);
- file_menu->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO, true);
+ file_menu->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO, true, true);
+ file_menu->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO, true, true);
file_menu->add_separator();
file_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/reload_saved_scene", TTR("Reload Saved Scene")), EDIT_RELOAD_SAVED_SCENE);
diff --git a/editor/export/editor_export.cpp b/editor/export/editor_export.cpp
index 6c4fb480d7..670fd0a06d 100644
--- a/editor/export/editor_export.cpp
+++ b/editor/export/editor_export.cpp
@@ -32,7 +32,6 @@
#include "core/config/project_settings.h"
#include "core/io/config_file.h"
-#include "editor/import/resource_importer_texture_settings.h"
EditorExport *EditorExport::singleton = nullptr;
@@ -138,22 +137,6 @@ void EditorExport::add_export_preset(const Ref<EditorExportPreset> &p_preset, in
}
}
-String EditorExportPlatform::test_etc2() const {
- if (!ResourceImporterTextureSettings::should_import_etc2_astc()) {
- return TTR("Target platform requires 'ETC2/ASTC' texture compression. Enable 'Import ETC2 ASTC' in Project Settings.") + "\n";
- }
-
- return String();
-}
-
-String EditorExportPlatform::test_bc() const {
- if (!ResourceImporterTextureSettings::should_import_s3tc_bptc()) {
- return TTR("Target platform requires 'S3TC/BPTC' texture compression. Enable 'Import S3TC BPTC' in Project Settings.") + "\n";
- }
-
- return String();
-}
-
int EditorExport::get_export_preset_count() const {
return export_presets.size();
}
diff --git a/editor/export/editor_export_platform.h b/editor/export/editor_export_platform.h
index 763836e3ec..5f5702026c 100644
--- a/editor/export/editor_export_platform.h
+++ b/editor/export/editor_export_platform.h
@@ -237,8 +237,6 @@ public:
virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) { return OK; }
virtual Ref<Texture2D> get_run_icon() const { return get_logo(); }
- String test_etc2() const;
- String test_bc() const;
bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const;
virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const = 0;
virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const = 0;
diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp
index 7c7762e0fd..61f875713f 100644
--- a/editor/export/project_export.cpp
+++ b/editor/export/project_export.cpp
@@ -39,6 +39,7 @@
#include "editor/editor_settings.h"
#include "editor/export/editor_export.h"
#include "editor/gui/editor_file_dialog.h"
+#include "editor/import/resource_importer_texture_settings.h"
#include "scene/gui/check_box.h"
#include "scene/gui/check_button.h"
#include "scene/gui/item_list.h"
@@ -51,6 +52,44 @@
#include "scene/gui/texture_rect.h"
#include "scene/gui/tree.h"
+void ProjectExportTextureFormatError::_on_fix_texture_format_pressed() {
+ ProjectSettings::get_singleton()->set_setting(setting_identifier, true);
+ ProjectSettings::get_singleton()->save();
+ EditorFileSystem::get_singleton()->scan_changes();
+ emit_signal("texture_format_enabled");
+}
+
+void ProjectExportTextureFormatError::_bind_methods() {
+ ADD_SIGNAL(MethodInfo("texture_format_enabled"));
+}
+
+void ProjectExportTextureFormatError::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
+ texture_format_error_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));
+ } break;
+ }
+}
+
+void ProjectExportTextureFormatError::show_for_texture_format(const String &p_friendly_name, const String &p_setting_identifier) {
+ texture_format_error_label->set_text(vformat(TTR("Target platform requires '%s' texture compression. Enable 'Import %s' to fix."), p_friendly_name, p_friendly_name.replace("/", " ")));
+ setting_identifier = p_setting_identifier;
+ show();
+}
+
+ProjectExportTextureFormatError::ProjectExportTextureFormatError() {
+ // Set up the label.
+ texture_format_error_label = memnew(Label);
+ add_child(texture_format_error_label);
+ // Set up the fix button.
+ fix_texture_format_button = memnew(LinkButton);
+ fix_texture_format_button->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
+ fix_texture_format_button->set_text(TTR("Fix Import"));
+ add_child(fix_texture_format_button);
+ fix_texture_format_button->connect("pressed", callable_mp(this, &ProjectExportTextureFormatError::_on_fix_texture_format_pressed));
+}
+
void ProjectExportDialog::_theme_changed() {
duplicate_preset->set_icon(presets->get_theme_icon(SNAME("Duplicate"), SNAME("EditorIcons")));
delete_preset->set_icon(presets->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
@@ -300,6 +339,14 @@ void ProjectExportDialog::_edit_preset(int p_index) {
_update_export_all();
child_controls_changed();
+ if ((feature_set.has("s3tc") || feature_set.has("bptc")) && !ResourceImporterTextureSettings::should_import_s3tc_bptc()) {
+ export_texture_format_error->show_for_texture_format("S3TC/BPTC", "rendering/textures/vram_compression/import_s3tc_bptc");
+ } else if ((feature_set.has("etc2") || feature_set.has("astc")) && !ResourceImporterTextureSettings::should_import_etc2_astc()) {
+ export_texture_format_error->show_for_texture_format("ETC2/ASTC", "rendering/textures/vram_compression/import_etc2_astc");
+ } else {
+ export_texture_format_error->hide();
+ }
+
String enc_in_filters_str = current->get_enc_in_filter();
String enc_ex_filters_str = current->get_enc_ex_filter();
if (!updating_enc_filters) {
@@ -343,29 +390,29 @@ void ProjectExportDialog::_update_feature_list() {
Ref<EditorExportPreset> current = get_current_preset();
ERR_FAIL_COND(current.is_null());
- RBSet<String> fset;
- List<String> features;
+ List<String> features_list;
- current->get_platform()->get_platform_features(&features);
- current->get_platform()->get_preset_features(current, &features);
+ current->get_platform()->get_platform_features(&features_list);
+ current->get_platform()->get_preset_features(current, &features_list);
String custom = current->get_custom_features();
Vector<String> custom_list = custom.split(",");
for (int i = 0; i < custom_list.size(); i++) {
String f = custom_list[i].strip_edges();
if (!f.is_empty()) {
- features.push_back(f);
+ features_list.push_back(f);
}
}
- for (const String &E : features) {
- fset.insert(E);
+ feature_set.clear();
+ for (const String &E : features_list) {
+ feature_set.insert(E);
}
custom_feature_display->clear();
String text;
bool first = true;
- for (const String &E : fset) {
+ for (const String &E : feature_set) {
if (!first) {
text += ", ";
} else {
@@ -1356,6 +1403,13 @@ ProjectExportDialog::ProjectExportDialog() {
add_child(export_pck_zip);
export_pck_zip->connect("file_selected", callable_mp(this, &ProjectExportDialog::_export_pck_zip_selected));
+ // Export warnings and errors bottom section.
+
+ export_texture_format_error = memnew(ProjectExportTextureFormatError);
+ main_vb->add_child(export_texture_format_error);
+ export_texture_format_error->hide();
+ export_texture_format_error->connect("texture_format_enabled", callable_mp(this, &ProjectExportDialog::_update_current_preset));
+
export_error = memnew(Label);
main_vb->add_child(export_error);
export_error->hide();
@@ -1390,6 +1444,8 @@ ProjectExportDialog::ProjectExportDialog() {
export_templates_error->add_child(download_templates);
download_templates->connect("pressed", callable_mp(this, &ProjectExportDialog::_open_export_template_manager));
+ // Export project file dialog.
+
export_project = memnew(EditorFileDialog);
export_project->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
add_child(export_project);
diff --git a/editor/export/project_export.h b/editor/export/project_export.h
index e53e393432..4f76167e22 100644
--- a/editor/export/project_export.h
+++ b/editor/export/project_export.h
@@ -41,6 +41,7 @@ class EditorFileSystemDirectory;
class EditorInspector;
class EditorPropertyPath;
class ItemList;
+class LinkButton;
class MenuButton;
class OptionButton;
class PopupMenu;
@@ -49,6 +50,23 @@ class TabContainer;
class Tree;
class TreeItem;
+class ProjectExportTextureFormatError : public HBoxContainer {
+ GDCLASS(ProjectExportTextureFormatError, HBoxContainer);
+
+ Label *texture_format_error_label = nullptr;
+ LinkButton *fix_texture_format_button = nullptr;
+ String setting_identifier;
+ void _on_fix_texture_format_pressed();
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ void show_for_texture_format(const String &p_friendly_name, const String &p_setting_identifier);
+ ProjectExportTextureFormatError();
+};
+
class ProjectExportDialog : public ConfirmationDialog {
GDCLASS(ProjectExportDialog, ConfirmationDialog);
@@ -86,12 +104,14 @@ private:
Button *export_all_button = nullptr;
AcceptDialog *export_all_dialog = nullptr;
+ RBSet<String> feature_set;
LineEdit *custom_features = nullptr;
RichTextLabel *custom_feature_display = nullptr;
LineEdit *script_key = nullptr;
Label *script_key_error = nullptr;
+ ProjectExportTextureFormatError *export_texture_format_error = nullptr;
Label *export_error = nullptr;
Label *export_warning = nullptr;
HBoxContainer *export_templates_error = nullptr;
diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp
index 4e5a3eb095..0a3de70e2a 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_3d_editor_plugin.cpp
@@ -52,10 +52,8 @@
#include "scene/scene_string_names.h"
void BoneTransformEditor::create_editors() {
- const Color section_color = get_theme_color(SNAME("prop_subsection"), SNAME("Editor"));
-
section = memnew(EditorInspectorSection);
- section->setup("trf_properties", label, this, section_color, true);
+ section->setup("trf_properties", label, this, Color(0.0f, 0.0f, 0.0f), true);
section->unfold();
add_child(section);
@@ -94,7 +92,7 @@ void BoneTransformEditor::create_editors() {
// Transform/Matrix section.
rest_section = memnew(EditorInspectorSection);
- rest_section->setup("trf_properties_transform", "Rest", this, section_color, true);
+ rest_section->setup("trf_properties_transform", "Rest", this, Color(0.0f, 0.0f, 0.0f), true);
section->get_vbox()->add_child(rest_section);
// Transform/Matrix property.
@@ -107,8 +105,10 @@ void BoneTransformEditor::create_editors() {
void BoneTransformEditor::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE: {
- create_editors();
+ case NOTIFICATION_THEME_CHANGED: {
+ const Color section_color = get_theme_color(SNAME("prop_subsection"), SNAME("Editor"));
+ section->set_bg_color(section_color);
+ rest_section->set_bg_color(section_color);
} break;
}
}
@@ -128,6 +128,7 @@ void BoneTransformEditor::_value_changed(const String &p_property, Variant p_val
BoneTransformEditor::BoneTransformEditor(Skeleton3D *p_skeleton) :
skeleton(p_skeleton) {
+ create_editors();
}
void BoneTransformEditor::set_keyable(const bool p_keyable) {
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index 8b58a45ea5..5815c85718 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -562,6 +562,16 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
return true;
}
+ Ref<InputEventKey> k = p_event;
+ if (k.is_valid() && k->is_pressed() && !k->is_echo()) {
+ for (BaseButton *b : viewport_shortcut_buttons) {
+ if (b->get_shortcut().is_valid() && b->get_shortcut()->matches_event(p_event)) {
+ b->set_pressed(b->get_button_group().is_valid() || !b->is_pressed());
+ return true;
+ }
+ }
+ }
+
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
has_mouse = true;
@@ -2075,6 +2085,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
select_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/selection_tool", TTR("Selection"), Key::S));
select_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(select_tool_button);
+ viewport_shortcut_buttons.push_back(select_tool_button);
paint_tool_button = memnew(Button);
paint_tool_button->set_flat(true);
@@ -2084,6 +2095,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
paint_tool_button->set_tooltip_text(TTR("Shift: Draw line.") + "\n" + TTR("Shift+Ctrl: Draw rectangle."));
paint_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(paint_tool_button);
+ viewport_shortcut_buttons.push_back(paint_tool_button);
line_tool_button = memnew(Button);
line_tool_button->set_flat(true);
@@ -2093,6 +2105,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
line_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/line_tool", TTR("Line", "Tool"), Key::L));
line_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(line_tool_button);
+ viewport_shortcut_buttons.push_back(line_tool_button);
rect_tool_button = memnew(Button);
rect_tool_button->set_flat(true);
@@ -2101,6 +2114,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
rect_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/rect_tool", TTR("Rect"), Key::R));
rect_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(rect_tool_button);
+ viewport_shortcut_buttons.push_back(rect_tool_button);
bucket_tool_button = memnew(Button);
bucket_tool_button->set_flat(true);
@@ -2110,6 +2124,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
bucket_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(bucket_tool_button);
toolbar->add_child(tilemap_tiles_tools_buttons);
+ viewport_shortcut_buttons.push_back(bucket_tool_button);
// -- TileMap tool settings --
tools_settings = memnew(HBoxContainer);
@@ -2126,6 +2141,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
picker_button->set_tooltip_text(TTR("Alternatively hold Ctrl with other tools to pick tile."));
picker_button->connect("pressed", callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
tools_settings->add_child(picker_button);
+ viewport_shortcut_buttons.push_back(picker_button);
// Erase button.
erase_button = memnew(Button);
@@ -2135,6 +2151,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
erase_button->set_tooltip_text(TTR("Alternatively use RMB to erase tiles."));
erase_button->connect("pressed", callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
tools_settings->add_child(erase_button);
+ viewport_shortcut_buttons.push_back(erase_button);
// Separator 2.
tools_settings_vsep_2 = memnew(VSeparator);
@@ -2860,6 +2877,16 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
_update_selection();
+ Ref<InputEventKey> k = p_event;
+ if (k.is_valid() && k->is_pressed() && !k->is_echo()) {
+ for (BaseButton *b : viewport_shortcut_buttons) {
+ if (b->get_shortcut().is_valid() && b->get_shortcut()->matches_event(p_event)) {
+ b->set_pressed(b->get_button_group().is_valid() || !b->is_pressed());
+ return true;
+ }
+ }
+ }
+
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
has_mouse = true;
@@ -3388,6 +3415,7 @@ TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
paint_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/paint_tool", TTR("Paint"), Key::D));
paint_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(paint_tool_button);
+ viewport_shortcut_buttons.push_back(paint_tool_button);
line_tool_button = memnew(Button);
line_tool_button->set_flat(true);
@@ -3396,6 +3424,7 @@ TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
line_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/line_tool", TTR("Line"), Key::L));
line_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(line_tool_button);
+ viewport_shortcut_buttons.push_back(line_tool_button);
rect_tool_button = memnew(Button);
rect_tool_button->set_flat(true);
@@ -3404,6 +3433,7 @@ TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
rect_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/rect_tool", TTR("Rect"), Key::R));
rect_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(rect_tool_button);
+ viewport_shortcut_buttons.push_back(rect_tool_button);
bucket_tool_button = memnew(Button);
bucket_tool_button->set_flat(true);
@@ -3412,6 +3442,7 @@ TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
bucket_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/bucket_tool", TTR("Bucket"), Key::B));
bucket_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(bucket_tool_button);
+ viewport_shortcut_buttons.push_back(bucket_tool_button);
toolbar->add_child(tilemap_tiles_tools_buttons);
@@ -3429,6 +3460,7 @@ TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", TTR("Picker"), Key::P));
picker_button->connect("pressed", callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
tools_settings->add_child(picker_button);
+ viewport_shortcut_buttons.push_back(picker_button);
// Erase button.
erase_button = memnew(Button);
@@ -3437,6 +3469,7 @@ TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
erase_button->set_shortcut(ED_SHORTCUT("tiles_editor/eraser", TTR("Eraser"), Key::E));
erase_button->connect("pressed", callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
tools_settings->add_child(erase_button);
+ viewport_shortcut_buttons.push_back(erase_button);
// Separator 2.
tools_settings_vsep_2 = memnew(VSeparator);
diff --git a/editor/plugins/tiles/tile_map_editor.h b/editor/plugins/tiles/tile_map_editor.h
index eb8c3937a0..ab5787b78f 100644
--- a/editor/plugins/tiles/tile_map_editor.h
+++ b/editor/plugins/tiles/tile_map_editor.h
@@ -203,6 +203,7 @@ private:
// General
void _update_theme();
+ List<BaseButton *> viewport_shortcut_buttons;
// Update callback
virtual void tile_set_changed() override;
@@ -293,6 +294,7 @@ private:
// Cache.
LocalVector<LocalVector<RBSet<TileSet::TerrainsPattern>>> per_terrain_terrains_patterns;
+ List<BaseButton *> viewport_shortcut_buttons;
// Update functions.
void _update_terrains_cache();
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 3a148b7ca9..3096d20c19 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -264,6 +264,9 @@ void SceneTreeDock::_perform_instantiate_scenes(const Vector<String> &p_files, N
}
void SceneTreeDock::_replace_with_branch_scene(const String &p_file, Node *base) {
+ // `move_child` + `get_index` doesn't really work for internal nodes.
+ ERR_FAIL_COND_MSG(base->get_internal_mode() != INTERNAL_MODE_DISABLED, "Trying to replace internal node, this is not supported.");
+
Ref<PackedScene> sdata = ResourceLoader::load(p_file);
if (!sdata.is_valid()) {
accept->set_text(vformat(TTR("Error loading scene from %s"), p_file));
@@ -284,7 +287,7 @@ void SceneTreeDock::_replace_with_branch_scene(const String &p_file, Node *base)
undo_redo->create_action(TTR("Replace with Branch Scene"));
Node *parent = base->get_parent();
- int pos = base->get_index();
+ int pos = base->get_index(false);
undo_redo->add_do_method(parent, "remove_child", base);
undo_redo->add_undo_method(parent, "remove_child", instantiated_scene);
undo_redo->add_do_method(parent, "add_child", instantiated_scene, true);
@@ -612,10 +615,12 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
selection.reverse();
}
- int lowest_id = common_parent->get_child_count() - 1;
+ int lowest_id = common_parent->get_child_count(false) - 1;
int highest_id = 0;
for (Node *E : selection) {
- int index = E->get_index();
+ // `move_child` + `get_index` doesn't really work for internal nodes.
+ ERR_FAIL_COND_MSG(E->get_internal_mode() != INTERNAL_MODE_DISABLED, "Trying to move internal node, this is not supported.");
+ int index = E->get_index(false);
if (index > highest_id) {
highest_id = index;
@@ -629,7 +634,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
}
}
- if (!common_parent || (MOVING_DOWN && highest_id >= common_parent->get_child_count() - MOVING_DOWN) || (MOVING_UP && lowest_id == 0)) {
+ if (!common_parent || (MOVING_DOWN && highest_id >= common_parent->get_child_count(false) - MOVING_DOWN) || (MOVING_UP && lowest_id == 0)) {
break; // one or more nodes can not be moved
}
@@ -648,8 +653,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
ERR_FAIL_COND(!top_node->get_parent());
ERR_FAIL_COND(!bottom_node->get_parent());
- int bottom_node_pos = bottom_node->get_index();
- int top_node_pos_next = top_node->get_index() + (MOVING_DOWN ? 1 : -1);
+ int bottom_node_pos = bottom_node->get_index(false);
+ int top_node_pos_next = top_node->get_index(false) + (MOVING_DOWN ? 1 : -1);
undo_redo->add_do_method(top_node->get_parent(), "move_child", top_node, top_node_pos_next);
undo_redo->add_undo_method(bottom_node->get_parent(), "move_child", bottom_node, bottom_node_pos);
@@ -780,6 +785,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
return;
}
+ // `move_child` + `get_index` doesn't really work for internal nodes.
+ ERR_FAIL_COND_MSG(node->get_internal_mode() != INTERNAL_MODE_DISABLED, "Trying to set internal node as scene root, this is not supported.");
+
//check that from node to root, all owners are right
if (root->get_scene_inherited_state().is_valid()) {
@@ -816,7 +824,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
undo_redo->add_undo_method(node, "remove_child", root);
undo_redo->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", root);
undo_redo->add_undo_method(node->get_parent(), "add_child", node, true);
- undo_redo->add_undo_method(node->get_parent(), "move_child", node, node->get_index());
+ undo_redo->add_undo_method(node->get_parent(), "move_child", node, node->get_index(false));
undo_redo->add_undo_method(root, "set_owner", (Object *)nullptr);
undo_redo->add_undo_method(node, "set_owner", root);
_node_replace_owner(root, root, root, MODE_UNDO);
@@ -1908,8 +1916,10 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
if (p_nodes[ni] == p_new_parent) {
return; // Attempt to reparent to itself.
}
+ // `move_child` + `get_index` doesn't really work for internal nodes.
+ ERR_FAIL_COND_MSG(p_nodes[ni]->get_internal_mode() != INTERNAL_MODE_DISABLED, "Trying to move internal node, this is not supported.");
- if (p_nodes[ni]->get_parent() != p_new_parent || p_position_in_parent + ni != p_nodes[ni]->get_index()) {
+ if (p_nodes[ni]->get_parent() != p_new_parent || p_position_in_parent + ni != p_nodes[ni]->get_index(false)) {
no_change = false;
}
}
@@ -1950,7 +1960,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
}
bool same_parent = new_parent == node->get_parent();
- if (same_parent && node->get_index() < p_position_in_parent + ni) {
+ if (same_parent && node->get_index(false) < p_position_in_parent + ni) {
inc--; // If the child will generate a gap when moved, adjust.
}
@@ -1992,7 +2002,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
}
undo_redo->add_do_method(ed, "live_debug_reparent_node", edited_scene->get_path_to(node), edited_scene->get_path_to(new_parent), new_name, new_position_in_parent);
- undo_redo->add_undo_method(ed, "live_debug_reparent_node", NodePath(String(edited_scene->get_path_to(new_parent)).path_join(new_name)), edited_scene->get_path_to(node->get_parent()), node->get_name(), node->get_index());
+ undo_redo->add_undo_method(ed, "live_debug_reparent_node", NodePath(String(edited_scene->get_path_to(new_parent)).path_join(new_name)), edited_scene->get_path_to(node->get_parent()), node->get_name(), node->get_index(false));
if (p_keep_global_xform) {
if (Object::cast_to<Node2D>(node)) {
@@ -2029,7 +2039,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
owners.push_back(E);
}
- int child_pos = node->get_index();
+ int child_pos = node->get_index(false);
undo_redo->add_undo_method(node->get_parent(), "add_child", node, true);
undo_redo->add_undo_method(node->get_parent(), "move_child", node, child_pos);
@@ -2177,6 +2187,10 @@ void SceneTreeDock::_delete_confirm(bool p_cut) {
undo_redo->add_undo_method(scene_tree, "update_tree");
undo_redo->add_undo_reference(edited_scene);
} else {
+ for (const Node *E : remove_list) {
+ // `move_child` + `get_index` doesn't really work for internal nodes.
+ ERR_FAIL_COND_MSG(E->get_internal_mode() != INTERNAL_MODE_DISABLED, "Trying to remove internal node, this is not supported.");
+ }
if (delete_tracks_checkbox->is_pressed() || p_cut) {
remove_list.sort_custom<Node::Comparator>(); // Sort nodes to keep positions.
HashMap<Node *, NodePath> path_renames;
@@ -2208,7 +2222,7 @@ void SceneTreeDock::_delete_confirm(bool p_cut) {
undo_redo->add_do_method(n->get_parent(), "remove_child", n);
undo_redo->add_undo_method(n->get_parent(), "add_child", n, true);
- undo_redo->add_undo_method(n->get_parent(), "move_child", n, n->get_index());
+ undo_redo->add_undo_method(n->get_parent(), "move_child", n, n->get_index(false));
if (AnimationPlayerEditor::get_singleton()->get_track_editor()->get_root() == n) {
undo_redo->add_undo_method(AnimationPlayerEditor::get_singleton()->get_track_editor(), "set_root", n);
}
@@ -2217,7 +2231,7 @@ void SceneTreeDock::_delete_confirm(bool p_cut) {
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
undo_redo->add_do_method(ed, "live_debug_remove_and_keep_node", edited_scene->get_path_to(n), n->get_instance_id());
- undo_redo->add_undo_method(ed, "live_debug_restore_node", n->get_instance_id(), edited_scene->get_path_to(n->get_parent()), n->get_index());
+ undo_redo->add_undo_method(ed, "live_debug_restore_node", n->get_instance_id(), edited_scene->get_path_to(n->get_parent()), n->get_index(false));
}
}
undo_redo->commit_action();
@@ -2710,7 +2724,7 @@ void SceneTreeDock::_normalize_drop(Node *&to_node, int &to_pos, int p_type) {
ERR_FAIL_MSG("Cannot perform drop above the root node!");
}
- to_pos = to_node->get_index();
+ to_pos = to_node->get_index(false);
to_node = to_node->get_parent();
} else if (p_type == 1) {
@@ -2726,15 +2740,15 @@ void SceneTreeDock::_normalize_drop(Node *&to_node, int &to_pos, int p_type) {
if (_has_visible_children(to_node)) {
to_pos = 0;
} else {
- for (int i = to_node->get_index() + 1; i < to_node->get_parent()->get_child_count(); i++) {
- Node *c = to_node->get_parent()->get_child(i);
+ for (int i = to_node->get_index(false) + 1; i < to_node->get_parent()->get_child_count(false); i++) {
+ Node *c = to_node->get_parent()->get_child(i, false);
if (_is_node_visible(c)) {
lower_sibling = c;
break;
}
}
if (lower_sibling) {
- to_pos = lower_sibling->get_index();
+ to_pos = lower_sibling->get_index(false);
}
to_node = to_node->get_parent();
diff --git a/main/main.cpp b/main/main.cpp
index f79a71474d..e6dd576b8a 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -1543,6 +1543,12 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
}
}
+#ifdef TOOLS_ENABLED
+ if (editor) {
+ Engine::get_singleton()->set_editor_hint(true);
+ }
+#endif
+
// Initialize user data dir.
OS::get_singleton()->ensure_user_data_dir();
@@ -1570,7 +1576,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
#ifdef TOOLS_ENABLED
if (editor) {
packed_data->set_disabled(true);
- Engine::get_singleton()->set_editor_hint(true);
main_args.push_back("--editor");
if (!init_windowed) {
init_maximized = true;
diff --git a/misc/extension_api_validation/4.0-stable.expected b/misc/extension_api_validation/4.0-stable.expected
index 823af03c8e..5f982cbff6 100644
--- a/misc/extension_api_validation/4.0-stable.expected
+++ b/misc/extension_api_validation/4.0-stable.expected
@@ -443,3 +443,11 @@ Validate extension JSON: API was removed: classes/SystemFont/properties/fallback
The property was moved to their common base class Font.
The setters and getters were already in Font, so this shouldn't affect compatibility.
+
+
+GH-36493
+--------
+Validate extension JSON: Error: Field 'classes/PopupMenu/methods/add_icon_shortcut/arguments': size changed value in new API, from 4 to 5.
+Validate extension JSON: Error: Field 'classes/PopupMenu/methods/add_shortcut/arguments': size changed value in new API, from 3 to 4.
+
+Added optional argument. Compatibility methods registered.
diff --git a/modules/denoise/SCsub b/modules/denoise/SCsub
index 779ce165d2..967a511e1e 100644
--- a/modules/denoise/SCsub
+++ b/modules/denoise/SCsub
@@ -109,6 +109,15 @@ env_oidn.AppendUnique(CPPDEFINES=["NDEBUG"]) # No assert() even in debug builds
env_thirdparty = env_oidn.Clone()
env_thirdparty.disable_warnings()
+
+if env["disable_exceptions"]:
+ # OIDN hard-requires exceptions, so we re-enable them here.
+ if env.msvc and ("_HAS_EXCEPTIONS", 0) in env_thirdparty["CPPDEFINES"]:
+ env_thirdparty["CPPDEFINES"].remove(("_HAS_EXCEPTIONS", 0))
+ env_thirdparty.AppendUnique(CCFLAGS=["/EHsc"])
+ elif not env.msvc and "-fno-exceptions" in env_thirdparty["CCFLAGS"]:
+ env_thirdparty["CCFLAGS"].remove("-fno-exceptions")
+
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
env.modules_sources += thirdparty_obj
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index ad4528747b..9f9accf507 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -3301,17 +3301,26 @@ void GDScriptAnalyzer::reduce_dictionary(GDScriptParser::DictionaryNode *p_dicti
void GDScriptAnalyzer::reduce_get_node(GDScriptParser::GetNodeNode *p_get_node) {
GDScriptParser::DataType result;
- result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- result.kind = GDScriptParser::DataType::NATIVE;
- result.native_type = SNAME("Node");
- result.builtin_type = Variant::OBJECT;
+ result.kind = GDScriptParser::DataType::VARIANT;
+
+ if (!ClassDB::is_parent_class(parser->current_class->base_type.native_type, SNAME("Node"))) {
+ push_error(vformat(R"*(Cannot use shorthand "get_node()" notation ("%c") on a class that isn't a node.)*", p_get_node->use_dollar ? '$' : '%'), p_get_node);
+ p_get_node->set_datatype(result);
+ return;
+ }
- if (!ClassDB::is_parent_class(parser->current_class->base_type.native_type, result.native_type)) {
- push_error(R"*(Cannot use shorthand "get_node()" notation ("$") on a class that isn't a node.)*", p_get_node);
+ if (static_context) {
+ push_error(vformat(R"*(Cannot use shorthand "get_node()" notation ("%c") in a static function.)*", p_get_node->use_dollar ? '$' : '%'), p_get_node);
+ p_get_node->set_datatype(result);
+ return;
}
mark_lambda_use_self();
+ result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ result.kind = GDScriptParser::DataType::NATIVE;
+ result.builtin_type = Variant::OBJECT;
+ result.native_type = SNAME("Node");
p_get_node->set_datatype(result);
}
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 9e1e8f0c75..b76ceea11f 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -3035,10 +3035,8 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p
if (previous.type == GDScriptTokenizer::Token::DOLLAR) {
// Detect initial slash, which will be handled in the loop if it matches.
match(GDScriptTokenizer::Token::SLASH);
-#ifdef DEBUG_ENABLED
} else {
get_node->use_dollar = false;
-#endif
}
int context_argument = 0;
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 20f5dcf06d..71660d8f60 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -848,9 +848,7 @@ public:
struct GetNodeNode : public ExpressionNode {
String full_path;
-#ifdef DEBUG_ENABLED
bool use_dollar = true;
-#endif
GetNodeNode() {
type = GET_NODE;
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 4f374b63b0..42b983ef45 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -1162,15 +1162,6 @@ void GDScriptTokenizer::check_indent() {
_advance();
}
- if (mixed && !(line_continuation || multiline_mode)) {
- Token error = make_error("Mixed use of tabs and spaces for indentation.");
- error.start_line = line;
- error.start_column = 1;
- error.leftmost_column = 1;
- error.rightmost_column = column;
- push_error(error);
- }
-
if (_is_at_end()) {
// Reached the end with an empty line, so just dedent as much as needed.
pending_indents -= indent_level();
@@ -1214,6 +1205,15 @@ void GDScriptTokenizer::check_indent() {
continue;
}
+ if (mixed && !line_continuation && !multiline_mode) {
+ Token error = make_error("Mixed use of tabs and spaces for indentation.");
+ error.start_line = line;
+ error.start_column = 1;
+ error.leftmost_column = 1;
+ error.rightmost_column = column;
+ push_error(error);
+ }
+
if (line_continuation || multiline_mode) {
// We cleared up all the whitespace at the beginning of the line.
// But if this is a continuation or multiline mode and we don't want any indentation change.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_in_static_function.gd b/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_in_static_function.gd
new file mode 100644
index 0000000000..caeea46977
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_in_static_function.gd
@@ -0,0 +1,9 @@
+# GH-75645
+
+extends Node
+
+static func static_func():
+ var a = $Node
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_in_static_function.out b/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_in_static_function.out
new file mode 100644
index 0000000000..1910b3e66b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_in_static_function.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot use shorthand "get_node()" notation ("$") in a static function.
diff --git a/modules/gdscript/tests/scripts/parser/.editorconfig b/modules/gdscript/tests/scripts/parser/.editorconfig
new file mode 100644
index 0000000000..fa43b3ad78
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/.editorconfig
@@ -0,0 +1,2 @@
+[*.{gd,out}]
+trim_trailing_whitespace = false
diff --git a/modules/gdscript/tests/scripts/parser/features/mixed_indentation_on_blank_lines.gd b/modules/gdscript/tests/scripts/parser/features/mixed_indentation_on_blank_lines.gd
new file mode 100644
index 0000000000..7ee2708999
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/mixed_indentation_on_blank_lines.gd
@@ -0,0 +1,20 @@
+# Empty line:
+
+
+# Comment line:
+ # Comment.
+
+func test():
+ print(1)
+
+ if true:
+
+ # Empty line:
+
+
+ print(2)
+
+ # Comment line:
+ # Comment.
+
+ print(3)
diff --git a/modules/gdscript/tests/scripts/parser/features/mixed_indentation_on_blank_lines.out b/modules/gdscript/tests/scripts/parser/features/mixed_indentation_on_blank_lines.out
new file mode 100644
index 0000000000..c40e402ba3
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/mixed_indentation_on_blank_lines.out
@@ -0,0 +1,4 @@
+GDTEST_OK
+1
+2
+3
diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp
index a300f7d2e2..d7ecf4ef1a 100644
--- a/modules/gridmap/editor/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp
@@ -743,6 +743,17 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D
}
}
+ // Consume input to avoid conflicts with other plugins.
+ if (k.is_valid() && k->is_pressed() && !k->is_echo()) {
+ for (int i = 0; i < options->get_popup()->get_item_count(); ++i) {
+ const Ref<Shortcut> &shortcut = options->get_popup()->get_item_shortcut(i);
+ if (shortcut.is_valid() && shortcut->matches_event(p_event)) {
+ _menu_option(options->get_popup()->get_item_id(i));
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ }
+ }
+
if (k->is_shift_pressed() && selection.active && input_action != INPUT_PASTE) {
if (k->get_keycode() == (Key)options->get_popup()->get_item_accelerator(options->get_popup()->get_item_index(MENU_OPTION_PREV_LEVEL))) {
selection.click[edit_axis]--;
@@ -1154,6 +1165,24 @@ void GridMapEditor::_bind_methods() {
}
GridMapEditor::GridMapEditor() {
+ ED_SHORTCUT("grid_map/previous_floor", TTR("Previous Floor"), Key::Q, true);
+ ED_SHORTCUT("grid_map/next_floor", TTR("Next Floor"), Key::E, true);
+ ED_SHORTCUT("grid_map/edit_x_axis", TTR("Edit X Axis"), Key::Z, true);
+ ED_SHORTCUT("grid_map/edit_y_axis", TTR("Edit Y Axis"), Key::X, true);
+ ED_SHORTCUT("grid_map/edit_z_axis", TTR("Edit Z Axis"), Key::C, true);
+ ED_SHORTCUT("grid_map/cursor_rotate_x", TTR("Cursor Rotate X"), Key::A, true);
+ ED_SHORTCUT("grid_map/cursor_rotate_y", TTR("Cursor Rotate Y"), Key::S, true);
+ ED_SHORTCUT("grid_map/cursor_rotate_z", TTR("Cursor Rotate Z"), Key::D, true);
+ ED_SHORTCUT("grid_map/cursor_back_rotate_x", TTR("Cursor Back Rotate X"), KeyModifierMask::SHIFT + Key::A, true);
+ ED_SHORTCUT("grid_map/cursor_back_rotate_y", TTR("Cursor Back Rotate Y"), KeyModifierMask::SHIFT + Key::S, true);
+ ED_SHORTCUT("grid_map/cursor_back_rotate_z", TTR("Cursor Back Rotate Z"), KeyModifierMask::SHIFT + Key::D, true);
+ ED_SHORTCUT("grid_map/cursor_clear_rotation", TTR("Cursor Clear Rotation"), Key::W, true);
+ ED_SHORTCUT("grid_map/paste_selects", TTR("Paste Selects"));
+ ED_SHORTCUT("grid_map/duplicate_selection", TTR("Duplicate Selection"), KeyModifierMask::CTRL + Key::C, true);
+ ED_SHORTCUT("grid_map/cut_selection", TTR("Cut Selection"), KeyModifierMask::CTRL + Key::X, true);
+ ED_SHORTCUT("grid_map/clear_selection", TTR("Clear Selection"), Key::KEY_DELETE);
+ ED_SHORTCUT("grid_map/fill_selection", TTR("Fill Selection"), KeyModifierMask::CTRL + Key::F);
+
int mw = EDITOR_DEF("editors/grid_map/palette_min_width", 230);
Control *ec = memnew(Control);
ec->set_custom_minimum_size(Size2(mw, 0) * EDSCALE);
@@ -1186,29 +1215,29 @@ GridMapEditor::GridMapEditor() {
spatial_editor_hb->hide();
options->set_text(TTR("Grid Map"));
- options->get_popup()->add_item(TTR("Previous Floor"), MENU_OPTION_PREV_LEVEL, Key::Q);
- options->get_popup()->add_item(TTR("Next Floor"), MENU_OPTION_NEXT_LEVEL, Key::E);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/previous_floor"), MENU_OPTION_PREV_LEVEL);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/next_floor"), MENU_OPTION_NEXT_LEVEL);
options->get_popup()->add_separator();
- options->get_popup()->add_radio_check_item(TTR("Edit X Axis"), MENU_OPTION_X_AXIS, Key::Z);
- options->get_popup()->add_radio_check_item(TTR("Edit Y Axis"), MENU_OPTION_Y_AXIS, Key::X);
- options->get_popup()->add_radio_check_item(TTR("Edit Z Axis"), MENU_OPTION_Z_AXIS, Key::C);
+ options->get_popup()->add_radio_check_shortcut(ED_GET_SHORTCUT("grid_map/edit_x_axis"), MENU_OPTION_X_AXIS);
+ options->get_popup()->add_radio_check_shortcut(ED_GET_SHORTCUT("grid_map/edit_y_axis"), MENU_OPTION_Y_AXIS);
+ options->get_popup()->add_radio_check_shortcut(ED_GET_SHORTCUT("grid_map/edit_z_axis"), MENU_OPTION_Z_AXIS);
options->get_popup()->set_item_checked(options->get_popup()->get_item_index(MENU_OPTION_Y_AXIS), true);
options->get_popup()->add_separator();
- options->get_popup()->add_item(TTR("Cursor Rotate X"), MENU_OPTION_CURSOR_ROTATE_X, Key::A);
- options->get_popup()->add_item(TTR("Cursor Rotate Y"), MENU_OPTION_CURSOR_ROTATE_Y, Key::S);
- options->get_popup()->add_item(TTR("Cursor Rotate Z"), MENU_OPTION_CURSOR_ROTATE_Z, Key::D);
- options->get_popup()->add_item(TTR("Cursor Back Rotate X"), MENU_OPTION_CURSOR_BACK_ROTATE_X, KeyModifierMask::SHIFT + Key::A);
- options->get_popup()->add_item(TTR("Cursor Back Rotate Y"), MENU_OPTION_CURSOR_BACK_ROTATE_Y, KeyModifierMask::SHIFT + Key::S);
- options->get_popup()->add_item(TTR("Cursor Back Rotate Z"), MENU_OPTION_CURSOR_BACK_ROTATE_Z, KeyModifierMask::SHIFT + Key::D);
- options->get_popup()->add_item(TTR("Cursor Clear Rotation"), MENU_OPTION_CURSOR_CLEAR_ROTATION, Key::W);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_rotate_x"), MENU_OPTION_CURSOR_ROTATE_X);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_rotate_y"), MENU_OPTION_CURSOR_ROTATE_Y);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_rotate_z"), MENU_OPTION_CURSOR_ROTATE_Z);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_back_rotate_x"), MENU_OPTION_CURSOR_BACK_ROTATE_X);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_back_rotate_y"), MENU_OPTION_CURSOR_BACK_ROTATE_Y);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_back_rotate_z"), MENU_OPTION_CURSOR_BACK_ROTATE_Z);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_clear_rotation"), MENU_OPTION_CURSOR_CLEAR_ROTATION);
options->get_popup()->add_separator();
// TRANSLATORS: This is a toggle to select after pasting the new content.
- options->get_popup()->add_check_item(TTR("Paste Selects"), MENU_OPTION_PASTE_SELECTS);
+ options->get_popup()->add_check_shortcut(ED_GET_SHORTCUT("grid_map/paste_selects"), MENU_OPTION_PASTE_SELECTS);
options->get_popup()->add_separator();
- options->get_popup()->add_item(TTR("Duplicate Selection"), MENU_OPTION_SELECTION_DUPLICATE, KeyModifierMask::CTRL + Key::C);
- options->get_popup()->add_item(TTR("Cut Selection"), MENU_OPTION_SELECTION_CUT, KeyModifierMask::CTRL + Key::X);
- options->get_popup()->add_item(TTR("Clear Selection"), MENU_OPTION_SELECTION_CLEAR, Key::KEY_DELETE);
- options->get_popup()->add_item(TTR("Fill Selection"), MENU_OPTION_SELECTION_FILL, KeyModifierMask::CTRL + Key::F);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/duplicate_selection"), MENU_OPTION_SELECTION_DUPLICATE);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cut_selection"), MENU_OPTION_SELECTION_CUT);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/clear_selection"), MENU_OPTION_SELECTION_CLEAR);
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/fill_selection"), MENU_OPTION_SELECTION_FILL);
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("Settings..."), MENU_OPTION_GRIDMAP_SETTINGS);
diff --git a/modules/navigation/godot_navigation_server.cpp b/modules/navigation/godot_navigation_server.cpp
index 8f56bf0f1d..9162fcf171 100644
--- a/modules/navigation/godot_navigation_server.cpp
+++ b/modules/navigation/godot_navigation_server.cpp
@@ -930,7 +930,7 @@ COMMAND_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, p_layers) {
obstacle->set_avoidance_layers(p_layers);
}
-void GodotNavigationServer::parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback) {
+void GodotNavigationServer::parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback) {
#ifndef _3D_DISABLED
ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "The SceneTree can only be parsed on the main thread. Call this function from the main thread or use call_deferred().");
ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh.");
@@ -942,26 +942,26 @@ void GodotNavigationServer::parse_source_geometry_data(const Ref<NavigationMesh>
#endif // _3D_DISABLED
}
-void GodotNavigationServer::bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback) {
+void GodotNavigationServer::bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback) {
#ifndef _3D_DISABLED
ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh.");
ERR_FAIL_COND_MSG(!p_source_geometry_data.is_valid(), "Invalid NavigationMeshSourceGeometryData3D.");
- if (!p_source_geometry_data->has_data()) {
- p_navigation_mesh->clear();
- if (p_callback.is_valid()) {
- Callable::CallError ce;
- Variant result;
- p_callback.callp(nullptr, 0, result, ce);
- }
- return;
- }
-
ERR_FAIL_NULL(NavMeshGenerator3D::get_singleton());
NavMeshGenerator3D::get_singleton()->bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_callback);
#endif // _3D_DISABLED
}
+void GodotNavigationServer::bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback) {
+#ifndef _3D_DISABLED
+ ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh.");
+ ERR_FAIL_COND_MSG(!p_source_geometry_data.is_valid(), "Invalid NavigationMeshSourceGeometryData3D.");
+
+ ERR_FAIL_NULL(NavMeshGenerator3D::get_singleton());
+ NavMeshGenerator3D::get_singleton()->bake_from_source_geometry_data_async(p_navigation_mesh, p_source_geometry_data, p_callback);
+#endif // _3D_DISABLED
+}
+
COMMAND_1(free, RID, p_object) {
if (map_owner.owns(p_object)) {
NavMap *map = map_owner.get_or_null(p_object);
@@ -1093,6 +1093,16 @@ void GodotNavigationServer::process(real_t p_delta_time) {
return;
}
+#ifndef _3D_DISABLED
+ // Sync finished navmesh bakes before doing NavMap updates.
+ if (navmesh_generator_3d) {
+ navmesh_generator_3d->sync();
+ // Finished bakes emit callbacks and users might have reacted to those.
+ // Flush queue again so users do not have to wait for the next sync.
+ flush_queries();
+ }
+#endif // _3D_DISABLED
+
int _new_pm_region_count = 0;
int _new_pm_agent_count = 0;
int _new_pm_link_count = 0;
diff --git a/modules/navigation/godot_navigation_server.h b/modules/navigation/godot_navigation_server.h
index 8b91ca36bd..40893bada6 100644
--- a/modules/navigation/godot_navigation_server.h
+++ b/modules/navigation/godot_navigation_server.h
@@ -228,8 +228,9 @@ public:
virtual void obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) override;
COMMAND_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, p_layers);
- virtual void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override;
- virtual void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override;
+ virtual void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override;
+ virtual void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override;
+ virtual void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override;
COMMAND_1(free, RID, p_object);
diff --git a/modules/navigation/nav_mesh_generator_3d.cpp b/modules/navigation/nav_mesh_generator_3d.cpp
index 0132e38ceb..7c27417e5f 100644
--- a/modules/navigation/nav_mesh_generator_3d.cpp
+++ b/modules/navigation/nav_mesh_generator_3d.cpp
@@ -32,6 +32,7 @@
#include "nav_mesh_generator_3d.h"
+#include "core/config/project_settings.h"
#include "core/math/convex_hull.h"
#include "core/os/thread.h"
#include "scene/3d/mesh_instance_3d.h"
@@ -63,7 +64,12 @@
NavMeshGenerator3D *NavMeshGenerator3D::singleton = nullptr;
Mutex NavMeshGenerator3D::baking_navmesh_mutex;
+Mutex NavMeshGenerator3D::generator_task_mutex;
+bool NavMeshGenerator3D::use_threads = true;
+bool NavMeshGenerator3D::baking_use_multiple_threads = true;
+bool NavMeshGenerator3D::baking_use_high_priority_threads = true;
HashSet<Ref<NavigationMesh>> NavMeshGenerator3D::baking_navmeshes;
+HashMap<WorkerThreadPool::TaskID, NavMeshGenerator3D::NavMeshGeneratorTask3D *> NavMeshGenerator3D::generator_tasks;
NavMeshGenerator3D *NavMeshGenerator3D::get_singleton() {
return singleton;
@@ -72,15 +78,67 @@ NavMeshGenerator3D *NavMeshGenerator3D::get_singleton() {
NavMeshGenerator3D::NavMeshGenerator3D() {
ERR_FAIL_COND(singleton != nullptr);
singleton = this;
+
+ baking_use_multiple_threads = GLOBAL_GET("navigation/baking/thread_model/baking_use_multiple_threads");
+ baking_use_high_priority_threads = GLOBAL_GET("navigation/baking/thread_model/baking_use_high_priority_threads");
+
+ // Using threads might cause problems on certain exports or with the Editor on certain devices.
+ // This is the main switch to turn threaded navmesh baking off should the need arise.
+ use_threads = baking_use_multiple_threads && !Engine::get_singleton()->is_editor_hint();
}
NavMeshGenerator3D::~NavMeshGenerator3D() {
cleanup();
}
+void NavMeshGenerator3D::sync() {
+ if (generator_tasks.size() == 0) {
+ return;
+ }
+
+ baking_navmesh_mutex.lock();
+ generator_task_mutex.lock();
+
+ LocalVector<WorkerThreadPool::TaskID> finished_task_ids;
+
+ for (KeyValue<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> &E : generator_tasks) {
+ if (WorkerThreadPool::get_singleton()->is_task_completed(E.key)) {
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(E.key);
+ finished_task_ids.push_back(E.key);
+
+ NavMeshGeneratorTask3D *generator_task = E.value;
+ DEV_ASSERT(generator_task->status == NavMeshGeneratorTask3D::TaskStatus::BAKING_FINISHED);
+
+ baking_navmeshes.erase(generator_task->navigation_mesh);
+ if (generator_task->callback.is_valid()) {
+ generator_emit_callback(generator_task->callback);
+ }
+ memdelete(generator_task);
+ }
+ }
+
+ for (WorkerThreadPool::TaskID finished_task_id : finished_task_ids) {
+ generator_tasks.erase(finished_task_id);
+ }
+
+ generator_task_mutex.unlock();
+ baking_navmesh_mutex.unlock();
+}
+
void NavMeshGenerator3D::cleanup() {
baking_navmesh_mutex.lock();
+ generator_task_mutex.lock();
+
baking_navmeshes.clear();
+
+ for (KeyValue<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> &E : generator_tasks) {
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(E.key);
+ NavMeshGeneratorTask3D *generator_task = E.value;
+ memdelete(generator_task);
+ }
+ generator_tasks.clear();
+
+ generator_task_mutex.unlock();
baking_navmesh_mutex.unlock();
}
@@ -88,7 +146,7 @@ void NavMeshGenerator3D::finish() {
cleanup();
}
-void NavMeshGenerator3D::parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback) {
+void NavMeshGenerator3D::parse_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback) {
ERR_FAIL_COND(!Thread::is_main_thread());
ERR_FAIL_COND(!p_navigation_mesh.is_valid());
ERR_FAIL_COND(p_root_node == nullptr);
@@ -102,19 +160,25 @@ void NavMeshGenerator3D::parse_source_geometry_data(const Ref<NavigationMesh> &p
}
}
-void NavMeshGenerator3D::bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback) {
+void NavMeshGenerator3D::bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback) {
ERR_FAIL_COND(!p_navigation_mesh.is_valid());
ERR_FAIL_COND(!p_source_geometry_data.is_valid());
- ERR_FAIL_COND(!p_source_geometry_data->has_data());
+
+ if (!p_source_geometry_data->has_data()) {
+ p_navigation_mesh->clear();
+ if (p_callback.is_valid()) {
+ generator_emit_callback(p_callback);
+ }
+ return;
+ }
baking_navmesh_mutex.lock();
if (baking_navmeshes.has(p_navigation_mesh)) {
baking_navmesh_mutex.unlock();
ERR_FAIL_MSG("NavigationMesh is already baking. Wait for current bake to finish.");
- } else {
- baking_navmeshes.insert(p_navigation_mesh);
- baking_navmesh_mutex.unlock();
}
+ baking_navmeshes.insert(p_navigation_mesh);
+ baking_navmesh_mutex.unlock();
generator_bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data);
@@ -127,6 +191,51 @@ void NavMeshGenerator3D::bake_from_source_geometry_data(Ref<NavigationMesh> p_na
}
}
+void NavMeshGenerator3D::bake_from_source_geometry_data_async(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback) {
+ ERR_FAIL_COND(!p_navigation_mesh.is_valid());
+ ERR_FAIL_COND(!p_source_geometry_data.is_valid());
+
+ if (!p_source_geometry_data->has_data()) {
+ p_navigation_mesh->clear();
+ if (p_callback.is_valid()) {
+ generator_emit_callback(p_callback);
+ }
+ return;
+ }
+
+ if (!use_threads) {
+ bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_callback);
+ return;
+ }
+
+ baking_navmesh_mutex.lock();
+ if (baking_navmeshes.has(p_navigation_mesh)) {
+ baking_navmesh_mutex.unlock();
+ ERR_FAIL_MSG("NavigationMesh is already baking. Wait for current bake to finish.");
+ return;
+ }
+ baking_navmeshes.insert(p_navigation_mesh);
+ baking_navmesh_mutex.unlock();
+
+ generator_task_mutex.lock();
+ NavMeshGeneratorTask3D *generator_task = memnew(NavMeshGeneratorTask3D);
+ generator_task->navigation_mesh = p_navigation_mesh;
+ generator_task->source_geometry_data = p_source_geometry_data;
+ generator_task->callback = p_callback;
+ generator_task->status = NavMeshGeneratorTask3D::TaskStatus::BAKING_STARTED;
+ generator_task->thread_task_id = WorkerThreadPool::get_singleton()->add_native_task(&NavMeshGenerator3D::generator_thread_bake, generator_task, NavMeshGenerator3D::baking_use_high_priority_threads, SNAME("NavMeshGeneratorBake3D"));
+ generator_tasks.insert(generator_task->thread_task_id, generator_task);
+ generator_task_mutex.unlock();
+}
+
+void NavMeshGenerator3D::generator_thread_bake(void *p_arg) {
+ NavMeshGeneratorTask3D *generator_task = static_cast<NavMeshGeneratorTask3D *>(p_arg);
+
+ generator_bake_from_source_geometry_data(generator_task->navigation_mesh, generator_task->source_geometry_data);
+
+ generator_task->status = NavMeshGeneratorTask3D::TaskStatus::BAKING_FINISHED;
+}
+
void NavMeshGenerator3D::generator_parse_geometry_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node, bool p_recurse_children) {
generator_parse_meshinstance3d_node(p_navigation_mesh, p_source_geometry_data, p_node);
generator_parse_multimeshinstance3d_node(p_navigation_mesh, p_source_geometry_data, p_node);
@@ -504,8 +613,8 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Ref<Navigation
return;
}
- const Vector<float> vertices = p_source_geometry_data->get_vertices();
- const Vector<int> indices = p_source_geometry_data->get_indices();
+ const Vector<float> &vertices = p_source_geometry_data->get_vertices();
+ const Vector<int> &indices = p_source_geometry_data->get_indices();
if (vertices.size() < 3 || indices.size() < 3) {
return;
diff --git a/modules/navigation/nav_mesh_generator_3d.h b/modules/navigation/nav_mesh_generator_3d.h
index dc844c2595..4220927641 100644
--- a/modules/navigation/nav_mesh_generator_3d.h
+++ b/modules/navigation/nav_mesh_generator_3d.h
@@ -34,6 +34,7 @@
#ifndef _3D_DISABLED
#include "core/object/class_db.h"
+#include "core/object/worker_thread_pool.h"
#include "modules/modules_enabled.gen.h" // For csg, gridmap.
class Node;
@@ -44,6 +45,31 @@ class NavMeshGenerator3D : public Object {
static NavMeshGenerator3D *singleton;
static Mutex baking_navmesh_mutex;
+ static Mutex generator_task_mutex;
+
+ static bool use_threads;
+ static bool baking_use_multiple_threads;
+ static bool baking_use_high_priority_threads;
+
+ struct NavMeshGeneratorTask3D {
+ enum TaskStatus {
+ BAKING_STARTED,
+ BAKING_FINISHED,
+ BAKING_FAILED,
+ CALLBACK_DISPATCHED,
+ CALLBACK_FAILED,
+ };
+
+ Ref<NavigationMesh> navigation_mesh;
+ Ref<NavigationMeshSourceGeometryData3D> source_geometry_data;
+ Callable callback;
+ WorkerThreadPool::TaskID thread_task_id = WorkerThreadPool::INVALID_TASK_ID;
+ NavMeshGeneratorTask3D::TaskStatus status = NavMeshGeneratorTask3D::TaskStatus::BAKING_STARTED;
+ };
+
+ static HashMap<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> generator_tasks;
+
+ static void generator_thread_bake(void *p_arg);
static HashSet<Ref<NavigationMesh>> baking_navmeshes;
@@ -66,11 +92,13 @@ class NavMeshGenerator3D : public Object {
public:
static NavMeshGenerator3D *get_singleton();
+ static void sync();
static void cleanup();
static void finish();
- static void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable());
- static void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable());
+ static void parse_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable());
+ static void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback = Callable());
+ static void bake_from_source_geometry_data_async(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback = Callable());
NavMeshGenerator3D();
~NavMeshGenerator3D();
diff --git a/modules/noise/doc_classes/FastNoiseLite.xml b/modules/noise/doc_classes/FastNoiseLite.xml
index 4c6cdfbf12..1ab2552547 100644
--- a/modules/noise/doc_classes/FastNoiseLite.xml
+++ b/modules/noise/doc_classes/FastNoiseLite.xml
@@ -5,7 +5,7 @@
</brief_description>
<description>
This class generates noise using the FastNoiseLite library, which is a collection of several noise algorithms including Cellular, Perlin, Value, and more.
- Most generated noise values are in the range of [code][-1,1][/code], however not always. Some of the cellular noise algorithms return results above [code]1[/code].
+ Most generated noise values are in the range of [code][-1, 1][/code], but not always. Some of the cellular noise algorithms return results above [code]1[/code].
</description>
<tutorials>
</tutorials>
diff --git a/modules/noise/doc_classes/Noise.xml b/modules/noise/doc_classes/Noise.xml
index dd232af1cc..7d74c84f93 100644
--- a/modules/noise/doc_classes/Noise.xml
+++ b/modules/noise/doc_classes/Noise.xml
@@ -5,7 +5,7 @@
</brief_description>
<description>
This class defines the interface for noise generation libraries to inherit from.
- A default get_seamless_noise() implementation is provided for libraries that do not provide seamless noise. This function requests a larger image from get_image(), reverses the quadrants of the image, then uses the strips of extra width to blend over the seams.
+ A default [method get_seamless_image] implementation is provided for libraries that do not provide seamless noise. This function requests a larger image from the [method get_image] method, reverses the quadrants of the image, then uses the strips of extra width to blend over the seams.
Inheriting noise classes can optionally override this function to provide a more optimal algorithm.
</description>
<tutorials>
diff --git a/modules/noise/doc_classes/NoiseTexture2D.xml b/modules/noise/doc_classes/NoiseTexture2D.xml
index e25af794d4..3f4afc5141 100644
--- a/modules/noise/doc_classes/NoiseTexture2D.xml
+++ b/modules/noise/doc_classes/NoiseTexture2D.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="NoiseTexture2D" inherits="Texture2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- A texture filled with noise generated by a [Noise] object.
+ A 2D texture filled with noise generated by a [Noise] object.
</brief_description>
<description>
- Uses [FastNoiseLite] or other libraries to fill the texture data of your desired size. [NoiseTexture2D] can also generate normal map textures.
+ Uses the [FastNoiseLite] library or other noise generators to fill the texture data of your desired size. [NoiseTexture2D] can also generate normal map textures.
The class uses [Thread]s to generate the texture data internally, so [method Texture2D.get_image] may return [code]null[/code] if the generation process has not completed yet. In that case, you need to wait for the texture to be generated before accessing the image and the generated byte data:
[codeblock]
var texture = NoiseTexture2D.new()
diff --git a/modules/noise/doc_classes/NoiseTexture3D.xml b/modules/noise/doc_classes/NoiseTexture3D.xml
index 0ada6942ad..e8e205bc68 100644
--- a/modules/noise/doc_classes/NoiseTexture3D.xml
+++ b/modules/noise/doc_classes/NoiseTexture3D.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="NoiseTexture3D" inherits="Texture3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- A texture filled with noise generated by a [Noise] object.
+ A 3D texture filled with noise generated by a [Noise] object.
</brief_description>
<description>
- Uses [FastNoiseLite] or other libraries to fill the texture data of your desired size.
+ Uses the [FastNoiseLite] library or other noise generators to fill the texture data of your desired size.
The class uses [Thread]s to generate the texture data internally, so [method Texture3D.get_data] may return [code]null[/code] if the generation process has not completed yet. In that case, you need to wait for the texture to be generated before accessing the image:
[codeblock]
var texture = NoiseTexture3D.new()
diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub
index f49dc390de..1bd10f1009 100644
--- a/modules/openxr/SCsub
+++ b/modules/openxr/SCsub
@@ -52,10 +52,11 @@ if env["builtin_openxr"]:
env_thirdparty = env_openxr.Clone()
env_thirdparty.disable_warnings()
+
env_thirdparty.AppendUnique(CPPDEFINES=["DISABLE_STD_FILESYSTEM"])
+ if env["disable_exceptions"]:
+ env_thirdparty.AppendUnique(CPPDEFINES=["XRLOADER_DISABLE_EXCEPTION_HANDLING", ("JSON_USE_EXCEPTION", 0)])
- if "-fno-exceptions" in env_thirdparty["CXXFLAGS"]:
- env_thirdparty["CXXFLAGS"].remove("-fno-exceptions")
env_thirdparty.Append(CPPPATH=[thirdparty_dir + "/src/loader"])
# add in external jsoncpp dependency
diff --git a/modules/zip/doc_classes/ZIPReader.xml b/modules/zip/doc_classes/ZIPReader.xml
index 0fa2672044..bf596806b2 100644
--- a/modules/zip/doc_classes/ZIPReader.xml
+++ b/modules/zip/doc_classes/ZIPReader.xml
@@ -25,6 +25,15 @@
Closes the underlying resources used by this instance.
</description>
</method>
+ <method name="file_exists">
+ <return type="bool" />
+ <param index="0" name="path" type="String" />
+ <param index="1" name="case_sensitive" type="bool" default="true" />
+ <description>
+ Returns [code]true[/code] if the file exists in the loaded zip archive.
+ Must be called after [method open].
+ </description>
+ </method>
<method name="get_files">
<return type="PackedStringArray" />
<description>
diff --git a/modules/zip/zip_reader.cpp b/modules/zip/zip_reader.cpp
index 2684875e1c..5752b829ef 100644
--- a/modules/zip/zip_reader.cpp
+++ b/modules/zip/zip_reader.cpp
@@ -118,6 +118,21 @@ PackedByteArray ZIPReader::read_file(String p_path, bool p_case_sensitive) {
return data;
}
+bool ZIPReader::file_exists(String p_path, bool p_case_sensitive) {
+ ERR_FAIL_COND_V_MSG(fa.is_null(), false, "ZIPReader must be opened before use.");
+
+ int cs = p_case_sensitive ? 1 : 2;
+ if (unzLocateFile(uzf, p_path.utf8().get_data(), cs) != UNZ_OK) {
+ return false;
+ }
+ if (unzOpenCurrentFile(uzf) != UNZ_OK) {
+ return false;
+ }
+
+ unzCloseCurrentFile(uzf);
+ return true;
+}
+
ZIPReader::ZIPReader() {}
ZIPReader::~ZIPReader() {
@@ -131,4 +146,5 @@ void ZIPReader::_bind_methods() {
ClassDB::bind_method(D_METHOD("close"), &ZIPReader::close);
ClassDB::bind_method(D_METHOD("get_files"), &ZIPReader::get_files);
ClassDB::bind_method(D_METHOD("read_file", "path", "case_sensitive"), &ZIPReader::read_file, DEFVAL(Variant(true)));
+ ClassDB::bind_method(D_METHOD("file_exists", "path", "case_sensitive"), &ZIPReader::file_exists, DEFVAL(Variant(true)));
}
diff --git a/modules/zip/zip_reader.h b/modules/zip/zip_reader.h
index c074197eb4..0f78352e3f 100644
--- a/modules/zip/zip_reader.h
+++ b/modules/zip/zip_reader.h
@@ -51,6 +51,7 @@ public:
PackedStringArray get_files();
PackedByteArray read_file(String p_path, bool p_case_sensitive);
+ bool file_exists(String p_path, bool p_case_sensitive);
ZIPReader();
~ZIPReader();
diff --git a/platform/android/detect.py b/platform/android/detect.py
index 2860898e5c..33c6565789 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -170,10 +170,6 @@ def configure(env: "Environment"):
env["RANLIB"] = compiler_path + "/llvm-ranlib"
env["AS"] = compiler_path + "/clang"
- # Disable exceptions on template builds
- if not env.editor_build:
- env.Append(CXXFLAGS=["-fno-exceptions"])
-
env.Append(
CCFLAGS=(
"-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fvisibility=hidden -fno-strict-aliasing".split()
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 20aaed1e3e..21de46f4fc 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -47,6 +47,7 @@
#include "editor/editor_paths.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
+#include "editor/import/resource_importer_texture_settings.h"
#include "main/splash.gen.h"
#include "scene/resources/image_texture.h"
@@ -2384,10 +2385,8 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit
}
}
- String etc_error = test_etc2();
- if (!etc_error.is_empty()) {
+ if (!ResourceImporterTextureSettings::should_import_etc2_astc()) {
valid = false;
- err += etc_error;
}
String min_sdk_str = p_preset->get("gradle_build/min_sdk");
diff --git a/platform/ios/detect.py b/platform/ios/detect.py
index a01f4b55db..e11c0b7d91 100644
--- a/platform/ios/detect.py
+++ b/platform/ios/detect.py
@@ -30,7 +30,6 @@ def get_opts():
),
("IOS_SDK_PATH", "Path to the iOS SDK", ""),
BoolVariable("ios_simulator", "Build for iOS Simulator", False),
- BoolVariable("ios_exceptions", "Enable exceptions", False),
("ios_triple", "Triple for ios toolchain", ""),
]
@@ -138,11 +137,6 @@ def configure(env: "Environment"):
env.Append(ASFLAGS=["-arch", "arm64"])
env.Append(CPPDEFINES=["NEED_LONG_INT"])
- if env["ios_exceptions"]:
- env.Append(CCFLAGS=["-fexceptions"])
- else:
- env.Append(CCFLAGS=["-fno-exceptions"])
-
# Temp fix for ABS/MAX/MIN macros in iOS SDK blocking compilation
env.Append(CCFLAGS=["-Wno-ambiguous-macro"])
diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp
index 3d63b7bb55..b6320fb22b 100644
--- a/platform/ios/export/export_plugin.cpp
+++ b/platform/ios/export/export_plugin.cpp
@@ -39,6 +39,7 @@
#include "editor/editor_paths.h"
#include "editor/editor_scale.h"
#include "editor/export/editor_export.h"
+#include "editor/import/resource_importer_texture_settings.h"
#include "editor/plugins/script_editor_plugin.h"
#include "modules/modules_enabled.gen.h" // For mono and svg.
@@ -1984,10 +1985,8 @@ bool EditorExportPlatformIOS::has_valid_project_configuration(const Ref<EditorEx
}
}
- const String etc_error = test_etc2();
- if (!etc_error.is_empty()) {
+ if (!ResourceImporterTextureSettings::should_import_etc2_astc()) {
valid = false;
- err += etc_error;
}
if (!err.is_empty()) {
diff --git a/platform/linuxbsd/freedesktop_portal_desktop.cpp b/platform/linuxbsd/freedesktop_portal_desktop.cpp
index c3cb0f411d..91c14e0e91 100644
--- a/platform/linuxbsd/freedesktop_portal_desktop.cpp
+++ b/platform/linuxbsd/freedesktop_portal_desktop.cpp
@@ -32,6 +32,7 @@
#ifdef DBUS_ENABLED
+#include "core/crypto/crypto_core.h"
#include "core/error/error_macros.h"
#include "core/os/os.h"
#include "core/string/ustring.h"
@@ -43,12 +44,15 @@
#include <dbus/dbus.h>
#endif
+#include <unistd.h>
+
#define BUS_OBJECT_NAME "org.freedesktop.portal.Desktop"
#define BUS_OBJECT_PATH "/org/freedesktop/portal/desktop"
#define BUS_INTERFACE_SETTINGS "org.freedesktop.portal.Settings"
+#define BUS_INTERFACE_FILE_CHOOSER "org.freedesktop.portal.FileChooser"
-static bool try_parse_variant(DBusMessage *p_reply_message, int p_type, void *r_value) {
+bool FreeDesktopPortalDesktop::try_parse_variant(DBusMessage *p_reply_message, int p_type, void *r_value) {
DBusMessageIter iter[3];
dbus_message_iter_init(p_reply_message, &iter[0]);
@@ -80,11 +84,11 @@ bool FreeDesktopPortalDesktop::read_setting(const char *p_namespace, const char
DBusConnection *bus = dbus_bus_get(DBUS_BUS_SESSION, &error);
if (dbus_error_is_set(&error)) {
- dbus_error_free(&error);
- unsupported = true;
if (OS::get_singleton()->is_stdout_verbose()) {
- ERR_PRINT(String() + "Error opening D-Bus connection: " + error.message);
+ ERR_PRINT(vformat("Error opening D-Bus connection: %s", error.message));
}
+ dbus_error_free(&error);
+ unsupported = true;
return false;
}
@@ -100,11 +104,11 @@ bool FreeDesktopPortalDesktop::read_setting(const char *p_namespace, const char
DBusMessage *reply = dbus_connection_send_with_reply_and_block(bus, message, 50, &error);
dbus_message_unref(message);
if (dbus_error_is_set(&error)) {
- dbus_error_free(&error);
- dbus_connection_unref(bus);
if (OS::get_singleton()->is_stdout_verbose()) {
- ERR_PRINT(String() + "Error on D-Bus communication: " + error.message);
+ ERR_PRINT(vformat("Error on D-Bus communication: %s", error.message));
}
+ dbus_error_free(&error);
+ dbus_connection_unref(bus);
return false;
}
@@ -126,6 +130,317 @@ uint32_t FreeDesktopPortalDesktop::get_appearance_color_scheme() {
return value;
}
+static const char *cs_empty = "";
+
+void FreeDesktopPortalDesktop::append_dbus_string(DBusMessageIter *p_iter, const String &p_string) {
+ CharString cs = p_string.utf8();
+ const char *cs_ptr = cs.ptr();
+ if (cs_ptr) {
+ dbus_message_iter_append_basic(p_iter, DBUS_TYPE_STRING, &cs_ptr);
+ } else {
+ dbus_message_iter_append_basic(p_iter, DBUS_TYPE_STRING, &cs_empty);
+ }
+}
+
+void FreeDesktopPortalDesktop::append_dbus_dict_filters(DBusMessageIter *p_iter, const Vector<String> &p_filters) {
+ DBusMessageIter dict_iter;
+ DBusMessageIter var_iter;
+ DBusMessageIter arr_iter;
+ const char *filters_key = "filters";
+
+ dbus_message_iter_open_container(p_iter, DBUS_TYPE_DICT_ENTRY, nullptr, &dict_iter);
+ dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, &filters_key);
+ dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "a(sa(us))", &var_iter);
+ dbus_message_iter_open_container(&var_iter, DBUS_TYPE_ARRAY, "(sa(us))", &arr_iter);
+ for (int i = 0; i < p_filters.size(); i++) {
+ Vector<String> tokens = p_filters[i].split(";");
+ if (tokens.size() == 2) {
+ DBusMessageIter struct_iter;
+ DBusMessageIter array_iter;
+ DBusMessageIter array_struct_iter;
+ dbus_message_iter_open_container(&arr_iter, DBUS_TYPE_STRUCT, nullptr, &struct_iter);
+ append_dbus_string(&struct_iter, tokens[0]);
+
+ dbus_message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "(us)", &array_iter);
+ dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, nullptr, &array_struct_iter);
+ {
+ const unsigned nil = 0;
+ dbus_message_iter_append_basic(&array_struct_iter, DBUS_TYPE_UINT32, &nil);
+ }
+ append_dbus_string(&array_struct_iter, tokens[1]);
+ dbus_message_iter_close_container(&array_iter, &array_struct_iter);
+ dbus_message_iter_close_container(&struct_iter, &array_iter);
+ dbus_message_iter_close_container(&arr_iter, &struct_iter);
+ }
+ }
+ dbus_message_iter_close_container(&var_iter, &arr_iter);
+ dbus_message_iter_close_container(&dict_iter, &var_iter);
+ dbus_message_iter_close_container(p_iter, &dict_iter);
+}
+
+void FreeDesktopPortalDesktop::append_dbus_dict_string(DBusMessageIter *p_iter, const String &p_key, const String &p_value, bool p_as_byte_array) {
+ DBusMessageIter dict_iter;
+ DBusMessageIter var_iter;
+ dbus_message_iter_open_container(p_iter, DBUS_TYPE_DICT_ENTRY, nullptr, &dict_iter);
+ append_dbus_string(&dict_iter, p_key);
+
+ if (p_as_byte_array) {
+ DBusMessageIter arr_iter;
+ dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "ay", &var_iter);
+ dbus_message_iter_open_container(&var_iter, DBUS_TYPE_ARRAY, "y", &arr_iter);
+ CharString cs = p_value.utf8();
+ const char *cs_ptr = cs.get_data();
+ do {
+ dbus_message_iter_append_basic(&arr_iter, DBUS_TYPE_BYTE, cs_ptr);
+ } while (*cs_ptr++);
+ dbus_message_iter_close_container(&var_iter, &arr_iter);
+ } else {
+ dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "s", &var_iter);
+ append_dbus_string(&var_iter, p_value);
+ }
+
+ dbus_message_iter_close_container(&dict_iter, &var_iter);
+ dbus_message_iter_close_container(p_iter, &dict_iter);
+}
+
+void FreeDesktopPortalDesktop::append_dbus_dict_bool(DBusMessageIter *p_iter, const String &p_key, bool p_value) {
+ DBusMessageIter dict_iter;
+ DBusMessageIter var_iter;
+ dbus_message_iter_open_container(p_iter, DBUS_TYPE_DICT_ENTRY, nullptr, &dict_iter);
+ append_dbus_string(&dict_iter, p_key);
+
+ dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "b", &var_iter);
+ {
+ int val = p_value;
+ dbus_message_iter_append_basic(&var_iter, DBUS_TYPE_BOOLEAN, &val);
+ }
+
+ dbus_message_iter_close_container(&dict_iter, &var_iter);
+ dbus_message_iter_close_container(p_iter, &dict_iter);
+}
+
+bool FreeDesktopPortalDesktop::file_chooser_parse_response(DBusMessageIter *p_iter, bool &r_cancel, Vector<String> &r_urls) {
+ ERR_FAIL_COND_V(dbus_message_iter_get_arg_type(p_iter) != DBUS_TYPE_UINT32, false);
+
+ dbus_uint32_t resp_code;
+ dbus_message_iter_get_basic(p_iter, &resp_code);
+ if (resp_code != 0) {
+ r_cancel = true;
+ } else {
+ r_cancel = false;
+ ERR_FAIL_COND_V(!dbus_message_iter_next(p_iter), false);
+ ERR_FAIL_COND_V(dbus_message_iter_get_arg_type(p_iter) != DBUS_TYPE_ARRAY, false);
+
+ DBusMessageIter dict_iter;
+ dbus_message_iter_recurse(p_iter, &dict_iter);
+ while (dbus_message_iter_get_arg_type(&dict_iter) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter iter;
+ dbus_message_iter_recurse(&dict_iter, &iter);
+ if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) {
+ const char *key;
+ dbus_message_iter_get_basic(&iter, &key);
+ dbus_message_iter_next(&iter);
+
+ DBusMessageIter var_iter;
+ dbus_message_iter_recurse(&iter, &var_iter);
+ if (strcmp(key, "uris") == 0) {
+ if (dbus_message_iter_get_arg_type(&var_iter) == DBUS_TYPE_ARRAY) {
+ DBusMessageIter uri_iter;
+ dbus_message_iter_recurse(&var_iter, &uri_iter);
+ while (dbus_message_iter_get_arg_type(&uri_iter) == DBUS_TYPE_STRING) {
+ const char *value;
+ dbus_message_iter_get_basic(&uri_iter, &value);
+ r_urls.push_back(String::utf8(value).trim_prefix("file://").uri_decode());
+ if (!dbus_message_iter_next(&uri_iter)) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (!dbus_message_iter_next(&dict_iter)) {
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+Error FreeDesktopPortalDesktop::file_dialog_show(const String &p_xid, const String &p_title, const String &p_current_directory, const String &p_filename, DisplayServer::FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) {
+ if (unsupported) {
+ return FAILED;
+ }
+
+ DBusError err;
+ dbus_error_init(&err);
+
+ // Open connection and add signal handler.
+ FileDialogData fd;
+ fd.callback = p_callback;
+
+ CryptoCore::RandomGenerator rng;
+ ERR_FAIL_COND_V_MSG(rng.init(), FAILED, "Failed to initialize random number generator.");
+ uint8_t uuid[64];
+ Error rng_err = rng.get_random_bytes(uuid, 64);
+ ERR_FAIL_COND_V_MSG(rng_err, rng_err, "Failed to generate unique token.");
+
+ fd.connection = dbus_bus_get(DBUS_BUS_SESSION, &err);
+ if (dbus_error_is_set(&err)) {
+ ERR_PRINT(vformat("Failed to open DBus connection: %s", err.message));
+ dbus_error_free(&err);
+ unsupported = true;
+ return FAILED;
+ }
+
+ String dbus_unique_name = String::utf8(dbus_bus_get_unique_name(fd.connection));
+ String token = String::hex_encode_buffer(uuid, 64);
+ String path = vformat("/org/freedesktop/portal/desktop/request/%s/%s", dbus_unique_name.replace(".", "_").replace(":", ""), token);
+
+ fd.path = vformat("type='signal',sender='org.freedesktop.portal.Desktop',path='%s',interface='org.freedesktop.portal.Request',member='Response',destination='%s'", path, dbus_unique_name);
+ dbus_bus_add_match(fd.connection, fd.path.utf8().get_data(), &err);
+ if (dbus_error_is_set(&err)) {
+ ERR_PRINT(vformat("Failed to add DBus match: %s", err.message));
+ dbus_error_free(&err);
+ dbus_connection_unref(fd.connection);
+ return FAILED;
+ }
+
+ // Generate FileChooser message.
+ const char *method = nullptr;
+ switch (p_mode) {
+ case DisplayServer::FILE_DIALOG_MODE_SAVE_FILE: {
+ method = "SaveFile";
+ } break;
+ case DisplayServer::FILE_DIALOG_MODE_OPEN_ANY:
+ case DisplayServer::FILE_DIALOG_MODE_OPEN_FILE:
+ case DisplayServer::FILE_DIALOG_MODE_OPEN_DIR:
+ case DisplayServer::FILE_DIALOG_MODE_OPEN_FILES: {
+ method = "OpenFile";
+ } break;
+ }
+
+ DBusMessage *message = dbus_message_new_method_call(BUS_OBJECT_NAME, BUS_OBJECT_PATH, BUS_INTERFACE_FILE_CHOOSER, method);
+ {
+ DBusMessageIter iter;
+ dbus_message_iter_init_append(message, &iter);
+
+ append_dbus_string(&iter, p_xid);
+ append_dbus_string(&iter, p_title);
+
+ DBusMessageIter arr_iter;
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &arr_iter);
+
+ append_dbus_dict_string(&arr_iter, "handle_token", token);
+ append_dbus_dict_bool(&arr_iter, "multiple", p_mode == DisplayServer::FILE_DIALOG_MODE_OPEN_FILES);
+ append_dbus_dict_bool(&arr_iter, "directory", p_mode == DisplayServer::FILE_DIALOG_MODE_OPEN_DIR);
+ append_dbus_dict_filters(&arr_iter, p_filters);
+ append_dbus_dict_string(&arr_iter, "current_folder", p_current_directory, true);
+ if (p_mode == DisplayServer::FILE_DIALOG_MODE_SAVE_FILE) {
+ append_dbus_dict_string(&arr_iter, "current_name", p_filename);
+ }
+
+ dbus_message_iter_close_container(&iter, &arr_iter);
+ }
+
+ DBusMessage *reply = dbus_connection_send_with_reply_and_block(fd.connection, message, DBUS_TIMEOUT_INFINITE, &err);
+ dbus_message_unref(message);
+
+ if (!reply || dbus_error_is_set(&err)) {
+ ERR_PRINT(vformat("Failed to send DBus message: %s", err.message));
+ dbus_error_free(&err);
+ dbus_bus_remove_match(fd.connection, fd.path.utf8().get_data(), &err);
+ dbus_connection_unref(fd.connection);
+ return FAILED;
+ }
+
+ // Update signal path.
+ {
+ DBusMessageIter iter;
+ if (dbus_message_iter_init(reply, &iter)) {
+ if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_OBJECT_PATH) {
+ const char *new_path = nullptr;
+ dbus_message_iter_get_basic(&iter, &new_path);
+ if (String::utf8(new_path) != path) {
+ dbus_bus_remove_match(fd.connection, fd.path.utf8().get_data(), &err);
+ if (dbus_error_is_set(&err)) {
+ ERR_PRINT(vformat("Failed to remove DBus match: %s", err.message));
+ dbus_error_free(&err);
+ dbus_connection_unref(fd.connection);
+ return FAILED;
+ }
+ fd.path = String::utf8(new_path);
+ dbus_bus_add_match(fd.connection, fd.path.utf8().get_data(), &err);
+ if (dbus_error_is_set(&err)) {
+ ERR_PRINT(vformat("Failed to add DBus match: %s", err.message));
+ dbus_error_free(&err);
+ dbus_connection_unref(fd.connection);
+ return FAILED;
+ }
+ }
+ }
+ }
+ }
+ dbus_message_unref(reply);
+
+ MutexLock lock(file_dialog_mutex);
+ file_dialogs.push_back(fd);
+
+ return OK;
+}
+
+void FreeDesktopPortalDesktop::_thread_file_dialog_monitor(void *p_ud) {
+ FreeDesktopPortalDesktop *portal = (FreeDesktopPortalDesktop *)p_ud;
+
+ while (!portal->file_dialog_thread_abort.is_set()) {
+ {
+ MutexLock lock(portal->file_dialog_mutex);
+ for (int i = portal->file_dialogs.size() - 1; i >= 0; i--) {
+ bool remove = false;
+ {
+ FreeDesktopPortalDesktop::FileDialogData &fd = portal->file_dialogs.write[i];
+ if (fd.connection) {
+ while (true) {
+ DBusMessage *msg = dbus_connection_pop_message(fd.connection);
+ if (!msg) {
+ break;
+ } else if (dbus_message_is_signal(msg, "org.freedesktop.portal.Request", "Response")) {
+ DBusMessageIter iter;
+ if (dbus_message_iter_init(msg, &iter)) {
+ bool cancel = false;
+ Vector<String> uris;
+ file_chooser_parse_response(&iter, cancel, uris);
+
+ if (fd.callback.is_valid()) {
+ Variant v_status = !cancel;
+ Variant v_files = uris;
+ Variant *v_args[2] = { &v_status, &v_files };
+ fd.callback.call_deferredp((const Variant **)&v_args, 2);
+ }
+ }
+ dbus_message_unref(msg);
+
+ DBusError err;
+ dbus_error_init(&err);
+ dbus_bus_remove_match(fd.connection, fd.path.utf8().get_data(), &err);
+ dbus_error_free(&err);
+ dbus_connection_unref(fd.connection);
+ remove = true;
+ break;
+ }
+ dbus_message_unref(msg);
+ }
+ dbus_connection_read_write(fd.connection, 0);
+ }
+ }
+ if (remove) {
+ portal->file_dialogs.remove_at(i);
+ }
+ }
+ }
+ usleep(50000);
+ }
+}
+
FreeDesktopPortalDesktop::FreeDesktopPortalDesktop() {
#ifdef SOWRAP_ENABLED
#ifdef DEBUG_ENABLED
@@ -153,6 +468,27 @@ FreeDesktopPortalDesktop::FreeDesktopPortalDesktop() {
print_verbose("PortalDesktop: Unsupported DBus library version!");
unsupported = true;
}
+
+ if (!unsupported) {
+ file_dialog_thread_abort.clear();
+ file_dialog_thread.start(FreeDesktopPortalDesktop::_thread_file_dialog_monitor, this);
+ }
+}
+
+FreeDesktopPortalDesktop::~FreeDesktopPortalDesktop() {
+ file_dialog_thread_abort.set();
+ if (file_dialog_thread.is_started()) {
+ file_dialog_thread.wait_to_finish();
+ }
+ for (FreeDesktopPortalDesktop::FileDialogData &fd : file_dialogs) {
+ if (fd.connection) {
+ DBusError err;
+ dbus_error_init(&err);
+ dbus_bus_remove_match(fd.connection, fd.path.utf8().get_data(), &err);
+ dbus_error_free(&err);
+ dbus_connection_unref(fd.connection);
+ }
+ }
}
#endif // DBUS_ENABLED
diff --git a/platform/linuxbsd/freedesktop_portal_desktop.h b/platform/linuxbsd/freedesktop_portal_desktop.h
index 1520ab3a1f..a9b83b3844 100644
--- a/platform/linuxbsd/freedesktop_portal_desktop.h
+++ b/platform/linuxbsd/freedesktop_portal_desktop.h
@@ -33,20 +33,48 @@
#ifdef DBUS_ENABLED
-#include <stdint.h>
+#include "core/os/thread.h"
+#include "servers/display_server.h"
+
+struct DBusMessage;
+struct DBusConnection;
+struct DBusMessageIter;
class FreeDesktopPortalDesktop {
private:
bool unsupported = false;
+ static bool try_parse_variant(DBusMessage *p_reply_message, int p_type, void *r_value);
// Read a setting from org.freekdesktop.portal.Settings
bool read_setting(const char *p_namespace, const char *p_key, int p_type, void *r_value);
+ static void append_dbus_string(DBusMessageIter *p_iter, const String &p_string);
+ static void append_dbus_dict_filters(DBusMessageIter *p_iter, const Vector<String> &p_filters);
+ static void append_dbus_dict_string(DBusMessageIter *p_iter, const String &p_key, const String &p_value, bool p_as_byte_array = false);
+ static void append_dbus_dict_bool(DBusMessageIter *p_iter, const String &p_key, bool p_value);
+ static bool file_chooser_parse_response(DBusMessageIter *p_iter, bool &r_cancel, Vector<String> &r_urls);
+
+ struct FileDialogData {
+ DBusConnection *connection = nullptr;
+ Callable callback;
+ String path;
+ };
+
+ Mutex file_dialog_mutex;
+ Vector<FileDialogData> file_dialogs;
+ Thread file_dialog_thread;
+ SafeFlag file_dialog_thread_abort;
+
+ static void _thread_file_dialog_monitor(void *p_ud);
+
public:
FreeDesktopPortalDesktop();
+ ~FreeDesktopPortalDesktop();
bool is_supported() { return !unsupported; }
+ Error file_dialog_show(const String &p_xid, const String &p_title, const String &p_current_directory, const String &p_filename, DisplayServer::FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback);
+
// Retrieve the system's preferred color scheme.
// 0: No preference or unknown.
// 1: Prefer dark appearance.
diff --git a/platform/linuxbsd/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp
index 5d636240b7..827c567785 100644
--- a/platform/linuxbsd/joypad_linux.cpp
+++ b/platform/linuxbsd/joypad_linux.cpp
@@ -82,31 +82,9 @@ void JoypadLinux::Joypad::reset() {
events.clear();
}
-#ifdef UDEV_ENABLED
-// This function is derived from SDL:
-// https://github.com/libsdl-org/SDL/blob/main/src/core/linux/SDL_sandbox.c#L28-L45
-static bool detect_sandbox() {
- if (access("/.flatpak-info", F_OK) == 0) {
- return true;
- }
-
- // For Snap, we check multiple variables because they might be set for
- // unrelated reasons. This is the same thing WebKitGTK does.
- if (OS::get_singleton()->has_environment("SNAP") && OS::get_singleton()->has_environment("SNAP_NAME") && OS::get_singleton()->has_environment("SNAP_REVISION")) {
- return true;
- }
-
- if (access("/run/host/container-manager", F_OK) == 0) {
- return true;
- }
-
- return false;
-}
-#endif // UDEV_ENABLED
-
JoypadLinux::JoypadLinux(Input *in) {
#ifdef UDEV_ENABLED
- if (detect_sandbox()) {
+ if (OS::get_singleton()->is_sandboxed()) {
// Linux binaries in sandboxes / containers need special handling because
// libudev doesn't work there. So we need to fallback to manual parsing
// of /dev/input in such case.
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
index 14d02a73c8..d22d398a67 100644
--- a/platform/linuxbsd/os_linuxbsd.cpp
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -164,6 +164,27 @@ String OS_LinuxBSD::get_processor_name() const {
ERR_FAIL_V_MSG("", String("Couldn't get the CPU model name from `/proc/cpuinfo`. Returning an empty string."));
}
+bool OS_LinuxBSD::is_sandboxed() const {
+ // This function is derived from SDL:
+ // https://github.com/libsdl-org/SDL/blob/main/src/core/linux/SDL_sandbox.c#L28-L45
+
+ if (access("/.flatpak-info", F_OK) == 0) {
+ return true;
+ }
+
+ // For Snap, we check multiple variables because they might be set for
+ // unrelated reasons. This is the same thing WebKitGTK does.
+ if (has_environment("SNAP") && has_environment("SNAP_NAME") && has_environment("SNAP_REVISION")) {
+ return true;
+ }
+
+ if (access("/run/host/container-manager", F_OK) == 0) {
+ return true;
+ }
+
+ return false;
+}
+
void OS_LinuxBSD::finalize() {
if (main_loop) {
memdelete(main_loop);
diff --git a/platform/linuxbsd/os_linuxbsd.h b/platform/linuxbsd/os_linuxbsd.h
index 007b90b82b..6917ea5ae7 100644
--- a/platform/linuxbsd/os_linuxbsd.h
+++ b/platform/linuxbsd/os_linuxbsd.h
@@ -123,6 +123,8 @@ public:
virtual String get_unique_id() const override;
virtual String get_processor_name() const override;
+ virtual bool is_sandboxed() const override;
+
virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override;
virtual bool _check_internal_feature_support(const String &p_feature) override;
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index f0864f5134..f38a9dd278 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -122,6 +122,9 @@ bool DisplayServerX11::has_feature(Feature p_feature) const {
case FEATURE_WINDOW_TRANSPARENCY:
//case FEATURE_HIDPI:
case FEATURE_ICON:
+#ifdef DBUS_ENABLED
+ case FEATURE_NATIVE_DIALOG:
+#endif
//case FEATURE_NATIVE_ICON:
case FEATURE_SWAP_BUFFERS:
#ifdef DBUS_ENABLED
@@ -360,6 +363,17 @@ bool DisplayServerX11::is_dark_mode() const {
}
}
+Error DisplayServerX11::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) {
+ WindowID window_id = _get_focused_window_or_popup();
+
+ if (!windows.has(window_id)) {
+ window_id = MAIN_WINDOW_ID;
+ }
+
+ String xid = vformat("x11:%x", (uint64_t)windows[window_id].x11_window);
+ return portal_desktop->file_dialog_show(xid, p_title, p_current_directory, p_filename, p_mode, p_filters, p_callback);
+}
+
#endif
void DisplayServerX11::mouse_set_mode(MouseMode p_mode) {
@@ -2112,9 +2126,10 @@ bool DisplayServerX11::_window_maximize_check(WindowID p_window, const char *p_a
bool DisplayServerX11::_window_minimize_check(WindowID p_window) const {
const WindowData &wd = windows[p_window];
- // Using ICCCM -- Inter-Client Communication Conventions Manual
- Atom property = XInternAtom(x11_display, "WM_STATE", True);
- if (property == None) {
+ // Using EWMH instead of ICCCM, might work better for Wayland users.
+ Atom property = XInternAtom(x11_display, "_NET_WM_STATE", True);
+ Atom hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", True);
+ if (property == None || hidden == None) {
return false;
}
@@ -2122,7 +2137,7 @@ bool DisplayServerX11::_window_minimize_check(WindowID p_window) const {
int format;
unsigned long len;
unsigned long remaining;
- unsigned char *data = nullptr;
+ Atom *atoms = nullptr;
int result = XGetWindowProperty(
x11_display,
@@ -2131,20 +2146,21 @@ bool DisplayServerX11::_window_minimize_check(WindowID p_window) const {
0,
32,
False,
- AnyPropertyType,
+ XA_ATOM,
&type,
&format,
&len,
&remaining,
- &data);
+ (unsigned char **)&atoms);
- if (result == Success && data) {
- long *state = (long *)data;
- if (state[0] == WM_IconicState) {
- XFree(data);
- return true;
+ if (result == Success && atoms) {
+ for (unsigned int i = 0; i < len; i++) {
+ if (atoms[i] == hidden) {
+ XFree(atoms);
+ return true;
+ }
}
- XFree(data);
+ XFree(atoms);
}
return false;
diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h
index cce44a92de..71beddce76 100644
--- a/platform/linuxbsd/x11/display_server_x11.h
+++ b/platform/linuxbsd/x11/display_server_x11.h
@@ -393,6 +393,8 @@ public:
#if defined(DBUS_ENABLED)
virtual bool is_dark_mode_supported() const override;
virtual bool is_dark_mode() const override;
+
+ virtual Error file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) override;
#endif
virtual void mouse_set_mode(MouseMode p_mode) override;
diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp
index e788ff1eec..6586fe7f82 100644
--- a/platform/macos/export/export_plugin.cpp
+++ b/platform/macos/export/export_plugin.cpp
@@ -41,6 +41,7 @@
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
#include "editor/editor_scale.h"
+#include "editor/import/resource_importer_texture_settings.h"
#include "scene/resources/image_texture.h"
#include "modules/modules_enabled.gen.h" // For svg and regex.
@@ -2124,16 +2125,12 @@ bool EditorExportPlatformMacOS::has_valid_export_configuration(const Ref<EditorE
// Check the texture formats, which vary depending on the target architecture.
String architecture = p_preset->get("binary_format/architecture");
if (architecture == "universal" || architecture == "x86_64") {
- const String bc_error = test_bc();
- if (!bc_error.is_empty()) {
+ if (!ResourceImporterTextureSettings::should_import_s3tc_bptc()) {
valid = false;
- err += bc_error;
}
} else if (architecture == "arm64") {
- const String etc_error = test_etc2();
- if (!etc_error.is_empty()) {
+ if (!ResourceImporterTextureSettings::should_import_etc2_astc()) {
valid = false;
- err += etc_error;
}
} else {
ERR_PRINT("Invalid architecture");
diff --git a/platform/web/SCsub b/platform/web/SCsub
index e44e59bfb9..1af0642554 100644
--- a/platform/web/SCsub
+++ b/platform/web/SCsub
@@ -70,6 +70,9 @@ if env["dlink_enabled"]:
sys_env.Append(LINKFLAGS=["-s", "WARN_ON_UNDEFINED_SYMBOLS=0"])
# Force exporting the standard library (printf, malloc, etc.)
sys_env["ENV"]["EMCC_FORCE_STDLIBS"] = "libc,libc++,libc++abi"
+ sys_env["CCFLAGS"].remove("-fvisibility=hidden")
+ sys_env["LINKFLAGS"].remove("-fvisibility=hidden")
+
# The main emscripten runtime, with exported standard libraries.
sys = sys_env.Program(build_targets, ["web_runtime.cpp"])
diff --git a/platform/web/detect.py b/platform/web/detect.py
index 2685cbcd63..4015c8ff16 100644
--- a/platform/web/detect.py
+++ b/platform/web/detect.py
@@ -97,12 +97,9 @@ def configure(env: "Environment"):
if env["use_assertions"]:
env.Append(LINKFLAGS=["-s", "ASSERTIONS=1"])
- if env.editor_build:
- if env["initial_memory"] < 64:
- print('Note: Forcing "initial_memory=64" as it is required for the web editor.')
- env["initial_memory"] = 64
- else:
- env.Append(CPPFLAGS=["-fno-exceptions"])
+ if env.editor_build and env["initial_memory"] < 64:
+ print('Note: Forcing "initial_memory=64" as it is required for the web editor.')
+ env["initial_memory"] = 64
env.Append(LINKFLAGS=["-s", "INITIAL_MEMORY=%sMB" % env["initial_memory"]])
@@ -211,6 +208,8 @@ def configure(env: "Environment"):
env.Append(CCFLAGS=["-s", "SIDE_MODULE=2"])
env.Append(LINKFLAGS=["-s", "SIDE_MODULE=2"])
+ env.Append(CCFLAGS=["-fvisibility=hidden"])
+ env.Append(LINKFLAGS=["-fvisibility=hidden"])
env.extra_suffix = ".dlink" + env.extra_suffix
# Reduce code size by generating less support code (e.g. skip NodeJS support).
diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp
index 38e2714d9f..993abd2cee 100644
--- a/platform/web/export/export_plugin.cpp
+++ b/platform/web/export/export_plugin.cpp
@@ -37,6 +37,7 @@
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/export/editor_export.h"
+#include "editor/import/resource_importer_texture_settings.h"
#include "scene/resources/image_texture.h"
#include "modules/modules_enabled.gen.h" // For mono and svg.
@@ -406,10 +407,8 @@ bool EditorExportPlatformWeb::has_valid_project_configuration(const Ref<EditorEx
// Validate the project configuration.
if (p_preset->get("vram_texture_compression/for_mobile")) {
- String etc_error = test_etc2();
- if (!etc_error.is_empty()) {
+ if (!ResourceImporterTextureSettings::should_import_etc2_astc()) {
valid = false;
- err += etc_error;
}
}
diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp
index 367a1d445f..e03640f6cb 100644
--- a/scene/3d/navigation_region_3d.cpp
+++ b/scene/3d/navigation_region_3d.cpp
@@ -236,62 +236,29 @@ RID NavigationRegion3D::get_navigation_map() const {
return RID();
}
-struct BakeThreadsArgs {
- NavigationRegion3D *nav_region = nullptr;
- Ref<NavigationMeshSourceGeometryData3D> source_geometry_data;
-};
-
-void _bake_navigation_mesh(void *p_user_data) {
- BakeThreadsArgs *args = static_cast<BakeThreadsArgs *>(p_user_data);
-
- if (args->nav_region->get_navigation_mesh().is_valid()) {
- Ref<NavigationMesh> nav_mesh = args->nav_region->get_navigation_mesh();
- Ref<NavigationMeshSourceGeometryData3D> source_geometry_data = args->source_geometry_data;
-
- NavigationServer3D::get_singleton()->bake_from_source_geometry_data(nav_mesh, source_geometry_data);
- if (!Thread::is_main_thread()) {
- args->nav_region->call_deferred(SNAME("_bake_finished"), nav_mesh);
- } else {
- args->nav_region->_bake_finished(nav_mesh);
- }
- memdelete(args);
- } else {
- ERR_PRINT("Can't bake the navigation mesh if the `NavigationMesh` resource doesn't exist");
- if (!Thread::is_main_thread()) {
- args->nav_region->call_deferred(SNAME("_bake_finished"), Ref<NavigationMesh>());
- } else {
- args->nav_region->_bake_finished(Ref<NavigationMesh>());
- }
- memdelete(args);
- }
-}
-
void NavigationRegion3D::bake_navigation_mesh(bool p_on_thread) {
ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "The SceneTree can only be parsed on the main thread. Call this function from the main thread or use call_deferred().");
ERR_FAIL_COND_MSG(!navigation_mesh.is_valid(), "Baking the navigation mesh requires a valid `NavigationMesh` resource.");
- ERR_FAIL_COND_MSG(bake_thread.is_started(), "Unable to start another bake request. The navigation mesh bake thread is already baking a navigation mesh.");
Ref<NavigationMeshSourceGeometryData3D> source_geometry_data;
source_geometry_data.instantiate();
NavigationServer3D::get_singleton()->parse_source_geometry_data(navigation_mesh, source_geometry_data, this);
- BakeThreadsArgs *args = memnew(BakeThreadsArgs);
- args->nav_region = this;
- args->source_geometry_data = source_geometry_data;
-
if (p_on_thread) {
- bake_thread.start(_bake_navigation_mesh, args);
+ NavigationServer3D::get_singleton()->bake_from_source_geometry_data_async(navigation_mesh, source_geometry_data, callable_mp(this, &NavigationRegion3D::_bake_finished).bind(navigation_mesh));
} else {
- _bake_navigation_mesh(args);
+ NavigationServer3D::get_singleton()->bake_from_source_geometry_data(navigation_mesh, source_geometry_data, callable_mp(this, &NavigationRegion3D::_bake_finished).bind(navigation_mesh));
}
}
-void NavigationRegion3D::_bake_finished(Ref<NavigationMesh> p_nav_mesh) {
- set_navigation_mesh(p_nav_mesh);
- if (bake_thread.is_started()) {
- bake_thread.wait_to_finish();
+void NavigationRegion3D::_bake_finished(Ref<NavigationMesh> p_navigation_mesh) {
+ if (!Thread::is_main_thread()) {
+ call_deferred(SNAME("_bake_finished"), p_navigation_mesh);
+ return;
}
+
+ set_navigation_mesh(p_navigation_mesh);
emit_signal(SNAME("bake_finished"));
}
@@ -452,10 +419,6 @@ NavigationRegion3D::NavigationRegion3D() {
}
NavigationRegion3D::~NavigationRegion3D() {
- if (bake_thread.is_started()) {
- bake_thread.wait_to_finish();
- }
-
if (navigation_mesh.is_valid()) {
navigation_mesh->disconnect_changed(callable_mp(this, &NavigationRegion3D::_navigation_mesh_changed));
}
diff --git a/scene/3d/navigation_region_3d.h b/scene/3d/navigation_region_3d.h
index e41d07f4cf..02fe5524b2 100644
--- a/scene/3d/navigation_region_3d.h
+++ b/scene/3d/navigation_region_3d.h
@@ -49,8 +49,6 @@ class NavigationRegion3D : public Node3D {
Transform3D current_global_transform;
- Thread bake_thread;
-
void _navigation_mesh_changed();
#ifdef DEBUG_ENABLED
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index 715d8a5bc1..8da1ef8e1d 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -150,7 +150,7 @@ double AnimationNodeAnimation::_process(double p_time, bool p_seek, bool p_is_ex
// Emit start & finish signal. Internally, the detections are the same for backward.
// We should use call_deferred since the track keys are still being prosessed.
- if (state->tree) {
+ if (state->tree && !p_test_only) {
// AnimationTree uses seek to 0 "internally" to process the first key of the animation, which is used as the start detection.
if (p_seek && !p_is_external_seeking && cur_time == 0) {
state->tree->call_deferred(SNAME("emit_signal"), "animation_started", animation);
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index b273f709f2..cff07c6e1c 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -675,11 +675,18 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
if (closest != -1 && (mb->get_button_index() == MouseButton::LEFT || (allow_rmb_select && mb->get_button_index() == MouseButton::RIGHT))) {
int i = closest;
+ if (items[i].disabled) {
+ // Don't emit any signal or do any action with clicked item when disabled.
+ return;
+ }
+
if (select_mode == SELECT_MULTI && items[i].selected && mb->is_command_or_control_pressed()) {
deselect(i);
emit_signal(SNAME("multi_selected"), i, false);
} else if (select_mode == SELECT_MULTI && mb->is_shift_pressed() && current >= 0 && current < items.size() && current != i) {
+ // Range selection.
+
int from = current;
int to = i;
if (i < current) {
@@ -687,6 +694,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
}
for (int j = from; j <= to; j++) {
if (!CAN_SELECT(j)) {
+ // Item is not selectable during a range selection, so skip it.
continue;
}
bool selected = !items[j].selected;
@@ -698,12 +706,17 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
emit_signal(SNAME("item_clicked"), i, get_local_mouse_position(), mb->get_button_index());
} else {
- if (!mb->is_double_click() && !mb->is_command_or_control_pressed() && select_mode == SELECT_MULTI && items[i].selectable && !items[i].disabled && items[i].selected && mb->get_button_index() == MouseButton::LEFT) {
+ if (!mb->is_double_click() &&
+ !mb->is_command_or_control_pressed() &&
+ select_mode == SELECT_MULTI &&
+ items[i].selectable &&
+ items[i].selected &&
+ mb->get_button_index() == MouseButton::LEFT) {
defer_select_single = i;
return;
}
- if (!items[i].selected || allow_reselect) {
+ if (items[i].selectable && (!items[i].selected || allow_reselect)) {
select(i, select_mode == SELECT_SINGLE || !mb->is_command_or_control_pressed());
if (select_mode == SELECT_SINGLE) {
@@ -722,7 +735,9 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
return;
} else if (closest != -1) {
- emit_signal(SNAME("item_clicked"), closest, get_local_mouse_position(), mb->get_button_index());
+ if (!items[closest].disabled) {
+ emit_signal(SNAME("item_clicked"), closest, get_local_mouse_position(), mb->get_button_index());
+ }
} else {
// Since closest is null, more likely we clicked on empty space, so send signal to interested controls. Allows, for example, implement items deselecting.
emit_signal(SNAME("empty_clicked"), get_local_mouse_position(), mb->get_button_index());
@@ -886,7 +901,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
search_string = "";
} else if (p_event->is_action("ui_select", true) && select_mode == SELECT_MULTI) {
if (current >= 0 && current < items.size()) {
- if (items[current].selectable && !items[current].disabled && !items[current].selected) {
+ if (CAN_SELECT(current) && !items[current].selected) {
select(current, false);
emit_signal(SNAME("multi_selected"), current, true);
} else if (items[current].selected) {
@@ -897,7 +912,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
} else if (p_event->is_action("ui_accept", true)) {
search_string = ""; //any mousepress cancels
- if (current >= 0 && current < items.size()) {
+ if (current >= 0 && current < items.size() && !items[current].disabled) {
emit_signal(SNAME("item_activated"), current);
}
} else {
diff --git a/scene/gui/menu_bar.cpp b/scene/gui/menu_bar.cpp
index 85068ac862..0dd258d92c 100644
--- a/scene/gui/menu_bar.cpp
+++ b/scene/gui/menu_bar.cpp
@@ -152,7 +152,7 @@ void MenuBar::shortcut_input(const Ref<InputEvent> &p_event) {
return;
}
- if (p_event->is_pressed() && !p_event->is_echo() && (Object::cast_to<InputEventKey>(p_event.ptr()) || Object::cast_to<InputEventJoypadButton>(p_event.ptr()) || Object::cast_to<InputEventAction>(*p_event) || Object::cast_to<InputEventShortcut>(*p_event))) {
+ if (p_event->is_pressed() && (Object::cast_to<InputEventKey>(p_event.ptr()) || Object::cast_to<InputEventJoypadButton>(p_event.ptr()) || Object::cast_to<InputEventAction>(*p_event) || Object::cast_to<InputEventShortcut>(*p_event))) {
if (!get_parent() || !is_visible_in_tree()) {
return;
}
diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp
index 4e80d7a2d6..868383b141 100644
--- a/scene/gui/menu_button.cpp
+++ b/scene/gui/menu_button.cpp
@@ -40,7 +40,7 @@ void MenuButton::shortcut_input(const Ref<InputEvent> &p_event) {
return;
}
- if (p_event->is_pressed() && !p_event->is_echo() && !is_disabled() && is_visible_in_tree() && popup->activate_item_by_event(p_event, false)) {
+ if (p_event->is_pressed() && !is_disabled() && is_visible_in_tree() && popup->activate_item_by_event(p_event, false)) {
accept_event();
return;
}
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index 2b8c85c823..a260385a46 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -30,10 +30,26 @@
#include "option_button.h"
+#include "core/os/keyboard.h"
#include "core/string/print_string.h"
static const int NONE_SELECTED = -1;
+void OptionButton::shortcut_input(const Ref<InputEvent> &p_event) {
+ ERR_FAIL_COND(p_event.is_null());
+
+ if (disable_shortcuts) {
+ return;
+ }
+
+ if (p_event->is_pressed() && !p_event->is_echo() && !is_disabled() && is_visible_in_tree() && popup->activate_item_by_event(p_event, false)) {
+ accept_event();
+ return;
+ }
+
+ Button::shortcut_input(p_event);
+}
+
Size2 OptionButton::get_minimum_size() const {
Size2 minsize;
if (fit_to_longest_item) {
@@ -574,6 +590,7 @@ void OptionButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_fit_to_longest_item"), &OptionButton::is_fit_to_longest_item);
ClassDB::bind_method(D_METHOD("set_allow_reselect", "allow"), &OptionButton::set_allow_reselect);
ClassDB::bind_method(D_METHOD("get_allow_reselect"), &OptionButton::get_allow_reselect);
+ ClassDB::bind_method(D_METHOD("set_disable_shortcuts", "disabled"), &OptionButton::set_disable_shortcuts);
// "selected" property must come after "item_count", otherwise GH-10213 occurs.
ADD_ARRAY_COUNT("Items", "item_count", "set_item_count", "get_item_count", "popup/item_");
@@ -584,9 +601,14 @@ void OptionButton::_bind_methods() {
ADD_SIGNAL(MethodInfo("item_focused", PropertyInfo(Variant::INT, "index")));
}
+void OptionButton::set_disable_shortcuts(bool p_disabled) {
+ disable_shortcuts = p_disabled;
+}
+
OptionButton::OptionButton(const String &p_text) :
Button(p_text) {
set_toggle_mode(true);
+ set_process_shortcut_input(true);
set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
set_action_mode(ACTION_MODE_BUTTON_PRESS);
diff --git a/scene/gui/option_button.h b/scene/gui/option_button.h
index 7dcb3319c6..e29f14ad54 100644
--- a/scene/gui/option_button.h
+++ b/scene/gui/option_button.h
@@ -37,6 +37,7 @@
class OptionButton : public Button {
GDCLASS(OptionButton, Button);
+ bool disable_shortcuts = false;
PopupMenu *popup = nullptr;
int current = -1;
bool fit_to_longest_item = true;
@@ -79,6 +80,7 @@ protected:
void _get_property_list(List<PropertyInfo> *p_list) const;
void _validate_property(PropertyInfo &p_property) const;
static void _bind_methods();
+ virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
public:
// ATTENTION: This is used by the POT generator's scene parser. If the number of properties returned by `_get_items()` ever changes,
@@ -129,6 +131,8 @@ public:
PopupMenu *get_popup() const;
void show_popup();
+ void set_disable_shortcuts(bool p_disabled);
+
OptionButton(const String &p_text = String());
~OptionButton();
};
diff --git a/scene/gui/popup_menu.compat.inc b/scene/gui/popup_menu.compat.inc
new file mode 100644
index 0000000000..ef74a17228
--- /dev/null
+++ b/scene/gui/popup_menu.compat.inc
@@ -0,0 +1,46 @@
+/**************************************************************************/
+/* popup_menu.compat.inc */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef DISABLE_DEPRECATED
+
+void PopupMenu::_add_shortcut_bind_compat_36493(const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
+ return add_shortcut(p_shortcut, p_id, p_global, false);
+}
+
+void PopupMenu::_add_icon_shortcut_bind_compat_36493(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
+ return add_icon_shortcut(p_icon, p_shortcut, p_id, p_global, false);
+}
+
+void PopupMenu::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("add_shortcut", "shortcut", "id", "global"), &PopupMenu::_add_shortcut_bind_compat_36493, DEFVAL(-1), DEFVAL(false));
+ ClassDB::bind_compatibility_method(D_METHOD("add_icon_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::_add_icon_shortcut_bind_compat_36493, DEFVAL(-1), DEFVAL(false));
+}
+
+#endif
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 40db8deaac..4bba33f18e 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "popup_menu.h"
+#include "popup_menu.compat.inc"
#include "core/config/project_settings.h"
#include "core/input/input.h"
@@ -1164,18 +1165,19 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int
_menu_changed();
}
-#define ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global) \
+#define ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, p_allow_echo) \
ERR_FAIL_COND_MSG(p_shortcut.is_null(), "Cannot add item with invalid Shortcut."); \
_ref_shortcut(p_shortcut); \
item.text = p_shortcut->get_name(); \
item.xl_text = atr(item.text); \
item.id = p_id == -1 ? items.size() : p_id; \
item.shortcut = p_shortcut; \
- item.shortcut_is_global = p_global;
+ item.shortcut_is_global = p_global; \
+ item.allow_echo = p_allow_echo;
-void PopupMenu::add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
+void PopupMenu::add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_global, bool p_allow_echo) {
Item item;
- ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
+ ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, p_allow_echo);
items.push_back(item);
_shape_item(items.size() - 1);
@@ -1185,9 +1187,9 @@ void PopupMenu::add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_g
_menu_changed();
}
-void PopupMenu::add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
+void PopupMenu::add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global, bool p_allow_echo) {
Item item;
- ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
+ ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, p_allow_echo);
item.icon = p_icon;
items.push_back(item);
@@ -1200,7 +1202,7 @@ void PopupMenu::add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortc
void PopupMenu::add_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
Item item;
- ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
+ ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, false); // Echo for check shortcuts doesn't make sense.
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
@@ -1213,7 +1215,7 @@ void PopupMenu::add_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bo
void PopupMenu::add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
Item item;
- ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
+ ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, false);
item.icon = p_icon;
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
@@ -1227,7 +1229,7 @@ void PopupMenu::add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<
void PopupMenu::add_radio_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
Item item;
- ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
+ ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, false);
item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
items.push_back(item);
@@ -1240,7 +1242,7 @@ void PopupMenu::add_radio_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_
void PopupMenu::add_icon_radio_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
Item item;
- ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
+ ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, false);
item.icon = p_icon;
item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
items.push_back(item);
@@ -1838,7 +1840,7 @@ bool PopupMenu::activate_item_by_event(const Ref<InputEvent> &p_event, bool p_fo
}
for (int i = 0; i < items.size(); i++) {
- if (is_item_disabled(i) || items[i].shortcut_is_disabled) {
+ if (is_item_disabled(i) || items[i].shortcut_is_disabled || (!items[i].allow_echo && p_event->is_echo())) {
continue;
}
@@ -2213,8 +2215,8 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_multistate_item", "label", "max_states", "default_state", "id", "accel"), &PopupMenu::add_multistate_item, DEFVAL(0), DEFVAL(-1), DEFVAL(0));
- ClassDB::bind_method(D_METHOD("add_shortcut", "shortcut", "id", "global"), &PopupMenu::add_shortcut, DEFVAL(-1), DEFVAL(false));
- ClassDB::bind_method(D_METHOD("add_icon_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_shortcut, DEFVAL(-1), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("add_shortcut", "shortcut", "id", "global", "allow_echo"), &PopupMenu::add_shortcut, DEFVAL(-1), DEFVAL(false), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("add_icon_shortcut", "texture", "shortcut", "id", "global", "allow_echo"), &PopupMenu::add_icon_shortcut, DEFVAL(-1), DEFVAL(false), DEFVAL(false));
ClassDB::bind_method(D_METHOD("add_check_shortcut", "shortcut", "id", "global"), &PopupMenu::add_check_shortcut, DEFVAL(-1), DEFVAL(false));
ClassDB::bind_method(D_METHOD("add_icon_check_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_check_shortcut, DEFVAL(-1), DEFVAL(false));
ClassDB::bind_method(D_METHOD("add_radio_check_shortcut", "shortcut", "id", "global"), &PopupMenu::add_radio_check_shortcut, DEFVAL(-1), DEFVAL(false));
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index 5ad9cd4303..ef754315f0 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -74,6 +74,7 @@ class PopupMenu : public Popup {
Ref<Shortcut> shortcut;
bool shortcut_is_global = false;
bool shortcut_is_disabled = false;
+ bool allow_echo = false;
// Returns (0,0) if icon is null.
Size2 get_icon_size() const {
@@ -199,6 +200,12 @@ protected:
void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
+#ifndef DISABLE_DEPRECATED
+ void _add_shortcut_bind_compat_36493(const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
+ void _add_icon_shortcut_bind_compat_36493(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
+ static void _bind_compatibility_methods();
+#endif
+
public:
// ATTENTION: This is used by the POT generator's scene parser. If the number of properties returned by `_get_items()` ever changes,
// this value should be updated to reflect the new size.
@@ -215,8 +222,8 @@ public:
void add_multistate_item(const String &p_label, int p_max_states, int p_default_state = 0, int p_id = -1, Key p_accel = Key::NONE);
- void add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
- void add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
+ void add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false, bool p_allow_echo = false);
+ void add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false, bool p_allow_echo = false);
void add_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
void add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
void add_radio_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 433ae656ba..3c1be2d5fe 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -1922,7 +1922,7 @@ void Tree::update_column(int p_col) {
columns.write[p_col].text_buf->set_direction((TextServer::Direction)columns[p_col].text_direction);
}
- columns.write[p_col].text_buf->add_string(columns[p_col].title, theme_cache.font, theme_cache.font_size, columns[p_col].language);
+ columns.write[p_col].text_buf->add_string(columns[p_col].title, theme_cache.tb_font, theme_cache.tb_font_size, columns[p_col].language);
columns.write[p_col].cached_minimum_width_dirty = true;
}
@@ -4108,7 +4108,7 @@ void Tree::update_scrollbars() {
}
int Tree::_get_title_button_height() const {
- ERR_FAIL_COND_V(theme_cache.font.is_null() || theme_cache.title_button.is_null(), 0);
+ ERR_FAIL_COND_V(theme_cache.tb_font.is_null() || theme_cache.title_button.is_null(), 0);
int h = 0;
if (show_column_titles) {
for (int i = 0; i < columns.size(); i++) {
@@ -4243,7 +4243,6 @@ void Tree::_notification(int p_what) {
int ofs2 = theme_cache.panel_style->get_margin(SIDE_LEFT);
for (int i = 0; i < columns.size(); i++) {
Ref<StyleBox> sb = (cache.click_type == Cache::CLICK_TITLE && cache.click_index == i) ? theme_cache.title_button_pressed : ((cache.hover_type == Cache::CLICK_TITLE && cache.hover_index == i) ? theme_cache.title_button_hover : theme_cache.title_button);
- Ref<Font> f = theme_cache.tb_font;
Rect2 tbrect = Rect2(ofs2 - theme_cache.offset.x, bg->get_margin(SIDE_TOP), get_column_width(i), tbh);
if (cache.rtl) {
tbrect.position.x = get_size().width - tbrect.size.x - tbrect.position.x;
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 8d5133311a..3500b2201a 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -1347,6 +1347,10 @@ void Node::_generate_serial_child_name(const Node *p_child, StringName &name) co
}
}
+Node::InternalMode Node::get_internal_mode() const {
+ return data.internal_mode;
+}
+
void Node::_add_child_nocheck(Node *p_child, const StringName &p_name, InternalMode p_internal_mode) {
//add a child node quickly, without name validation
diff --git a/scene/main/node.h b/scene/main/node.h
index ed8f699d7b..feda200b24 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -386,6 +386,8 @@ public:
String get_description() const;
void set_name(const String &p_name);
+ InternalMode get_internal_mode() const;
+
void add_child(Node *p_child, bool p_force_readable_name = false, InternalMode p_internal = INTERNAL_MODE_DISABLED);
void add_sibling(Node *p_sibling, bool p_force_readable_name = false);
void remove_child(Node *p_child);
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 180efaaa60..41034466f9 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -2439,6 +2439,7 @@ Window *Viewport::get_base_window() const {
return w;
}
+
void Viewport::_gui_remove_focus_for_window(Node *p_window) {
if (get_base_window() == p_window) {
gui_release_focus();
@@ -3656,6 +3657,15 @@ bool Viewport::is_embedding_subwindows() const {
return gui.embed_subwindows_hint;
}
+TypedArray<Window> Viewport::get_embedded_subwindows() const {
+ TypedArray<Window> windows;
+ for (int i = 0; i < gui.sub_windows.size(); i++) {
+ windows.append(gui.sub_windows[i].window);
+ }
+
+ return windows;
+}
+
void Viewport::subwindow_set_popup_safe_rect(Window *p_window, const Rect2i &p_rect) {
int index = _sub_window_find(p_window);
ERR_FAIL_COND(index == -1);
@@ -4384,6 +4394,7 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_embedding_subwindows", "enable"), &Viewport::set_embedding_subwindows);
ClassDB::bind_method(D_METHOD("is_embedding_subwindows"), &Viewport::is_embedding_subwindows);
+ ClassDB::bind_method(D_METHOD("get_embedded_subwindows"), &Viewport::get_embedded_subwindows);
ClassDB::bind_method(D_METHOD("set_canvas_cull_mask", "mask"), &Viewport::set_canvas_cull_mask);
ClassDB::bind_method(D_METHOD("get_canvas_cull_mask"), &Viewport::get_canvas_cull_mask);
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 1e107ea99c..346dc6af7e 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -653,6 +653,7 @@ public:
void set_embedding_subwindows(bool p_embed);
bool is_embedding_subwindows() const;
+ TypedArray<Window> get_embedded_subwindows() const;
void subwindow_set_popup_safe_rect(Window *p_window, const Rect2i &p_rect);
Rect2i subwindow_get_popup_safe_rect(Window *p_window) const;
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index d6b0fe797f..eef46a6798 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -762,6 +762,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_font("title_button_font", "Tree", Ref<Font>());
theme->set_font("font", "Tree", Ref<Font>());
theme->set_font_size("font_size", "Tree", -1);
+ theme->set_font_size("title_button_font_size", "Tree", -1);
theme->set_color("title_button_color", "Tree", control_font_color);
theme->set_color("font_color", "Tree", control_font_low_color);
diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h
index 02a7651631..983ca8892d 100644
--- a/servers/audio/audio_stream.h
+++ b/servers/audio/audio_stream.h
@@ -223,8 +223,8 @@ private:
HashSet<AudioStreamPlaybackRandomizer *> playbacks;
Vector<PoolEntry> audio_stream_pool;
- float random_pitch_scale = 1.1f;
- float random_volume_offset_db = 5.0f;
+ float random_pitch_scale = 1.0f;
+ float random_volume_offset_db = 0.0f;
Ref<AudioStreamPlayback> instance_playback_random();
Ref<AudioStreamPlayback> instance_playback_no_repeats();
diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp
index 04facdb8d9..75036b935b 100644
--- a/servers/navigation_server_3d.cpp
+++ b/servers/navigation_server_3d.cpp
@@ -155,6 +155,7 @@ void NavigationServer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("parse_source_geometry_data", "navigation_mesh", "source_geometry_data", "root_node", "callback"), &NavigationServer3D::parse_source_geometry_data, DEFVAL(Callable()));
ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data", "navigation_mesh", "source_geometry_data", "callback"), &NavigationServer3D::bake_from_source_geometry_data, DEFVAL(Callable()));
+ ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data_async", "navigation_mesh", "source_geometry_data", "callback"), &NavigationServer3D::bake_from_source_geometry_data_async, DEFVAL(Callable()));
ClassDB::bind_method(D_METHOD("free_rid", "rid"), &NavigationServer3D::free);
@@ -204,6 +205,9 @@ NavigationServer3D::NavigationServer3D() {
GLOBAL_DEF("navigation/avoidance/thread_model/avoidance_use_multiple_threads", true);
GLOBAL_DEF("navigation/avoidance/thread_model/avoidance_use_high_priority_threads", true);
+ GLOBAL_DEF("navigation/baking/thread_model/baking_use_multiple_threads", true);
+ GLOBAL_DEF("navigation/baking/thread_model/baking_use_high_priority_threads", true);
+
#ifdef DEBUG_ENABLED
debug_navigation_edge_connection_color = GLOBAL_DEF("debug/shapes/navigation/edge_connection_color", Color(1.0, 0.0, 1.0, 1.0));
debug_navigation_geometry_edge_color = GLOBAL_DEF("debug/shapes/navigation/geometry_edge_color", Color(0.5, 1.0, 1.0, 1.0));
diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h
index 4bf25f7a33..39f147357a 100644
--- a/servers/navigation_server_3d.h
+++ b/servers/navigation_server_3d.h
@@ -309,8 +309,9 @@ public:
virtual NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const = 0;
- virtual void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) = 0;
- virtual void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
+ virtual void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) = 0;
+ virtual void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
+ virtual void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
NavigationServer3D();
~NavigationServer3D() override;
diff --git a/servers/navigation_server_3d_dummy.h b/servers/navigation_server_3d_dummy.h
index a5c9fc57f2..b1ec214bb0 100644
--- a/servers/navigation_server_3d_dummy.h
+++ b/servers/navigation_server_3d_dummy.h
@@ -145,8 +145,9 @@ public:
void obstacle_set_position(RID p_obstacle, Vector3 p_position) override {}
void obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) override {}
void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) override {}
- void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override {}
- void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
+ void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override {}
+ void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
+ void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
void free(RID p_object) override {}
void set_active(bool p_active) override {}
void process(real_t delta_time) override {}
diff --git a/tests/SCsub b/tests/SCsub
index c59ce69b92..d96a1142e4 100644
--- a/tests/SCsub
+++ b/tests/SCsub
@@ -13,10 +13,8 @@ env_tests = env.Clone()
if env_tests["platform"] == "windows":
env_tests.Append(CPPDEFINES=[("DOCTEST_THREAD_LOCAL", "")])
-# Increase number of addressable sections in object files
-# due to doctest's heavy use of templates and macros.
-if env_tests.msvc:
- env_tests.Append(CCFLAGS=["/bigobj"])
+if env["disable_exceptions"]:
+ env_tests.Append(CPPDEFINES=["DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS"])
env_tests.add_source_files(env.tests_sources, "*.cpp")
diff --git a/tests/core/io/test_image.h b/tests/core/io/test_image.h
index 92ab166ae8..1897971113 100644
--- a/tests/core/io/test_image.h
+++ b/tests/core/io/test_image.h
@@ -262,9 +262,7 @@ TEST_CASE("[Image] Modifying pixels of an image") {
for (const Rect2i &rect : rects) {
Ref<Image> img = memnew(Image(img_width, img_height, false, Image::FORMAT_RGBA8));
- CHECK_NOTHROW_MESSAGE(
- img->fill_rect(rect, Color(1, 1, 1, 1)),
- "fill_rect() shouldn't throw for any rect.");
+ img->fill_rect(rect, Color(1, 1, 1, 1));
for (int y = 0; y < img->get_height(); y++) {
for (int x = 0; x < img->get_width(); x++) {
if (rect.abs().has_point(Point2(x, y))) {
@@ -317,7 +315,7 @@ TEST_CASE("[Image] Modifying pixels of an image") {
// Pre-multiply Alpha then Convert from RGBA to L8, checking alpha
{
Ref<Image> gray_image = memnew(Image(3, 3, false, Image::FORMAT_RGBA8));
- CHECK_NOTHROW_MESSAGE(gray_image->fill_rect(Rect2i(0, 0, 3, 3), Color(1, 1, 1, 0)), "fill_rect() shouldn't throw for any rect.");
+ gray_image->fill_rect(Rect2i(0, 0, 3, 3), Color(1, 1, 1, 0));
gray_image->set_pixel(1, 1, Color(1, 1, 1, 1));
gray_image->set_pixel(1, 2, Color(0.5, 0.5, 0.5, 0.5));
gray_image->set_pixel(2, 1, Color(0.25, 0.05, 0.5, 1.0));
diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h
index fc1e0590fc..659fb003d3 100644
--- a/tests/core/string/test_string.h
+++ b/tests/core/string/test_string.h
@@ -1609,6 +1609,11 @@ TEST_CASE("[String] Repeat") {
CHECK(t == s);
}
+TEST_CASE("[String] Reverse") {
+ String s = "Abcd";
+ CHECK(s.reverse() == "dcbA");
+}
+
TEST_CASE("[String] SHA1/SHA256/MD5") {
String s = "Godot";
String sha1 = "a1e91f39b9fce6a9998b14bdbe2aa2b39dc2d201";
diff --git a/tests/scene/test_viewport.h b/tests/scene/test_viewport.h
index ab17459a41..dd4786977e 100644
--- a/tests/scene/test_viewport.h
+++ b/tests/scene/test_viewport.h
@@ -226,7 +226,7 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
SUBCASE("[Viewport][GuiInputEvent] nullptr as argument doesn't lead to a crash.") {
ERR_PRINT_OFF;
- CHECK_NOTHROW(root->push_input(nullptr));
+ root->push_input(nullptr);
ERR_PRINT_ON;
}