diff options
178 files changed, 1450 insertions, 665 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/config/engine.cpp b/core/config/engine.cpp index 7fdea7d1aa..6727c58fd1 100644 --- a/core/config/engine.cpp +++ b/core/config/engine.cpp @@ -239,6 +239,10 @@ bool Engine::is_validation_layers_enabled() const { return use_validation_layers; } +bool Engine::is_generate_spirv_debug_info_enabled() const { + return generate_spirv_debug_info; +} + void Engine::set_print_error_messages(bool p_enabled) { CoreGlobals::print_error_enabled = p_enabled; } diff --git a/core/config/engine.h b/core/config/engine.h index 5ea653ba6c..73d40d50ae 100644 --- a/core/config/engine.h +++ b/core/config/engine.h @@ -67,6 +67,7 @@ private: double _physics_interpolation_fraction = 0.0f; bool abort_on_gpu_errors = false; bool use_validation_layers = false; + bool generate_spirv_debug_info = false; int32_t gpu_idx = -1; uint64_t _process_frames = 0; @@ -156,6 +157,7 @@ public: bool is_abort_on_gpu_errors_enabled() const; bool is_validation_layers_enabled() const; + bool is_generate_spirv_debug_info_enabled() const; int32_t get_gpu_index() const; Engine(); diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 8ea92f6692..1bfb745662 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -1349,6 +1349,7 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "display/window/stretch/mode", PROPERTY_HINT_ENUM, "disabled,canvas_items,viewport"), "disabled"); GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "display/window/stretch/aspect", PROPERTY_HINT_ENUM, "ignore,keep,keep_width,keep_height,expand"), "keep"); GLOBAL_DEF_BASIC(PropertyInfo(Variant::FLOAT, "display/window/stretch/scale", PROPERTY_HINT_RANGE, "0.5,8.0,0.01"), 1.0); + GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "display/window/stretch/scale_mode", PROPERTY_HINT_ENUM, "fractional,integer"), "fractional"); GLOBAL_DEF(PropertyInfo(Variant::INT, "debug/settings/profiler/max_functions", PROPERTY_HINT_RANGE, "128,65535,1"), 16384); diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 4e220d0839..05fe393a2f 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -929,6 +929,17 @@ Geometry3D *Geometry3D::get_singleton() { return singleton; } +Vector<Vector3> Geometry3D::compute_convex_mesh_points(const TypedArray<Plane> &p_planes) { + Vector<Plane> planes_vec; + int size = p_planes.size(); + planes_vec.resize(size); + for (int i = 0; i < size; ++i) { + planes_vec.set(i, p_planes[i]); + } + Variant ret = ::Geometry3D::compute_convex_mesh_points(planes_vec.ptr(), size); + return ret; +} + TypedArray<Plane> Geometry3D::build_box_planes(const Vector3 &p_extents) { Variant ret = ::Geometry3D::build_box_planes(p_extents); return ret; @@ -1029,6 +1040,7 @@ Vector<Vector3> Geometry3D::clip_polygon(const Vector<Vector3> &p_points, const } void Geometry3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("compute_convex_mesh_points", "planes"), &Geometry3D::compute_convex_mesh_points); ClassDB::bind_method(D_METHOD("build_box_planes", "extents"), &Geometry3D::build_box_planes); ClassDB::bind_method(D_METHOD("build_cylinder_planes", "radius", "height", "sides", "axis"), &Geometry3D::build_cylinder_planes, DEFVAL(Vector3::AXIS_Z)); ClassDB::bind_method(D_METHOD("build_capsule_planes", "radius", "height", "sides", "lats", "axis"), &Geometry3D::build_capsule_planes, DEFVAL(Vector3::AXIS_Z)); diff --git a/core/core_bind.h b/core/core_bind.h index 1cbbcdd251..5f51b64eb7 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -320,6 +320,7 @@ protected: public: static Geometry3D *get_singleton(); + Vector<Vector3> compute_convex_mesh_points(const TypedArray<Plane> &p_planes); TypedArray<Plane> build_box_planes(const Vector3 &p_extents); TypedArray<Plane> build_cylinder_planes(float p_radius, float p_height, int p_sides, Vector3::Axis p_axis = Vector3::AXIS_Z); TypedArray<Plane> build_capsule_planes(float p_radius, float p_height, int p_sides, int p_lats, Vector3::Axis p_axis = Vector3::AXIS_Z); diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index 67b55db3db..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; } @@ -607,12 +614,13 @@ Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String } bool compatible = true; - if (VERSION_MAJOR < compatibility_minimum[0]) { - compatible = false; - } else if (VERSION_MINOR < compatibility_minimum[1]) { - compatible = false; - } else if (VERSION_PATCH < compatibility_minimum[2]) { - compatible = false; + // Check version lexicographically. + if (VERSION_MAJOR != compatibility_minimum[0]) { + compatible = VERSION_MAJOR > compatibility_minimum[0]; + } else if (VERSION_MINOR != compatibility_minimum[1]) { + compatible = VERSION_MINOR > compatibility_minimum[1]; + } else { + compatible = VERSION_PATCH >= compatibility_minimum[2]; } if (!compatible) { if (r_error) { @@ -640,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/io/remote_filesystem_client.h b/core/io/remote_filesystem_client.h index 42eba98eb1..fcb5c1cfc3 100644 --- a/core/io/remote_filesystem_client.h +++ b/core/io/remote_filesystem_client.h @@ -44,8 +44,8 @@ protected: String _get_cache_path() { return cache_path; } struct FileCache { String path; // Local path (as in "folder/to/file.png") - uint64_t server_modified_time; // MD5 checksum. - uint64_t modified_time; + uint64_t server_modified_time = 0; // MD5 checksum. + uint64_t modified_time = 0; }; virtual bool _is_configured() { return !cache_path.is_empty(); } // Can be re-implemented per platform. If so, feel free to ignore get_cache_path() 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/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/Callable.xml b/doc/classes/Callable.xml index c7b2d6df94..b903e98319 100644 --- a/doc/classes/Callable.xml +++ b/doc/classes/Callable.xml @@ -138,7 +138,7 @@ <method name="get_method" qualifiers="const"> <return type="StringName" /> <description> - Returns the name of the method represented by this [Callable]. If the callable is a lambda function, returns the function's name. + Returns the name of the method represented by this [Callable]. If the callable is a GDScript lambda function, returns the function's name or [code]"<anonymous lambda>"[/code]. </description> </method> <method name="get_object" qualifiers="const"> 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/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/Geometry3D.xml b/doc/classes/Geometry3D.xml index 85b8965faf..a85d17d925 100644 --- a/doc/classes/Geometry3D.xml +++ b/doc/classes/Geometry3D.xml @@ -45,6 +45,13 @@ Clips the polygon defined by the points in [param points] against the [param plane] and returns the points of the clipped polygon. </description> </method> + <method name="compute_convex_mesh_points"> + <return type="PackedVector3Array" /> + <param index="0" name="planes" type="Plane[]" /> + <description> + Calculates and returns all the vertex points of a convex shape defined by an array of [param planes]. + </description> + </method> <method name="get_closest_point_to_segment"> <return type="Vector3" /> <param index="0" name="point" type="Vector3" /> 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/Node.xml b/doc/classes/Node.xml index 49ab3918bb..7510d6bb64 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -1022,12 +1022,12 @@ Notification received right after the scene with the node is saved in the editor. This notification is only sent in the Godot editor and will not occur in exported projects. </constant> <constant name="NOTIFICATION_WM_MOUSE_ENTER" value="1002"> - Notification received from the OS when the mouse enters the game window. - Implemented on desktop and web platforms. + Notification received when the mouse enters the window. + Implemented for embedded windows and on desktop and web platforms. </constant> <constant name="NOTIFICATION_WM_MOUSE_EXIT" value="1003"> - Notification received from the OS when the mouse leaves the game window. - Implemented on desktop and web platforms. + Notification received when the mouse leaves the window. + Implemented for embedded windows and on desktop and web platforms. </constant> <constant name="NOTIFICATION_WM_WINDOW_FOCUS_IN" value="1004"> Notification received when the node's parent [Window] is focused. This may be a change of focus between two windows of the same engine instance, or from the OS desktop or a third-party application to a window of the game (in which case [constant NOTIFICATION_APPLICATION_FOCUS_IN] is also emitted). 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/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 52419882b0..1be69052e4 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -298,7 +298,7 @@ [b]Note:[/b] Changing this value can help on platforms or with third-party tools where hidden directory patterns are disallowed. Only modify this setting if you know that your environment requires it, as changing the default can impact compatibility with some external tools or plugins which expect the default [code].godot[/code] folder. </member> <member name="application/config/version" type="String" setter="" getter="" default=""""> - The project's human-readable version identifier. This should always be set to a non-empty string, as some exporters rely on this value being defined. + The project's human-readable version identifier. This is used by exporters if the version identifier isn't overridden there. If [member application/config/version] is an empty string and the version identifier isn't overridden in an exporter, the exporter will use [code]1.0.0[/code] as a version identifier. </member> <member name="application/config/windows_native_icon" type="String" setter="" getter="" default=""""> Icon set in [code].ico[/code] format used on Windows to set the game's icon. This is done automatically on start by calling [method DisplayServer.set_native_icon]. @@ -813,6 +813,10 @@ [b]"viewport"[/b]: The size of the root [Viewport] is set precisely to the base size specified in the Project Settings' Display section. The scene is rendered to this viewport first. Finally, this viewport is scaled to fit the screen (taking [member display/window/stretch/aspect] into account). Recommended for games that use a pixel art esthetic. </member> <member name="display/window/stretch/scale" type="float" setter="" getter="" default="1.0"> + The scale factor multiplier to use for 2D elements. This multiplies the final scale factor determined by [member display/window/stretch/mode]. If using the [b]Disabled[/b] stretch mode, this scale factor is applied as-is. This can be adjusted to make the UI easier to read on certain displays. + </member> + <member name="display/window/stretch/scale_mode" type="String" setter="" getter="" default=""fractional""> + The policy to use to determine the final scale factor for 2D elements. This affects how [member display/window/stretch/scale] is applied, in addition to the automatic scale factor determined by [member display/window/stretch/mode]. </member> <member name="display/window/subwindows/embed_subwindows" type="bool" setter="" getter="" default="true"> If [code]true[/code] subwindows are embedded in the main window. 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 > b[/code] and [code]a < 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 > b[/code], or [code]a < b[/code] respectively. </description> <tutorials> </tutorials> diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml index 82498b9ba4..92cd11d720 100644 --- a/doc/classes/Window.xml +++ b/doc/classes/Window.xml @@ -569,6 +569,9 @@ <member name="content_scale_size" type="Vector2i" setter="set_content_scale_size" getter="get_content_scale_size" default="Vector2i(0, 0)"> Base size of the content (i.e. nodes that are drawn inside the window). If non-zero, [Window]'s content will be scaled when the window is resized to a different size. </member> + <member name="content_scale_stretch" type="int" setter="set_content_scale_stretch" getter="get_content_scale_stretch" enum="Window.ContentScaleStretch" default="0"> + The policy to use to determine the final scale factor for 2D elements. This affects how [member content_scale_factor] is applied, in addition to the automatic scale factor determined by [member content_scale_size]. + </member> <member name="current_screen" type="int" setter="set_current_screen" getter="get_current_screen"> The screen the window is currently on. </member> @@ -840,6 +843,12 @@ <constant name="CONTENT_SCALE_ASPECT_EXPAND" value="4" enum="ContentScaleAspect"> The content's aspect will be preserved. If the target size has different aspect from the base one, the content will stay in the top-left corner and add an extra visible area in the stretched space. </constant> + <constant name="CONTENT_SCALE_STRETCH_FRACTIONAL" value="0" enum="ContentScaleStretch"> + The content will be stretched according to a fractional factor. This fills all the space available in the window, but allows "pixel wobble" to occur due to uneven pixel scaling. + </constant> + <constant name="CONTENT_SCALE_STRETCH_INTEGER" value="1" enum="ContentScaleStretch"> + The content will be stretched only according to an integer factor, preserving sharp pixels. This may leave a black background visible on the window's edges depending on the window size. + </constant> <constant name="LAYOUT_DIRECTION_INHERITED" value="0" enum="LayoutDirection"> Automatic layout direction, determined from the parent window layout direction. </constant> diff --git a/doc/classes/int.xml b/doc/classes/int.xml index bc0da03e98..914cf75929 100644 --- a/doc/classes/int.xml +++ b/doc/classes/int.xml @@ -390,7 +390,7 @@ <operator name="operator ~"> <return type="int" /> <description> - Performs the bitwise [code]NOT[/code] operation on the [int]. Due to [url=https://en.wikipedia.org/wiki/Two%27s_complement/]2's complement[/url], it's effectively equal to [code]-(int + 1)[/code]. + Performs the bitwise [code]NOT[/code] operation on the [int]. Due to [url=https://en.wikipedia.org/wiki/Two%27s_complement]2's complement[/url], it's effectively equal to [code]-(int + 1)[/code]. [codeblock] print(~4) # Prints -5 print(~(-7)) # Prints 6 diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index aa6319f0ef..a36004209b 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -1957,14 +1957,16 @@ void MaterialStorage::global_shader_parameters_load_settings(bool p_load_texture if (gvtype >= RS::GLOBAL_VAR_TYPE_SAMPLER2D) { //textire if (!p_load_textures) { - value = RID(); continue; } String path = value; - Ref<Resource> resource = ResourceLoader::load(path); - ERR_CONTINUE(resource.is_null()); - value = resource; + if (path.is_empty()) { + value = RID(); + } else { + Ref<Resource> resource = ResourceLoader::load(path); + value = resource; + } } if (global_shader_uniforms.variables.has(name)) { diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index 1ed6839dd7..a55abe6a82 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -5905,6 +5905,64 @@ void RenderingDeviceVulkan::uniform_set_set_invalidation_callback(RID p_uniform_ us->invalidated_callback_userdata = p_userdata; } +Error RenderingDeviceVulkan::buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t p_src_offset, uint32_t p_dst_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V_MSG(draw_list, ERR_INVALID_PARAMETER, + "Copying buffers is forbidden during creation of a draw list"); + ERR_FAIL_COND_V_MSG(compute_list, ERR_INVALID_PARAMETER, + "Copying buffers is forbidden during creation of a compute list"); + + // This method assumes the barriers have been pushed prior to being called, therefore no barriers are pushed + // for the source or destination buffers before performing the copy. These masks are effectively ignored. + VkPipelineShaderStageCreateFlags src_stage_mask = 0; + VkAccessFlags src_access_mask = 0; + Buffer *src_buffer = _get_buffer_from_owner(p_src_buffer, src_stage_mask, src_access_mask, BARRIER_MASK_NO_BARRIER); + if (!src_buffer) { + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Source buffer argument is not a valid buffer of any type."); + } + + VkPipelineStageFlags dst_stage_mask = 0; + VkAccessFlags dst_access = 0; + if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { + // If the post barrier mask defines it, we indicate the destination buffer will require a barrier with these flags set + // after the copy command is queued. + dst_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT; + dst_access = VK_ACCESS_TRANSFER_WRITE_BIT; + } + + Buffer *dst_buffer = _get_buffer_from_owner(p_dst_buffer, dst_stage_mask, dst_access, p_post_barrier); + if (!dst_buffer) { + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Destination buffer argument is not a valid buffer of any type."); + } + + // Validate the copy's dimensions for both buffers. + ERR_FAIL_COND_V_MSG((p_size + p_src_offset) > src_buffer->size, ERR_INVALID_PARAMETER, "Size is larger than the source buffer."); + ERR_FAIL_COND_V_MSG((p_size + p_dst_offset) > dst_buffer->size, ERR_INVALID_PARAMETER, "Size is larger than the destination buffer."); + + // Perform the copy. + VkBufferCopy region; + region.srcOffset = p_src_offset; + region.dstOffset = p_dst_offset; + region.size = p_size; + vkCmdCopyBuffer(frames[frame].draw_command_buffer, src_buffer->buffer, dst_buffer->buffer, 1, ®ion); + +#ifdef FORCE_FULL_BARRIER + _full_barrier(true); +#else + if (dst_stage_mask == 0) { + dst_stage_mask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + } + + // As indicated by the post barrier mask, push a new barrier. + if (p_post_barrier != RD::BARRIER_MASK_NO_BARRIER) { + _buffer_memory_barrier(dst_buffer->buffer, p_dst_offset, p_size, VK_PIPELINE_STAGE_TRANSFER_BIT, dst_stage_mask, VK_ACCESS_TRANSFER_WRITE_BIT, dst_access, true); + } +#endif + + return OK; +} + Error RenderingDeviceVulkan::buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, BitField<BarrierMask> p_post_barrier) { _THREAD_SAFE_METHOD_ diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h index fd832312ac..edff19a70c 100644 --- a/drivers/vulkan/rendering_device_vulkan.h +++ b/drivers/vulkan/rendering_device_vulkan.h @@ -1152,6 +1152,7 @@ public: virtual bool uniform_set_is_valid(RID p_uniform_set); virtual void uniform_set_set_invalidation_callback(RID p_uniform_set, InvalidationCallback p_callback, void *p_userdata); + virtual Error buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t p_src_offset, uint32_t p_dst_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); virtual Error buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); // Works for any buffer. virtual Error buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); virtual Vector<uint8_t> buffer_get_data(RID p_buffer, uint32_t p_offset = 0, uint32_t p_size = 0); diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp index c167caeb7c..c1632907eb 100644 --- a/drivers/vulkan/vulkan_context.cpp +++ b/drivers/vulkan/vulkan_context.cpp @@ -504,6 +504,10 @@ Error VulkanContext::_initialize_device_extensions() { register_requested_device_extension(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, false); register_requested_device_extension(VK_KHR_MAINTENANCE_2_EXTENSION_NAME, false); + if (Engine::get_singleton()->is_generate_spirv_debug_info_enabled()) { + register_requested_device_extension(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME, true); + } + // TODO consider the following extensions: // - VK_KHR_spirv_1_4 // - VK_KHR_swapchain_mutable_format @@ -1700,17 +1704,6 @@ Error VulkanContext::_window_create(DisplayServer::WindowID p_window_id, Display Error err = _update_swap_chain(&window); ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); - VkSemaphoreCreateInfo semaphoreCreateInfo = { - /*sType*/ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, - /*pNext*/ nullptr, - /*flags*/ 0, - }; - - for (uint32_t i = 0; i < FRAME_LAG; i++) { - VkResult vkerr = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &window.image_acquired_semaphores[i]); - ERR_FAIL_COND_V(vkerr, ERR_CANT_CREATE); - } - windows[p_window_id] = window; return OK; } @@ -1760,9 +1753,6 @@ VkFramebuffer VulkanContext::window_get_framebuffer(DisplayServer::WindowID p_wi void VulkanContext::window_destroy(DisplayServer::WindowID p_window_id) { ERR_FAIL_COND(!windows.has(p_window_id)); _clean_up_swap_chain(&windows[p_window_id]); - for (uint32_t i = 0; i < FRAME_LAG; i++) { - vkDestroySemaphore(device, windows[p_window_id].image_acquired_semaphores[i], nullptr); - } vkDestroySurfaceKHR(inst, windows[p_window_id].surface, nullptr); windows.erase(p_window_id); @@ -1792,6 +1782,17 @@ Error VulkanContext::_clean_up_swap_chain(Window *window) { if (separate_present_queue) { vkDestroyCommandPool(device, window->present_cmd_pool, nullptr); } + + for (uint32_t i = 0; i < FRAME_LAG; i++) { + // Destroy the semaphores now (we'll re-create it later if we have to). + // We must do this because the semaphore cannot be reused if it's in a signaled state + // (which happens if vkAcquireNextImageKHR returned VK_ERROR_OUT_OF_DATE_KHR or VK_SUBOPTIMAL_KHR) + // The only way to reset it would be to present the swapchain... the one we just destroyed. + // And the API has no way to "unsignal" the semaphore. + vkDestroySemaphore(device, window->image_acquired_semaphores[i], nullptr); + window->image_acquired_semaphores[i] = 0; + } + return OK; } @@ -2175,6 +2176,17 @@ Error VulkanContext::_update_swap_chain(Window *window) { // Reset current buffer. window->current_buffer = 0; + VkSemaphoreCreateInfo semaphoreCreateInfo = { + /*sType*/ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + /*pNext*/ nullptr, + /*flags*/ 0, + }; + + for (uint32_t i = 0; i < FRAME_LAG; i++) { + VkResult vkerr = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &window->image_acquired_semaphores[i]); + ERR_FAIL_COND_V(vkerr, ERR_CANT_CREATE); + } + return OK; } 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/icons/CurveTexture.svg b/editor/icons/CurveTexture.svg index 50232fdc4a..a5524f145c 100644 --- a/editor/icons/CurveTexture.svg +++ b/editor/icons/CurveTexture.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v9.16A3 3 0 0 1 2 11c.331 0 .666-.008 1-.014V3h10v1.135a3 3 0 0 1 2-.006V2a1 1 0 0 0-1-1H2zm7 4v1H8v1H6v1H5v1H4v1h4.39c1.113-.567 1.968-1.454 2.61-3.473V6h-1V5H9zm4.967.988a1 1 0 0 0-.928.739c-.927 3.246-2.636 4.682-4.652 5.466C6.37 12.978 4 13 2 13a1 1 0 1 0 0 2c2 0 4.63.024 7.113-.941 2.484-.966 4.775-3.03 5.848-6.784a1 1 0 0 0-.994-1.287z" fill="#e0e0e0"/></svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v9.16A3 3 0 0 1 2 11h1V3h10v1.135a3 3 0 0 1 2 0V2a1 1 0 0 0-1-1zm7 4v1H8v1H6v1H5v1H4v1h4.39c1.113-.567 1.968-1.454 2.61-3.473V6h-1V5H9zm4.039 1.727c-.927 3.246-2.636 4.682-4.652 5.466C6.37 12.978 4 13 2 13a1 1 0 1 0 0 2c2 0 4.63.024 7.113-.941 2.484-.966 4.775-3.03 5.848-6.784a1 1 0 0 0-1.922-.548z" fill="#e0e0e0"/></svg> diff --git a/editor/icons/CurveXYZTexture.svg b/editor/icons/CurveXYZTexture.svg new file mode 100644 index 0000000000..e376dd434b --- /dev/null +++ b/editor/icons/CurveXYZTexture.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v10a2 2 0 0 1 .75-.1Q3 11.9 3 10.62V3h10v6.5a2 2 0 0 1 2 0V2a1 1 0 0 0-1-1zm7 4v1H8v1H6v1H5v1H4v.5a2 2 0 0 1 2.7.5h1.2a2 2 0 0 1 3.2 0h.9V8h-1V6h-1V5z" fill="#e0e0e0"/><g stroke-width="1.6" stroke-linecap="round" fill="none"><path d="M2 14.2q2.5 0 3-3" stroke="#ff5f5f"/><path d="M6.5 14.2q2.5 0 3-3" stroke="#5fff97"/><path d="M11 14.2q2.5 0 3-3" stroke="#5fb2ff"/></g></svg> diff --git a/editor/icons/ImageTexture.svg b/editor/icons/ImageTexture.svg index 25d4b53d00..17fc57d31d 100644 --- a/editor/icons/ImageTexture.svg +++ b/editor/icons/ImageTexture.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 1a1 1 0 0 0 -1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-12a1 1 0 0 0 -1-1zm1 2h10v8h-10zm6 2v1h-1v1h-2v1h-1v1h-1v1h2 2 2 2v-2h-1v-2h-1v-1z" fill="#e0e0e0"/></svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zm1 2h10v8H3zm6 2v1H8v1H6v1H5v1H4v1h8V8h-1V6h-1V5z" fill="#e0e0e0"/></svg> diff --git a/editor/icons/NavigationAgent2D.svg b/editor/icons/NavigationAgent2D.svg index 09b9b35262..ad11096afc 100644 --- a/editor/icons/NavigationAgent2D.svg +++ b/editor/icons/NavigationAgent2D.svg @@ -1 +1 @@ -<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1v2.5a2.5 2.5 0 0 1 0 5V15c2-3 5.007-6.03 5-9s-2-5-5-5z" fill="#8da5f3"/><path d="M8 1C5 1 3 3 3 6s3 6 5 9V8.5a1 1 0 0 1 0-5z" fill="#e0e0e0"/></svg> +<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1v2.5a2.5 2.5 0 0 1 0 5V15c2-3 5-6 5-9s-2-5-5-5z" fill="#8da5f3"/><path d="M8 1C5 1 3 3 3 6s3 6 5 9V8.5a1 1 0 0 1 0-5z" fill="#e0e0e0"/></svg> diff --git a/editor/icons/NavigationAgent3D.svg b/editor/icons/NavigationAgent3D.svg index cc34201f9b..6f887611a9 100644 --- a/editor/icons/NavigationAgent3D.svg +++ b/editor/icons/NavigationAgent3D.svg @@ -1 +1 @@ -<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1.086c-.845.156-1.476.514-1.94.988L4.05 4.059Q4 8 7 9.879V15l1-1z" fill="#e0e0e0" fill-opacity=".5"/><path d="M7 3C4 3 3 5 3 7s2 5 4 8c.338-.507.672-1.012 1-1.514v-4.76a2 2 0 1 1 0-3.453V3.084A5.663 5.663 0 0 0 7 3z" fill="#e0e0e0"/><g fill="#fc7f7f"><path d="M9 1c-.363 0-.695.03-1 .086V14l1-1c2-3 4-6 4-8s-1-4-4-4z" fill-opacity=".5"/><path d="M8 3.084v2.19a2 2 0 0 1 0 3.453v4.76c1.615-2.47 3-4.825 3-6.487 0-1.759-.774-3.517-3-3.916z"/></g></svg> +<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1C5 1 3 3 3 6s3 6 5 9V8.5a1 1 0 0 1 0-5z" fill="#e0e0e0"/><path d="M8 1v2.5a2.5 2.5 0 0 1 0 5V15c2-3 5-6 5-9s-2-5-5-5z" fill="#fc7f7f"/></svg> diff --git a/editor/icons/NavigationObstacle3D.svg b/editor/icons/NavigationObstacle3D.svg index 2f6f198aab..52080bf3fc 100644 --- a/editor/icons/NavigationObstacle3D.svg +++ b/editor/icons/NavigationObstacle3D.svg @@ -1 +1 @@ -<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><path d="M4.607 8.379C2.81 9.307 1 10.45 1 11c0 1 6 4 7 4v-2c-2.5 0-5-1-4-3z" fill-opacity=".5"/><path d="M8 .875c-.375 0-.75.375-1 1.125l-3 8c-1 2 1.5 3 4 3V9.25c-.875 0-1.75-.25-2.5-.75l1-3.5c.5.25 1 .375 1.5.375z"/></g><g fill="#fc7f7f"><path d="M11.393 8.379 12 10c1.002 2.005-1.512 3.005-4.018 3v1.998L8 15c1 0 7-3 7-4 0-.549-1.81-1.693-3.607-2.621z" fill-opacity=".5"/><path d="m8 .875-.018.002v4.498A3.323 3.323 0 0 0 9.5 5l1 3.5a4.508 4.508 0 0 1-2.518.75V13c2.506.005 5.02-.995 4.018-3L9 2C8.75 1.25 8.375.875 8 .875z"/></g></svg> +<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 .875C7.375.875 6.75 1.25 6.5 2l-3 10H8V9H5.5l1-4H8zM2 13c-1 0-1 2 0 2h6v-2z" fill="#e0e0e0"/><path d="M8 .875V5h1.5l1 4H8v3h4.5l-3-10C9.25 1.25 8.625.875 8 .875zM8 13v2h6c1 0 1-2 0-2z" fill="#fc7f7f"/></svg> diff --git a/editor/icons/PortableCompressedTexture2D.svg b/editor/icons/PortableCompressedTexture2D.svg new file mode 100644 index 0000000000..3f63ddae51 --- /dev/null +++ b/editor/icons/PortableCompressedTexture2D.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h7v-4H3V3h6V1zm6 5v1H6v1H5v1H4v1h5V6zm3-5h4v4h-4Zm0 5h4v4h-4Zm0 5h4v4h-4Z" fill="#e0e0e0"/></svg> diff --git a/editor/icons/ProxyTexture.svg b/editor/icons/ProxyTexture.svg deleted file mode 100644 index 5fe39f4da8..0000000000 --- a/editor/icons/ProxyTexture.svg +++ /dev/null @@ -1 +0,0 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M1 1v4h4V1zm6 0v2h6v8H7v4h7a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zm2 4v1H8v1H7v3h5V8h-1V6h-1V5zM1 6v4h4V6zm0 5v4h4v-4z" fill="#e0e0e0"/></svg> diff --git a/editor/plugin_config_dialog.cpp b/editor/plugin_config_dialog.cpp index 97398b8b69..7d7b156bd0 100644 --- a/editor/plugin_config_dialog.cpp +++ b/editor/plugin_config_dialog.cpp @@ -35,6 +35,7 @@ #include "editor/editor_node.h" #include "editor/editor_plugin.h" #include "editor/editor_scale.h" +#include "editor/gui/editor_validation_panel.h" #include "editor/project_settings_editor.h" #include "scene/gui/grid_container.h" @@ -96,52 +97,28 @@ void PluginConfigDialog::_on_canceled() { _clear_fields(); } -void PluginConfigDialog::_on_language_changed(const int) { - _on_required_text_changed(String()); -} - -void PluginConfigDialog::_on_required_text_changed(const String &) { +void PluginConfigDialog::_on_required_text_changed() { int lang_idx = script_option_edit->get_selected(); String ext = ScriptServer::get_language(lang_idx)->get_extension(); - Ref<Texture2D> valid_icon = get_theme_icon(SNAME("StatusSuccess"), SNAME("EditorIcons")); - Ref<Texture2D> invalid_icon = get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons")); - - // Set variables to assume all is valid - bool is_valid = true; - name_validation->set_texture(valid_icon); - subfolder_validation->set_texture(valid_icon); - script_validation->set_texture(valid_icon); - name_validation->set_tooltip_text(""); - subfolder_validation->set_tooltip_text(""); - script_validation->set_tooltip_text(""); - - // Change valid status to invalid depending on conditions. - Vector<String> errors; if (name_edit->get_text().is_empty()) { - is_valid = false; - name_validation->set_texture(invalid_icon); - name_validation->set_tooltip_text(TTR("Plugin name cannot be blank.")); + validation_panel->set_message(MSG_ID_PLUGIN, TTR("Plugin name cannot be blank."), EditorValidationPanel::MSG_ERROR); } if ((!script_edit->get_text().get_extension().is_empty() && script_edit->get_text().get_extension() != ext) || script_edit->get_text().ends_with(".")) { - is_valid = false; - script_validation->set_texture(invalid_icon); - script_validation->set_tooltip_text(vformat(TTR("Script extension must match chosen language extension (.%s)."), ext)); + validation_panel->set_message(MSG_ID_SCRIPT, vformat(TTR("Script extension must match chosen language extension (.%s)."), ext), EditorValidationPanel::MSG_ERROR); } - if (!subfolder_edit->get_text().is_empty() && !subfolder_edit->get_text().is_valid_filename()) { - is_valid = false; - subfolder_validation->set_texture(invalid_icon); - subfolder_validation->set_tooltip_text(TTR("Subfolder name is not a valid folder name.")); - } else { - String path = "res://addons/" + _get_subfolder(); - if (!_edit_mode && DirAccess::exists(path)) { // Only show this error if in "create" mode. - is_valid = false; - subfolder_validation->set_texture(invalid_icon); - subfolder_validation->set_tooltip_text(TTR("Subfolder cannot be one which already exists.")); + if (subfolder_edit->is_visible()) { + if (!subfolder_edit->get_text().is_empty() && !subfolder_edit->get_text().is_valid_filename()) { + validation_panel->set_message(MSG_ID_SUBFOLDER, TTR("Subfolder name is not a valid folder name."), EditorValidationPanel::MSG_ERROR); + } else { + String path = "res://addons/" + _get_subfolder(); + if (!_edit_mode && DirAccess::exists(path)) { // Only show this error if in "create" mode. + validation_panel->set_message(MSG_ID_SUBFOLDER, TTR("Subfolder cannot be one which already exists."), EditorValidationPanel::MSG_ERROR); + } } + } else { + validation_panel->set_message(MSG_ID_SUBFOLDER, "", EditorValidationPanel::MSG_OK); } - - get_ok_button()->set_disabled(!is_valid); } String PluginConfigDialog::_get_subfolder() { @@ -182,23 +159,20 @@ void PluginConfigDialog::config(const String &p_config_path) { _edit_mode = true; active_edit->hide(); - Object::cast_to<Label>(active_edit->get_parent()->get_child(active_edit->get_index() - 2))->hide(); + Object::cast_to<Label>(active_edit->get_parent()->get_child(active_edit->get_index() - 1))->hide(); subfolder_edit->hide(); - subfolder_validation->hide(); - Object::cast_to<Label>(subfolder_edit->get_parent()->get_child(subfolder_edit->get_index() - 2))->hide(); + Object::cast_to<Label>(subfolder_edit->get_parent()->get_child(subfolder_edit->get_index() - 1))->hide(); set_title(TTR("Edit a Plugin")); } else { _clear_fields(); _edit_mode = false; active_edit->show(); - Object::cast_to<Label>(active_edit->get_parent()->get_child(active_edit->get_index() - 2))->show(); + Object::cast_to<Label>(active_edit->get_parent()->get_child(active_edit->get_index() - 1))->show(); subfolder_edit->show(); - subfolder_validation->show(); - Object::cast_to<Label>(subfolder_edit->get_parent()->get_child(subfolder_edit->get_index() - 2))->show(); + Object::cast_to<Label>(subfolder_edit->get_parent()->get_child(subfolder_edit->get_index() - 1))->show(); set_title(TTR("Create a Plugin")); } - // Simulate text changing so the errors populate. - _on_required_text_changed(""); + validation_panel->update(); get_ok_button()->set_disabled(!_edit_mode); set_ok_button_text(_edit_mode ? TTR("Update") : TTR("Create")); @@ -218,7 +192,7 @@ PluginConfigDialog::PluginConfigDialog() { add_child(vbox); GridContainer *grid = memnew(GridContainer); - grid->set_columns(3); + grid->set_columns(2); grid->set_v_size_flags(Control::SIZE_EXPAND_FILL); vbox->add_child(grid); @@ -228,12 +202,7 @@ PluginConfigDialog::PluginConfigDialog() { name_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); grid->add_child(name_lb); - name_validation = memnew(TextureRect); - name_validation->set_v_size_flags(Control::SIZE_SHRINK_CENTER); - grid->add_child(name_validation); - name_edit = memnew(LineEdit); - name_edit->connect("text_changed", callable_mp(this, &PluginConfigDialog::_on_required_text_changed)); name_edit->set_placeholder("MyPlugin"); name_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); grid->add_child(name_edit); @@ -244,14 +213,9 @@ PluginConfigDialog::PluginConfigDialog() { subfolder_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); grid->add_child(subfolder_lb); - subfolder_validation = memnew(TextureRect); - subfolder_validation->set_v_size_flags(Control::SIZE_SHRINK_CENTER); - grid->add_child(subfolder_validation); - subfolder_edit = memnew(LineEdit); subfolder_edit->set_placeholder("\"my_plugin\" -> res://addons/my_plugin"); subfolder_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); - subfolder_edit->connect("text_changed", callable_mp(this, &PluginConfigDialog::_on_required_text_changed)); grid->add_child(subfolder_edit); // Description @@ -260,9 +224,6 @@ PluginConfigDialog::PluginConfigDialog() { desc_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); grid->add_child(desc_lb); - Control *desc_spacer = memnew(Control); - grid->add_child(desc_spacer); - desc_edit = memnew(TextEdit); desc_edit->set_custom_minimum_size(Size2(400, 80) * EDSCALE); desc_edit->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY); @@ -276,9 +237,6 @@ PluginConfigDialog::PluginConfigDialog() { author_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); grid->add_child(author_lb); - Control *author_spacer = memnew(Control); - grid->add_child(author_spacer); - author_edit = memnew(LineEdit); author_edit->set_placeholder("Godette"); author_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); @@ -290,9 +248,6 @@ PluginConfigDialog::PluginConfigDialog() { version_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); grid->add_child(version_lb); - Control *version_spacer = memnew(Control); - grid->add_child(version_spacer); - version_edit = memnew(LineEdit); version_edit->set_placeholder("1.0"); version_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); @@ -304,9 +259,6 @@ PluginConfigDialog::PluginConfigDialog() { script_option_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); grid->add_child(script_option_lb); - Control *script_opt_spacer = memnew(Control); - grid->add_child(script_opt_spacer); - script_option_edit = memnew(OptionButton); int default_lang = 0; for (int i = 0; i < ScriptServer::get_language_count(); i++) { @@ -318,7 +270,6 @@ PluginConfigDialog::PluginConfigDialog() { } script_option_edit->select(default_lang); grid->add_child(script_option_edit); - script_option_edit->connect("item_selected", callable_mp(this, &PluginConfigDialog::_on_language_changed)); // Plugin Script Name Label *script_lb = memnew(Label); @@ -326,12 +277,7 @@ PluginConfigDialog::PluginConfigDialog() { script_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); grid->add_child(script_lb); - script_validation = memnew(TextureRect); - script_validation->set_v_size_flags(Control::SIZE_SHRINK_CENTER); - grid->add_child(script_validation); - script_edit = memnew(LineEdit); - script_edit->connect("text_changed", callable_mp(this, &PluginConfigDialog::_on_required_text_changed)); script_edit->set_placeholder("\"plugin.gd\" -> res://addons/my_plugin/plugin.gd"); script_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); grid->add_child(script_edit); @@ -343,12 +289,26 @@ PluginConfigDialog::PluginConfigDialog() { active_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); grid->add_child(active_lb); - Control *active_spacer = memnew(Control); - grid->add_child(active_spacer); - active_edit = memnew(CheckBox); active_edit->set_pressed(true); grid->add_child(active_edit); + + Control *spacing = memnew(Control); + vbox->add_child(spacing); + spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE)); + + validation_panel = memnew(EditorValidationPanel); + vbox->add_child(validation_panel); + validation_panel->add_line(MSG_ID_PLUGIN, TTR("Plugin name is valid.")); + validation_panel->add_line(MSG_ID_SCRIPT, TTR("Script extension is valid.")); + validation_panel->add_line(MSG_ID_SUBFOLDER, TTR("Subfolder name is valid.")); + validation_panel->set_update_callback(callable_mp(this, &PluginConfigDialog::_on_required_text_changed)); + validation_panel->set_accept_button(get_ok_button()); + + script_option_edit->connect("item_selected", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1)); + name_edit->connect("text_changed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1)); + subfolder_edit->connect("text_changed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1)); + script_edit->connect("text_changed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1)); } PluginConfigDialog::~PluginConfigDialog() { diff --git a/editor/plugin_config_dialog.h b/editor/plugin_config_dialog.h index 50b901a39e..1221d347a7 100644 --- a/editor/plugin_config_dialog.h +++ b/editor/plugin_config_dialog.h @@ -35,12 +35,21 @@ #include "scene/gui/dialogs.h" #include "scene/gui/line_edit.h" #include "scene/gui/option_button.h" +#include "scene/gui/panel_container.h" #include "scene/gui/text_edit.h" #include "scene/gui/texture_rect.h" +class EditorValidationPanel; + class PluginConfigDialog : public ConfirmationDialog { GDCLASS(PluginConfigDialog, ConfirmationDialog); + enum { + MSG_ID_PLUGIN, + MSG_ID_SUBFOLDER, + MSG_ID_SCRIPT, + }; + LineEdit *name_edit = nullptr; LineEdit *subfolder_edit = nullptr; TextEdit *desc_edit = nullptr; @@ -50,17 +59,14 @@ class PluginConfigDialog : public ConfirmationDialog { LineEdit *script_edit = nullptr; CheckBox *active_edit = nullptr; - TextureRect *name_validation = nullptr; - TextureRect *subfolder_validation = nullptr; - TextureRect *script_validation = nullptr; + EditorValidationPanel *validation_panel = nullptr; bool _edit_mode = false; void _clear_fields(); void _on_confirmed(); void _on_canceled(); - void _on_language_changed(const int p_language); - void _on_required_text_changed(const String &p_text); + void _on_required_text_changed(); String _get_subfolder(); static String _to_absolute_plugin_path(const String &p_plugin_name); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index d05db7aa63..65563bd1a3 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -1675,6 +1675,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { if (_edit.mode != TRANSFORM_NONE && b->is_pressed()) { cancel_transform(); + break; } if (b->is_pressed()) { @@ -2007,7 +2008,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { _edit.mode = TRANSFORM_TRANSLATE; } - if (_edit.mode == TRANSFORM_NONE) { + if (_edit.mode == TRANSFORM_NONE || _edit.numeric_input != 0 || _edit.numeric_next_decimal != 0) { return; } @@ -2145,6 +2146,43 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { return; } + if (_edit.instant) { + // In a Blender-style transform, numbers set the magnitude of the transform. + // E.g. pressing g4.5x means "translate 4.5 units along the X axis". + // Use the Unicode value because we care about the text, not the actual keycode. + // This ensures numbers work consistently across different keyboard language layouts. + bool processed = true; + Key key = k->get_physical_keycode(); + char32_t unicode = k->get_unicode(); + if (unicode >= '0' && unicode <= '9') { + uint32_t value = uint32_t(unicode - Key::KEY_0); + if (_edit.numeric_next_decimal < 0) { + _edit.numeric_input = _edit.numeric_input + value * Math::pow(10.0, _edit.numeric_next_decimal--); + } else { + _edit.numeric_input = _edit.numeric_input * 10 + value; + } + update_transform_numeric(); + } else if (unicode == '-') { + _edit.numeric_negate = !_edit.numeric_negate; + update_transform_numeric(); + } else if (unicode == '.') { + if (_edit.numeric_next_decimal == 0) { + _edit.numeric_next_decimal = -1; + } + } else if (key == Key::ENTER || key == Key::KP_ENTER || key == Key::SPACE) { + commit_transform(); + } else { + processed = false; + } + + if (processed) { + // Ignore mouse inputs once we receive a numeric input. + set_process_input(false); + accept_event(); + return; + } + } + if (EDITOR_GET("editors/3d/navigation/emulate_numpad")) { const Key code = k->get_physical_keycode(); if (code >= Key::KEY_0 && code <= Key::KEY_9) { @@ -2165,26 +2203,19 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { } else { // We're actively transforming, handle keys specially TransformPlane new_plane = TRANSFORM_VIEW; - String new_message; if (ED_IS_SHORTCUT("spatial_editor/lock_transform_x", p_event)) { new_plane = TRANSFORM_X_AXIS; - new_message = TTR("X-Axis Transform."); } else if (ED_IS_SHORTCUT("spatial_editor/lock_transform_y", p_event)) { new_plane = TRANSFORM_Y_AXIS; - new_message = TTR("Y-Axis Transform."); } else if (ED_IS_SHORTCUT("spatial_editor/lock_transform_z", p_event)) { new_plane = TRANSFORM_Z_AXIS; - new_message = TTR("Z-Axis Transform."); } else if (_edit.mode != TRANSFORM_ROTATE) { // rotating on a plane doesn't make sense if (ED_IS_SHORTCUT("spatial_editor/lock_transform_yz", p_event)) { new_plane = TRANSFORM_YZ; - new_message = TTR("YZ-Plane Transform."); } else if (ED_IS_SHORTCUT("spatial_editor/lock_transform_xz", p_event)) { new_plane = TRANSFORM_XZ; - new_message = TTR("XZ-Plane Transform."); } else if (ED_IS_SHORTCUT("spatial_editor/lock_transform_xy", p_event)) { new_plane = TRANSFORM_XY; - new_message = TTR("XY-Plane Transform."); } } @@ -2201,8 +2232,11 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { _edit.plane = TRANSFORM_VIEW; spatial_editor->set_local_coords_enabled(false); } - update_transform(Input::get_singleton()->is_key_pressed(Key::SHIFT)); - set_message(new_message, 2); + if (_edit.numeric_input != 0 || _edit.numeric_next_decimal != 0) { + update_transform_numeric(); + } else { + update_transform(Input::get_singleton()->is_key_pressed(Key::SHIFT)); + } accept_event(); return; } @@ -4575,6 +4609,43 @@ void Node3DEditorViewport::commit_transform() { set_message(""); } +void Node3DEditorViewport::apply_transform(Vector3 p_motion, double p_snap) { + bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); + List<Node *> &selection = editor_selection->get_selected_node_list(); + for (Node *E : selection) { + Node3D *sp = Object::cast_to<Node3D>(E); + if (!sp) { + continue; + } + + Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp); + if (!se) { + continue; + } + + if (sp->has_meta("_edit_lock_")) { + continue; + } + + if (se->gizmo.is_valid()) { + for (KeyValue<int, Transform3D> &GE : se->subgizmos) { + Transform3D xform = GE.value; + Transform3D new_xform = _compute_transform(_edit.mode, se->original * xform, xform, p_motion, p_snap, local_coords, _edit.plane != TRANSFORM_VIEW); // Force orthogonal with subgizmo. + if (!local_coords) { + new_xform = se->original.affine_inverse() * new_xform; + } + se->gizmo->set_subgizmo_transform(GE.key, new_xform); + } + } else { + Transform3D new_xform = _compute_transform(_edit.mode, se->original, se->original_local, p_motion, p_snap, local_coords, sp->get_rotation_edit_mode() != Node3D::ROTATION_EDIT_MODE_BASIS && _edit.plane != TRANSFORM_VIEW); + _transform_gizmo_apply(se->sp, new_xform, local_coords); + } + } + + spatial_editor->update_transform_gizmo(); + surface->queue_redraw(); +} + // Update the current transform operation in response to an input. void Node3DEditorViewport::update_transform(bool p_shift) { Vector3 ray_pos = _get_ray_pos(_edit.mouse_pos); @@ -4670,43 +4741,11 @@ void Node3DEditorViewport::update_transform(bool p_shift) { set_message(TTR("Scaling:") + " (" + String::num(motion_snapped.x, snap_step_decimals) + ", " + String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")"); if (local_coords) { + // TODO: needed? motion = _edit.original.basis.inverse().xform(motion); } - List<Node *> &selection = editor_selection->get_selected_node_list(); - for (Node *E : selection) { - Node3D *sp = Object::cast_to<Node3D>(E); - if (!sp) { - continue; - } - - Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp); - if (!se) { - continue; - } - - if (sp->has_meta("_edit_lock_")) { - continue; - } - - if (se->gizmo.is_valid()) { - for (KeyValue<int, Transform3D> &GE : se->subgizmos) { - Transform3D xform = GE.value; - Transform3D new_xform = _compute_transform(TRANSFORM_SCALE, se->original * xform, xform, motion, snap, local_coords, _edit.plane != TRANSFORM_VIEW); // Force orthogonal with subgizmo. - if (!local_coords) { - new_xform = se->original.affine_inverse() * new_xform; - } - se->gizmo->set_subgizmo_transform(GE.key, new_xform); - } - } else { - Transform3D new_xform = _compute_transform(TRANSFORM_SCALE, se->original, se->original_local, motion, snap, local_coords, sp->get_rotation_edit_mode() != Node3D::ROTATION_EDIT_MODE_BASIS && _edit.plane != TRANSFORM_VIEW); - _transform_gizmo_apply(se->sp, new_xform, local_coords); - } - } - - spatial_editor->update_transform_gizmo(); - surface->queue_redraw(); - + apply_transform(motion, snap); } break; case TRANSFORM_TRANSLATE: { @@ -4776,38 +4815,7 @@ void Node3DEditorViewport::update_transform(bool p_shift) { motion = spatial_editor->get_gizmo_transform().basis.inverse().xform(motion); } - List<Node *> &selection = editor_selection->get_selected_node_list(); - for (Node *E : selection) { - Node3D *sp = Object::cast_to<Node3D>(E); - if (!sp) { - continue; - } - - Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp); - if (!se) { - continue; - } - - if (sp->has_meta("_edit_lock_")) { - continue; - } - - if (se->gizmo.is_valid()) { - for (KeyValue<int, Transform3D> &GE : se->subgizmos) { - Transform3D xform = GE.value; - Transform3D new_xform = _compute_transform(TRANSFORM_TRANSLATE, se->original * xform, xform, motion, snap, local_coords, true); // Force orthogonal with subgizmo. - new_xform = se->original.affine_inverse() * new_xform; - se->gizmo->set_subgizmo_transform(GE.key, new_xform); - } - } else { - Transform3D new_xform = _compute_transform(TRANSFORM_TRANSLATE, se->original, se->original_local, motion, snap, local_coords, sp->get_rotation_edit_mode() != Node3D::ROTATION_EDIT_MODE_BASIS); - _transform_gizmo_apply(se->sp, new_xform, false); - } - } - - spatial_editor->update_transform_gizmo(); - surface->queue_redraw(); - + apply_transform(motion, snap); } break; case TRANSFORM_ROTATE: { @@ -4876,53 +4884,85 @@ void Node3DEditorViewport::update_transform(bool p_shift) { bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); // Disable local transformation for TRANSFORM_VIEW - List<Node *> &selection = editor_selection->get_selected_node_list(); - for (Node *E : selection) { - Node3D *sp = Object::cast_to<Node3D>(E); - if (!sp) { - continue; - } - - Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp); - if (!se) { - continue; - } - - if (sp->has_meta("_edit_lock_")) { - continue; - } - - Vector3 compute_axis = local_coords ? local_axis : global_axis; - if (se->gizmo.is_valid()) { - for (KeyValue<int, Transform3D> &GE : se->subgizmos) { - Transform3D xform = GE.value; - - Transform3D new_xform = _compute_transform(TRANSFORM_ROTATE, se->original * xform, xform, compute_axis, angle, local_coords, true); // Force orthogonal with subgizmo. - if (!local_coords) { - new_xform = se->original.affine_inverse() * new_xform; - } - se->gizmo->set_subgizmo_transform(GE.key, new_xform); - } - } else { - Transform3D new_xform = _compute_transform(TRANSFORM_ROTATE, se->original, se->original_local, compute_axis, angle, local_coords, sp->get_rotation_edit_mode() != Node3D::ROTATION_EDIT_MODE_BASIS); - _transform_gizmo_apply(se->sp, new_xform, local_coords); - } - } - - spatial_editor->update_transform_gizmo(); - surface->queue_redraw(); - + Vector3 compute_axis = local_coords ? local_axis : global_axis; + apply_transform(compute_axis, angle); } break; default: { } } } -// Perform cleanup after a transform operation is committed or canceled. +void Node3DEditorViewport::update_transform_numeric() { + Vector3 motion; + switch (_edit.plane) { + case TRANSFORM_VIEW: { + switch (_edit.mode) { + case TRANSFORM_TRANSLATE: + motion = Vector3(1, 0, 0); + break; + case TRANSFORM_ROTATE: + motion = spatial_editor->get_gizmo_transform().basis.xform_inv(_get_camera_normal()).normalized(); + break; + case TRANSFORM_SCALE: + motion = Vector3(1, 1, 1); + break; + case TRANSFORM_NONE: + ERR_FAIL_MSG("_edit.mode cannot be TRANSFORM_NONE in update_transform_numeric."); + } + break; + } + case TRANSFORM_X_AXIS: + motion = Vector3(1, 0, 0); + break; + case TRANSFORM_Y_AXIS: + motion = Vector3(0, 1, 0); + break; + case TRANSFORM_Z_AXIS: + motion = Vector3(0, 0, 1); + break; + case TRANSFORM_XY: + motion = Vector3(1, 1, 0); + break; + case TRANSFORM_XZ: + motion = Vector3(1, 0, 1); + break; + case TRANSFORM_YZ: + motion = Vector3(0, 1, 1); + break; + } + + double value = _edit.numeric_input * (_edit.numeric_negate ? -1 : 1); + double extra = 0.0; + switch (_edit.mode) { + case TRANSFORM_TRANSLATE: + motion *= value; + set_message(vformat(TTR("Translating %s."), motion)); + break; + case TRANSFORM_ROTATE: + extra = Math::deg_to_rad(value); + set_message(vformat(TTR("Rotating %f degrees."), value)); + break; + case TRANSFORM_SCALE: + // To halve the size of an object in Blender, you scale it by 0.5. + // Doing the same in Godot is considered scaling it by -0.5. + motion *= (value - 1.0); + set_message(vformat(TTR("Scaling %s."), motion)); + break; + case TRANSFORM_NONE: + ERR_FAIL_MSG("_edit.mode cannot be TRANSFORM_NONE in update_transform_numeric."); + } + + apply_transform(motion, extra); +} + +// Perform cleanup after a transform operation is committed or cancelled. void Node3DEditorViewport::finish_transform() { - spatial_editor->set_local_coords_enabled(_edit.original_local); _edit.mode = TRANSFORM_NONE; _edit.instant = false; + _edit.numeric_input = 0; + _edit.numeric_next_decimal = 0; + _edit.numeric_negate = false; + spatial_editor->set_local_coords_enabled(_edit.original_local); spatial_editor->update_transform_gizmo(); surface->queue_redraw(); set_process_input(false); diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index 79674bdd64..e58e224ff4 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -349,6 +349,15 @@ private: Variant gizmo_initial_value; bool original_local; bool instant; + + // Numeric blender-style transforms (e.g. 'g5x'). + // numeric_input tracks the current input value, e.g. 1.23. + // numeric_negate indicates whether '-' has been pressed to negate the value + // while numeric_next_decimal is 0, numbers are input before the decimal point + // after pressing '.', numeric next decimal changes to -1, and decrements after each press. + double numeric_input = 0.0; + bool numeric_negate = false; + int numeric_next_decimal = 0; } _edit; struct Cursor { @@ -445,7 +454,9 @@ private: void begin_transform(TransformMode p_mode, bool instant); void commit_transform(); + void apply_transform(Vector3 p_motion, double p_snap); void update_transform(bool p_shift); + void update_transform_numeric(); void finish_transform(); void register_shortcut_action(const String &p_path, const String &p_name, Key p_keycode, bool p_physical = false); 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_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index c98d9086d1..b1111be006 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -2655,6 +2655,12 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { TileSetAtlasSourceEditor::~TileSetAtlasSourceEditor() { memdelete(tile_proxy_object); memdelete(atlas_source_proxy_object); + + // Remove listener for old objects, so the TileSet doesn't + // try to call the destroyed TileSetAtlasSourceEditor. + if (tile_set.is_valid()) { + tile_set->disconnect_changed(callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed)); + } } ////// EditorPropertyTilePolygon ////// diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp index 121b70a74f..e432704702 100644 --- a/editor/plugins/tiles/tiles_editor_plugin.cpp +++ b/editor/plugins/tiles/tiles_editor_plugin.cpp @@ -325,6 +325,7 @@ void TileMapEditorPlugin::_tile_map_changed() { } void TileMapEditorPlugin::_update_tile_map() { + TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); if (tile_map) { Ref<TileSet> tile_set = tile_map->get_tileset(); if (tile_set.is_valid() && edited_tileset != tile_set->get_instance_id()) { @@ -347,11 +348,17 @@ void TileMapEditorPlugin::_notification(int p_notification) { } void TileMapEditorPlugin::edit(Object *p_object) { + TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); if (tile_map) { tile_map->disconnect("changed", callable_mp(this, &TileMapEditorPlugin::_tile_map_changed)); } tile_map = Object::cast_to<TileMap>(p_object); + if (tile_map) { + tile_map_id = tile_map->get_instance_id(); + } else { + tile_map_id = ObjectID(); + } editor->edit(tile_map); if (tile_map) { diff --git a/editor/plugins/tiles/tiles_editor_plugin.h b/editor/plugins/tiles/tiles_editor_plugin.h index f8e944af81..81cb48eb00 100644 --- a/editor/plugins/tiles/tiles_editor_plugin.h +++ b/editor/plugins/tiles/tiles_editor_plugin.h @@ -115,7 +115,7 @@ class TileMapEditorPlugin : public EditorPlugin { TileMapEditor *editor = nullptr; Button *button = nullptr; - TileMap *tile_map = nullptr; + ObjectID tile_map_id; bool tile_map_changed_needs_update = false; ObjectID edited_tileset; 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 4bb5e7bf13..e6dd576b8a 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -458,6 +458,7 @@ void Main::print_help(const char *p_binary) { #if DEBUG_ENABLED OS::get_singleton()->print(" --gpu-abort Abort on graphics API usage errors (usually validation layer errors). May help see the problem if your system freezes.\n"); #endif + OS::get_singleton()->print(" --generate-spirv-debug-info Generate SPIR-V debug information. This allows source-level shader debugging with RenderDoc.\n"); OS::get_singleton()->print(" --remote-debug <uri> Remote debug (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007).\n"); OS::get_singleton()->print(" --single-threaded-scene Scene tree runs in single-threaded mode. Sub-thread groups are disabled and run on the main thread.\n"); #if defined(DEBUG_ENABLED) @@ -1019,6 +1020,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } else if (I->get() == "--gpu-abort") { Engine::singleton->abort_on_gpu_errors = true; #endif + } else if (I->get() == "--generate-spirv-debug-info") { + Engine::singleton->generate_spirv_debug_info = true; } else if (I->get() == "--tablet-driver") { if (I->next()) { tablet_driver = I->next()->get(); @@ -1540,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(); @@ -1567,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; @@ -3122,6 +3130,7 @@ bool Main::start() { Size2i stretch_size = Size2i(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height")); real_t stretch_scale = GLOBAL_GET("display/window/stretch/scale"); + String stretch_scale_mode = GLOBAL_GET("display/window/stretch/scale_mode"); Window::ContentScaleMode cs_sm = Window::CONTENT_SCALE_MODE_DISABLED; if (stretch_mode == "canvas_items") { @@ -3141,8 +3150,14 @@ bool Main::start() { cs_aspect = Window::CONTENT_SCALE_ASPECT_EXPAND; } + Window::ContentScaleStretch cs_stretch = Window::CONTENT_SCALE_STRETCH_FRACTIONAL; + if (stretch_scale_mode == "integer") { + cs_stretch = Window::CONTENT_SCALE_STRETCH_INTEGER; + } + sml->get_root()->set_content_scale_mode(cs_sm); sml->get_root()->set_content_scale_aspect(cs_aspect); + sml->get_root()->set_content_scale_stretch(cs_stretch); sml->get_root()->set_content_scale_size(stretch_size); sml->get_root()->set_content_scale_factor(stretch_scale); diff --git a/methods.py b/methods.py index 571a3f739e..7c1781d699 100644 --- a/methods.py +++ b/methods.py @@ -868,6 +868,9 @@ def generate_vs_project(env, num_jobs, project_name="godot"): if env["custom_modules"]: common_build_postfix.append("custom_modules=%s" % env["custom_modules"]) + if env["windows_subsystem"] == "console": + common_build_postfix.append("windows_subsystem=console") + if env["precision"] == "double": common_build_postfix.append("precision=double") 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/csg/doc_classes/CSGMesh3D.xml b/modules/csg/doc_classes/CSGMesh3D.xml index 9a0f121e19..96d89ff486 100644 --- a/modules/csg/doc_classes/CSGMesh3D.xml +++ b/modules/csg/doc_classes/CSGMesh3D.xml @@ -16,7 +16,8 @@ </member> <member name="mesh" type="Mesh" setter="set_mesh" getter="get_mesh"> The [Mesh] resource to use as a CSG shape. - [b]Note:[/b] When using an [ArrayMesh], avoid meshes with vertex normals unless a flat shader is required. By default, CSGMesh will ignore the mesh's vertex normals and use a smooth shader calculated using the faces' normals. If a flat shader is required, ensure that all faces' vertex normals are parallel. + [b]Note:[/b] When using an [ArrayMesh], all vertex attributes except [constant Mesh.ARRAY_VERTEX], [constant Mesh.ARRAY_NORMAL] and [constant Mesh.ARRAY_TEX_UV] are left unused. Only [constant Mesh.ARRAY_VERTEX] and [constant Mesh.ARRAY_TEX_UV] will be passed to the GPU. + [constant Mesh.ARRAY_NORMAL] is only used to determine which faces require the use of flat shading. By default, CSGMesh will ignore the mesh's vertex normals, recalculate them for each vertex and use a smooth shader. If a flat shader is required for a face, ensure that all vertex normals of the face are approximately equal. </member> </members> </class> 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 cb04913620..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, result.native_type)) { - push_error(R"*(Cannot use shorthand "get_node()" notation ("$") on a class that isn't a node.)*", p_get_node); + 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 (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); } @@ -3469,6 +3478,9 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod for (GDScriptParser::ClassNode *script_class : script_classes) { if (p_base == nullptr && script_class->identifier && script_class->identifier->name == name) { reduce_identifier_from_base_set_class(p_identifier, script_class->get_datatype()); + if (script_class->outer != nullptr) { + p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CLASS; + } return; } diff --git a/modules/gdscript/gdscript_lambda_callable.cpp b/modules/gdscript/gdscript_lambda_callable.cpp index 9e14e43a58..3b89f077bd 100644 --- a/modules/gdscript/gdscript_lambda_callable.cpp +++ b/modules/gdscript/gdscript_lambda_callable.cpp @@ -67,6 +67,10 @@ ObjectID GDScriptLambdaCallable::get_object() const { return script->get_instance_id(); } +StringName GDScriptLambdaCallable::get_method() const { + return function->get_name(); +} + void GDScriptLambdaCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { int captures_amount = captures.size(); diff --git a/modules/gdscript/gdscript_lambda_callable.h b/modules/gdscript/gdscript_lambda_callable.h index 33bdf6dfc1..1c7a18fb9d 100644 --- a/modules/gdscript/gdscript_lambda_callable.h +++ b/modules/gdscript/gdscript_lambda_callable.h @@ -56,6 +56,7 @@ public: CompareEqualFunc get_compare_equal_func() const override; CompareLessFunc get_compare_less_func() const override; ObjectID get_object() const override; + StringName get_method() const override; void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override; GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptFunction *p_function, const Vector<Variant> &p_captures); diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index debc85ebbf..b76ceea11f 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -2446,7 +2446,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_operator(Expression complete_extents(operation); if (operation->right_operand == nullptr) { - push_error(vformat(R"(Expected expression after "%s" operator.")", op.get_name())); + push_error(vformat(R"(Expected expression after "%s" operator.)", op.get_name())); } // TODO: Also for unary, ternary, and assignment. @@ -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_rpc_callable.cpp b/modules/gdscript/gdscript_rpc_callable.cpp index a4dd8a8d3c..265e624b6c 100644 --- a/modules/gdscript/gdscript_rpc_callable.cpp +++ b/modules/gdscript/gdscript_rpc_callable.cpp @@ -63,6 +63,10 @@ ObjectID GDScriptRPCCallable::get_object() const { return object->get_instance_id(); } +StringName GDScriptRPCCallable::get_method() const { + return method; +} + void GDScriptRPCCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { r_return_value = object->callp(method, p_arguments, p_argcount, r_call_error); } diff --git a/modules/gdscript/gdscript_rpc_callable.h b/modules/gdscript/gdscript_rpc_callable.h index c1007b18b0..66052157be 100644 --- a/modules/gdscript/gdscript_rpc_callable.h +++ b/modules/gdscript/gdscript_rpc_callable.h @@ -51,6 +51,7 @@ public: CompareEqualFunc get_compare_equal_func() const override; CompareLessFunc get_compare_less_func() const override; ObjectID get_object() const override; + StringName get_method() const override; void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override; Error rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const override; 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/analyzer/features/inner_class_access_from_inside.gd b/modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.gd new file mode 100644 index 0000000000..51c589b8e0 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.gd @@ -0,0 +1,21 @@ +# GH-80508 + +class A: + func a(): + return A.new() + func b(): + return B.new() + +class B: + func a(): + return A.new() + func b(): + return B.new() + +func test(): + var a := A.new() + var b := B.new() + print(a.a() is A) + print(a.b() is B) + print(b.a() is A) + print(b.b() is B) diff --git a/modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.out b/modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.out new file mode 100644 index 0000000000..f9783e4362 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.out @@ -0,0 +1,5 @@ +GDTEST_OK +true +true +true +true diff --git a/modules/glslang/register_types.cpp b/modules/glslang/register_types.cpp index 622910761d..2b070c24b8 100644 --- a/modules/glslang/register_types.cpp +++ b/modules/glslang/register_types.cpp @@ -32,6 +32,7 @@ #include "glslang_resource_limits.h" +#include "core/config/engine.h" #include "servers/rendering/rendering_device.h" #include <glslang/Include/Types.h> @@ -56,7 +57,6 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage glslang::EShTargetClientVersion ClientVersion = glslang::EShTargetVulkan_1_2; glslang::EShTargetLanguageVersion TargetVersion = glslang::EShTargetSpv_1_5; - glslang::TShader::ForbidIncluder includer; if (capabilities->device_family == RenderingDevice::DeviceFamily::DEVICE_VULKAN) { if (capabilities->version_major == 1 && capabilities->version_minor == 0) { @@ -127,23 +127,10 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage } EShMessages messages = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules); - const int DefaultVersion = 100; - std::string pre_processed_code; - - //preprocess - if (!shader.preprocess(&DefaultTBuiltInResource, DefaultVersion, ENoProfile, false, false, messages, &pre_processed_code, includer)) { - if (r_error) { - (*r_error) = "Failed pre-process:\n"; - (*r_error) += shader.getInfoLog(); - (*r_error) += "\n"; - (*r_error) += shader.getInfoDebugLog(); - } - - return ret; + if (Engine::get_singleton()->is_generate_spirv_debug_info_enabled()) { + messages = (EShMessages)(messages | EShMsgDebugInfo); } - //set back.. - cs_strings = pre_processed_code.c_str(); - shader.setStrings(&cs_strings, 1); + const int DefaultVersion = 100; //parse if (!shader.parse(&DefaultTBuiltInResource, DefaultVersion, false, messages)) { @@ -174,6 +161,13 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage std::vector<uint32_t> SpirV; spv::SpvBuildLogger logger; glslang::SpvOptions spvOptions; + + if (Engine::get_singleton()->is_generate_spirv_debug_info_enabled()) { + spvOptions.generateDebugInfo = true; + spvOptions.emitNonSemanticShaderDebugInfo = true; + spvOptions.emitNonSemanticShaderDebugSource = true; + } + glslang::GlslangToSpv(*program.getIntermediate(stages[p_stage]), SpirV, &logger, &spvOptions); ret.resize(SpirV.size() * sizeof(uint32_t)); @@ -188,7 +182,7 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage static String _get_cache_key_function_glsl(const RenderingDevice *p_render_device) { const RD::Capabilities *capabilities = p_render_device->get_device_capabilities(); String version; - version = "SpirVGen=" + itos(glslang::GetSpirvGeneratorVersion()) + ", major=" + itos(capabilities->version_major) + ", minor=" + itos(capabilities->version_minor) + " , subgroup_size=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_SIZE)) + " , subgroup_ops=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_OPERATIONS)) + " , subgroup_in_shaders=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_IN_SHADERS)); + version = "SpirVGen=" + itos(glslang::GetSpirvGeneratorVersion()) + ", major=" + itos(capabilities->version_major) + ", minor=" + itos(capabilities->version_minor) + " , subgroup_size=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_SIZE)) + " , subgroup_ops=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_OPERATIONS)) + " , subgroup_in_shaders=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_IN_SHADERS)) + " , debug=" + itos(Engine::get_singleton()->is_generate_spirv_debug_info_enabled()); return version; } diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 342c94cbd9..006aca6c73 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -148,7 +148,7 @@ static String fix_doc_description(const String &p_bbcode) { .strip_edges(); } -String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype) { +String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype, bool p_is_signal) { // Based on the version in EditorHelp if (p_bbcode.is_empty()) { @@ -305,11 +305,11 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf _append_xml_enum(xml_output, target_itype, target_cname, link_target, link_target_parts); } else if (link_tag == "constant") { _append_xml_constant(xml_output, target_itype, target_cname, link_target, link_target_parts); + } else if (link_tag == "param") { + _append_xml_param(xml_output, link_target, p_is_signal); } else if (link_tag == "theme_item") { // We do not declare theme_items in any way in C#, so there is nothing to reference _append_xml_undeclared(xml_output, link_target); - } else if (link_tag == "param") { - _append_xml_undeclared(xml_output, snake_to_camel_case(link_target, false)); } pos = brk_end + 1; @@ -684,7 +684,7 @@ void BindingsGenerator::_append_xml_constant(StringBuilder &p_xml_output, const p_xml_output.append("<see cref=\"" BINDINGS_NAMESPACE "."); p_xml_output.append(p_target_itype->proxy_name); p_xml_output.append("."); - p_xml_output.append(target_ienum->cname); + p_xml_output.append(target_ienum->proxy_name); p_xml_output.append("."); p_xml_output.append(target_iconst->proxy_name); p_xml_output.append("\"/>"); @@ -725,7 +725,7 @@ void BindingsGenerator::_append_xml_constant_in_global_scope(StringBuilder &p_xm if (target_iconst) { p_xml_output.append("<see cref=\"" BINDINGS_NAMESPACE "."); - p_xml_output.append(target_ienum->cname); + p_xml_output.append(target_ienum->proxy_name); p_xml_output.append("."); p_xml_output.append(target_iconst->proxy_name); p_xml_output.append("\"/>"); @@ -736,6 +736,21 @@ void BindingsGenerator::_append_xml_constant_in_global_scope(StringBuilder &p_xm } } +void BindingsGenerator::_append_xml_param(StringBuilder &p_xml_output, const String &p_link_target, bool p_is_signal) { + const String link_target = snake_to_camel_case(p_link_target); + + if (!p_is_signal) { + p_xml_output.append("<paramref name=\""); + p_xml_output.append(link_target); + p_xml_output.append("\"/>"); + } else { + // Documentation in C# is added to an event, not the delegate itself; + // as such, we treat these parameters as codeblocks instead. + // See: https://github.com/godotengine/godot/pull/65529 + _append_xml_undeclared(p_xml_output, link_target); + } +} + void BindingsGenerator::_append_xml_undeclared(StringBuilder &p_xml_output, const String &p_link_target) { p_xml_output.append("<c>"); p_xml_output.append(p_link_target); @@ -981,7 +996,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) { for (const EnumInterface &ienum : global_enums) { CRASH_COND(ienum.constants.is_empty()); - String enum_proxy_name = ienum.cname.operator String(); + String enum_proxy_name = ienum.proxy_name; bool enum_in_static_class = false; @@ -995,7 +1010,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) { _log("Declaring global enum '%s' inside struct '%s'\n", enum_proxy_name.utf8().get_data(), enum_class_name.utf8().get_data()); p_output.append("\npublic partial struct "); - p_output.append(pascal_to_pascal_case(enum_class_name)); + p_output.append(enum_class_name); p_output.append("\n" OPEN_BLOCK); } @@ -1004,7 +1019,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) { } p_output.append("\npublic enum "); - p_output.append(pascal_to_pascal_case(enum_proxy_name)); + p_output.append(enum_proxy_name); p_output.append(" : long"); p_output.append("\n" OPEN_BLOCK); @@ -1469,7 +1484,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str } output.append(MEMBER_BEGIN "public enum "); - output.append(pascal_to_pascal_case(ienum.cname.operator String())); + output.append(ienum.proxy_name); output.append(" : long"); output.append(MEMBER_BEGIN OPEN_BLOCK); @@ -2330,31 +2345,31 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf // Generate signal { - p_output.append(MEMBER_BEGIN "/// <summary>\n"); - p_output.append(INDENT1 "/// "); - p_output.append("Represents the method that handles the "); - p_output.append("<see cref=\"" BINDINGS_NAMESPACE "." + p_itype.proxy_name + "." + p_isignal.proxy_name + "\"/>"); - p_output.append(" event of a "); - p_output.append("<see cref=\"" BINDINGS_NAMESPACE "." + p_itype.proxy_name + "\"/>"); - p_output.append(" class.\n"); - p_output.append(INDENT1 "/// </summary>"); - - if (p_isignal.is_deprecated) { - if (p_isignal.deprecation_message.is_empty()) { - WARN_PRINT("An empty deprecation message is discouraged. Signal: '" + p_isignal.proxy_name + "'."); - } - - p_output.append(MEMBER_BEGIN "[Obsolete(\""); - p_output.append(p_isignal.deprecation_message); - p_output.append("\")]"); - } - bool is_parameterless = p_isignal.arguments.size() == 0; // Delegate name is [SignalName]EventHandler String delegate_name = is_parameterless ? "Action" : p_isignal.proxy_name + "EventHandler"; if (!is_parameterless) { + p_output.append(MEMBER_BEGIN "/// <summary>\n"); + p_output.append(INDENT1 "/// "); + p_output.append("Represents the method that handles the "); + p_output.append("<see cref=\"" BINDINGS_NAMESPACE "." + p_itype.proxy_name + "." + p_isignal.proxy_name + "\"/>"); + p_output.append(" event of a "); + p_output.append("<see cref=\"" BINDINGS_NAMESPACE "." + p_itype.proxy_name + "\"/>"); + p_output.append(" class.\n"); + p_output.append(INDENT1 "/// </summary>"); + + if (p_isignal.is_deprecated) { + if (p_isignal.deprecation_message.is_empty()) { + WARN_PRINT("An empty deprecation message is discouraged. Signal: '" + p_isignal.proxy_name + "'."); + } + + p_output.append(MEMBER_BEGIN "[Obsolete(\""); + p_output.append(p_isignal.deprecation_message); + p_output.append("\")]"); + } + // Generate delegate p_output.append(MEMBER_BEGIN "public delegate void "); p_output.append(delegate_name); @@ -2390,7 +2405,7 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf } if (p_isignal.method_doc && p_isignal.method_doc->description.size()) { - String xml_summary = bbcode_to_xml(fix_doc_description(p_isignal.method_doc->description), &p_itype); + String xml_summary = bbcode_to_xml(fix_doc_description(p_isignal.method_doc->description), &p_itype, true); Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>(); if (summary_lines.size()) { @@ -3316,8 +3331,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() { enum_proxy_name += "Enum"; enum_proxy_cname = StringName(enum_proxy_name); } - EnumInterface ienum(enum_proxy_cname); - ienum.is_flags = E.value.is_bitfield; + EnumInterface ienum(enum_proxy_cname, enum_proxy_name, E.value.is_bitfield); const List<StringName> &enum_constants = E.value.constants; for (const StringName &constant_cname : enum_constants) { String constant_name = constant_cname.operator String(); @@ -3950,8 +3964,7 @@ void BindingsGenerator::_populate_global_constants() { iconstant.const_doc = const_doc; if (enum_name != StringName()) { - EnumInterface ienum(enum_name); - ienum.is_flags = CoreConstants::is_global_constant_bitfield(i); + EnumInterface ienum(enum_name, pascal_to_pascal_case(enum_name.operator String()), CoreConstants::is_global_constant_bitfield(i)); List<EnumInterface>::Element *enum_match = global_enums.find(ienum); if (enum_match) { enum_match->get().constants.push_back(iconstant); @@ -3969,7 +3982,7 @@ void BindingsGenerator::_populate_global_constants() { enum_itype.is_enum = true; enum_itype.name = ienum.cname.operator String(); enum_itype.cname = ienum.cname; - enum_itype.proxy_name = pascal_to_pascal_case(enum_itype.name); + enum_itype.proxy_name = ienum.proxy_name; TypeInterface::postsetup_enum_type(enum_itype); enum_types.insert(enum_itype.cname, enum_itype); diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index d4c7a59e74..6118576bb6 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -60,6 +60,7 @@ class BindingsGenerator { struct EnumInterface { StringName cname; + String proxy_name; List<ConstantInterface> constants; bool is_flags = false; @@ -69,8 +70,10 @@ class BindingsGenerator { EnumInterface() {} - EnumInterface(const StringName &p_cname) { + EnumInterface(const StringName &p_cname, const String &p_proxy_name, bool p_is_flags) { cname = p_cname; + proxy_name = p_proxy_name; + is_flags = p_is_flags; } }; @@ -751,7 +754,7 @@ class BindingsGenerator { return p_type->name; } - String bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype); + String bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype, bool p_is_signal = false); void _append_xml_method(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector<String> &p_link_target_parts); void _append_xml_member(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector<String> &p_link_target_parts); @@ -759,6 +762,7 @@ class BindingsGenerator { void _append_xml_enum(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector<String> &p_link_target_parts); void _append_xml_constant(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector<String> &p_link_target_parts); void _append_xml_constant_in_global_scope(StringBuilder &p_xml_output, const String &p_target_cname, const String &p_link_target); + void _append_xml_param(StringBuilder &p_xml_output, const String &p_link_target, bool p_is_signal); void _append_xml_undeclared(StringBuilder &p_xml_output, const String &p_link_target); int _determine_enum_prefix(const EnumInterface &p_ienum); diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp index 00ef4ccdde..c84ecf4ceb 100644 --- a/modules/mono/godotsharp_dirs.cpp +++ b/modules/mono/godotsharp_dirs.cpp @@ -33,10 +33,6 @@ #include "mono_gd/gd_mono.h" #include "utils/path_utils.h" -#ifdef ANDROID_ENABLED -#include "mono_gd/support/android_support.h" -#endif - #include "core/config/project_settings.h" #include "core/io/dir_access.h" #include "core/os/os.h" 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/openxr/extensions/openxr_extension_wrapper_extension.cpp b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp index 81ba9c56b8..4829f713d2 100644 --- a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp +++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp @@ -126,7 +126,7 @@ void OpenXRExtensionWrapperExtension::on_before_instance_created() { } void OpenXRExtensionWrapperExtension::on_instance_created(const XrInstance p_instance) { - uint64_t instance = reinterpret_cast<uint64_t>(p_instance); + uint64_t instance = (uint64_t)p_instance; GDVIRTUAL_CALL(_on_instance_created, instance); } @@ -135,7 +135,7 @@ void OpenXRExtensionWrapperExtension::on_instance_destroyed() { } void OpenXRExtensionWrapperExtension::on_session_created(const XrSession p_session) { - uint64_t session = reinterpret_cast<uint64_t>(p_session); + uint64_t session = (uint64_t)p_session; GDVIRTUAL_CALL(_on_session_created, session); } diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index 043a33ab35..2a502d081e 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -2113,12 +2113,10 @@ Dictionary TextServerAdvanced::_font_get_ot_name_strings(const RID &p_font_rid) name = vformat("unknown_%d", names[i].name_id); } break; } + String text; unsigned int text_size = hb_ot_name_get_utf32(hb_face, names[i].name_id, names[i].language, nullptr, nullptr) + 1; - // @todo After godot-cpp#1141 is fixed, use text.resize() and write directly to text.wptr() instead of using a temporary buffer. - char32_t *buffer = memnew_arr(char32_t, text_size); - hb_ot_name_get_utf32(hb_face, names[i].name_id, names[i].language, &text_size, (uint32_t *)buffer); - String text(buffer); - memdelete_arr(buffer); + text.resize(text_size); + hb_ot_name_get_utf32(hb_face, names[i].name_id, names[i].language, &text_size, (uint32_t *)text.ptrw()); if (!text.is_empty()) { Dictionary &id_string = names_for_lang[String(hb_language_to_string(names[i].language))]; id_string[name] = text; diff --git a/modules/text_server_adv/thorvg_svg_in_ot.cpp b/modules/text_server_adv/thorvg_svg_in_ot.cpp index 2f48f1564c..951ca15503 100644 --- a/modules/text_server_adv/thorvg_svg_in_ot.cpp +++ b/modules/text_server_adv/thorvg_svg_in_ot.cpp @@ -155,21 +155,10 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin } String xml_code_str = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"" + rtos(min_x) + " " + rtos(min_y) + " " + rtos(new_w) + " " + rtos(new_h) + "\">" + xml_body; -#ifndef GDEXTENSION gl_state.xml_code = xml_code_str.utf8(); -#else - CharString xml_code = xml_code_str.utf8(); - gl_state.xml_code_length = xml_code.length(); - gl_state.xml_code = memnew_arr(char, gl_state.xml_code_length); - memcpy(gl_state.xml_code, xml_code.get_data(), gl_state.xml_code_length); -#endif picture = tvg::Picture::gen(); -#ifndef GDEXTENSION result = picture->load(gl_state.xml_code.get_data(), gl_state.xml_code.length(), "svg+xml", false); -#else - result = picture->load(gl_state.xml_code, gl_state.xml_code_length, "svg+xml", false); -#endif if (result != tvg::Result::Success) { ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (glyph metrics)."); } @@ -257,11 +246,7 @@ FT_Error tvg_svg_in_ot_render(FT_GlyphSlot p_slot, FT_Pointer *p_state) { ERR_FAIL_COND_V_MSG(!gl_state.ready, FT_Err_Invalid_SVG_Document, "SVG glyph not ready."); std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen(); -#ifndef GDEXTENSION tvg::Result res = picture->load(gl_state.xml_code.get_data(), gl_state.xml_code.length(), "svg+xml", false); -#else - tvg::Result res = picture->load(gl_state.xml_code, gl_state.xml_code_length, "svg+xml", false); -#endif if (res != tvg::Result::Success) { ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (glyph rendering)."); } diff --git a/modules/text_server_adv/thorvg_svg_in_ot.h b/modules/text_server_adv/thorvg_svg_in_ot.h index 5e79c8e444..034fffb5e6 100644 --- a/modules/text_server_adv/thorvg_svg_in_ot.h +++ b/modules/text_server_adv/thorvg_svg_in_ot.h @@ -66,22 +66,8 @@ struct GL_State { float y = 0; float w = 0; float h = 0; -#ifndef GDEXTENSION CharString xml_code; -#else - // @todo After godot-cpp#1142 is fixed, use CharString just like when compiled as a Godot module. - char *xml_code = nullptr; - int xml_code_length = 0; -#endif tvg::Matrix m; - -#ifdef GDEXTENSION - ~GL_State() { - if (xml_code) { - memdelete_arr(xml_code); - } - } -#endif }; struct TVG_State { 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/doc_classes/EditorExportPlatformAndroid.xml b/platform/android/doc_classes/EditorExportPlatformAndroid.xml index c0c57cafbe..d270980d72 100644 --- a/platform/android/doc_classes/EditorExportPlatformAndroid.xml +++ b/platform/android/doc_classes/EditorExportPlatformAndroid.xml @@ -110,6 +110,10 @@ <member name="package/show_in_android_tv" type="bool" setter="" getter=""> If [code]true[/code], this app will show in Android TV launcher UI. </member> + <member name="package/show_in_app_library" type="bool" setter="" getter=""> + If [code]true[/code], this app will show in the device's app library. + [b]Note:[/b] This is [code]true[/code] by default. + </member> <member name="package/signed" type="bool" setter="" getter=""> If [code]true[/code], package signing is enabled. </member> diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index 9e46085b2a..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" @@ -1838,6 +1839,7 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/retain_data_on_uninstall"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/exclude_from_recents"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/show_in_android_tv"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/show_in_app_library"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/show_as_launcher_app"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_icon_option, PROPERTY_HINT_FILE, "*.png"), "")); @@ -2225,17 +2227,15 @@ String EditorExportPlatformAndroid::get_apksigner_path(int p_target_sdk, bool p_ } bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const { -#ifdef MODULE_MONO_ENABLED - // Don't check for additional errors, as this particular error cannot be resolved. - r_error += TTR("Exporting to Android is currently not supported in Godot 4 when using C#/.NET. Use Godot 3 to target Android with C#/Mono instead.") + "\n"; - r_error += TTR("If this project does not use C#, use a non-C# editor build to export the project.") + "\n"; - return false; -#else - String err; bool valid = false; const bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build"); +#ifdef MODULE_MONO_ENABLED + // Android export is still a work in progress, keep a message as a warning. + err += TTR("Exporting to Android when using C#/.NET is experimental.") + "\n"; +#endif + // Look for export templates (first official, and if defined custom templates). if (!gradle_build_enabled) { @@ -2365,7 +2365,6 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito } return valid; -#endif // !MODULE_MONO_ENABLED } bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const { @@ -2386,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/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp index d0d0c34bb4..0915009235 100644 --- a/platform/android/export/gradle_export_util.cpp +++ b/platform/android/export/gradle_export_util.cpp @@ -269,7 +269,12 @@ String _get_activity_tag(const Ref<EditorExportPlatform> &p_export_platform, con manifest_activity_text += " <intent-filter>\n" " <action android:name=\"android.intent.action.MAIN\" />\n" - " <category android:name=\"android.intent.category.LAUNCHER\" />\n"; + " <category android:name=\"android.intent.category.DEFAULT\" />\n"; + + bool show_in_app_library = p_preset->get("package/show_in_app_library"); + if (show_in_app_library) { + manifest_activity_text += " <category android:name=\"android.intent.category.LAUNCHER\" />\n"; + } bool uses_leanback_category = p_preset->get("package/show_in_android_tv"); if (uses_leanback_category) { @@ -279,7 +284,6 @@ String _get_activity_tag(const Ref<EditorExportPlatform> &p_export_platform, con bool uses_home_category = p_preset->get("package/show_as_launcher_app"); if (uses_home_category) { manifest_activity_text += " <category android:name=\"android.intent.category.HOME\" />\n"; - manifest_activity_text += " <category android:name=\"android.intent.category.DEFAULT\" />\n"; } manifest_activity_text += " </intent-filter>\n"; diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml index 542ab51660..56d403a263 100644 --- a/platform/android/java/app/AndroidManifest.xml +++ b/platform/android/java/app/AndroidManifest.xml @@ -45,6 +45,7 @@ <intent-filter> <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> diff --git a/platform/android/java/editor/src/main/AndroidManifest.xml b/platform/android/java/editor/src/main/AndroidManifest.xml index c7b2c8ad67..cb89d6e1b0 100644 --- a/platform/android/java/editor/src/main/AndroidManifest.xml +++ b/platform/android/java/editor/src/main/AndroidManifest.xml @@ -45,6 +45,7 @@ <intent-filter> <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> 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/x11/gl_manager_x11.h b/platform/linuxbsd/x11/gl_manager_x11.h index 59e20fec45..d3a25506a8 100644 --- a/platform/linuxbsd/x11/gl_manager_x11.h +++ b/platform/linuxbsd/x11/gl_manager_x11.h @@ -74,17 +74,17 @@ private: }; struct GLDisplay { - GLDisplay() { context = nullptr; } + GLDisplay() {} ~GLDisplay(); GLManager_X11_Private *context = nullptr; - ::Display *x11_display; - XVisualInfo x_vi; + ::Display *x11_display = nullptr; + XVisualInfo x_vi = {}; }; // just for convenience, window and display struct struct XWinDisp { ::Window x11_window; - ::Display *x11_display; + ::Display *x11_display = nullptr; } _x_windisp; LocalVector<GLWindow> _windows; 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/detect.py b/platform/web/detect.py index 2685cbcd63..dc8f4e0dd9 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"]]) 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/platform/windows/detect.py b/platform/windows/detect.py index bec1fd2cb6..9548939695 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -355,6 +355,9 @@ def configure_msvc(env, vcvars_msvc_config): else: env.AppendUnique(CCFLAGS=["/MD"]) + # MSVC incremental linking is broken and _increases_ link time (GH-77968). + env.Append(LINKFLAGS=["/INCREMENTAL:NO"]) + if env["arch"] == "x86_32": env["x86_libtheora_opt_vc"] = True diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index e7003ab98b..78987738a5 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -33,20 +33,21 @@ #include "core/config/project_settings.h" #include "scene/main/window.h" +bool Camera2D::_is_editing_in_editor() const { +#ifdef TOOLS_ENABLED + return is_part_of_edited_scene(); +#else + return false; +#endif // TOOLS_ENABLED +} + void Camera2D::_update_scroll() { - if (!is_inside_tree()) { + if (!is_inside_tree() || !viewport) { return; } - if (Engine::get_singleton()->is_editor_hint()) { + if (_is_editing_in_editor()) { queue_redraw(); - // Only set viewport transform when not bound to the main viewport. - if (get_tree()->get_edited_scene_root() && get_viewport() == get_tree()->get_edited_scene_root()->get_viewport()) { - return; - } - } - - if (!viewport) { return; } @@ -65,7 +66,7 @@ void Camera2D::_update_scroll() { } void Camera2D::_update_process_callback() { - if (Engine::get_singleton()->is_editor_hint()) { + if (_is_editing_in_editor()) { set_process_internal(false); set_physics_process_internal(false); } else if (process_callback == CAMERA2D_PROCESS_IDLE) { @@ -106,7 +107,7 @@ Transform2D Camera2D::get_camera_transform() { if (!first) { if (anchor_mode == ANCHOR_MODE_DRAG_CENTER) { - if (drag_horizontal_enabled && !Engine::get_singleton()->is_editor_hint() && !drag_horizontal_offset_changed) { + if (drag_horizontal_enabled && !_is_editing_in_editor() && !drag_horizontal_offset_changed) { camera_pos.x = MIN(camera_pos.x, (new_camera_pos.x + screen_size.x * 0.5 * zoom_scale.x * drag_margin[SIDE_LEFT])); camera_pos.x = MAX(camera_pos.x, (new_camera_pos.x - screen_size.x * 0.5 * zoom_scale.x * drag_margin[SIDE_RIGHT])); } else { @@ -119,7 +120,7 @@ Transform2D Camera2D::get_camera_transform() { drag_horizontal_offset_changed = false; } - if (drag_vertical_enabled && !Engine::get_singleton()->is_editor_hint() && !drag_vertical_offset_changed) { + if (drag_vertical_enabled && !_is_editing_in_editor() && !drag_vertical_offset_changed) { camera_pos.y = MIN(camera_pos.y, (new_camera_pos.y + screen_size.y * 0.5 * zoom_scale.y * drag_margin[SIDE_TOP])); camera_pos.y = MAX(camera_pos.y, (new_camera_pos.y - screen_size.y * 0.5 * zoom_scale.y * drag_margin[SIDE_BOTTOM])); @@ -158,7 +159,7 @@ Transform2D Camera2D::get_camera_transform() { } } - if (position_smoothing_enabled && !Engine::get_singleton()->is_editor_hint()) { + if (position_smoothing_enabled && !_is_editing_in_editor()) { real_t c = position_smoothing_speed * (process_callback == CAMERA2D_PROCESS_PHYSICS ? get_physics_process_delta_time() : get_process_delta_time()); smoothed_camera_pos = ((camera_pos - smoothed_camera_pos) * c) + smoothed_camera_pos; ret_camera_pos = smoothed_camera_pos; @@ -175,7 +176,7 @@ Transform2D Camera2D::get_camera_transform() { Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5 * zoom_scale) : Point2()); if (!ignore_rotation) { - if (rotation_smoothing_enabled && !Engine::get_singleton()->is_editor_hint()) { + if (rotation_smoothing_enabled && !_is_editing_in_editor()) { real_t step = rotation_smoothing_speed * (process_callback == CAMERA2D_PROCESS_PHYSICS ? get_physics_process_delta_time() : get_process_delta_time()); camera_angle = Math::lerp_angle(camera_angle, get_global_rotation(), step); } else { @@ -250,7 +251,7 @@ void Camera2D::_notification(int p_what) { add_to_group(group_name); add_to_group(canvas_group_name); - if (!Engine::get_singleton()->is_editor_hint() && enabled && !viewport->get_camera_2d()) { + if (!_is_editing_in_editor() && enabled && !viewport->get_camera_2d()) { make_current(); } @@ -272,7 +273,7 @@ void Camera2D::_notification(int p_what) { #ifdef TOOLS_ENABLED case NOTIFICATION_DRAW: { - if (!is_inside_tree() || !Engine::get_singleton()->is_editor_hint()) { + if (!is_inside_tree() || !_is_editing_in_editor()) { break; } @@ -398,7 +399,11 @@ void Camera2D::set_process_callback(Camera2DProcessCallback p_mode) { void Camera2D::set_enabled(bool p_enabled) { enabled = p_enabled; - if (enabled && is_inside_tree() && !viewport->get_camera_2d()) { + if (!is_inside_tree()) { + return; + } + + if (enabled && !viewport->get_camera_2d()) { make_current(); } else if (!enabled && is_current()) { clear_current(); @@ -414,27 +419,27 @@ Camera2D::Camera2DProcessCallback Camera2D::get_process_callback() const { } void Camera2D::_make_current(Object *p_which) { - if (!viewport || (custom_viewport && !ObjectDB::get_instance(custom_viewport_id))) { + if (!is_inside_tree() || !viewport) { return; } + if (custom_viewport && !ObjectDB::get_instance(custom_viewport_id)) { + return; + } + + queue_redraw(); + if (p_which == this) { - if (is_inside_tree()) { - viewport->_camera_2d_set(this); - queue_redraw(); - } + viewport->_camera_2d_set(this); } else { - if (is_inside_tree()) { - if (viewport->get_camera_2d() == this) { - viewport->_camera_2d_set(nullptr); - } - queue_redraw(); + if (viewport->get_camera_2d() == this) { + viewport->_camera_2d_set(nullptr); } } } void Camera2D::_update_process_internal_for_smoothing() { - bool is_not_in_scene_or_editor = !(is_inside_tree() && Engine::get_singleton()->is_editor_hint()); + bool is_not_in_scene_or_editor = !(is_inside_tree() && _is_editing_in_editor()); bool is_any_smoothing_valid = position_smoothing_speed > 0 || rotation_smoothing_speed > 0; bool enable = is_any_smoothing_valid && is_not_in_scene_or_editor; @@ -453,13 +458,22 @@ void Camera2D::make_current() { void Camera2D::clear_current() { ERR_FAIL_COND(!is_current()); - if (viewport && !(custom_viewport && !ObjectDB::get_instance(custom_viewport_id)) && viewport->is_inside_tree()) { + + if (!viewport || !viewport->is_inside_tree()) { + return; + } + + if (!custom_viewport || ObjectDB::get_instance(custom_viewport_id)) { viewport->assign_next_enabled_camera_2d(group_name); } } bool Camera2D::is_current() const { - if (viewport && !(custom_viewport && !ObjectDB::get_instance(custom_viewport_id))) { + if (!viewport) { + return false; + } + + if (!custom_viewport || ObjectDB::get_instance(custom_viewport_id)) { return viewport->get_camera_2d() == this; } return false; @@ -567,8 +581,7 @@ Point2 Camera2D::get_camera_screen_center() const { } Size2 Camera2D::_get_camera_screen_size() const { - // special case if the camera2D is in the root viewport - if (Engine::get_singleton()->is_editor_hint() && get_viewport()->get_parent_viewport() == get_tree()->get_root()) { + if (_is_editing_in_editor()) { return Size2(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height")); } return get_viewport_rect().size; diff --git a/scene/2d/camera_2d.h b/scene/2d/camera_2d.h index 808529b0fb..5693d05ee5 100644 --- a/scene/2d/camera_2d.h +++ b/scene/2d/camera_2d.h @@ -85,6 +85,7 @@ protected: bool drag_vertical_offset_changed = false; Point2 camera_screen_center; + bool _is_editing_in_editor() const; void _update_process_callback(); void _update_scroll(); diff --git a/scene/2d/canvas_modulate.cpp b/scene/2d/canvas_modulate.cpp index 7dd2e75f09..2c5c6a1a16 100644 --- a/scene/2d/canvas_modulate.cpp +++ b/scene/2d/canvas_modulate.cpp @@ -30,32 +30,67 @@ #include "canvas_modulate.h" +void CanvasModulate::_on_in_canvas_visibility_changed(bool p_new_visibility) { + RID canvas = get_canvas(); + StringName group_name = "_canvas_modulate_" + itos(canvas.get_id()); + + ERR_FAIL_COND_MSG(p_new_visibility == is_in_group(group_name), vformat("CanvasModulate becoming %s in the canvas already %s in the modulate group. Buggy logic, please report.", p_new_visibility ? "visible" : "invisible", p_new_visibility ? "was" : "was not")); + + if (p_new_visibility) { + bool has_active_canvas_modulate = get_tree()->has_group(group_name); // Group would be removed if empty; otherwise one CanvasModulate within must be active. + add_to_group(group_name); + if (!has_active_canvas_modulate) { + is_active = true; + RS::get_singleton()->canvas_set_modulate(canvas, color); + } + } else { + remove_from_group(group_name); + if (is_active) { + is_active = false; + CanvasModulate *new_active = Object::cast_to<CanvasModulate>(get_tree()->get_first_node_in_group(group_name)); + if (new_active) { + new_active->is_active = true; + RS::get_singleton()->canvas_set_modulate(canvas, new_active->color); + } else { + RS::get_singleton()->canvas_set_modulate(canvas, Color(1, 1, 1, 1)); + } + } + } + + update_configuration_warnings(); +} + void CanvasModulate::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_CANVAS: { - if (is_visible_in_tree()) { - RS::get_singleton()->canvas_set_modulate(get_canvas(), color); - add_to_group("_canvas_modulate_" + itos(get_canvas().get_id())); + is_in_canvas = true; + bool visible_in_tree = is_visible_in_tree(); + if (visible_in_tree) { + _on_in_canvas_visibility_changed(true); } + was_visible_in_tree = visible_in_tree; } break; case NOTIFICATION_EXIT_CANVAS: { - if (is_visible_in_tree()) { - RS::get_singleton()->canvas_set_modulate(get_canvas(), Color(1, 1, 1, 1)); - remove_from_group("_canvas_modulate_" + itos(get_canvas().get_id())); + is_in_canvas = false; + if (was_visible_in_tree) { + _on_in_canvas_visibility_changed(false); } } break; case NOTIFICATION_VISIBILITY_CHANGED: { - if (is_visible_in_tree()) { - RS::get_singleton()->canvas_set_modulate(get_canvas(), color); - add_to_group("_canvas_modulate_" + itos(get_canvas().get_id())); - } else { - RS::get_singleton()->canvas_set_modulate(get_canvas(), Color(1, 1, 1, 1)); - remove_from_group("_canvas_modulate_" + itos(get_canvas().get_id())); + if (!is_in_canvas) { + return; } - update_configuration_warnings(); + bool visible_in_tree = is_visible_in_tree(); + if (visible_in_tree == was_visible_in_tree) { + return; + } + + _on_in_canvas_visibility_changed(visible_in_tree); + + was_visible_in_tree = visible_in_tree; } break; } } @@ -69,7 +104,7 @@ void CanvasModulate::_bind_methods() { void CanvasModulate::set_color(const Color &p_color) { color = p_color; - if (is_visible_in_tree()) { + if (is_active) { RS::get_singleton()->canvas_set_modulate(get_canvas(), color); } } @@ -81,12 +116,12 @@ Color CanvasModulate::get_color() const { PackedStringArray CanvasModulate::get_configuration_warnings() const { PackedStringArray warnings = Node::get_configuration_warnings(); - if (is_visible_in_tree() && is_inside_tree()) { + if (is_in_canvas && is_visible_in_tree()) { List<Node *> nodes; get_tree()->get_nodes_in_group("_canvas_modulate_" + itos(get_canvas().get_id()), &nodes); if (nodes.size() > 1) { - warnings.push_back(RTR("Only one visible CanvasModulate is allowed per scene (or set of instantiated scenes). The first created one will work, while the rest will be ignored.")); + warnings.push_back(RTR("Only one visible CanvasModulate is allowed per canvas.\nWhen there are more than one, only one of them will be active. Which one is undefined.")); } } diff --git a/scene/2d/canvas_modulate.h b/scene/2d/canvas_modulate.h index 3b11cf71f1..08ded52e23 100644 --- a/scene/2d/canvas_modulate.h +++ b/scene/2d/canvas_modulate.h @@ -38,6 +38,14 @@ class CanvasModulate : public Node2D { Color color = Color(1, 1, 1, 1); + // CanvasModulate is in canvas-specific modulate group when both in canvas and visible in tree. + // Exactly one CanvasModulate in each such non-empty group is active. + bool is_in_canvas = false; + bool was_visible_in_tree = false; // Relevant only when in canvas. + bool is_active = false; + + void _on_in_canvas_visibility_changed(bool p_new_visibility); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 1836cc20b6..09effe6596 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -1839,6 +1839,17 @@ void TileMapLayer::set_cell(const Vector2i &p_coords, int p_source_id, const Vec ERR_FAIL_COND(!Q); TileMapQuadrant &q = Q->value; + // Find node in scenes and remove it. + HashMap<Vector2i, String>::Iterator entry = q.scenes.find(pk); + if (entry != q.scenes.end()) { + String scene_name = entry->value; + Node *scene = tile_map_node->get_node_or_null(scene_name); + if (scene) { + scene->queue_free(); + instantiated_scenes.erase(Vector2i(pk.x, pk.y)); + } + } + q.cells.erase(pk); // Remove or make the quadrant dirty. diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp index 4f94f0d9f2..6e75be268d 100644 --- a/scene/gui/dialogs.cpp +++ b/scene/gui/dialogs.cpp @@ -38,8 +38,7 @@ // AcceptDialog void AcceptDialog::_input_from_window(const Ref<InputEvent> &p_event) { - Ref<InputEventKey> key = p_event; - if (close_on_escape && key.is_valid() && key->is_action_pressed(SNAME("ui_cancel"), false, true)) { + if (close_on_escape && p_event->is_action_pressed(SNAME("ui_cancel"), false, true)) { _cancel_pressed(); } } 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/popup.cpp b/scene/gui/popup.cpp index c0a2dc81d0..6915f3d242 100644 --- a/scene/gui/popup.cpp +++ b/scene/gui/popup.cpp @@ -35,8 +35,7 @@ #include "scene/gui/panel.h" void Popup::_input_from_window(const Ref<InputEvent> &p_event) { - Ref<InputEventKey> key = p_event; - if (get_flag(FLAG_POPUP) && key.is_valid() && key->is_action_pressed(SNAME("ui_cancel"), false, true)) { + if (get_flag(FLAG_POPUP) && p_event->is_action_pressed(SNAME("ui_cancel"), false, true)) { _close_pressed(); } } 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/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 3a0fb6d89c..1e7e376fc8 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -2745,6 +2745,9 @@ void RichTextLabel::_thread_function(void *p_userdata) { void RichTextLabel::_thread_end() { set_physics_process_internal(false); + if (!scroll_visible) { + vscroll->hide(); + } if (is_visible_in_tree()) { queue_redraw(); } @@ -2814,7 +2817,6 @@ _FORCE_INLINE_ float RichTextLabel::_update_scroll_exceeds(float p_total_height, } else { scroll_visible = false; scroll_w = 0; - vscroll->hide(); } main->first_resized_line.store(0); @@ -2862,6 +2864,9 @@ bool RichTextLabel::_validate_line_caches() { if (main->first_resized_line.load() == (int)main->lines.size()) { vscroll->set_value(old_scroll); validating.store(false); + if (!scroll_visible) { + vscroll->hide(); + } return true; } @@ -2881,6 +2886,9 @@ bool RichTextLabel::_validate_line_caches() { update_minimum_size(); } validating.store(false); + if (!scroll_visible) { + vscroll->hide(); + } return true; } validating.store(false); @@ -2896,6 +2904,9 @@ bool RichTextLabel::_validate_line_caches() { updating.store(true); _process_line_caches(); updating.store(false); + if (!scroll_visible) { + vscroll->hide(); + } queue_redraw(); return true; } diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp index fcf9302953..06b3882d25 100644 --- a/scene/gui/scroll_bar.cpp +++ b/scene/gui/scroll_bar.cpp @@ -428,6 +428,14 @@ void ScrollBar::_notification(int p_what) { } } break; + case NOTIFICATION_VISIBILITY_CHANGED: { + if (!is_visible()) { + incr_active = false; + decr_active = false; + drag.active = false; + } + } break; + case NOTIFICATION_MOUSE_EXIT: { highlight = HIGHLIGHT_NONE; queue_redraw(); 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 2d3aa66f2c..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(); @@ -2955,7 +2956,7 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) { void Viewport::_update_mouse_over() { // Update gui.mouse_over and gui.subwindow_over in all Viewports. - // Send necessary mouse_enter/mouse_exit signals and the NOTIFICATION_VP_MOUSE_ENTER/NOTIFICATION_VP_MOUSE_EXIT notifications for every Viewport in the SceneTree. + // Send necessary mouse_enter/mouse_exit signals and the MOUSE_ENTER/MOUSE_EXIT notifications for every Viewport in the SceneTree. if (is_attached_in_viewport()) { // Execute this function only, when it is processed by a native Window or a SubViewport, that has no SubViewportContainer as parent. @@ -3009,7 +3010,7 @@ void Viewport::_update_mouse_over(Vector2 p_pos) { } gui.subwindow_over = sw; if (!sw->is_input_disabled()) { - sw->notification(NOTIFICATION_VP_MOUSE_ENTER); + sw->_propagate_window_notification(sw, NOTIFICATION_WM_MOUSE_ENTER); } } if (!sw->is_input_disabled()) { @@ -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 331ce98cdd..346dc6af7e 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -468,7 +468,8 @@ private: SubWindowResize _sub_window_get_resize_margin(Window *p_subwindow, const Point2 &p_point); void _update_mouse_over(); - void _update_mouse_over(Vector2 p_pos); + virtual void _update_mouse_over(Vector2 p_pos); + virtual void _mouse_leave_viewport(); virtual bool _can_consume_input_events() const { return true; } uint64_t event_count = 0; @@ -482,8 +483,6 @@ protected: Size2i _get_size_2d_override() const; bool _is_size_allocated() const; - void _mouse_leave_viewport(); - void _notification(int p_what); void _process_picking(); static void _bind_methods(); @@ -654,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/main/window.cpp b/scene/main/window.cpp index 875b53203a..1af279d94c 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -679,7 +679,7 @@ void Window::_event_callback(DisplayServer::WindowEvent p_event) { } _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_ENTER); root->gui.windowmanager_window_over = this; - notification(NOTIFICATION_VP_MOUSE_ENTER); + mouse_in_window = true; if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE)) { DisplayServer::get_singleton()->cursor_set_shape(DisplayServer::CURSOR_ARROW); //restore cursor shape } @@ -692,6 +692,7 @@ void Window::_event_callback(DisplayServer::WindowEvent p_event) { #endif // DEV_ENABLED return; } + mouse_in_window = false; root->gui.windowmanager_window_over->_mouse_leave_viewport(); root->gui.windowmanager_window_over = nullptr; _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_EXIT); @@ -1001,6 +1002,17 @@ void Window::_update_viewport_size() { float font_oversampling = 1.0; window_transform = Transform2D(); + if (content_scale_stretch == Window::CONTENT_SCALE_STRETCH_INTEGER) { + // We always want to make sure that the content scale factor is a whole + // number, else there will be pixel wobble no matter what. + content_scale_factor = Math::floor(content_scale_factor); + + // A content scale factor of zero is pretty useless. + if (content_scale_factor < 1) { + content_scale_factor = 1; + } + } + if (content_scale_mode == CONTENT_SCALE_MODE_DISABLED || content_scale_size.x == 0 || content_scale_size.y == 0) { font_oversampling = content_scale_factor; final_size = size; @@ -1054,13 +1066,26 @@ void Window::_update_viewport_size() { screen_size = screen_size.floor(); viewport_size = viewport_size.floor(); + if (content_scale_stretch == Window::CONTENT_SCALE_STRETCH_INTEGER) { + Size2i screen_scale = (screen_size / viewport_size).floor(); + int scale_factor = MIN(screen_scale.x, screen_scale.y); + + if (scale_factor < 1) { + scale_factor = 1; + } + + screen_size = viewport_size * scale_factor; + } + Size2 margin; Size2 offset; - if (content_scale_aspect != CONTENT_SCALE_ASPECT_EXPAND && screen_size.x < video_mode.x) { + if (screen_size.x < video_mode.x) { margin.x = Math::round((video_mode.x - screen_size.x) / 2.0); offset.x = Math::round(margin.x * viewport_size.y / screen_size.y); - } else if (content_scale_aspect != CONTENT_SCALE_ASPECT_EXPAND && screen_size.y < video_mode.y) { + } + + if (screen_size.y < video_mode.y) { margin.y = Math::round((video_mode.y - screen_size.y) / 2.0); offset.y = Math::round(margin.y * viewport_size.x / screen_size.x); } @@ -1337,6 +1362,15 @@ Window::ContentScaleAspect Window::get_content_scale_aspect() const { return content_scale_aspect; } +void Window::set_content_scale_stretch(ContentScaleStretch p_stretch) { + content_scale_stretch = p_stretch; + _update_viewport_size(); +} + +Window::ContentScaleStretch Window::get_content_scale_stretch() const { + return content_scale_stretch; +} + void Window::set_content_scale_factor(real_t p_factor) { ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(p_factor <= 0); @@ -2519,6 +2553,41 @@ bool Window::is_attached_in_viewport() const { return get_embedder(); } +void Window::_update_mouse_over(Vector2 p_pos) { + if (!mouse_in_window) { + if (is_embedded()) { + mouse_in_window = true; + _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_ENTER); + } else { + // Prevent update based on delayed InputEvents from DisplayServer. + return; + } + } + + bool new_in = get_visible_rect().has_point(p_pos); + if (new_in == gui.mouse_in_viewport) { + if (new_in) { + Viewport::_update_mouse_over(p_pos); + } + return; + } + + if (new_in) { + notification(NOTIFICATION_VP_MOUSE_ENTER); + Viewport::_update_mouse_over(p_pos); + } else { + Viewport::_mouse_leave_viewport(); + } +} + +void Window::_mouse_leave_viewport() { + Viewport::_mouse_leave_viewport(); + if (is_embedded()) { + mouse_in_window = false; + _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_EXIT); + } +} + void Window::_bind_methods() { ClassDB::bind_method(D_METHOD("set_title", "title"), &Window::set_title); ClassDB::bind_method(D_METHOD("get_title"), &Window::get_title); @@ -2593,6 +2662,9 @@ void Window::_bind_methods() { ClassDB::bind_method(D_METHOD("set_content_scale_aspect", "aspect"), &Window::set_content_scale_aspect); ClassDB::bind_method(D_METHOD("get_content_scale_aspect"), &Window::get_content_scale_aspect); + ClassDB::bind_method(D_METHOD("set_content_scale_stretch", "stretch"), &Window::set_content_scale_stretch); + ClassDB::bind_method(D_METHOD("get_content_scale_stretch"), &Window::get_content_scale_stretch); + ClassDB::bind_method(D_METHOD("set_content_scale_factor", "factor"), &Window::set_content_scale_factor); ClassDB::bind_method(D_METHOD("get_content_scale_factor"), &Window::get_content_scale_factor); @@ -2711,7 +2783,8 @@ void Window::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "content_scale_size"), "set_content_scale_size", "get_content_scale_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_mode", PROPERTY_HINT_ENUM, "Disabled,Canvas Items,Viewport"), "set_content_scale_mode", "get_content_scale_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_aspect", PROPERTY_HINT_ENUM, "Ignore,Keep,Keep Width,Keep Height,Expand"), "set_content_scale_aspect", "get_content_scale_aspect"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "content_scale_factor"), "set_content_scale_factor", "get_content_scale_factor"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_stretch", PROPERTY_HINT_ENUM, "Fractional,Integer"), "set_content_scale_stretch", "get_content_scale_stretch"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "content_scale_factor", PROPERTY_HINT_RANGE, "0.5,8.0,0.01"), "set_content_scale_factor", "get_content_scale_factor"); ADD_GROUP("Localization", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate"), "set_auto_translate", "is_auto_translating"); @@ -2763,6 +2836,9 @@ void Window::_bind_methods() { BIND_ENUM_CONSTANT(CONTENT_SCALE_ASPECT_KEEP_HEIGHT); BIND_ENUM_CONSTANT(CONTENT_SCALE_ASPECT_EXPAND); + BIND_ENUM_CONSTANT(CONTENT_SCALE_STRETCH_FRACTIONAL); + BIND_ENUM_CONSTANT(CONTENT_SCALE_STRETCH_INTEGER); + BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_INHERITED); BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_LOCALE); BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_LTR); diff --git a/scene/main/window.h b/scene/main/window.h index 18ddd89662..c387ffa92a 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -78,6 +78,11 @@ public: CONTENT_SCALE_ASPECT_EXPAND, }; + enum ContentScaleStretch { + CONTENT_SCALE_STRETCH_FRACTIONAL, + CONTENT_SCALE_STRETCH_INTEGER, + }; + enum LayoutDirection { LAYOUT_DIRECTION_INHERITED, LAYOUT_DIRECTION_LOCALE, @@ -135,6 +140,7 @@ private: Size2i content_scale_size; ContentScaleMode content_scale_mode = CONTENT_SCALE_MODE_DISABLED; ContentScaleAspect content_scale_aspect = CONTENT_SCALE_ASPECT_IGNORE; + ContentScaleStretch content_scale_stretch = CONTENT_SCALE_STRETCH_FRACTIONAL; real_t content_scale_factor = 1.0; void _make_window(); @@ -197,6 +203,10 @@ private: void _event_callback(DisplayServer::WindowEvent p_event); virtual bool _can_consume_input_events() const override; + bool mouse_in_window = false; + void _update_mouse_over(Vector2 p_pos) override; + void _mouse_leave_viewport() override; + Ref<Shortcut> debugger_stop_shortcut; protected: @@ -299,6 +309,9 @@ public: void set_content_scale_aspect(ContentScaleAspect p_aspect); ContentScaleAspect get_content_scale_aspect() const; + void set_content_scale_stretch(ContentScaleStretch p_stretch); + ContentScaleStretch get_content_scale_stretch() const; + void set_content_scale_factor(real_t p_factor); real_t get_content_scale_factor() const; @@ -420,6 +433,7 @@ VARIANT_ENUM_CAST(Window::Mode); VARIANT_ENUM_CAST(Window::Flags); VARIANT_ENUM_CAST(Window::ContentScaleMode); VARIANT_ENUM_CAST(Window::ContentScaleAspect); +VARIANT_ENUM_CAST(Window::ContentScaleStretch); VARIANT_ENUM_CAST(Window::LayoutDirection); VARIANT_ENUM_CAST(Window::WindowInitialPosition); 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/scene/resources/environment.cpp b/scene/resources/environment.cpp index e48f744c72..abe1561310 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -1446,7 +1446,7 @@ void Environment::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_emission_energy", PROPERTY_HINT_RANGE, "0,1024,0.01,or_greater"), "set_volumetric_fog_emission_energy", "get_volumetric_fog_emission_energy"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_gi_inject", PROPERTY_HINT_RANGE, "0.0,16,0.01,exp"), "set_volumetric_fog_gi_inject", "get_volumetric_fog_gi_inject"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_anisotropy", PROPERTY_HINT_RANGE, "-0.9,0.9,0.01"), "set_volumetric_fog_anisotropy", "get_volumetric_fog_anisotropy"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_length", PROPERTY_HINT_RANGE, "0,1024,0.01,or_greater"), "set_volumetric_fog_length", "get_volumetric_fog_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_length", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m"), "set_volumetric_fog_length", "get_volumetric_fog_length"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_detail_spread", PROPERTY_HINT_EXP_EASING, "positive_only"), "set_volumetric_fog_detail_spread", "get_volumetric_fog_detail_spread"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_ambient_inject", PROPERTY_HINT_RANGE, "0.0,16,0.01,exp"), "set_volumetric_fog_ambient_inject", "get_volumetric_fog_ambient_inject"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_sky_affect", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_volumetric_fog_sky_affect", "get_volumetric_fog_sky_affect"); diff --git a/scene/resources/particle_process_material.cpp b/scene/resources/particle_process_material.cpp index 745c71626c..72d38ec8ce 100644 --- a/scene/resources/particle_process_material.cpp +++ b/scene/resources/particle_process_material.cpp @@ -683,11 +683,13 @@ void ParticleProcessMaterial::_update_shader() { code += " pos.z = 0.0;\n"; } code += " // apply linear acceleration\n"; - code += " force += length(VELOCITY) > 0.0 ? normalize(VELOCITY) * tex_linear_accel * mix(linear_accel_min, linear_accel_max, rand_from_seed(alt_seed)) : vec3(0.0);\n"; + code += " float linear_accel_rand = rand_from_seed(alt_seed);\n"; + code += " force += length(VELOCITY) > 0.0 ? normalize(VELOCITY) * tex_linear_accel * mix(linear_accel_min, linear_accel_max, linear_accel_rand) : vec3(0.0);\n"; code += " // apply radial acceleration\n"; code += " vec3 org = EMISSION_TRANSFORM[3].xyz;\n"; code += " vec3 diff = pos - org;\n"; - code += " force += length(diff) > 0.0 ? normalize(diff) * tex_radial_accel * mix(radial_accel_min, radial_accel_max, rand_from_seed(alt_seed)) : vec3(0.0);\n"; + code += " float radial_accel_rand = rand_from_seed(alt_seed);\n"; + code += " force += length(diff) > 0.0 ? normalize(diff) * tex_radial_accel * mix(radial_accel_min, radial_accel_max, radial_accel_rand) : vec3(0.0);\n"; code += " // apply tangential acceleration;\n"; code += " float tangent_accel_val = tex_tangent_accel * mix(tangent_accel_min, tangent_accel_max, rand_from_seed(alt_seed));\n"; if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { diff --git a/servers/rendering/renderer_rd/effects/tone_mapper.cpp b/servers/rendering/renderer_rd/effects/tone_mapper.cpp index 2c537c3a1c..48c6511408 100644 --- a/servers/rendering/renderer_rd/effects/tone_mapper.cpp +++ b/servers/rendering/renderer_rd/effects/tone_mapper.cpp @@ -255,5 +255,5 @@ void ToneMapper::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_col RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 3, u_color_correction_texture), 3); RD::get_singleton()->draw_list_set_push_constant(p_subpass_draw_list, &tonemap.push_constant, sizeof(TonemapPushConstant)); - RD::get_singleton()->draw_list_draw(p_subpass_draw_list, false, 1u, 0u); + RD::get_singleton()->draw_list_draw(p_subpass_draw_list, false, 1u, 3u); } diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index 773ea9098a..0ee76e0aa4 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -348,7 +348,7 @@ void RendererCanvasRenderRD::free_polygon(PolygonID p_polygon) { //////////////////// -void RendererCanvasRenderRD::_bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size) { +void RendererCanvasRenderRD::_bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size, bool p_texture_is_data) { if (p_texture == RID()) { p_texture = default_canvas_texture; } @@ -363,7 +363,7 @@ void RendererCanvasRenderRD::_bind_canvas_texture(RD::DrawListID p_draw_list, RI bool use_normal; bool use_specular; - bool success = RendererRD::TextureStorage::get_singleton()->canvas_texture_get_uniform_set(p_texture, p_base_filter, p_base_repeat, shader.default_version_rd_shader, CANVAS_TEXTURE_UNIFORM_SET, bool(push_constant.flags & FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR), uniform_set, size, specular_shininess, use_normal, use_specular); + bool success = RendererRD::TextureStorage::get_singleton()->canvas_texture_get_uniform_set(p_texture, p_base_filter, p_base_repeat, shader.default_version_rd_shader, CANVAS_TEXTURE_UNIFORM_SET, bool(push_constant.flags & FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR), uniform_set, size, specular_shininess, use_normal, use_specular, p_texture_is_data); //something odd happened if (!success) { _bind_canvas_texture(p_draw_list, default_canvas_texture, p_base_filter, p_base_repeat, r_last_texture, push_constant, r_texpixel_size); @@ -507,7 +507,7 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend //bind textures - _bind_canvas_texture(p_draw_list, rect->texture, current_filter, current_repeat, last_texture, push_constant, texpixel_size); + _bind_canvas_texture(p_draw_list, rect->texture, current_filter, current_repeat, last_texture, push_constant, texpixel_size, bool(rect->flags & CANVAS_RECT_MSDF)); Rect2 src_rect; Rect2 dst_rect; diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h index af8736a445..4c8cbd1c9f 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h @@ -418,7 +418,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender { RID _create_base_uniform_set(RID p_to_render_target, bool p_backbuffer); - inline void _bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size); //recursive, so regular inline used instead. + inline void _bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size, bool p_texture_is_data = false); //recursive, so regular inline used instead. void _render_item(RenderingDevice::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used); void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false); diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp index a96893570e..fda341bbc9 100644 --- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp @@ -1821,14 +1821,16 @@ void MaterialStorage::global_shader_parameters_load_settings(bool p_load_texture if (gvtype >= RS::GLOBAL_VAR_TYPE_SAMPLER2D) { //textire if (!p_load_textures) { - value = RID(); continue; } String path = value; - Ref<Resource> resource = ResourceLoader::load(path); - ERR_CONTINUE(resource.is_null()); - value = resource; + if (path.is_empty()) { + value = RID(); + } else { + Ref<Resource> resource = ResourceLoader::load(path); + value = resource; + } } if (global_shader_uniforms.variables.has(name)) { diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp index 4bfec8ae8d..56f2ea0b0c 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp @@ -1309,16 +1309,13 @@ void MeshStorage::_multimesh_enable_motion_vectors(MultiMesh *multimesh) { RID new_buffer = RD::get_singleton()->storage_buffer_create(new_buffer_size); if (multimesh->buffer_set && multimesh->data_cache.is_empty()) { - // If the buffer was set but there's no data cached in the CPU, we must download it from the GPU and - // upload it because RD does not provide a way to copy the buffer directly yet. + // If the buffer was set but there's no data cached in the CPU, we copy the buffer directly on the GPU. RD::get_singleton()->barrier(); - Vector<uint8_t> buffer_data = RD::get_singleton()->buffer_get_data(multimesh->buffer); - ERR_FAIL_COND(buffer_data.size() != int(buffer_size)); - RD::get_singleton()->buffer_update(new_buffer, 0, buffer_size, buffer_data.ptr(), RD::BARRIER_MASK_NO_BARRIER); - RD::get_singleton()->buffer_update(new_buffer, buffer_size, buffer_size, buffer_data.ptr()); + RD::get_singleton()->buffer_copy(multimesh->buffer, new_buffer, 0, 0, buffer_size, RD::BARRIER_MASK_NO_BARRIER); + RD::get_singleton()->buffer_copy(multimesh->buffer, new_buffer, 0, buffer_size, buffer_size); } else if (!multimesh->data_cache.is_empty()) { // Simply upload the data cached in the CPU, which should already be doubled in size. - ERR_FAIL_COND(multimesh->data_cache.size() != int(new_buffer_size)); + ERR_FAIL_COND(multimesh->data_cache.size() * sizeof(float) != size_t(new_buffer_size)); RD::get_singleton()->buffer_update(new_buffer, 0, new_buffer_size, multimesh->data_cache.ptr()); } @@ -1328,6 +1325,9 @@ void MeshStorage::_multimesh_enable_motion_vectors(MultiMesh *multimesh) { multimesh->buffer = new_buffer; multimesh->uniform_set_3d = RID(); // Cleared by dependency. + + // Invalidate any references to the buffer that was released and the uniform set that was pointing to it. + multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MULTIMESH); } void MeshStorage::_multimesh_get_motion_vectors_offsets(RID p_multimesh, uint32_t &r_current_offset, uint32_t &r_prev_offset) { diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp index f009318d24..286a9528fc 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp @@ -643,7 +643,7 @@ void TextureStorage::canvas_texture_set_texture_repeat(RID p_canvas_texture, RS: ct->clear_sets(); } -bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID p_base_shader, int p_base_set, bool p_use_srgb, RID &r_uniform_set, Size2i &r_size, Color &r_specular_shininess, bool &r_use_normal, bool &r_use_specular) { +bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID p_base_shader, int p_base_set, bool p_use_srgb, RID &r_uniform_set, Size2i &r_size, Color &r_specular_shininess, bool &r_use_normal, bool &r_use_specular, bool p_texture_is_data) { MaterialStorage *material_storage = MaterialStorage::get_singleton(); CanvasTexture *ct = nullptr; @@ -690,7 +690,7 @@ bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasIte u.append_id(texture_rd_get_default(DEFAULT_RD_TEXTURE_WHITE)); ct->size_cache = Size2i(1, 1); } else { - u.append_id(t->rd_texture_srgb.is_valid() && (p_use_srgb) ? t->rd_texture_srgb : t->rd_texture); + u.append_id(t->rd_texture_srgb.is_valid() && p_use_srgb && !p_texture_is_data ? t->rd_texture_srgb : t->rd_texture); ct->size_cache = Size2i(t->width_2d, t->height_2d); if (t->render_target) { t->render_target->was_used = true; diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.h b/servers/rendering/renderer_rd/storage_rd/texture_storage.h index 2137c5f383..276c8c4ce2 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.h @@ -475,7 +475,7 @@ public: virtual void canvas_texture_set_texture_filter(RID p_item, RS::CanvasItemTextureFilter p_filter) override; virtual void canvas_texture_set_texture_repeat(RID p_item, RS::CanvasItemTextureRepeat p_repeat) override; - bool canvas_texture_get_uniform_set(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID p_base_shader, int p_base_set, bool p_use_srgb, RID &r_uniform_set, Size2i &r_size, Color &r_specular_shininess, bool &r_use_normal, bool &r_use_specular); + bool canvas_texture_get_uniform_set(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID p_base_shader, int p_base_set, bool p_use_srgb, RID &r_uniform_set, Size2i &r_size, Color &r_specular_shininess, bool &r_use_normal, bool &r_use_specular, bool p_texture_is_data); /* Texture API */ diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 30d9b1c7b7..1ade1b25c4 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -840,6 +840,7 @@ public: virtual bool uniform_set_is_valid(RID p_uniform_set) = 0; virtual void uniform_set_set_invalidation_callback(RID p_uniform_set, InvalidationCallback p_callback, void *p_userdata) = 0; + virtual Error buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t p_src_offset, uint32_t p_dst_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS) = 0; virtual Error buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS) = 0; virtual Error buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS) = 0; virtual Vector<uint8_t> buffer_get_data(RID p_buffer, uint32_t p_offset = 0, uint32_t p_size = 0) = 0; // This causes stall, only use to retrieve large buffers for saving. 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; } diff --git a/tests/scene/test_window.h b/tests/scene/test_window.h new file mode 100644 index 0000000000..e0c55101de --- /dev/null +++ b/tests/scene/test_window.h @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* test_window.h */ +/**************************************************************************/ +/* 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 TEST_WINDOW_H +#define TEST_WINDOW_H + +#include "scene/gui/control.h" +#include "scene/main/window.h" + +#include "tests/test_macros.h" + +namespace TestWindow { + +class NotificationControl : public Control { + GDCLASS(NotificationControl, Control); + +protected: + void _notification(int p_what) { + switch (p_what) { + case NOTIFICATION_MOUSE_ENTER: { + mouse_over = true; + } break; + + case NOTIFICATION_MOUSE_EXIT: { + mouse_over = false; + } break; + } + } + +public: + bool mouse_over = false; +}; + +TEST_CASE("[SceneTree][Window]") { + Window *root = SceneTree::get_singleton()->get_root(); + + SUBCASE("Control-mouse-over within Window-black bars should not happen") { + Window *w = memnew(Window); + root->add_child(w); + w->set_size(Size2i(400, 200)); + w->set_position(Size2i(0, 0)); + w->set_content_scale_size(Size2i(200, 200)); + w->set_content_scale_mode(Window::CONTENT_SCALE_MODE_CANVAS_ITEMS); + w->set_content_scale_aspect(Window::CONTENT_SCALE_ASPECT_KEEP); + NotificationControl *c = memnew(NotificationControl); + w->add_child(c); + c->set_size(Size2i(100, 100)); + c->set_position(Size2i(-50, -50)); + + CHECK_FALSE(c->mouse_over); + SEND_GUI_MOUSE_MOTION_EVENT(Point2i(110, 10), MouseButtonMask::NONE, Key::NONE); + CHECK(c->mouse_over); + SEND_GUI_MOUSE_MOTION_EVENT(Point2i(90, 10), MouseButtonMask::NONE, Key::NONE); + CHECK_FALSE(c->mouse_over); // GH-80011 + + /* TODO: + SEND_GUI_MOUSE_BUTTON_EVENT(Point2i(90, 10), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); + SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(Point2i(90, 10), MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE); + CHECK(Control was not pressed); + */ + + memdelete(c); + memdelete(w); + } +} + +} // namespace TestWindow + +#endif // TEST_WINDOW_H diff --git a/tests/servers/test_navigation_server_3d.h b/tests/servers/test_navigation_server_3d.h index 8ea69ec67c..a116559cb2 100644 --- a/tests/servers/test_navigation_server_3d.h +++ b/tests/servers/test_navigation_server_3d.h @@ -423,6 +423,7 @@ TEST_SUITE("[Navigation]") { navigation_server->free(map); } +#ifndef DISABLE_DEPRECATED // This test case uses only public APIs on purpose - other test cases use simplified baking. // FIXME: Remove once deprecated `region_bake_navigation_mesh()` is removed. TEST_CASE("[NavigationServer3D][SceneTree][DEPRECATED] Server should be able to bake map correctly") { @@ -470,6 +471,7 @@ TEST_SUITE("[Navigation]") { memdelete(mesh_instance); memdelete(node_3d); } +#endif // DISABLE_DEPRECATED // This test case uses only public APIs on purpose - other test cases use simplified baking. TEST_CASE("[NavigationServer3D][SceneTree] Server should be able to bake map correctly") { diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 5ca03a20af..f1e348345b 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -115,6 +115,7 @@ #include "tests/scene/test_theme.h" #include "tests/scene/test_viewport.h" #include "tests/scene/test_visual_shader.h" +#include "tests/scene/test_window.h" #include "tests/servers/rendering/test_shader_preprocessor.h" #include "tests/servers/test_navigation_server_2d.h" #include "tests/servers/test_navigation_server_3d.h" |