summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/web_builds.yml4
-rw-r--r--.pre-commit-config.yaml8
-rw-r--r--core/io/resource_importer.h2
-rw-r--r--core/object/class_db.cpp104
-rw-r--r--doc/classes/AnimationNodeStateMachineTransition.xml4
-rw-r--r--doc/classes/AudioStreamPlaybackPolyphonic.xml2
-rw-r--r--doc/classes/Control.xml3
-rw-r--r--doc/classes/EditorFileSystemImportFormatSupportQuery.xml2
-rw-r--r--doc/classes/EditorImportPlugin.xml3
-rw-r--r--doc/classes/EditorResourcePreview.xml4
-rw-r--r--doc/classes/EditorScenePostImportPlugin.xml6
-rw-r--r--doc/classes/EditorSettings.xml2
-rw-r--r--doc/classes/EditorUndoRedoManager.xml2
-rw-r--r--doc/classes/GraphNode.xml2
-rw-r--r--doc/classes/Image.xml4
-rw-r--r--doc/classes/MultiplayerPeer.xml2
-rw-r--r--doc/classes/NavigationServer2D.xml8
-rw-r--r--doc/classes/NavigationServer3D.xml12
-rw-r--r--doc/classes/Object.xml2
-rw-r--r--doc/classes/ProjectSettings.xml10
-rw-r--r--doc/classes/RDPipelineDepthStencilState.xml6
-rw-r--r--doc/classes/RenderingServer.xml4
-rw-r--r--doc/classes/ResourceLoader.xml2
-rw-r--r--doc/classes/Shader.xml4
-rw-r--r--doc/classes/SkeletonIK3D.xml2
-rw-r--r--doc/classes/Slider.xml4
-rw-r--r--doc/classes/SpringArm3D.xml1
-rw-r--r--doc/classes/TileMap.xml4
-rw-r--r--doc/classes/TileMapLayer.xml4
-rw-r--r--doc/classes/TreeItem.xml6
-rw-r--r--doc/classes/VideoStreamPlayback.xml6
-rw-r--r--doc/classes/Viewport.xml6
-rw-r--r--doc/classes/VisualShaderNodeCompare.xml4
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp45
-rw-r--r--drivers/vulkan/rendering_device_driver_vulkan.cpp21
-rw-r--r--drivers/vulkan/rendering_device_driver_vulkan.h1
-rw-r--r--editor/editor_audio_buses.cpp20
-rw-r--r--editor/gui/editor_quick_open_dialog.cpp4
-rw-r--r--editor/import/3d/resource_importer_obj.cpp4
-rw-r--r--editor/import/3d/resource_importer_obj.h3
-rw-r--r--editor/import/3d/resource_importer_scene.cpp5
-rw-r--r--editor/import/3d/resource_importer_scene.h2
-rw-r--r--editor/import/3d/scene_import_settings.cpp1
-rw-r--r--editor/import/resource_importer_bitmask.h2
-rw-r--r--editor/import/resource_importer_bmfont.h2
-rw-r--r--editor/import/resource_importer_csv_translation.h2
-rw-r--r--editor/import/resource_importer_dynamic_font.h2
-rw-r--r--editor/import/resource_importer_image.h2
-rw-r--r--editor/import/resource_importer_imagefont.h2
-rw-r--r--editor/import/resource_importer_layered_texture.h2
-rw-r--r--editor/import/resource_importer_shader_file.h2
-rw-r--r--editor/import/resource_importer_texture.h2
-rw-r--r--editor/import/resource_importer_texture_atlas.h2
-rw-r--r--editor/import/resource_importer_wav.h2
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp2
-rw-r--r--methods.py70
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp48
-rw-r--r--modules/gdscript/gdscript_warning.cpp7
-rw-r--r--modules/gdscript/gdscript_warning.h4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_initializer.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_loop.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/lambda_shadowing_arg.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/shadowing_base.notest.gd7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/shadowning.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/shadowning.out32
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/shadowed_constant.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_class.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_function.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.out4
-rw-r--r--modules/gltf/doc_classes/GLTFAnimation.xml2
-rw-r--r--modules/gltf/doc_classes/GLTFBufferView.xml4
-rw-r--r--modules/gltf/doc_classes/GLTFCamera.xml6
-rw-r--r--modules/gltf/doc_classes/GLTFDocument.xml2
-rw-r--r--modules/gltf/doc_classes/GLTFDocumentExtension.xml8
-rw-r--r--modules/gltf/doc_classes/GLTFMesh.xml2
-rw-r--r--modules/gltf/doc_classes/GLTFNode.xml4
-rw-r--r--modules/gltf/doc_classes/GLTFObjectModelProperty.xml2
-rw-r--r--modules/gltf/doc_classes/GLTFState.xml8
-rw-r--r--modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp2
-rw-r--r--modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp2
-rw-r--r--modules/gltf/editor/editor_scene_exporter_gltf_settings.h2
-rw-r--r--modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp1
-rw-r--r--modules/gltf/gltf_document.cpp1
-rw-r--r--modules/interactive_music/doc_classes/AudioStreamInteractive.xml2
-rw-r--r--modules/meshoptimizer/register_types.cpp4
-rw-r--r--modules/minimp3/resource_importer_mp3.h2
-rw-r--r--modules/openxr/doc_classes/OpenXRCompositionLayer.xml2
-rw-r--r--modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml15
-rw-r--r--modules/openxr/extensions/openxr_extension_wrapper_extension.cpp10
-rw-r--r--modules/openxr/extensions/openxr_extension_wrapper_extension.h4
-rw-r--r--modules/text_server_adv/text_server_adv.cpp62
-rw-r--r--modules/text_server_fb/text_server_fb.cpp62
-rw-r--r--modules/vorbis/resource_importer_ogg_vorbis.h2
-rw-r--r--platform/SCsub2
-rw-r--r--platform/windows/SCsub1
-rw-r--r--platform/windows/detect.py124
-rw-r--r--platform/windows/display_server_windows.cpp64
-rw-r--r--platform/windows/display_server_windows.h7
-rw-r--r--platform/windows/drop_target_windows.cpp375
-rw-r--r--platform/windows/drop_target_windows.h77
-rw-r--r--platform/windows/godot.natvis20
-rw-r--r--platform/windows/platform_windows_builders.py18
-rw-r--r--scene/3d/skeleton_3d.cpp18
-rw-r--r--scene/gui/line_edit.cpp2
-rw-r--r--scene/gui/text_edit.cpp2
-rw-r--r--scene/gui/tree.cpp10
-rw-r--r--scene/register_scene_types.cpp3
-rw-r--r--scene/resources/2d/tile_set.cpp18
-rw-r--r--scene/resources/3d/importer_mesh.cpp103
-rw-r--r--scene/resources/3d/importer_mesh.h2
-rw-r--r--scene/resources/surface_tool.cpp2
-rw-r--r--scene/resources/surface_tool.h8
-rw-r--r--scene/resources/visual_shader.cpp4
-rw-r--r--scu_builders.py4
-rw-r--r--servers/audio_server.cpp73
-rw-r--r--servers/audio_server.h8
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp47
-rw-r--r--thirdparty/README.md7
-rw-r--r--thirdparty/certs/ca-certificates.crt149
120 files changed, 1324 insertions, 595 deletions
diff --git a/.github/workflows/web_builds.yml b/.github/workflows/web_builds.yml
index 8e30c99fbc..ec57fa2f0d 100644
--- a/.github/workflows/web_builds.yml
+++ b/.github/workflows/web_builds.yml
@@ -8,7 +8,6 @@ env:
GODOT_BASE_BRANCH: master
SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no use_closure_compiler=yes strict_checks=yes
EM_VERSION: 3.1.64
- EM_CACHE_FOLDER: emsdk-cache
concurrency:
group: ci-${{ github.actor }}-${{ github.head_ref || github.run_number }}-${{ github.ref }}-web
@@ -46,8 +45,7 @@ jobs:
uses: mymindstorm/setup-emsdk@v14
with:
version: ${{ env.EM_VERSION }}
- actions-cache-folder: ${{ env.EM_CACHE_FOLDER }}
- cache-key: emsdk-${{ matrix.cache-name }}-${{ env.GODOT_BASE_BRANCH }}-${{ github.ref }}-${{ github.sha }}
+ no-cache: true
- name: Verify Emscripten setup
run: |
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 29263f07ee..d81c1043a7 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -9,7 +9,7 @@ exclude: |
repos:
- repo: https://github.com/pre-commit/mirrors-clang-format
- rev: v19.1.0
+ rev: v19.1.3
hooks:
- id: clang-format
files: \.(c|h|cpp|hpp|cc|hh|cxx|hxx|m|mm|inc|java)$
@@ -22,7 +22,7 @@ repos:
)
- id: clang-format
name: clang-format-glsl
- files: \.(glsl)$
+ files: \.glsl$
types_or: [text]
exclude: |
(?x)^(
@@ -30,7 +30,7 @@ repos:
platform/android/java/editor/src/main/java/com/android/.*|
platform/android/java/lib/src/com/.*
)
- args: ["-style=file:misc/utility/.clang-format-glsl"]
+ args: ['-style=file:misc/utility/.clang-format-glsl']
- repo: https://github.com/pocc/pre-commit-hooks
rev: v1.3.5
@@ -45,7 +45,7 @@ repos:
platform/android/java/editor/src/main/java/com/android/.*|
platform/android/java/lib/src/com/.*
)
- additional_dependencies: [clang-tidy==18.1.1]
+ additional_dependencies: [clang-tidy==19.1.0]
require_serial: true
stages: [manual] # Not automatically triggered, invoked via `pre-commit run --hook-stage manual clang-tidy`
diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h
index 221f38494b..3ca8f7c05d 100644
--- a/core/io/resource_importer.h
+++ b/core/io/resource_importer.h
@@ -148,7 +148,7 @@ public:
virtual String get_option_group_file() const { return String(); }
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) = 0;
- virtual bool can_import_threaded() const { return true; }
+ virtual bool can_import_threaded() const { return false; }
virtual void import_threaded_begin() {}
virtual void import_threaded_end() {}
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp
index bad224eff4..d48e1a3622 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -750,69 +750,87 @@ void ClassDB::set_object_extension_instance(Object *p_object, const StringName &
}
bool ClassDB::can_instantiate(const StringName &p_class) {
- OBJTYPE_RLOCK;
+ String script_path;
+ {
+ OBJTYPE_RLOCK;
- ClassInfo *ti = classes.getptr(p_class);
- if (!ti) {
- if (!ScriptServer::is_global_class(p_class)) {
- ERR_FAIL_V_MSG(false, vformat("Cannot get class '%s'.", String(p_class)));
+ ClassInfo *ti = classes.getptr(p_class);
+ if (!ti) {
+ if (!ScriptServer::is_global_class(p_class)) {
+ ERR_FAIL_V_MSG(false, vformat("Cannot get class '%s'.", String(p_class)));
+ }
+ script_path = ScriptServer::get_global_class_path(p_class);
+ goto use_script; // Open the lock for resource loading.
}
- String path = ScriptServer::get_global_class_path(p_class);
- Ref<Script> scr = ResourceLoader::load(path);
- return scr.is_valid() && scr->is_valid() && !scr->is_abstract();
- }
#ifdef TOOLS_ENABLED
- if ((ti->api == API_EDITOR || ti->api == API_EDITOR_EXTENSION) && !Engine::get_singleton()->is_editor_hint()) {
- return false;
- }
+ if ((ti->api == API_EDITOR || ti->api == API_EDITOR_EXTENSION) && !Engine::get_singleton()->is_editor_hint()) {
+ return false;
+ }
#endif
- return _can_instantiate(ti);
+ return _can_instantiate(ti);
+ }
+
+use_script:
+ Ref<Script> scr = ResourceLoader::load(script_path);
+ return scr.is_valid() && scr->is_valid() && !scr->is_abstract();
}
bool ClassDB::is_abstract(const StringName &p_class) {
- OBJTYPE_RLOCK;
+ String script_path;
+ {
+ OBJTYPE_RLOCK;
- ClassInfo *ti = classes.getptr(p_class);
- if (!ti) {
- if (!ScriptServer::is_global_class(p_class)) {
- ERR_FAIL_V_MSG(false, vformat("Cannot get class '%s'.", String(p_class)));
+ ClassInfo *ti = classes.getptr(p_class);
+ if (!ti) {
+ if (!ScriptServer::is_global_class(p_class)) {
+ ERR_FAIL_V_MSG(false, vformat("Cannot get class '%s'.", String(p_class)));
+ }
+ script_path = ScriptServer::get_global_class_path(p_class);
+ goto use_script; // Open the lock for resource loading.
}
- String path = ScriptServer::get_global_class_path(p_class);
- Ref<Script> scr = ResourceLoader::load(path);
- return scr.is_valid() && scr->is_valid() && scr->is_abstract();
- }
- if (ti->creation_func != nullptr) {
- return false;
- }
- if (!ti->gdextension) {
- return true;
- }
+ if (ti->creation_func != nullptr) {
+ return false;
+ }
+ if (!ti->gdextension) {
+ return true;
+ }
#ifndef DISABLE_DEPRECATED
- return ti->gdextension->create_instance2 == nullptr && ti->gdextension->create_instance == nullptr;
+ return ti->gdextension->create_instance2 == nullptr && ti->gdextension->create_instance == nullptr;
#else
- return ti->gdextension->create_instance2 == nullptr;
+ return ti->gdextension->create_instance2 == nullptr;
#endif // DISABLE_DEPRECATED
+ }
+
+use_script:
+ Ref<Script> scr = ResourceLoader::load(script_path);
+ return scr.is_valid() && scr->is_valid() && scr->is_abstract();
}
bool ClassDB::is_virtual(const StringName &p_class) {
- OBJTYPE_RLOCK;
+ String script_path;
+ {
+ OBJTYPE_RLOCK;
- ClassInfo *ti = classes.getptr(p_class);
- if (!ti) {
- if (!ScriptServer::is_global_class(p_class)) {
- ERR_FAIL_V_MSG(false, vformat("Cannot get class '%s'.", String(p_class)));
+ ClassInfo *ti = classes.getptr(p_class);
+ if (!ti) {
+ if (!ScriptServer::is_global_class(p_class)) {
+ ERR_FAIL_V_MSG(false, vformat("Cannot get class '%s'.", String(p_class)));
+ }
+ script_path = ScriptServer::get_global_class_path(p_class);
+ goto use_script; // Open the lock for resource loading.
}
- String path = ScriptServer::get_global_class_path(p_class);
- Ref<Script> scr = ResourceLoader::load(path);
- return scr.is_valid() && scr->is_valid() && scr->is_abstract();
- }
#ifdef TOOLS_ENABLED
- if ((ti->api == API_EDITOR || ti->api == API_EDITOR_EXTENSION) && !Engine::get_singleton()->is_editor_hint()) {
- return false;
- }
+ if ((ti->api == API_EDITOR || ti->api == API_EDITOR_EXTENSION) && !Engine::get_singleton()->is_editor_hint()) {
+ return false;
+ }
#endif
- return (_can_instantiate(ti) && ti->is_virtual);
+ return (_can_instantiate(ti) && ti->is_virtual);
+ }
+
+use_script:
+ Ref<Script> scr = ResourceLoader::load(script_path);
+ return scr.is_valid() && scr->is_valid() && scr->is_abstract();
}
void ClassDB::_add_class2(const StringName &p_class, const StringName &p_inherits) {
diff --git a/doc/classes/AnimationNodeStateMachineTransition.xml b/doc/classes/AnimationNodeStateMachineTransition.xml
index 7bd0bd7e7e..c729eeebba 100644
--- a/doc/classes/AnimationNodeStateMachineTransition.xml
+++ b/doc/classes/AnimationNodeStateMachineTransition.xml
@@ -26,7 +26,7 @@
Use an expression as a condition for state machine transitions. It is possible to create complex animation advance conditions for switching between states and gives much greater flexibility for creating complex state machines by directly interfacing with the script code.
</member>
<member name="advance_mode" type="int" setter="set_advance_mode" getter="get_advance_mode" enum="AnimationNodeStateMachineTransition.AdvanceMode" default="1">
- Determines whether the transition should disabled, enabled when using [method AnimationNodeStateMachinePlayback.travel], or traversed automatically if the [member advance_condition] and [member advance_expression] checks are true (if assigned).
+ Determines whether the transition should be disabled, enabled when using [method AnimationNodeStateMachinePlayback.travel], or traversed automatically if the [member advance_condition] and [member advance_expression] checks are [code]true[/code] (if assigned).
</member>
<member name="break_loop_at_end" type="bool" setter="set_break_loop_at_end" getter="is_loop_broken_at_end" default="false">
If [code]true[/code], breaks the loop at the end of the loop cycle for transition, even if the animation is looping.
@@ -72,7 +72,7 @@
Only use this transition during [method AnimationNodeStateMachinePlayback.travel].
</constant>
<constant name="ADVANCE_MODE_AUTO" value="2" enum="AdvanceMode">
- Automatically use this transition if the [member advance_condition] and [member advance_expression] checks are true (if assigned).
+ Automatically use this transition if the [member advance_condition] and [member advance_expression] checks are [code]true[/code] (if assigned).
</constant>
</constants>
</class>
diff --git a/doc/classes/AudioStreamPlaybackPolyphonic.xml b/doc/classes/AudioStreamPlaybackPolyphonic.xml
index f71762d6a5..894c9b2262 100644
--- a/doc/classes/AudioStreamPlaybackPolyphonic.xml
+++ b/doc/classes/AudioStreamPlaybackPolyphonic.xml
@@ -13,7 +13,7 @@
<return type="bool" />
<param index="0" name="stream" type="int" />
<description>
- Return true whether the stream associated with an integer ID is still playing. Check [method play_stream] for information on when this ID becomes invalid.
+ Returns [code]true[/code] if the stream associated with the given integer ID is still playing. Check [method play_stream] for information on when this ID becomes invalid.
</description>
</method>
<method name="play_stream">
diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index d455bd7d68..0131f8f4af 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -993,8 +993,9 @@
Controls whether the control will be able to receive mouse button input events through [method _gui_input] and how these events should be handled. Also controls whether the control can receive the [signal mouse_entered], and [signal mouse_exited] signals. See the constants to learn what each does.
</member>
<member name="mouse_force_pass_scroll_events" type="bool" setter="set_force_pass_scroll_events" getter="is_force_pass_scroll_events" default="true">
- When enabled, scroll wheel events processed by [method _gui_input] will be passed to the parent control even if [member mouse_filter] is set to [constant MOUSE_FILTER_STOP]. As it defaults to true, this allows nested scrollable containers to work out of the box.
+ When enabled, scroll wheel events processed by [method _gui_input] will be passed to the parent control even if [member mouse_filter] is set to [constant MOUSE_FILTER_STOP].
You should disable it on the root of your UI if you do not want scroll events to go to the [method Node._unhandled_input] processing.
+ [b]Note:[/b] Because this property defaults to [code]true[/code], this allows nested scrollable containers to work out of the box.
</member>
<member name="offset_bottom" type="float" setter="set_offset" getter="get_offset" default="0.0">
Distance between the node's bottom edge and its parent control, based on [member anchor_bottom].
diff --git a/doc/classes/EditorFileSystemImportFormatSupportQuery.xml b/doc/classes/EditorFileSystemImportFormatSupportQuery.xml
index b1a810fe6e..fc2feca911 100644
--- a/doc/classes/EditorFileSystemImportFormatSupportQuery.xml
+++ b/doc/classes/EditorFileSystemImportFormatSupportQuery.xml
@@ -24,7 +24,7 @@
<method name="_query" qualifiers="virtual const">
<return type="bool" />
<description>
- Query support. Return false if import must not continue.
+ Query support. Return [code]false[/code] if import must not continue.
</description>
</method>
</methods>
diff --git a/doc/classes/EditorImportPlugin.xml b/doc/classes/EditorImportPlugin.xml
index 28614b6631..e5f3010366 100644
--- a/doc/classes/EditorImportPlugin.xml
+++ b/doc/classes/EditorImportPlugin.xml
@@ -124,7 +124,8 @@
<return type="bool" />
<description>
Tells whether this importer can be run in parallel on threads, or, on the contrary, it's only safe for the editor to call it from the main thread, for one file at a time.
- If this method is not overridden, it will return [code]true[/code] by default (i.e., safe for parallel importing).
+ If this method is not overridden, it will return [code]false[/code] by default.
+ If this importer's implementation is thread-safe and can be run in parallel, override this with [code]true[/code] to optimize for concurrency.
</description>
</method>
<method name="_get_import_options" qualifiers="virtual const">
diff --git a/doc/classes/EditorResourcePreview.xml b/doc/classes/EditorResourcePreview.xml
index ed3fdae352..a0c1a43fb2 100644
--- a/doc/classes/EditorResourcePreview.xml
+++ b/doc/classes/EditorResourcePreview.xml
@@ -32,7 +32,7 @@
<param index="3" name="userdata" type="Variant" />
<description>
Queue the [param resource] being edited for preview. Once the preview is ready, the [param receiver]'s [param receiver_func] will be called. The [param receiver_func] must take the following four arguments: [String] path, [Texture2D] preview, [Texture2D] thumbnail_preview, [Variant] userdata. [param userdata] can be anything, and will be returned when [param receiver_func] is called.
- [b]Note:[/b] If it was not possible to create the preview the [param receiver_func] will still be called, but the preview will be null.
+ [b]Note:[/b] If it was not possible to create the preview the [param receiver_func] will still be called, but the preview will be [code]null[/code].
</description>
</method>
<method name="queue_resource_preview">
@@ -43,7 +43,7 @@
<param index="3" name="userdata" type="Variant" />
<description>
Queue a resource file located at [param path] for preview. Once the preview is ready, the [param receiver]'s [param receiver_func] will be called. The [param receiver_func] must take the following four arguments: [String] path, [Texture2D] preview, [Texture2D] thumbnail_preview, [Variant] userdata. [param userdata] can be anything, and will be returned when [param receiver_func] is called.
- [b]Note:[/b] If it was not possible to create the preview the [param receiver_func] will still be called, but the preview will be null.
+ [b]Note:[/b] If it was not possible to create the preview the [param receiver_func] will still be called, but the preview will be [code]null[/code].
</description>
</method>
<method name="remove_preview_generator">
diff --git a/doc/classes/EditorScenePostImportPlugin.xml b/doc/classes/EditorScenePostImportPlugin.xml
index 5c1c898c9d..0004ec65c6 100644
--- a/doc/classes/EditorScenePostImportPlugin.xml
+++ b/doc/classes/EditorScenePostImportPlugin.xml
@@ -28,7 +28,7 @@
<param index="0" name="category" type="int" />
<param index="1" name="option" type="String" />
<description>
- Return true whether updating the 3D view of the import dialog needs to be updated if an option has changed.
+ Should return [code]true[/code] if the 3D view of the import dialog needs to update when changing the given option.
</description>
</method>
<method name="_get_internal_option_visibility" qualifiers="virtual const">
@@ -37,7 +37,7 @@
<param index="1" name="for_animation" type="bool" />
<param index="2" name="option" type="String" />
<description>
- Return true or false whether a given option should be visible. Return null to ignore.
+ Should return [code]true[/code] to show the given option, [code]false[/code] to hide the given option, or [code]null[/code] to ignore.
</description>
</method>
<method name="_get_option_visibility" qualifiers="virtual const">
@@ -46,7 +46,7 @@
<param index="1" name="for_animation" type="bool" />
<param index="2" name="option" type="String" />
<description>
- Return true or false whether a given option should be visible. Return null to ignore.
+ Should return [code]true[/code] to show the given option, [code]false[/code] to hide the given option, or [code]null[/code] to ignore.
</description>
</method>
<method name="_internal_process" qualifiers="virtual">
diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml
index a5097521dc..2398748917 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -152,7 +152,7 @@
<param index="1" name="value" type="Variant" />
<param index="2" name="update_current" type="bool" />
<description>
- Sets the initial value of the setting specified by [param name] to [param value]. This is used to provide a value for the Revert button in the Editor Settings. If [param update_current] is true, the current value of the setting will be set to [param value] as well.
+ Sets the initial value of the setting specified by [param name] to [param value]. This is used to provide a value for the Revert button in the Editor Settings. If [param update_current] is [code]true[/code], the setting is reset to [param value] as well.
</description>
</method>
<method name="set_project_metadata">
diff --git a/doc/classes/EditorUndoRedoManager.xml b/doc/classes/EditorUndoRedoManager.xml
index 0f8c69a1bb..6a66a35ce8 100644
--- a/doc/classes/EditorUndoRedoManager.xml
+++ b/doc/classes/EditorUndoRedoManager.xml
@@ -87,7 +87,7 @@
<return type="void" />
<param index="0" name="execute" type="bool" default="true" />
<description>
- Commit the action. If [param execute] is true (default), all "do" methods/properties are called/set when this function is called.
+ Commits the action. If [param execute] is [code]true[/code] (default), all "do" methods/properties are called/set when this function is called.
</description>
</method>
<method name="create_action">
diff --git a/doc/classes/GraphNode.xml b/doc/classes/GraphNode.xml
index cc3acad6d6..fc8bc15b5c 100644
--- a/doc/classes/GraphNode.xml
+++ b/doc/classes/GraphNode.xml
@@ -154,7 +154,7 @@
<return type="bool" />
<param index="0" name="slot_index" type="int" />
<description>
- Returns true if the background [StyleBox] of the slot with the given [param slot_index] is drawn.
+ Returns [code]true[/code] if the background [StyleBox] of the slot with the given [param slot_index] is drawn.
</description>
</method>
<method name="is_slot_enabled_left" qualifiers="const">
diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml
index 0fd84fb452..9b71a8c37f 100644
--- a/doc/classes/Image.xml
+++ b/doc/classes/Image.xml
@@ -510,7 +510,7 @@
<param index="1" name="lossy" type="bool" default="false" />
<param index="2" name="quality" type="float" default="0.75" />
<description>
- Saves the image as a WebP (Web Picture) file to the file at [param path]. By default it will save lossless. If [param lossy] is true, the image will be saved lossy, using the [param quality] setting between 0.0 and 1.0 (inclusive). Lossless WebP offers more efficient compression than PNG.
+ Saves the image as a WebP (Web Picture) file to the file at [param path]. By default it will save lossless. If [param lossy] is [code]true[/code], the image will be saved lossy, using the [param quality] setting between [code]0.0[/code] and [code]1.0[/code] (inclusive). Lossless WebP offers more efficient compression than PNG.
[b]Note:[/b] The WebP format is limited to a size of 16383×16383 pixels, while PNG can save larger images.
</description>
</method>
@@ -519,7 +519,7 @@
<param index="0" name="lossy" type="bool" default="false" />
<param index="1" name="quality" type="float" default="0.75" />
<description>
- Saves the image as a WebP (Web Picture) file to a byte array. By default it will save lossless. If [param lossy] is true, the image will be saved lossy, using the [param quality] setting between 0.0 and 1.0 (inclusive). Lossless WebP offers more efficient compression than PNG.
+ Saves the image as a WebP (Web Picture) file to a byte array. By default it will save lossless. If [param lossy] is [code]true[/code], the image will be saved lossy, using the [param quality] setting between [code]0.0[/code] and [code]1.0[/code] (inclusive). Lossless WebP offers more efficient compression than PNG.
[b]Note:[/b] The WebP format is limited to a size of 16383×16383 pixels, while PNG can save larger images.
</description>
</method>
diff --git a/doc/classes/MultiplayerPeer.xml b/doc/classes/MultiplayerPeer.xml
index edb2c39e24..cec9464102 100644
--- a/doc/classes/MultiplayerPeer.xml
+++ b/doc/classes/MultiplayerPeer.xml
@@ -65,7 +65,7 @@
<method name="is_server_relay_supported" qualifiers="const">
<return type="bool" />
<description>
- Returns true if the server can act as a relay in the current configuration (i.e. if the higher level [MultiplayerAPI] should notify connected clients of other peers, and implement a relay protocol to allow communication between them).
+ Returns [code]true[/code] if the server can act as a relay in the current configuration. That is, if the higher level [MultiplayerAPI] should notify connected clients of other peers, and implement a relay protocol to allow communication between them.
</description>
</method>
<method name="poll">
diff --git a/doc/classes/NavigationServer2D.xml b/doc/classes/NavigationServer2D.xml
index 7e78006240..5c19a6b355 100644
--- a/doc/classes/NavigationServer2D.xml
+++ b/doc/classes/NavigationServer2D.xml
@@ -133,7 +133,7 @@
<return type="bool" />
<param index="0" name="agent" type="RID" />
<description>
- Returns true if the map got changed the previous frame.
+ Returns [code]true[/code] if the map got changed the previous frame.
</description>
</method>
<method name="agent_set_avoidance_callback">
@@ -215,7 +215,7 @@
<param index="0" name="agent" type="RID" />
<param index="1" name="paused" type="bool" />
<description>
- If [param paused] is true the specified [param agent] will not be processed, e.g. calculate avoidance velocities or receive avoidance callbacks.
+ If [param paused] is [code]true[/code] the specified [param agent] will not be processed, e.g. calculate avoidance velocities or receive avoidance callbacks.
</description>
</method>
<method name="agent_set_position">
@@ -573,7 +573,7 @@
<return type="bool" />
<param index="0" name="map" type="RID" />
<description>
- Returns true if the map is active.
+ Returns [code]true[/code] if the map is active.
</description>
</method>
<method name="map_set_active">
@@ -707,7 +707,7 @@
<param index="0" name="obstacle" type="RID" />
<param index="1" name="paused" type="bool" />
<description>
- If [param paused] is true the specified [param obstacle] will not be processed, e.g. affect avoidance velocities.
+ If [param paused] is [code]true[/code] the specified [param obstacle] will not be processed, e.g. affect avoidance velocities.
</description>
</method>
<method name="obstacle_set_position">
diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml
index 7e206046d6..66a286758b 100644
--- a/doc/classes/NavigationServer3D.xml
+++ b/doc/classes/NavigationServer3D.xml
@@ -147,7 +147,7 @@
<return type="bool" />
<param index="0" name="agent" type="RID" />
<description>
- Returns true if the map got changed the previous frame.
+ Returns [code]true[/code] if the map got changed the previous frame.
</description>
</method>
<method name="agent_set_avoidance_callback">
@@ -237,7 +237,7 @@
<param index="0" name="agent" type="RID" />
<param index="1" name="paused" type="bool" />
<description>
- If [param paused] is true the specified [param agent] will not be processed, e.g. calculate avoidance velocities or receive avoidance callbacks.
+ If [param paused] is [code]true[/code] the specified [param agent] will not be processed, e.g. calculate avoidance velocities or receive avoidance callbacks.
</description>
</method>
<method name="agent_set_position">
@@ -645,14 +645,14 @@
<return type="bool" />
<param index="0" name="map" type="RID" />
<description>
- Returns true if the navigation [param map] allows navigation regions to use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin.
+ Returns [code]true[/code] if the navigation [param map] allows navigation regions to use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin.
</description>
</method>
<method name="map_is_active" qualifiers="const">
<return type="bool" />
<param index="0" name="map" type="RID" />
<description>
- Returns true if the map is active.
+ Returns [code]true[/code] if the map is active.
</description>
</method>
<method name="map_set_active">
@@ -832,7 +832,7 @@
<param index="0" name="obstacle" type="RID" />
<param index="1" name="paused" type="bool" />
<description>
- If [param paused] is true the specified [param obstacle] will not be processed, e.g. affect avoidance velocities.
+ If [param paused] is [code]true[/code] the specified [param obstacle] will not be processed, e.g. affect avoidance velocities.
</description>
</method>
<method name="obstacle_set_position">
@@ -1023,7 +1023,7 @@
<return type="bool" />
<param index="0" name="region" type="RID" />
<description>
- Returns true if the navigation [param region] is set to use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin.
+ Returns [code]true[/code] if the navigation [param region] is set to use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin.
</description>
</method>
<method name="region_owns_point" qualifiers="const">
diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml
index 2767a11e80..a130a71826 100644
--- a/doc/classes/Object.xml
+++ b/doc/classes/Object.xml
@@ -454,7 +454,7 @@
<return type="Variant" />
<param index="0" name="method" type="StringName" />
<description>
- Calls the [param method] on the object during idle time. Always returns null, [b]not[/b] the method's result.
+ Calls the [param method] on the object during idle time. Always returns [code]null[/code], [b]not[/b] the method's result.
Idle time happens mainly at the end of process and physics frames. In it, deferred calls will be run until there are none left, which means you can defer calls from other deferred calls and they'll still be run in the current idle time cycle. This means you should not call a method deferred from itself (or from a method called by it), as this causes infinite recursion the same way as if you had called the method directly.
This method supports a variable number of arguments, so parameters can be passed as a comma separated list.
[codeblocks]
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 3358e6838c..fff6c8d3bb 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -469,10 +469,10 @@
If the [code]--log-file &lt;file&gt;[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url] is used, log rotation is always disabled.
</member>
<member name="debug/gdscript/warnings/assert_always_false" type="int" setter="" getter="" default="1">
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an [code]assert[/code] call always evaluates to false.
+ When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an [code]assert[/code] call always evaluates to [code]false[/code].
</member>
<member name="debug/gdscript/warnings/assert_always_true" type="int" setter="" getter="" default="1">
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an [code]assert[/code] call always evaluates to true.
+ When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an [code]assert[/code] call always evaluates to [code]true[/code].
</member>
<member name="debug/gdscript/warnings/confusable_capture_reassignment" type="int" setter="" getter="" default="1">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a local variable captured by a lambda is reassigned, since this does not modify the outer local variable.
@@ -561,10 +561,10 @@
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when defining a local or member variable, signal, or enum that would have the same name as a built-in function or global class name, thus shadowing it.
</member>
<member name="debug/gdscript/warnings/shadowed_variable" type="int" setter="" getter="" default="1">
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when defining a local or member variable that would shadow a member variable that the class defines.
+ When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a local variable or local constant shadows a member declared in the current class.
</member>
<member name="debug/gdscript/warnings/shadowed_variable_base_class" type="int" setter="" getter="" default="1">
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when defining a local or subclass member variable that would shadow a variable that is inherited from a parent class.
+ When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a local variable or local constant shadows a member declared in a base class.
</member>
<member name="debug/gdscript/warnings/standalone_expression" type="int" setter="" getter="" default="1">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling an expression that may have no effect on the surrounding code, such as writing [code]2 + 2[/code] as a statement.
@@ -3014,7 +3014,7 @@
Specify whether OpenXR should be configured for an HMD or a hand held device.
</member>
<member name="xr/openxr/foveation_dynamic" type="bool" setter="" getter="" default="false">
- If true and foveation is supported, will automatically adjust foveation level based on framerate up to the level set on [member xr/openxr/foveation_level].
+ If [code]true[/code] and foveation is supported, will automatically adjust foveation level based on framerate up to the level set on [member xr/openxr/foveation_level].
[b]Note:[/b] Only works on the Compatibility rendering method.
</member>
<member name="xr/openxr/foveation_level" type="int" setter="" getter="" default="&quot;0&quot;">
diff --git a/doc/classes/RDPipelineDepthStencilState.xml b/doc/classes/RDPipelineDepthStencilState.xml
index dc1e70eb55..425890bb8d 100644
--- a/doc/classes/RDPipelineDepthStencilState.xml
+++ b/doc/classes/RDPipelineDepthStencilState.xml
@@ -34,10 +34,10 @@
The method used for comparing the previous and current depth values.
</member>
<member name="depth_range_max" type="float" setter="set_depth_range_max" getter="get_depth_range_max" default="0.0">
- The maximum depth that returns true for [member enable_depth_range].
+ The maximum depth that returns [code]true[/code] for [member enable_depth_range].
</member>
<member name="depth_range_min" type="float" setter="set_depth_range_min" getter="get_depth_range_min" default="0.0">
- The minimum depth that returns true for [member enable_depth_range].
+ The minimum depth that returns [code]true[/code] for [member enable_depth_range].
</member>
<member name="enable_depth_range" type="bool" setter="set_enable_depth_range" getter="get_enable_depth_range" default="false">
If [code]true[/code], each depth value will be tested to see if it is between [member depth_range_min] and [member depth_range_max]. If it is outside of these values, it is discarded.
@@ -46,7 +46,7 @@
If [code]true[/code], enables depth testing which allows objects to be automatically occluded by other objects based on their depth. This also allows objects to be partially occluded by other objects. If [code]false[/code], objects will appear in the order they were drawn (like in Godot's 2D renderer).
</member>
<member name="enable_depth_write" type="bool" setter="set_enable_depth_write" getter="get_enable_depth_write" default="false">
- If [code]true[/code], writes to the depth buffer whenever the depth test returns true. Only works when enable_depth_test is also true.
+ If [code]true[/code], writes to the depth buffer whenever the depth test returns [code]true[/code]. Only works when enable_depth_test is also [code]true[/code].
</member>
<member name="enable_stencil" type="bool" setter="set_enable_stencil" getter="get_enable_stencil" default="false">
If [code]true[/code], enables stencil testing. There are separate stencil buffers for front-facing triangles and back-facing triangles. See properties that begin with "front_op" and properties with "back_op" for each.
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 90a0798265..284dc07fd4 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -224,7 +224,7 @@
<param index="0" name="item" type="RID" />
<param index="1" name="ignore" type="bool" />
<description>
- If [param ignore] is [code]true[/code], ignore clipping on items drawn with this canvas item until this is called again with [param ignore] set to false.
+ If [param ignore] is [code]true[/code], ignore clipping on items drawn with this canvas item until this is called again with [param ignore] set to [code]false[/code].
</description>
</method>
<method name="canvas_item_add_lcd_texture_rect_region">
@@ -2957,7 +2957,7 @@
<param index="0" name="particles" type="RID" />
<param index="1" name="emitting" type="bool" />
<description>
- If [code]true[/code], particles will emit over time. Setting to false does not reset the particles, but only stops their emission. Equivalent to [member GPUParticles3D.emitting].
+ If [code]true[/code], particles will emit over time. Setting to [code]false[/code] does not reset the particles, but only stops their emission. Equivalent to [member GPUParticles3D.emitting].
</description>
</method>
<method name="particles_set_explosiveness_ratio">
diff --git a/doc/classes/ResourceLoader.xml b/doc/classes/ResourceLoader.xml
index 56c3208fc3..f718ad15d8 100644
--- a/doc/classes/ResourceLoader.xml
+++ b/doc/classes/ResourceLoader.xml
@@ -104,7 +104,7 @@
<param index="1" name="progress" type="Array" default="[]" />
<description>
Returns the status of a threaded loading operation started with [method load_threaded_request] for the resource at [param path]. See [enum ThreadLoadStatus] for possible return values.
- An array variable can optionally be passed via [param progress], and will return a one-element array containing the percentage of completion of the threaded loading.
+ An array variable can optionally be passed via [param progress], and will return a one-element array containing the ratio of completion of the threaded loading (between [code]0.0[/code] and [code]1.0[/code]).
[b]Note:[/b] The recommended way of using this method is to call it during different frames (e.g., in [method Node._process], instead of a loop).
</description>
</method>
diff --git a/doc/classes/Shader.xml b/doc/classes/Shader.xml
index 1e7f9e5ee5..3fbe1da687 100644
--- a/doc/classes/Shader.xml
+++ b/doc/classes/Shader.xml
@@ -31,8 +31,8 @@
<return type="Array" />
<param index="0" name="get_groups" type="bool" default="false" />
<description>
- Get the list of shader uniforms that can be assigned to a [ShaderMaterial], for use with [method ShaderMaterial.set_shader_parameter] and [method ShaderMaterial.get_shader_parameter]. The parameters returned are contained in dictionaries in a similar format to the ones returned by [method Object.get_property_list].
- If argument [param get_groups] is true, parameter grouping hints will be provided.
+ Returns the list of shader uniforms that can be assigned to a [ShaderMaterial], for use with [method ShaderMaterial.set_shader_parameter] and [method ShaderMaterial.get_shader_parameter]. The parameters returned are contained in dictionaries in a similar format to the ones returned by [method Object.get_property_list].
+ If argument [param get_groups] is [code]true[/code], parameter grouping hints are also included in the list.
</description>
</method>
<method name="inspect_native_shader_code">
diff --git a/doc/classes/SkeletonIK3D.xml b/doc/classes/SkeletonIK3D.xml
index 4858a6ce22..dbb099a22b 100644
--- a/doc/classes/SkeletonIK3D.xml
+++ b/doc/classes/SkeletonIK3D.xml
@@ -31,7 +31,7 @@
<method name="get_parent_skeleton" qualifiers="const">
<return type="Skeleton3D" />
<description>
- Returns the parent [Skeleton3D] Node that was present when SkeletonIK entered the [SceneTree]. Returns null if the parent node was not a [Skeleton3D] Node when SkeletonIK3D entered the [SceneTree].
+ Returns the parent [Skeleton3D] node that was present when SkeletonIK entered the scene tree. Returns [code]null[/code] if the parent node was not a [Skeleton3D] node when SkeletonIK3D entered the scene tree.
</description>
</method>
<method name="is_running">
diff --git a/doc/classes/Slider.xml b/doc/classes/Slider.xml
index a4ffa5c1e7..b1e78e8987 100644
--- a/doc/classes/Slider.xml
+++ b/doc/classes/Slider.xml
@@ -28,12 +28,12 @@
<signal name="drag_ended">
<param index="0" name="value_changed" type="bool" />
<description>
- Emitted when dragging stops. If [param value_changed] is true, [member Range.value] is different from the value when you started the dragging.
+ Emitted when the grabber stops being dragged. If [param value_changed] is [code]true[/code], [member Range.value] is different from the value when the dragging was started.
</description>
</signal>
<signal name="drag_started">
<description>
- Emitted when dragging is started. This is emitted before the corresponding [signal Range.value_changed] signal.
+ Emitted when the grabber starts being dragged. This is emitted before the corresponding [signal Range.value_changed] signal.
</description>
</signal>
</signals>
diff --git a/doc/classes/SpringArm3D.xml b/doc/classes/SpringArm3D.xml
index 99389fb78d..330317f352 100644
--- a/doc/classes/SpringArm3D.xml
+++ b/doc/classes/SpringArm3D.xml
@@ -7,6 +7,7 @@
[SpringArm3D] casts a ray or a shape along its Z axis and moves all its direct children to the collision point, with an optional margin. This is useful for 3rd person cameras that move closer to the player when inside a tight space (you may need to exclude the player's collider from the [SpringArm3D]'s collision check).
</description>
<tutorials>
+ <link title="Third-person camera with spring arm">$DOCS_URL/tutorials/3d/spring_arm.html</link>
</tutorials>
<methods>
<method name="add_excluded_object">
diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml
index 0995a5a672..4683f063f1 100644
--- a/doc/classes/TileMap.xml
+++ b/doc/classes/TileMap.xml
@@ -380,7 +380,7 @@
<param index="4" name="ignore_empty_terrains" type="bool" default="true" />
<description>
Update all the cells in the [param cells] coordinates array so that they use the given [param terrain] for the given [param terrain_set]. If an updated cell has the same terrain as one of its neighboring cells, this function tries to join the two. This function might update neighboring tiles if needed to create correct terrain transitions.
- If [param ignore_empty_terrains] is true, empty terrains will be ignored when trying to find the best fitting tile for the given terrain constraints.
+ If [param ignore_empty_terrains] is [code]true[/code], empty terrains will be ignored when trying to find the best fitting tile for the given terrain constraints.
If [param layer] is negative, the layers are accessed from the last one.
[b]Note:[/b] To work correctly, this method requires the TileMap's TileSet to have terrains set up with all required terrain combinations. Otherwise, it may produce unexpected results.
</description>
@@ -394,7 +394,7 @@
<param index="4" name="ignore_empty_terrains" type="bool" default="true" />
<description>
Update all the cells in the [param path] coordinates array so that they use the given [param terrain] for the given [param terrain_set]. The function will also connect two successive cell in the path with the same terrain. This function might update neighboring tiles if needed to create correct terrain transitions.
- If [param ignore_empty_terrains] is true, empty terrains will be ignored when trying to find the best fitting tile for the given terrain constraints.
+ If [param ignore_empty_terrains] is [code]true[/code], empty terrains will be ignored when trying to find the best fitting tile for the given terrain constraints.
If [param layer] is negative, the layers are accessed from the last one.
[b]Note:[/b] To work correctly, this method requires the TileMap's TileSet to have terrains set up with all required terrain combinations. Otherwise, it may produce unexpected results.
</description>
diff --git a/doc/classes/TileMapLayer.xml b/doc/classes/TileMapLayer.xml
index 135f85de69..6cbec9c2aa 100644
--- a/doc/classes/TileMapLayer.xml
+++ b/doc/classes/TileMapLayer.xml
@@ -228,7 +228,7 @@
<param index="3" name="ignore_empty_terrains" type="bool" default="true" />
<description>
Update all the cells in the [param cells] coordinates array so that they use the given [param terrain] for the given [param terrain_set]. If an updated cell has the same terrain as one of its neighboring cells, this function tries to join the two. This function might update neighboring tiles if needed to create correct terrain transitions.
- If [param ignore_empty_terrains] is true, empty terrains will be ignored when trying to find the best fitting tile for the given terrain constraints.
+ If [param ignore_empty_terrains] is [code]true[/code], empty terrains will be ignored when trying to find the best fitting tile for the given terrain constraints.
[b]Note:[/b] To work correctly, this method requires the [TileMapLayer]'s TileSet to have terrains set up with all required terrain combinations. Otherwise, it may produce unexpected results.
</description>
</method>
@@ -240,7 +240,7 @@
<param index="3" name="ignore_empty_terrains" type="bool" default="true" />
<description>
Update all the cells in the [param path] coordinates array so that they use the given [param terrain] for the given [param terrain_set]. The function will also connect two successive cell in the path with the same terrain. This function might update neighboring tiles if needed to create correct terrain transitions.
- If [param ignore_empty_terrains] is true, empty terrains will be ignored when trying to find the best fitting tile for the given terrain constraints.
+ If [param ignore_empty_terrains] is [code]true[/code], empty terrains will be ignored when trying to find the best fitting tile for the given terrain constraints.
[b]Note:[/b] To work correctly, this method requires the [TileMapLayer]'s TileSet to have terrains set up with all required terrain combinations. Otherwise, it may produce unexpected results.
</description>
</method>
diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml
index 132ecc3f92..d04a6f7316 100644
--- a/doc/classes/TreeItem.xml
+++ b/doc/classes/TreeItem.xml
@@ -267,7 +267,7 @@
<method name="get_next" qualifiers="const">
<return type="TreeItem" />
<description>
- Returns the next sibling TreeItem in the tree or a null object if there is none.
+ Returns the next sibling TreeItem in the tree or a [code]null[/code] object if there is none.
</description>
</method>
<method name="get_next_in_tree">
@@ -289,13 +289,13 @@
<method name="get_parent" qualifiers="const">
<return type="TreeItem" />
<description>
- Returns the parent TreeItem or a null object if there is none.
+ Returns the parent TreeItem or a [code]null[/code] object if there is none.
</description>
</method>
<method name="get_prev">
<return type="TreeItem" />
<description>
- Returns the previous sibling TreeItem in the tree or a null object if there is none.
+ Returns the previous sibling TreeItem in the tree or a [code]null[/code] object if there is none.
</description>
</method>
<method name="get_prev_in_tree">
diff --git a/doc/classes/VideoStreamPlayback.xml b/doc/classes/VideoStreamPlayback.xml
index 8b4a7f8d29..17e29ae337 100644
--- a/doc/classes/VideoStreamPlayback.xml
+++ b/doc/classes/VideoStreamPlayback.xml
@@ -54,7 +54,7 @@
<method name="_play" qualifiers="virtual">
<return type="void" />
<description>
- Called in response to [member VideoStreamPlayer.autoplay] or [method VideoStreamPlayer.play]. Note that manual playback may also invoke [method _stop] multiple times before this method is called. [method _is_playing] should return true once playing.
+ Called in response to [member VideoStreamPlayer.autoplay] or [method VideoStreamPlayer.play]. Note that manual playback may also invoke [method _stop] multiple times before this method is called. [method _is_playing] should return [code]true[/code] once playing.
</description>
</method>
<method name="_seek" qualifiers="virtual">
@@ -81,14 +81,14 @@
<method name="_stop" qualifiers="virtual">
<return type="void" />
<description>
- Stops playback. May be called multiple times before [method _play], or in response to [method VideoStreamPlayer.stop]. [method _is_playing] should return false once stopped.
+ Stops playback. May be called multiple times before [method _play], or in response to [method VideoStreamPlayer.stop]. [method _is_playing] should return [code]false[/code] once stopped.
</description>
</method>
<method name="_update" qualifiers="virtual">
<return type="void" />
<param index="0" name="delta" type="float" />
<description>
- Ticks video playback for [param delta] seconds. Called every frame as long as [method _is_paused] and [method _is_playing] return true.
+ Ticks video playback for [param delta] seconds. Called every frame as long as both [method _is_paused] and [method _is_playing] return [code]true[/code].
</description>
</method>
<method name="mix_audio">
diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml
index 333e61d03f..c4319fd360 100644
--- a/doc/classes/Viewport.xml
+++ b/doc/classes/Viewport.xml
@@ -48,7 +48,7 @@
<method name="get_camera_2d" qualifiers="const">
<return type="Camera2D" />
<description>
- Returns the currently active 2D camera. Returns null if there are no active cameras.
+ Returns the currently active 2D camera. Returns [code]null[/code] if there are no active cameras.
</description>
</method>
<method name="get_camera_3d" qualifiers="const">
@@ -144,13 +144,13 @@
<method name="gui_get_focus_owner" qualifiers="const">
<return type="Control" />
<description>
- Returns the [Control] having the focus within this viewport. If no [Control] has the focus, returns null.
+ Returns the currently focused [Control] within this viewport. If no [Control] is focused, returns [code]null[/code].
</description>
</method>
<method name="gui_get_hovered_control" qualifiers="const">
<return type="Control" />
<description>
- Returns the [Control] that the mouse is currently hovering over in this viewport. If no [Control] has the cursor, returns null.
+ Returns the [Control] that the mouse is currently hovering over in this viewport. If no [Control] has the cursor, returns [code]null[/code].
Typically the leaf [Control] node or deepest level of the subtree which claims hover. This is very useful when used together with [method Node.is_ancestor_of] to find if the mouse is within a control tree.
</description>
</method>
diff --git a/doc/classes/VisualShaderNodeCompare.xml b/doc/classes/VisualShaderNodeCompare.xml
index b566ca62f3..9f554dacb4 100644
--- a/doc/classes/VisualShaderNodeCompare.xml
+++ b/doc/classes/VisualShaderNodeCompare.xml
@@ -69,10 +69,10 @@
Represents the size of the [enum Function] enum.
</constant>
<constant name="COND_ALL" value="0" enum="Condition">
- The result will be true if all of component in vector satisfy the comparison condition.
+ The result will be [code]true[/code] if all components in the vector satisfy the comparison condition.
</constant>
<constant name="COND_ANY" value="1" enum="Condition">
- The result will be true if any of component in vector satisfy the comparison condition.
+ The result will be [code]true[/code] if any component in the vector satisfies the comparison condition.
</constant>
<constant name="COND_MAX" value="2" enum="Condition">
Represents the size of the [enum Condition] enum.
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index 3c959f0143..0138f99d50 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -1654,28 +1654,39 @@ void RasterizerCanvasGLES3::light_update_shadow(RID p_rid, int p_shadow_index, c
return;
}
- for (int i = 0; i < 4; i++) {
- glViewport((state.shadow_texture_size / 4) * i, p_shadow_index * 2, (state.shadow_texture_size / 4), 2);
+ Projection projection;
+ {
+ real_t fov = 90;
+ real_t nearp = p_near;
+ real_t farp = p_far;
+ real_t aspect = 1.0;
- Projection projection;
- {
- real_t fov = 90;
- real_t nearp = p_near;
- real_t farp = p_far;
- real_t aspect = 1.0;
+ real_t ymax = nearp * Math::tan(Math::deg_to_rad(fov * 0.5));
+ real_t ymin = -ymax;
+ real_t xmin = ymin * aspect;
+ real_t xmax = ymax * aspect;
- real_t ymax = nearp * Math::tan(Math::deg_to_rad(fov * 0.5));
- real_t ymin = -ymax;
- real_t xmin = ymin * aspect;
- real_t xmax = ymax * aspect;
+ projection.set_frustum(xmin, xmax, ymin, ymax, nearp, farp);
+ }
- projection.set_frustum(xmin, xmax, ymin, ymax, nearp, farp);
- }
+ // Precomputed:
+ // Vector3 cam_target = Basis::from_euler(Vector3(0, 0, Math_TAU * ((i + 3) / 4.0))).xform(Vector3(0, 1, 0));
+ // projection = projection * Projection(Transform3D().looking_at(cam_targets[i], Vector3(0, 0, -1)).affine_inverse());
+ const Projection projections[4] = {
+ projection * Projection(Vector4(0, 0, -1, 0), Vector4(1, 0, 0, 0), Vector4(0, -1, 0, 0), Vector4(0, 0, 0, 1)),
- Vector3 cam_target = Basis::from_euler(Vector3(0, 0, Math_TAU * ((i + 3) / 4.0))).xform(Vector3(0, 1, 0));
+ projection * Projection(Vector4(-1, 0, 0, 0), Vector4(0, 0, -1, 0), Vector4(0, -1, 0, 0), Vector4(0, 0, 0, 1)),
+
+ projection * Projection(Vector4(0, 0, 1, 0), Vector4(-1, 0, 0, 0), Vector4(0, -1, 0, 0), Vector4(0, 0, 0, 1)),
+
+ projection * Projection(Vector4(1, 0, 0, 0), Vector4(0, 0, 1, 0), Vector4(0, -1, 0, 0), Vector4(0, 0, 0, 1))
+
+ };
+
+ for (int i = 0; i < 4; i++) {
+ glViewport((state.shadow_texture_size / 4) * i, p_shadow_index * 2, (state.shadow_texture_size / 4), 2);
- projection = projection * Projection(Transform3D().looking_at(cam_target, Vector3(0, 0, -1)).affine_inverse());
- shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::PROJECTION, projection, shadow_render.shader_version, variant);
+ shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::PROJECTION, projections[i], shadow_render.shader_version, variant);
static const Vector2 directions[4] = { Vector2(1, 0), Vector2(0, 1), Vector2(-1, 0), Vector2(0, -1) };
shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::DIRECTION, directions[i].x, directions[i].y, shadow_render.shader_version, variant);
diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp
index 015c0c6d2e..f50771ddde 100644
--- a/drivers/vulkan/rendering_device_driver_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp
@@ -2887,21 +2887,28 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue,
// No swapchain yet, this is the first time we're creating it.
if (!swap_chain->vk_swapchain) {
- uint32_t width = surface_capabilities.currentExtent.width;
- uint32_t height = surface_capabilities.currentExtent.height;
+ if (surface_capabilities.currentExtent.width == 0xFFFFFFFF) {
+ // The current extent is currently undefined, so the current surface width and height will be clamped to the surface's capabilities.
+ // We make sure to overwrite surface_capabilities.currentExtent.width so that the same check further below
+ // does not set extent.width = CLAMP( surface->width, ... ) on the first run of this function, because
+ // that'd be potentially unswapped.
+ surface_capabilities.currentExtent.width = CLAMP(surface->width, surface_capabilities.minImageExtent.width, surface_capabilities.maxImageExtent.width);
+ surface_capabilities.currentExtent.height = CLAMP(surface->height, surface_capabilities.minImageExtent.height, surface_capabilities.maxImageExtent.height);
+ }
+
+ // We must SWAP() only once otherwise we'll keep ping-ponging between
+ // the right and wrong resolutions after multiple calls to swap_chain_resize().
if (surface_capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR ||
surface_capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) {
// Swap to get identity width and height.
- surface_capabilities.currentExtent.height = width;
- surface_capabilities.currentExtent.width = height;
+ SWAP(surface_capabilities.currentExtent.width, surface_capabilities.currentExtent.height);
}
-
- native_display_size = surface_capabilities.currentExtent;
}
VkExtent2D extent;
if (surface_capabilities.currentExtent.width == 0xFFFFFFFF) {
// The current extent is currently undefined, so the current surface width and height will be clamped to the surface's capabilities.
+ // We can only be here on the second call to swap_chain_resize(), by which time surface->width & surface->height should already be swapped if needed.
extent.width = CLAMP(surface->width, surface_capabilities.minImageExtent.width, surface_capabilities.maxImageExtent.width);
extent.height = CLAMP(surface->height, surface_capabilities.minImageExtent.height, surface_capabilities.maxImageExtent.height);
} else {
@@ -2991,7 +2998,7 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue,
swap_create_info.minImageCount = desired_swapchain_images;
swap_create_info.imageFormat = swap_chain->format;
swap_create_info.imageColorSpace = swap_chain->color_space;
- swap_create_info.imageExtent = native_display_size;
+ swap_create_info.imageExtent = extent;
swap_create_info.imageArrayLayers = 1;
swap_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
swap_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
diff --git a/drivers/vulkan/rendering_device_driver_vulkan.h b/drivers/vulkan/rendering_device_driver_vulkan.h
index 2b420e8feb..6931015a22 100644
--- a/drivers/vulkan/rendering_device_driver_vulkan.h
+++ b/drivers/vulkan/rendering_device_driver_vulkan.h
@@ -367,7 +367,6 @@ private:
};
void _swap_chain_release(SwapChain *p_swap_chain);
- VkExtent2D native_display_size;
public:
virtual SwapChainID swap_chain_create(RenderingContextDriver::SurfaceID p_surface) override final;
diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp
index 084ecf7229..0649272216 100644
--- a/editor/editor_audio_buses.cpp
+++ b/editor/editor_audio_buses.cpp
@@ -91,17 +91,26 @@ void EditorAudioBus::_notification(int p_what) {
Color mute_color = EditorThemeManager::is_dark_theme() ? Color(1.0, 0.16, 0.16) : Color(2.35, 1.03, 1.03);
Color bypass_color = EditorThemeManager::is_dark_theme() ? Color(0.13, 0.8, 1.0) : Color(1.03, 2.04, 2.35);
float darkening_factor = EditorThemeManager::is_dark_theme() ? 0.15 : 0.65;
+ Color solo_color_darkened = solo_color.darkened(darkening_factor);
+ Color mute_color_darkened = mute_color.darkened(darkening_factor);
+ Color bypass_color_darkened = bypass_color.darkened(darkening_factor);
- Ref<StyleBoxFlat>(solo->get_theme_stylebox(SceneStringName(pressed)))->set_border_color(solo_color.darkened(darkening_factor));
- Ref<StyleBoxFlat>(mute->get_theme_stylebox(SceneStringName(pressed)))->set_border_color(mute_color.darkened(darkening_factor));
- Ref<StyleBoxFlat>(bypass->get_theme_stylebox(SceneStringName(pressed)))->set_border_color(bypass_color.darkened(darkening_factor));
+ Ref<StyleBoxFlat>(solo->get_theme_stylebox(SceneStringName(pressed)))->set_border_color(solo_color_darkened);
+ Ref<StyleBoxFlat>(mute->get_theme_stylebox(SceneStringName(pressed)))->set_border_color(mute_color_darkened);
+ Ref<StyleBoxFlat>(bypass->get_theme_stylebox(SceneStringName(pressed)))->set_border_color(bypass_color_darkened);
+ Ref<StyleBoxFlat>(solo->get_theme_stylebox("hover_pressed"))->set_border_color(solo_color_darkened);
+ Ref<StyleBoxFlat>(mute->get_theme_stylebox("hover_pressed"))->set_border_color(mute_color_darkened);
+ Ref<StyleBoxFlat>(bypass->get_theme_stylebox("hover_pressed"))->set_border_color(bypass_color_darkened);
solo->set_button_icon(get_editor_theme_icon(SNAME("AudioBusSolo")));
solo->add_theme_color_override("icon_pressed_color", solo_color);
+ solo->add_theme_color_override("icon_hover_pressed_color", solo_color_darkened);
mute->set_button_icon(get_editor_theme_icon(SNAME("AudioBusMute")));
mute->add_theme_color_override("icon_pressed_color", mute_color);
+ mute->add_theme_color_override("icon_hover_pressed_color", mute_color_darkened);
bypass->set_button_icon(get_editor_theme_icon(SNAME("AudioBusBypass")));
bypass->add_theme_color_override("icon_pressed_color", bypass_color);
+ bypass->add_theme_color_override("icon_hover_pressed_color", bypass_color_darkened);
bus_options->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl")));
@@ -841,13 +850,18 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
child->begin_bulk_theme_override();
child->add_theme_style_override(CoreStringName(normal), sbempty);
child->add_theme_style_override("hover", sbempty);
+ child->add_theme_style_override("hover_mirrored", sbempty);
child->add_theme_style_override("focus", sbempty);
+ child->add_theme_style_override("focus_mirrored", sbempty);
Ref<StyleBoxFlat> sbflat = memnew(StyleBoxFlat);
sbflat->set_content_margin_all(0);
sbflat->set_bg_color(Color(1, 1, 1, 0));
sbflat->set_border_width(Side::SIDE_BOTTOM, Math::round(3 * EDSCALE));
child->add_theme_style_override(SceneStringName(pressed), sbflat);
+ child->add_theme_style_override("pressed_mirrored", sbflat);
+ child->add_theme_style_override("hover_pressed", sbflat);
+ child->add_theme_style_override("hover_pressed_mirrored", sbflat);
child->end_bulk_theme_override();
}
diff --git a/editor/gui/editor_quick_open_dialog.cpp b/editor/gui/editor_quick_open_dialog.cpp
index b8f3b259ca..0128f1f54c 100644
--- a/editor/gui/editor_quick_open_dialog.cpp
+++ b/editor/gui/editor_quick_open_dialog.cpp
@@ -489,6 +489,10 @@ void QuickOpenResultContainer::handle_search_box_input(const Ref<InputEvent> &p_
}
void QuickOpenResultContainer::_move_selection_index(Key p_key) {
+ // Don't move selection if there are no results.
+ if (num_visible_results <= 0) {
+ return;
+ }
const int max_index = num_visible_results - 1;
int idx = selection_index;
diff --git a/editor/import/3d/resource_importer_obj.cpp b/editor/import/3d/resource_importer_obj.cpp
index e77f5ec2b1..3669844207 100644
--- a/editor/import/3d/resource_importer_obj.cpp
+++ b/editor/import/3d/resource_importer_obj.cpp
@@ -535,8 +535,6 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes,
}
}
- mesh->optimize_indices_for_cache();
-
if (p_generate_lods) {
// Use normal merge/split angles that match the defaults used for 3D scene importing.
mesh->generate_lods(60.0f, {});
@@ -546,6 +544,8 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes,
mesh->create_shadow_mesh();
}
+ mesh->optimize_indices();
+
if (p_single_mesh && mesh->get_surface_count() > 0) {
r_meshes.push_back(mesh);
}
diff --git a/editor/import/3d/resource_importer_obj.h b/editor/import/3d/resource_importer_obj.h
index faf0f336c0..9d299bc31a 100644
--- a/editor/import/3d/resource_importer_obj.h
+++ b/editor/import/3d/resource_importer_obj.h
@@ -63,9 +63,6 @@ public:
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
- // Threaded import can currently cause deadlocks, see GH-48265.
- virtual bool can_import_threaded() const override { return false; }
-
ResourceImporterOBJ();
};
diff --git a/editor/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp
index 58af558e7b..edf7aa66f0 100644
--- a/editor/import/3d/resource_importer_scene.cpp
+++ b/editor/import/3d/resource_importer_scene.cpp
@@ -2567,8 +2567,6 @@ Node *ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_
}
}
- src_mesh_node->get_mesh()->optimize_indices_for_cache();
-
if (generate_lods) {
Array skin_pose_transform_array = _get_skinned_pose_transforms(src_mesh_node);
src_mesh_node->get_mesh()->generate_lods(merge_angle, skin_pose_transform_array);
@@ -2578,6 +2576,8 @@ Node *ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_
src_mesh_node->get_mesh()->create_shadow_mesh();
}
+ src_mesh_node->get_mesh()->optimize_indices();
+
if (!save_to_file.is_empty()) {
Ref<Mesh> existing = ResourceCache::get_ref(save_to_file);
if (existing.is_valid()) {
@@ -2622,6 +2622,7 @@ Node *ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_
mesh_node->set_layer_mask(src_mesh_node->get_layer_mask());
mesh_node->set_cast_shadows_setting(src_mesh_node->get_cast_shadows_setting());
+ mesh_node->set_visible(src_mesh_node->is_visible());
mesh_node->set_visibility_range_begin(src_mesh_node->get_visibility_range_begin());
mesh_node->set_visibility_range_begin_margin(src_mesh_node->get_visibility_range_begin_margin());
mesh_node->set_visibility_range_end(src_mesh_node->get_visibility_range_end());
diff --git a/editor/import/3d/resource_importer_scene.h b/editor/import/3d/resource_importer_scene.h
index fe757dc2a3..daeab2ae03 100644
--- a/editor/import/3d/resource_importer_scene.h
+++ b/editor/import/3d/resource_importer_scene.h
@@ -304,8 +304,6 @@ public:
virtual bool has_advanced_options() const override;
virtual void show_advanced_options(const String &p_path) override;
- virtual bool can_import_threaded() const override { return false; }
-
ResourceImporterScene(const String &p_scene_import_type = "PackedScene", bool p_singleton = false);
~ResourceImporterScene();
diff --git a/editor/import/3d/scene_import_settings.cpp b/editor/import/3d/scene_import_settings.cpp
index 645b7fda88..945c1811d7 100644
--- a/editor/import/3d/scene_import_settings.cpp
+++ b/editor/import/3d/scene_import_settings.cpp
@@ -368,6 +368,7 @@ void SceneImportSettingsDialog::_fill_scene(Node *p_node, TreeItem *p_parent_ite
mesh_node->set_transform(src_mesh_node->get_transform());
mesh_node->set_skin(src_mesh_node->get_skin());
mesh_node->set_skeleton_path(src_mesh_node->get_skeleton_path());
+ mesh_node->set_visible(src_mesh_node->is_visible());
if (src_mesh_node->get_mesh().is_valid()) {
Ref<ImporterMesh> editor_mesh = src_mesh_node->get_mesh();
mesh_node->set_mesh(editor_mesh->get_mesh());
diff --git a/editor/import/resource_importer_bitmask.h b/editor/import/resource_importer_bitmask.h
index 8963c8d918..30564bf0fe 100644
--- a/editor/import/resource_importer_bitmask.h
+++ b/editor/import/resource_importer_bitmask.h
@@ -50,6 +50,8 @@ public:
virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
+ virtual bool can_import_threaded() const override { return true; }
+
ResourceImporterBitMap();
~ResourceImporterBitMap();
};
diff --git a/editor/import/resource_importer_bmfont.h b/editor/import/resource_importer_bmfont.h
index d31cd03736..48f036ff13 100644
--- a/editor/import/resource_importer_bmfont.h
+++ b/editor/import/resource_importer_bmfont.h
@@ -50,6 +50,8 @@ public:
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
+ virtual bool can_import_threaded() const override { return true; }
+
ResourceImporterBMFont();
};
diff --git a/editor/import/resource_importer_csv_translation.h b/editor/import/resource_importer_csv_translation.h
index c6b05eb043..9c83719ed1 100644
--- a/editor/import/resource_importer_csv_translation.h
+++ b/editor/import/resource_importer_csv_translation.h
@@ -51,6 +51,8 @@ public:
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
+ virtual bool can_import_threaded() const override { return true; }
+
ResourceImporterCSVTranslation();
};
diff --git a/editor/import/resource_importer_dynamic_font.h b/editor/import/resource_importer_dynamic_font.h
index de89e6b76f..7c7a16cf92 100644
--- a/editor/import/resource_importer_dynamic_font.h
+++ b/editor/import/resource_importer_dynamic_font.h
@@ -60,6 +60,8 @@ public:
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
+ virtual bool can_import_threaded() const override { return true; }
+
ResourceImporterDynamicFont();
};
diff --git a/editor/import/resource_importer_image.h b/editor/import/resource_importer_image.h
index 1490ab30d5..dd395009c1 100644
--- a/editor/import/resource_importer_image.h
+++ b/editor/import/resource_importer_image.h
@@ -52,6 +52,8 @@ public:
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
+ virtual bool can_import_threaded() const override { return true; }
+
ResourceImporterImage();
};
diff --git a/editor/import/resource_importer_imagefont.h b/editor/import/resource_importer_imagefont.h
index 065351c361..6b30a3cd6e 100644
--- a/editor/import/resource_importer_imagefont.h
+++ b/editor/import/resource_importer_imagefont.h
@@ -50,6 +50,8 @@ public:
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
+ virtual bool can_import_threaded() const override { return true; }
+
ResourceImporterImageFont();
};
diff --git a/editor/import/resource_importer_layered_texture.h b/editor/import/resource_importer_layered_texture.h
index 26495eed8d..d8b5bc2d14 100644
--- a/editor/import/resource_importer_layered_texture.h
+++ b/editor/import/resource_importer_layered_texture.h
@@ -117,6 +117,8 @@ public:
virtual bool are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const override;
virtual String get_import_settings_string() const override;
+ virtual bool can_import_threaded() const override { return true; }
+
void set_mode(Mode p_mode) { mode = p_mode; }
ResourceImporterLayeredTexture(bool p_singleton = false);
diff --git a/editor/import/resource_importer_shader_file.h b/editor/import/resource_importer_shader_file.h
index aefc967989..b28dea36d6 100644
--- a/editor/import/resource_importer_shader_file.h
+++ b/editor/import/resource_importer_shader_file.h
@@ -51,6 +51,8 @@ public:
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
+ virtual bool can_import_threaded() const override { return true; }
+
ResourceImporterShaderFile();
};
diff --git a/editor/import/resource_importer_texture.h b/editor/import/resource_importer_texture.h
index 6d74c4e2f9..6c87cd0abb 100644
--- a/editor/import/resource_importer_texture.h
+++ b/editor/import/resource_importer_texture.h
@@ -102,6 +102,8 @@ public:
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
+ virtual bool can_import_threaded() const override { return true; }
+
void update_imports();
virtual bool are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const override;
diff --git a/editor/import/resource_importer_texture_atlas.h b/editor/import/resource_importer_texture_atlas.h
index 0f2b10424c..e4ad9ac230 100644
--- a/editor/import/resource_importer_texture_atlas.h
+++ b/editor/import/resource_importer_texture_atlas.h
@@ -67,6 +67,8 @@ public:
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
virtual Error import_group_file(const String &p_group_file, const HashMap<String, HashMap<StringName, Variant>> &p_source_file_options, const HashMap<String, String> &p_base_paths) override;
+ virtual bool can_import_threaded() const override { return true; }
+
ResourceImporterTextureAtlas();
};
diff --git a/editor/import/resource_importer_wav.h b/editor/import/resource_importer_wav.h
index 47af37ba41..2253756554 100644
--- a/editor/import/resource_importer_wav.h
+++ b/editor/import/resource_importer_wav.h
@@ -142,6 +142,8 @@ public:
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
+ virtual bool can_import_threaded() const override { return true; }
+
ResourceImporterWAV();
};
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index b877c0b83e..5afe01025d 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -8480,7 +8480,7 @@ void Node3DEditor::clear() {
}
for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) {
- viewports[i]->view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(Node3DEditorViewport::VIEW_AUDIO_LISTENER), i == 0);
+ viewports[i]->view_menu->get_popup()->set_item_checked(viewports[i]->view_menu->get_popup()->get_item_index(Node3DEditorViewport::VIEW_AUDIO_LISTENER), i == 0);
viewports[i]->viewport->set_as_audio_listener_3d(i == 0);
}
diff --git a/methods.py b/methods.py
index 6f8b11819b..afaf5b80e5 100644
--- a/methods.py
+++ b/methods.py
@@ -90,35 +90,6 @@ def add_source_files_orig(self, sources, files, allow_gen=False):
sources.append(obj)
-# The section name is used for checking
-# the hash table to see whether the folder
-# is included in the SCU build.
-# It will be something like "core/math".
-def _find_scu_section_name(subdir):
- section_path = os.path.abspath(subdir) + "/"
-
- folders = []
- folder = ""
-
- for i in range(8):
- folder = os.path.dirname(section_path)
- folder = os.path.basename(folder)
- if folder == base_folder_only:
- break
- folders += [folder]
- section_path += "../"
- section_path = os.path.abspath(section_path) + "/"
-
- section_name = ""
- for n in range(len(folders)):
- # section_name += folders[len(folders) - n - 1] + " "
- section_name += folders[len(folders) - n - 1]
- if n != (len(folders) - 1):
- section_name += "/"
-
- return section_name
-
-
def add_source_files_scu(self, sources, files, allow_gen=False):
if self["scu_build"] and isinstance(files, str):
if "*." not in files:
@@ -127,10 +98,8 @@ def add_source_files_scu(self, sources, files, allow_gen=False):
# If the files are in a subdirectory, we want to create the scu gen
# files inside this subdirectory.
subdir = os.path.dirname(files)
- if subdir != "":
- subdir += "/"
-
- section_name = _find_scu_section_name(subdir)
+ subdir = subdir if subdir == "" else subdir + "/"
+ section_name = self.Dir(subdir).tpath
# if the section name is in the hash table?
# i.e. is it part of the SCU build?
global _scu_folders
@@ -555,40 +524,7 @@ def detect_visual_c_compiler_version(tools_env):
vc_chosen_compiler_index = -1
vc_chosen_compiler_str = ""
- # Start with Pre VS 2017 checks which uses VCINSTALLDIR:
- if "VCINSTALLDIR" in tools_env:
- # print("Checking VCINSTALLDIR")
-
- # find() works with -1 so big ifs below are needed... the simplest solution, in fact
- # First test if amd64 and amd64_x86 compilers are present in the path
- vc_amd64_compiler_detection_index = tools_env["PATH"].find(tools_env["VCINSTALLDIR"] + "BIN\\amd64;")
- if vc_amd64_compiler_detection_index > -1:
- vc_chosen_compiler_index = vc_amd64_compiler_detection_index
- vc_chosen_compiler_str = "amd64"
-
- vc_amd64_x86_compiler_detection_index = tools_env["PATH"].find(tools_env["VCINSTALLDIR"] + "BIN\\amd64_x86;")
- if vc_amd64_x86_compiler_detection_index > -1 and (
- vc_chosen_compiler_index == -1 or vc_chosen_compiler_index > vc_amd64_x86_compiler_detection_index
- ):
- vc_chosen_compiler_index = vc_amd64_x86_compiler_detection_index
- vc_chosen_compiler_str = "amd64_x86"
-
- # Now check the 32 bit compilers
- vc_x86_compiler_detection_index = tools_env["PATH"].find(tools_env["VCINSTALLDIR"] + "BIN;")
- if vc_x86_compiler_detection_index > -1 and (
- vc_chosen_compiler_index == -1 or vc_chosen_compiler_index > vc_x86_compiler_detection_index
- ):
- vc_chosen_compiler_index = vc_x86_compiler_detection_index
- vc_chosen_compiler_str = "x86"
-
- vc_x86_amd64_compiler_detection_index = tools_env["PATH"].find(tools_env["VCINSTALLDIR"] + "BIN\\x86_amd64;")
- if vc_x86_amd64_compiler_detection_index > -1 and (
- vc_chosen_compiler_index == -1 or vc_chosen_compiler_index > vc_x86_amd64_compiler_detection_index
- ):
- vc_chosen_compiler_index = vc_x86_amd64_compiler_detection_index
- vc_chosen_compiler_str = "x86_amd64"
-
- # and for VS 2017 and newer we check VCTOOLSINSTALLDIR:
+ # VS 2017 and newer should set VCTOOLSINSTALLDIR
if "VCTOOLSINSTALLDIR" in tools_env:
# Newer versions have a different path available
vc_amd64_compiler_detection_index = (
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 93d4a512a9..6241ada06a 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -5809,8 +5809,6 @@ void GDScriptAnalyzer::validate_call_arg(const List<GDScriptParser::DataType> &p
#ifdef DEBUG_ENABLED
void GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_identifier, const String &p_context, const bool p_in_local_scope) {
const StringName &name = p_identifier->name;
- GDScriptParser::DataType base = parser->current_class->get_datatype();
- GDScriptParser::ClassNode *base_class = base.class_type;
{
List<MethodInfo> gdscript_funcs;
@@ -5838,37 +5836,53 @@ void GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_identifier
}
}
+ const GDScriptParser::DataType current_class_type = parser->current_class->get_datatype();
if (p_in_local_scope) {
- while (base_class != nullptr) {
+ GDScriptParser::ClassNode *base_class = current_class_type.class_type;
+
+ if (base_class != nullptr) {
if (base_class->has_member(name)) {
parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE, p_context, p_identifier->name, base_class->get_member(name).get_type_name(), itos(base_class->get_member(name).get_line()));
return;
}
base_class = base_class->base_type.class_type;
}
+
+ while (base_class != nullptr) {
+ if (base_class->has_member(name)) {
+ String base_class_name = base_class->get_global_name();
+ if (base_class_name.is_empty()) {
+ base_class_name = base_class->fqcn;
+ }
+
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, base_class->get_member(name).get_type_name(), itos(base_class->get_member(name).get_line()), base_class_name);
+ return;
+ }
+ base_class = base_class->base_type.class_type;
+ }
}
- StringName parent = base.native_type;
- while (parent != StringName()) {
- ERR_FAIL_COND_MSG(!class_exists(parent), "Non-existent native base class.");
+ StringName native_base_class = current_class_type.native_type;
+ while (native_base_class != StringName()) {
+ ERR_FAIL_COND_MSG(!class_exists(native_base_class), "Non-existent native base class.");
- if (ClassDB::has_method(parent, name, true)) {
- parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "method", parent);
+ if (ClassDB::has_method(native_base_class, name, true)) {
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "method", native_base_class);
return;
- } else if (ClassDB::has_signal(parent, name, true)) {
- parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "signal", parent);
+ } else if (ClassDB::has_signal(native_base_class, name, true)) {
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "signal", native_base_class);
return;
- } else if (ClassDB::has_property(parent, name, true)) {
- parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "property", parent);
+ } else if (ClassDB::has_property(native_base_class, name, true)) {
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "property", native_base_class);
return;
- } else if (ClassDB::has_integer_constant(parent, name, true)) {
- parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "constant", parent);
+ } else if (ClassDB::has_integer_constant(native_base_class, name, true)) {
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "constant", native_base_class);
return;
- } else if (ClassDB::has_enum(parent, name, true)) {
- parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "enum", parent);
+ } else if (ClassDB::has_enum(native_base_class, name, true)) {
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "enum", native_base_class);
return;
}
- parent = ClassDB::get_parent_class(parent);
+ native_base_class = ClassDB::get_parent_class(native_base_class);
}
}
#endif // DEBUG_ENABLED
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index 4ffb4bd9d1..a601cc4993 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -61,10 +61,13 @@ String GDScriptWarning::get_message() const {
return vformat(R"(The signal "%s" is declared but never explicitly used in the class.)", symbols[0]);
case SHADOWED_VARIABLE:
CHECK_SYMBOLS(4);
- return vformat(R"(The local %s "%s" is shadowing an already-declared %s at line %s.)", symbols[0], symbols[1], symbols[2], symbols[3]);
+ return vformat(R"(The local %s "%s" is shadowing an already-declared %s at line %s in the current class.)", symbols[0], symbols[1], symbols[2], symbols[3]);
case SHADOWED_VARIABLE_BASE_CLASS:
CHECK_SYMBOLS(4);
- return vformat(R"(The local %s "%s" is shadowing an already-declared %s at the base class "%s".)", symbols[0], symbols[1], symbols[2], symbols[3]);
+ if (symbols.size() > 4) {
+ return vformat(R"(The local %s "%s" is shadowing an already-declared %s at line %s in the base class "%s".)", symbols[0], symbols[1], symbols[2], symbols[3], symbols[4]);
+ }
+ return vformat(R"(The local %s "%s" is shadowing an already-declared %s in the base class "%s".)", symbols[0], symbols[1], symbols[2], symbols[3]);
case SHADOWED_GLOBAL_IDENTIFIER:
CHECK_SYMBOLS(3);
return vformat(R"(The %s "%s" has the same name as a %s.)", symbols[0], symbols[1], symbols[2]);
diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h
index ffcf00a830..99e9b30af5 100644
--- a/modules/gdscript/gdscript_warning.h
+++ b/modules/gdscript/gdscript_warning.h
@@ -53,8 +53,8 @@ public:
UNUSED_PRIVATE_CLASS_VARIABLE, // Class variable is declared private ("_" prefix) but never used in the class.
UNUSED_PARAMETER, // Function parameter is never used.
UNUSED_SIGNAL, // Signal is defined but never explicitly used in the class.
- SHADOWED_VARIABLE, // Variable name shadowed by other variable in same class.
- SHADOWED_VARIABLE_BASE_CLASS, // Variable name shadowed by other variable in some base class.
+ SHADOWED_VARIABLE, // A local variable/constant shadows a current class member.
+ SHADOWED_VARIABLE_BASE_CLASS, // A local variable/constant shadows a base class member.
SHADOWED_GLOBAL_IDENTIFIER, // A global class or function has the same name as variable.
UNREACHABLE_CODE, // Code after a return statement.
UNREACHABLE_PATTERN, // Pattern in a match statement after a catch all pattern (wildcard or bind).
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage.out b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage.out
index 0e0d607831..cfe91e00bd 100644
--- a/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage.out
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage.out
@@ -6,6 +6,6 @@ GDTEST_OK
>> WARNING
>> Line: 5
>> SHADOWED_VARIABLE
->> The local variable "a" is shadowing an already-declared variable at line 1.
+>> The local variable "a" is shadowing an already-declared variable at line 1 in the current class.
1
2
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_initializer.out b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_initializer.out
index 228a510490..ae0f2d8b8b 100644
--- a/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_initializer.out
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_initializer.out
@@ -10,6 +10,6 @@ GDTEST_OK
>> WARNING
>> Line: 5
>> SHADOWED_VARIABLE
->> The local variable "a" is shadowing an already-declared variable at line 1.
+>> The local variable "a" is shadowing an already-declared variable at line 1 in the current class.
1
2
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_loop.out b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_loop.out
index 0d20e9f7a0..101d27df9d 100644
--- a/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_loop.out
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_loop.out
@@ -6,7 +6,7 @@ GDTEST_OK
>> WARNING
>> Line: 6
>> SHADOWED_VARIABLE
->> The local variable "a" is shadowing an already-declared variable at line 1.
+>> The local variable "a" is shadowing an already-declared variable at line 1 in the current class.
1
2
1
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/lambda_shadowing_arg.out b/modules/gdscript/tests/scripts/analyzer/warnings/lambda_shadowing_arg.out
index a98d80514c..5d059b9193 100644
--- a/modules/gdscript/tests/scripts/analyzer/warnings/lambda_shadowing_arg.out
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/lambda_shadowing_arg.out
@@ -2,5 +2,5 @@ GDTEST_OK
>> WARNING
>> Line: 4
>> SHADOWED_VARIABLE
->> The local function parameter "shadow" is shadowing an already-declared variable at line 1.
+>> The local function parameter "shadow" is shadowing an already-declared variable at line 1 in the current class.
shadow
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/shadowing_base.notest.gd b/modules/gdscript/tests/scripts/analyzer/warnings/shadowing_base.notest.gd
new file mode 100644
index 0000000000..5819246ded
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/shadowing_base.notest.gd
@@ -0,0 +1,7 @@
+class_name ShadowingBase
+
+const base_const_member = 1
+var base_variable_member
+
+func base_function_member():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.gd b/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.gd
index 939e787ea5..6a16ae6bcc 100644
--- a/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.gd
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.gd
@@ -1,4 +1,5 @@
class_name ShadowedClass
+extends ShadowingBase
var member: int = 0
@@ -7,6 +8,7 @@ var print_debug := 'print_debug'
var print := 'print'
@warning_ignore("unused_variable")
+@warning_ignore("unused_local_constant")
func test():
var Array := 'Array'
var Node := 'Node'
@@ -15,5 +17,8 @@ func test():
var member := 'member'
var reference := 'reference'
var ShadowedClass := 'ShadowedClass'
+ var base_variable_member
+ const base_function_member = 1
+ var base_const_member
print('warn')
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.out b/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.out
index 8297eed4b8..075f5d3225 100644
--- a/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.out
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.out
@@ -1,34 +1,46 @@
GDTEST_OK
>> WARNING
->> Line: 5
+>> Line: 6
>> SHADOWED_GLOBAL_IDENTIFIER
>> The variable "print_debug" has the same name as a built-in function.
>> WARNING
->> Line: 11
+>> Line: 13
>> SHADOWED_GLOBAL_IDENTIFIER
>> The variable "Array" has the same name as a built-in type.
>> WARNING
->> Line: 12
+>> Line: 14
>> SHADOWED_GLOBAL_IDENTIFIER
>> The variable "Node" has the same name as a native class.
>> WARNING
->> Line: 13
+>> Line: 15
>> SHADOWED_GLOBAL_IDENTIFIER
>> The variable "is_same" has the same name as a built-in function.
>> WARNING
->> Line: 14
+>> Line: 16
>> SHADOWED_GLOBAL_IDENTIFIER
>> The variable "sqrt" has the same name as a built-in function.
>> WARNING
->> Line: 15
+>> Line: 17
>> SHADOWED_VARIABLE
->> The local variable "member" is shadowing an already-declared variable at line 3.
+>> The local variable "member" is shadowing an already-declared variable at line 4 in the current class.
>> WARNING
->> Line: 16
+>> Line: 18
>> SHADOWED_VARIABLE_BASE_CLASS
->> The local variable "reference" is shadowing an already-declared method at the base class "RefCounted".
+>> The local variable "reference" is shadowing an already-declared method in the base class "RefCounted".
>> WARNING
->> Line: 17
+>> Line: 19
>> SHADOWED_GLOBAL_IDENTIFIER
>> The variable "ShadowedClass" has the same name as a global class defined in "shadowning.gd".
+>> WARNING
+>> Line: 20
+>> SHADOWED_VARIABLE_BASE_CLASS
+>> The local variable "base_variable_member" is shadowing an already-declared variable at line 4 in the base class "ShadowingBase".
+>> WARNING
+>> Line: 21
+>> SHADOWED_VARIABLE_BASE_CLASS
+>> The local constant "base_function_member" is shadowing an already-declared function at line 6 in the base class "ShadowingBase".
+>> WARNING
+>> Line: 22
+>> SHADOWED_VARIABLE_BASE_CLASS
+>> The local variable "base_const_member" is shadowing an already-declared constant at line 3 in the base class "ShadowingBase".
warn
diff --git a/modules/gdscript/tests/scripts/parser/warnings/shadowed_constant.out b/modules/gdscript/tests/scripts/parser/warnings/shadowed_constant.out
index 75fa01f928..04df229f66 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/shadowed_constant.out
+++ b/modules/gdscript/tests/scripts/parser/warnings/shadowed_constant.out
@@ -6,4 +6,4 @@ GDTEST_OK
>> WARNING
>> Line: 8
>> SHADOWED_VARIABLE
->> The local constant "TEST" is shadowing an already-declared constant at line 2.
+>> The local constant "TEST" is shadowing an already-declared constant at line 2 in the current class.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_class.out b/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_class.out
index aab27e78e2..4a6964f503 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_class.out
+++ b/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_class.out
@@ -6,4 +6,4 @@ GDTEST_OK
>> WARNING
>> Line: 8
>> SHADOWED_VARIABLE
->> The local variable "foo" is shadowing an already-declared variable at line 1.
+>> The local variable "foo" is shadowing an already-declared variable at line 1 in the current class.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_function.out b/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_function.out
index e3cd358126..45fb771829 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_function.out
+++ b/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_function.out
@@ -6,4 +6,4 @@ GDTEST_OK
>> WARNING
>> Line: 2
>> SHADOWED_VARIABLE
->> The local variable "test" is shadowing an already-declared function at line 1.
+>> The local variable "test" is shadowing an already-declared function at line 1 in the current class.
diff --git a/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.out b/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.out
index 5492c8f763..1650acadb5 100644
--- a/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.out
+++ b/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.out
@@ -2,11 +2,11 @@ GDTEST_OK
>> WARNING
>> Line: 5
>> SHADOWED_VARIABLE
->> The local function parameter "a" is shadowing an already-declared variable at line 3.
+>> The local function parameter "a" is shadowing an already-declared variable at line 3 in the current class.
>> WARNING
>> Line: 15
>> SHADOWED_VARIABLE
->> The local function parameter "v" is shadowing an already-declared variable at line 13.
+>> The local function parameter "v" is shadowing an already-declared variable at line 13 in the current class.
a
1
b
diff --git a/modules/gltf/doc_classes/GLTFAnimation.xml b/modules/gltf/doc_classes/GLTFAnimation.xml
index d269145bbd..f11dea8ee8 100644
--- a/modules/gltf/doc_classes/GLTFAnimation.xml
+++ b/modules/gltf/doc_classes/GLTFAnimation.xml
@@ -13,7 +13,7 @@
<param index="0" name="extension_name" type="StringName" />
<description>
Gets additional arbitrary data in this [GLTFAnimation] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless.
- The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the return value can be anything you set. If nothing was set, the return value is null.
+ The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the return value can be anything you set. If nothing was set, the return value is [code]null[/code].
</description>
</method>
<method name="set_additional_data">
diff --git a/modules/gltf/doc_classes/GLTFBufferView.xml b/modules/gltf/doc_classes/GLTFBufferView.xml
index b7f499ad72..9c2ee3197f 100644
--- a/modules/gltf/doc_classes/GLTFBufferView.xml
+++ b/modules/gltf/doc_classes/GLTFBufferView.xml
@@ -34,10 +34,10 @@
The stride, in bytes, between interleaved data. If [code]-1[/code], this buffer view is not interleaved.
</member>
<member name="indices" type="bool" setter="set_indices" getter="get_indices" default="false">
- True if the GLTFBufferView's OpenGL GPU buffer type is an [code]ELEMENT_ARRAY_BUFFER[/code] used for vertex indices (integer constant [code]34963[/code]). False if the buffer type is any other value. See [url=https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_005_BuffersBufferViewsAccessors.md]Buffers, BufferViews, and Accessors[/url] for possible values. This property is set on import and used on export.
+ [code]true[/code] if the GLTFBufferView's OpenGL GPU buffer type is an [code]ELEMENT_ARRAY_BUFFER[/code] used for vertex indices (integer constant [code]34963[/code]). [code]false[/code] if the buffer type is any other value. See [url=https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_005_BuffersBufferViewsAccessors.md]Buffers, BufferViews, and Accessors[/url] for possible values. This property is set on import and used on export.
</member>
<member name="vertex_attributes" type="bool" setter="set_vertex_attributes" getter="get_vertex_attributes" default="false">
- True if the GLTFBufferView's OpenGL GPU buffer type is an [code]ARRAY_BUFFER[/code] used for vertex attributes (integer constant [code]34962[/code]). False if the buffer type is any other value. See [url=https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_005_BuffersBufferViewsAccessors.md]Buffers, BufferViews, and Accessors[/url] for possible values. This property is set on import and used on export.
+ [code]true[/code] if the GLTFBufferView's OpenGL GPU buffer type is an [code]ARRAY_BUFFER[/code] used for vertex attributes (integer constant [code]34962[/code]). [code]false[/code] if the buffer type is any other value. See [url=https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_005_BuffersBufferViewsAccessors.md]Buffers, BufferViews, and Accessors[/url] for possible values. This property is set on import and used on export.
</member>
</members>
</class>
diff --git a/modules/gltf/doc_classes/GLTFCamera.xml b/modules/gltf/doc_classes/GLTFCamera.xml
index 9fce21659c..12334683ba 100644
--- a/modules/gltf/doc_classes/GLTFCamera.xml
+++ b/modules/gltf/doc_classes/GLTFCamera.xml
@@ -47,13 +47,13 @@
The distance to the near culling boundary for this camera relative to its local Z axis, in meters. This maps to glTF's [code]znear[/code] property.
</member>
<member name="fov" type="float" setter="set_fov" getter="get_fov" default="1.309">
- The FOV of the camera. This class and glTF define the camera FOV in radians, while Godot uses degrees. This maps to glTF's [code]yfov[/code] property. This value is only used for perspective cameras, when [member perspective] is true.
+ The FOV of the camera. This class and glTF define the camera FOV in radians, while Godot uses degrees. This maps to glTF's [code]yfov[/code] property. This value is only used for perspective cameras, when [member perspective] is [code]true[/code].
</member>
<member name="perspective" type="bool" setter="set_perspective" getter="get_perspective" default="true">
- Whether or not the camera is in perspective mode. If false, the camera is in orthographic/orthogonal mode. This maps to glTF's camera [code]type[/code] property. See [member Camera3D.projection] and the glTF spec for more information.
+ If [code]true[/code], the camera is in perspective mode. Otherwise, the camera is in orthographic/orthogonal mode. This maps to glTF's camera [code]type[/code] property. See [member Camera3D.projection] and the glTF spec for more information.
</member>
<member name="size_mag" type="float" setter="set_size_mag" getter="get_size_mag" default="0.5">
- The size of the camera. This class and glTF define the camera size magnitude as a radius in meters, while Godot defines it as a diameter in meters. This maps to glTF's [code]ymag[/code] property. This value is only used for orthographic/orthogonal cameras, when [member perspective] is false.
+ The size of the camera. This class and glTF define the camera size magnitude as a radius in meters, while Godot defines it as a diameter in meters. This maps to glTF's [code]ymag[/code] property. This value is only used for orthographic/orthogonal cameras, when [member perspective] is [code]false[/code].
</member>
</members>
</class>
diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml
index ffc3ab926c..47ffc624ba 100644
--- a/modules/gltf/doc_classes/GLTFDocument.xml
+++ b/modules/gltf/doc_classes/GLTFDocument.xml
@@ -93,7 +93,7 @@
<param index="0" name="extension" type="GLTFDocumentExtension" />
<param index="1" name="first_priority" type="bool" default="false" />
<description>
- Registers the given [GLTFDocumentExtension] instance with GLTFDocument. If [param first_priority] is true, this extension will be run first. Otherwise, it will be run last.
+ Registers the given [GLTFDocumentExtension] instance with GLTFDocument. If [param first_priority] is [code]true[/code], this extension will be run first. Otherwise, it will be run last.
[b]Note:[/b] Like GLTFDocument itself, all GLTFDocumentExtension classes must be stateless in order to function properly. If you need to store data, use the [code]set_additional_data[/code] and [code]get_additional_data[/code] methods in [GLTFState] or [GLTFNode].
</description>
</method>
diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml
index 8fcb925a48..abdbce7eeb 100644
--- a/modules/gltf/doc_classes/GLTFDocumentExtension.xml
+++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml
@@ -30,7 +30,7 @@
<param index="3" name="node" type="Node" />
<description>
Part of the export process. This method is run after [method _get_saveable_image_formats] and before [method _export_post]. If this [GLTFDocumentExtension] is used for exporting images, this runs after [method _serialize_texture_json].
- This method can be used to modify the final JSON of each node. Data should be primarily stored in [param gltf_node] prior to serializing the JSON, but the original Godot [param node] is also provided if available. The node may be null if not available, such as when exporting glTF data not generated from a Godot scene.
+ This method can be used to modify the final JSON of each node. Data should be primarily stored in [param gltf_node] prior to serializing the JSON, but the original Godot [Node] is also provided if available. [param node] may be [code]null[/code] if not available, such as when exporting glTF data not generated from a Godot scene.
</description>
</method>
<method name="_export_object_model_property" qualifiers="virtual">
@@ -43,7 +43,7 @@
<param index="5" name="target_depth" type="int" />
<description>
Part of the export process. Allows GLTFDocumentExtension classes to provide mappings for properties of nodes in the Godot scene tree, to JSON pointers to glTF properties, as defined by the glTF object model.
- Returns a [GLTFObjectModelProperty] instance that defines how the property should be mapped. If your extension can't handle the property, return null, or an instance without any JSON pointers (see [method GLTFObjectModelProperty.has_json_pointers]). You should use [method GLTFObjectModelProperty.set_types] to set the types, and set the JSON pointer(s) using the [member GLTFObjectModelProperty.json_pointers] property.
+ Returns a [GLTFObjectModelProperty] instance that defines how the property should be mapped. If your extension can't handle the property, return [code]null[/code] or an instance without any JSON pointers (see [method GLTFObjectModelProperty.has_json_pointers]). You should use [method GLTFObjectModelProperty.set_types] to set the types, and set the JSON pointer(s) using the [member GLTFObjectModelProperty.json_pointers] property.
The parameters provide context for the property, including the NodePath, the Godot node, the GLTF node index, and the target object. The [param target_object] will be equal to [param godot_node] if no sub-object can be found, otherwise it will point to a sub-object. For example, if the path is [code]^"A/B/C/MeshInstance3D:mesh:surface_0/material:emission_intensity"[/code], it will get the node, then the mesh, and then the material, so [param target_object] will be the [Material] resource, and [param target_depth] will be 2 because 2 levels were traversed to get to the target.
</description>
</method>
@@ -89,7 +89,7 @@
<description>
Part of the import process. This method is run after [method _import_pre_generate] and before [method _import_node].
Runs when generating a Godot scene node from a GLTFNode. The returned node will be added to the scene tree. Multiple nodes can be generated in this step if they are added as a child of the returned node.
- [b]Note:[/b] The [param scene_parent] parameter may be null if this is the single root node.
+ [b]Note:[/b] The [param scene_parent] parameter may be [code]null[/code] if this is the single root node.
</description>
</method>
<method name="_get_image_file_extension" qualifiers="virtual">
@@ -130,7 +130,7 @@
<param index="2" name="partial_paths" type="NodePath[]" />
<description>
Part of the import process. Allows GLTFDocumentExtension classes to provide mappings for JSON pointers to glTF properties, as defined by the glTF object model, to properties of nodes in the Godot scene tree.
- Returns a [GLTFObjectModelProperty] instance that defines how the property should be mapped. If your extension can't handle the property, return null, or an instance without any NodePaths (see [method GLTFObjectModelProperty.has_node_paths]). You should use [method GLTFObjectModelProperty.set_types] to set the types, and [method GLTFObjectModelProperty.append_path_to_property] function is useful for most simple cases.
+ Returns a [GLTFObjectModelProperty] instance that defines how the property should be mapped. If your extension can't handle the property, return [code]null[/code] or an instance without any NodePaths (see [method GLTFObjectModelProperty.has_node_paths]). You should use [method GLTFObjectModelProperty.set_types] to set the types, and [method GLTFObjectModelProperty.append_path_to_property] function is useful for most simple cases.
In many cases, [param partial_paths] will contain the start of a path, allowing the extension to complete the path. For example, for [code]/nodes/3/extensions/MY_ext/prop[/code], Godot will pass you a NodePath that leads to node 3, so the GLTFDocumentExtension class only needs to resolve the last [code]MY_ext/prop[/code] part of the path. In this example, the extension should check [code]split.size() &gt; 4 and split[0] == "nodes" and split[2] == "extensions" and split[3] == "MY_ext"[/code] at the start of the function to check if this JSON pointer applies to it, then it can use [param partial_paths] and handle [code]split[4][/code].
</description>
</method>
diff --git a/modules/gltf/doc_classes/GLTFMesh.xml b/modules/gltf/doc_classes/GLTFMesh.xml
index da73c20c1d..439078ffcc 100644
--- a/modules/gltf/doc_classes/GLTFMesh.xml
+++ b/modules/gltf/doc_classes/GLTFMesh.xml
@@ -15,7 +15,7 @@
<param index="0" name="extension_name" type="StringName" />
<description>
Gets additional arbitrary data in this [GLTFMesh] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless.
- The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the return value can be anything you set. If nothing was set, the return value is null.
+ The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the return value can be anything you set. If nothing was set, the return value is [code]null[/code].
</description>
</method>
<method name="set_additional_data">
diff --git a/modules/gltf/doc_classes/GLTFNode.xml b/modules/gltf/doc_classes/GLTFNode.xml
index eb92723a06..0fb85e4029 100644
--- a/modules/gltf/doc_classes/GLTFNode.xml
+++ b/modules/gltf/doc_classes/GLTFNode.xml
@@ -24,7 +24,7 @@
<param index="0" name="extension_name" type="StringName" />
<description>
Gets additional arbitrary data in this [GLTFNode] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless.
- The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the return value can be anything you set. If nothing was set, the return value is null.
+ The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the return value can be anything you set. If nothing was set, the return value is [code]null[/code].
</description>
</method>
<method name="get_scene_node_path">
@@ -33,7 +33,7 @@
<param index="1" name="handle_skeletons" type="bool" default="true" />
<description>
Returns the [NodePath] that this GLTF node will have in the Godot scene tree after being imported. This is useful when importing glTF object model pointers with [GLTFObjectModelProperty], for handling extensions such as [code]KHR_animation_pointer[/code] or [code]KHR_interactivity[/code].
- If [param handle_skeletons] is true, paths to skeleton bone glTF nodes will be resolved properly. For example, a path that would be [code]^"A/B/C/Bone1/Bone2/Bone3"[/code] if false will become [code]^"A/B/C/Skeleton3D:Bone3"[/code].
+ If [param handle_skeletons] is [code]true[/code], paths to skeleton bone glTF nodes will be resolved properly. For example, a path that would be [code]^"A/B/C/Bone1/Bone2/Bone3"[/code] if [code]false[/code] will become [code]^"A/B/C/Skeleton3D:Bone3"[/code].
</description>
</method>
<method name="set_additional_data">
diff --git a/modules/gltf/doc_classes/GLTFObjectModelProperty.xml b/modules/gltf/doc_classes/GLTFObjectModelProperty.xml
index e983269ccc..a457d94c41 100644
--- a/modules/gltf/doc_classes/GLTFObjectModelProperty.xml
+++ b/modules/gltf/doc_classes/GLTFObjectModelProperty.xml
@@ -81,7 +81,7 @@
Unknown or not set object model type. If the object model type is set to this value, the real type still needs to be determined.
</constant>
<constant name="GLTF_OBJECT_MODEL_TYPE_BOOL" value="1" enum="GLTFObjectModelType">
- Object model type "bool". Represented in the glTF JSON as a boolean, and encoded in a [GLTFAccessor] as "SCALAR". When encoded in an accessor, a value of 0 is false, and any other value is true.
+ Object model type "bool". Represented in the glTF JSON as a boolean, and encoded in a [GLTFAccessor] as "SCALAR". When encoded in an accessor, a value of [code]0[/code] is [code]false[/code], and any other value is [code]true[/code].
</constant>
<constant name="GLTF_OBJECT_MODEL_TYPE_FLOAT" value="2" enum="GLTFObjectModelType">
Object model type "float". Represented in the glTF JSON as a number, and encoded in a [GLTFAccessor] as "SCALAR".
diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml
index de7ec2a4ca..376c3c89f5 100644
--- a/modules/gltf/doc_classes/GLTFState.xml
+++ b/modules/gltf/doc_classes/GLTFState.xml
@@ -17,7 +17,7 @@
<param index="0" name="extension_name" type="String" />
<param index="1" name="required" type="bool" />
<description>
- Appends an extension to the list of extensions used by this glTF file during serialization. If [param required] is true, the extension will also be added to the list of required extensions. Do not run this in [method GLTFDocumentExtension._export_post], as that stage is too late to add extensions. The final list is sorted alphabetically.
+ Appends an extension to the list of extensions used by this glTF file during serialization. If [param required] is [code]true[/code], the extension will also be added to the list of required extensions. Do not run this in [method GLTFDocumentExtension._export_post], as that stage is too late to add extensions. The final list is sorted alphabetically.
</description>
</method>
<method name="append_data_to_buffers">
@@ -25,7 +25,7 @@
<param index="0" name="data" type="PackedByteArray" />
<param index="1" name="deduplication" type="bool" />
<description>
- Appends the given byte array data to the buffers and creates a [GLTFBufferView] for it. The index of the destination [GLTFBufferView] is returned. If [param deduplication] is true, the buffers will first be searched for duplicate data, otherwise new bytes will always be appended.
+ Appends the given byte array data to the buffers and creates a [GLTFBufferView] for it. The index of the destination [GLTFBufferView] is returned. If [param deduplication] is [code]true[/code], the buffers will first be searched for duplicate data, otherwise new bytes will always be appended.
</description>
</method>
<method name="append_gltf_node">
@@ -35,7 +35,7 @@
<param index="2" name="parent_node_index" type="int" />
<description>
Append the given [GLTFNode] to the state, and return its new index. This can be used to export one Godot node as multiple glTF nodes, or inject new glTF nodes at import time. On import, this must be called before [method GLTFDocumentExtension._generate_scene_node] finishes for the parent node. On export, this must be called before [method GLTFDocumentExtension._export_node] runs for the parent node.
- The [param godot_scene_node] parameter is the Godot scene node that corresponds to this glTF node. This is highly recommended to be set to a valid node, but may be null if there is no corresponding Godot scene node. One Godot scene node may be used for multiple glTF nodes, so if exporting multiple glTF nodes for one Godot scene node, use the same Godot scene node for each.
+ The [param godot_scene_node] parameter is the Godot scene node that corresponds to this glTF node. This is highly recommended to be set to a valid node, but may be [code]null[/code] if there is no corresponding Godot scene node. One Godot scene node may be used for multiple glTF nodes, so if exporting multiple glTF nodes for one Godot scene node, use the same Godot scene node for each.
The [param parent_node_index] parameter is the index of the parent [GLTFNode] in the state. If [code]-1[/code], the node will be a root node, otherwise the new node will be added to the parent's list of children. The index will also be written to the [member GLTFNode.parent] property of the new node.
</description>
</method>
@@ -49,7 +49,7 @@
<param index="0" name="extension_name" type="StringName" />
<description>
Gets additional arbitrary data in this [GLTFState] instance. This can be used to keep per-file state data in [GLTFDocumentExtension] classes, which is important because they are stateless.
- The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the return value can be anything you set. If nothing was set, the return value is null.
+ The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the return value can be anything you set. If nothing was set, the return value is [code]null[/code].
</description>
</method>
<method name="get_animation_player">
diff --git a/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp
index 022d2e4477..872054ec2e 100644
--- a/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp
+++ b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp
@@ -88,7 +88,7 @@ void SceneExporterGLTFPlugin::_popup_gltf_export_dialog() {
}
_file_dialog->set_current_file(filename + String(".gltf"));
// Generate and refresh the export settings.
- _export_settings->generate_property_list(_gltf_document);
+ _export_settings->generate_property_list(_gltf_document, root);
_settings_inspector->edit(nullptr);
_settings_inspector->edit(_export_settings.ptr());
// Show the file dialog.
diff --git a/modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp b/modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp
index 511da078d8..c14e92c3a0 100644
--- a/modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp
+++ b/modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp
@@ -129,7 +129,7 @@ String get_friendly_config_prefix(Ref<GLTFDocumentExtension> p_extension) {
}
// Run this before popping up the export settings, because the extensions may have changed.
-void EditorSceneExporterGLTFSettings::generate_property_list(Ref<GLTFDocument> p_document) {
+void EditorSceneExporterGLTFSettings::generate_property_list(Ref<GLTFDocument> p_document, Node *p_root) {
_property_list.clear();
_document = p_document;
String image_format_hint_string = "None,PNG,JPEG";
diff --git a/modules/gltf/editor/editor_scene_exporter_gltf_settings.h b/modules/gltf/editor/editor_scene_exporter_gltf_settings.h
index 898cddfd68..aa0e54078d 100644
--- a/modules/gltf/editor/editor_scene_exporter_gltf_settings.h
+++ b/modules/gltf/editor/editor_scene_exporter_gltf_settings.h
@@ -55,7 +55,7 @@ protected:
bool _get_extension_setting(const String &p_name_str, Variant &r_ret) const;
public:
- void generate_property_list(Ref<GLTFDocument> p_document);
+ void generate_property_list(Ref<GLTFDocument> p_document, Node *p_root = nullptr);
String get_copyright() const;
void set_copyright(const String &p_copyright);
diff --git a/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp
index cde30bce18..b5edd35ad5 100644
--- a/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp
+++ b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp
@@ -63,6 +63,7 @@ Error GLTFDocumentExtensionConvertImporterMesh::import_post(Ref<GLTFState> p_sta
mesh_instance_node_3d->set_mesh(array_mesh);
mesh_instance_node_3d->set_skin(importer_mesh_3d->get_skin());
mesh_instance_node_3d->set_skeleton_path(importer_mesh_3d->get_skeleton_path());
+ mesh_instance_node_3d->set_visible(importer_mesh_3d->is_visible());
node->replace_by(mesh_instance_node_3d);
_copy_meta(importer_mesh_3d, mesh_instance_node_3d);
_copy_meta(mesh.ptr(), array_mesh.ptr());
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 571a04936d..aa482615da 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -3482,6 +3482,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
}
}
array[Mesh::ARRAY_WEIGHTS] = weights;
+ flags |= Mesh::ARRAY_FLAG_USE_8_BONE_WEIGHTS;
}
if (!indices.is_empty()) {
diff --git a/modules/interactive_music/doc_classes/AudioStreamInteractive.xml b/modules/interactive_music/doc_classes/AudioStreamInteractive.xml
index 17448724d1..d47d6ecde6 100644
--- a/modules/interactive_music/doc_classes/AudioStreamInteractive.xml
+++ b/modules/interactive_music/doc_classes/AudioStreamInteractive.xml
@@ -119,7 +119,7 @@
<param index="0" name="from_clip" type="int" />
<param index="1" name="to_clip" type="int" />
<description>
- Return true if a given transition exists (was added via [method add_transition]).
+ Returns [code]true[/code] if a given transition exists (was added via [method add_transition]).
</description>
</method>
<method name="is_transition_holding_previous" qualifiers="const">
diff --git a/modules/meshoptimizer/register_types.cpp b/modules/meshoptimizer/register_types.cpp
index 781f928f66..ebfe5d9c5b 100644
--- a/modules/meshoptimizer/register_types.cpp
+++ b/modules/meshoptimizer/register_types.cpp
@@ -40,10 +40,10 @@ void initialize_meshoptimizer_module(ModuleInitializationLevel p_level) {
}
SurfaceTool::optimize_vertex_cache_func = meshopt_optimizeVertexCache;
+ SurfaceTool::optimize_vertex_fetch_remap_func = meshopt_optimizeVertexFetchRemap;
SurfaceTool::simplify_func = meshopt_simplify;
SurfaceTool::simplify_with_attrib_func = meshopt_simplifyWithAttributes;
SurfaceTool::simplify_scale_func = meshopt_simplifyScale;
- SurfaceTool::simplify_sloppy_func = meshopt_simplifySloppy;
SurfaceTool::generate_remap_func = meshopt_generateVertexRemap;
SurfaceTool::remap_vertex_func = meshopt_remapVertexBuffer;
SurfaceTool::remap_index_func = meshopt_remapIndexBuffer;
@@ -55,9 +55,9 @@ void uninitialize_meshoptimizer_module(ModuleInitializationLevel p_level) {
}
SurfaceTool::optimize_vertex_cache_func = nullptr;
+ SurfaceTool::optimize_vertex_fetch_remap_func = nullptr;
SurfaceTool::simplify_func = nullptr;
SurfaceTool::simplify_scale_func = nullptr;
- SurfaceTool::simplify_sloppy_func = nullptr;
SurfaceTool::generate_remap_func = nullptr;
SurfaceTool::remap_vertex_func = nullptr;
SurfaceTool::remap_index_func = nullptr;
diff --git a/modules/minimp3/resource_importer_mp3.h b/modules/minimp3/resource_importer_mp3.h
index 2df44deaea..037756328f 100644
--- a/modules/minimp3/resource_importer_mp3.h
+++ b/modules/minimp3/resource_importer_mp3.h
@@ -59,6 +59,8 @@ public:
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
+ virtual bool can_import_threaded() const override { return true; }
+
ResourceImporterMP3();
};
diff --git a/modules/openxr/doc_classes/OpenXRCompositionLayer.xml b/modules/openxr/doc_classes/OpenXRCompositionLayer.xml
index 341b50065c..cfc7cd4d97 100644
--- a/modules/openxr/doc_classes/OpenXRCompositionLayer.xml
+++ b/modules/openxr/doc_classes/OpenXRCompositionLayer.xml
@@ -29,7 +29,7 @@
<method name="is_natively_supported" qualifiers="const">
<return type="bool" />
<description>
- Returns true if the OpenXR runtime natively supports this composition layer type.
+ Returns [code]true[/code] if the OpenXR runtime natively supports this composition layer type.
[b]Note:[/b] This will only return an accurate result after the OpenXR session has started.
</description>
</method>
diff --git a/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml
index 813c9d582e..182fe32f9c 100644
--- a/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml
+++ b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml
@@ -90,6 +90,21 @@
Called right after the main swapchains are (re)created.
</description>
</method>
+ <method name="_on_post_draw_viewport" qualifiers="virtual">
+ <return type="void" />
+ <param index="0" name="viewport" type="RID" />
+ <description>
+ Called right after the given viewport is rendered.
+ [b]Note:[/b] The draw commands might only be queued at this point, not executed.
+ </description>
+ </method>
+ <method name="_on_pre_draw_viewport" qualifiers="virtual">
+ <return type="void" />
+ <param index="0" name="viewport" type="RID" />
+ <description>
+ Called right before the given viewport is rendered.
+ </description>
+ </method>
<method name="_on_pre_render" qualifiers="virtual">
<return type="void" />
<description>
diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
index c205e7f5bf..fb8d1b9d69 100644
--- a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
+++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
@@ -51,6 +51,8 @@ void OpenXRExtensionWrapperExtension::_bind_methods() {
GDVIRTUAL_BIND(_on_process);
GDVIRTUAL_BIND(_on_pre_render);
GDVIRTUAL_BIND(_on_main_swapchains_created);
+ GDVIRTUAL_BIND(_on_pre_draw_viewport, "viewport");
+ GDVIRTUAL_BIND(_on_post_draw_viewport, "viewport");
GDVIRTUAL_BIND(_on_session_destroyed);
GDVIRTUAL_BIND(_on_state_idle);
GDVIRTUAL_BIND(_on_state_ready);
@@ -208,6 +210,14 @@ void OpenXRExtensionWrapperExtension::on_session_destroyed() {
GDVIRTUAL_CALL(_on_session_destroyed);
}
+void OpenXRExtensionWrapperExtension::on_pre_draw_viewport(RID p_render_target) {
+ GDVIRTUAL_CALL(_on_pre_draw_viewport, p_render_target);
+}
+
+void OpenXRExtensionWrapperExtension::on_post_draw_viewport(RID p_render_target) {
+ GDVIRTUAL_CALL(_on_post_draw_viewport, p_render_target);
+}
+
void OpenXRExtensionWrapperExtension::on_state_idle() {
GDVIRTUAL_CALL(_on_state_idle);
}
diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.h b/modules/openxr/extensions/openxr_extension_wrapper_extension.h
index 5cdf288c93..8fc6511277 100644
--- a/modules/openxr/extensions/openxr_extension_wrapper_extension.h
+++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.h
@@ -88,6 +88,8 @@ public:
virtual void on_pre_render() override;
virtual void on_main_swapchains_created() override;
virtual void on_session_destroyed() override;
+ virtual void on_pre_draw_viewport(RID p_render_target) override;
+ virtual void on_post_draw_viewport(RID p_render_target) override;
GDVIRTUAL0(_on_register_metadata);
GDVIRTUAL0(_on_before_instance_created);
@@ -98,6 +100,8 @@ public:
GDVIRTUAL0(_on_pre_render);
GDVIRTUAL0(_on_main_swapchains_created);
GDVIRTUAL0(_on_session_destroyed);
+ GDVIRTUAL1(_on_pre_draw_viewport, RID);
+ GDVIRTUAL1(_on_post_draw_viewport, RID);
virtual void on_state_idle() override;
virtual void on_state_ready() override;
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 1c6e62650a..8d557aae2a 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -5264,42 +5264,44 @@ void TextServerAdvanced::_shaped_text_overrun_trim_to_width(const RID &p_shaped_
// Find usable fonts, if fonts from the last glyph do not have required chars.
RID dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
- if (!_font_has_char(dot_gl_font_rid, sd->el_char)) {
- const Array &fonts = spans[spans.size() - 1].fonts;
- for (int i = 0; i < fonts.size(); i++) {
- if (_font_has_char(fonts[i], sd->el_char)) {
- dot_gl_font_rid = fonts[i];
- found_el_char = true;
- break;
- }
- }
- if (!found_el_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) {
- const char32_t u32str[] = { sd->el_char, 0 };
- RID rid = _find_sys_font_for_text(fonts[0], String(), spans[spans.size() - 1].language, u32str);
- if (rid.is_valid()) {
- dot_gl_font_rid = rid;
- found_el_char = true;
- }
- }
- } else {
- found_el_char = true;
- }
- if (!found_el_char) {
- bool found_dot_char = false;
- dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
- if (!_font_has_char(dot_gl_font_rid, '.')) {
+ if (add_ellipsis || enforce_ellipsis) {
+ if (!_font_has_char(dot_gl_font_rid, sd->el_char)) {
const Array &fonts = spans[spans.size() - 1].fonts;
for (int i = 0; i < fonts.size(); i++) {
- if (_font_has_char(fonts[i], '.')) {
+ if (_font_has_char(fonts[i], sd->el_char)) {
dot_gl_font_rid = fonts[i];
- found_dot_char = true;
+ found_el_char = true;
break;
}
}
- if (!found_dot_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) {
- RID rid = _find_sys_font_for_text(fonts[0], String(), spans[spans.size() - 1].language, ".");
+ if (!found_el_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) {
+ const char32_t u32str[] = { sd->el_char, 0 };
+ RID rid = _find_sys_font_for_text(fonts[0], String(), spans[spans.size() - 1].language, u32str);
if (rid.is_valid()) {
dot_gl_font_rid = rid;
+ found_el_char = true;
+ }
+ }
+ } else {
+ found_el_char = true;
+ }
+ if (!found_el_char) {
+ bool found_dot_char = false;
+ dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
+ if (!_font_has_char(dot_gl_font_rid, '.')) {
+ const Array &fonts = spans[spans.size() - 1].fonts;
+ for (int i = 0; i < fonts.size(); i++) {
+ if (_font_has_char(fonts[i], '.')) {
+ dot_gl_font_rid = fonts[i];
+ found_dot_char = true;
+ break;
+ }
+ }
+ if (!found_dot_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) {
+ RID rid = _find_sys_font_for_text(fonts[0], String(), spans[spans.size() - 1].language, ".");
+ if (rid.is_valid()) {
+ dot_gl_font_rid = rid;
+ }
}
}
}
@@ -5315,8 +5317,8 @@ void TextServerAdvanced::_shaped_text_overrun_trim_to_width(const RID &p_shaped_
}
}
- int32_t dot_gl_idx = dot_gl_font_rid.is_valid() ? _font_get_glyph_index(dot_gl_font_rid, last_gl_font_size, (found_el_char ? sd->el_char : '.'), 0) : -1;
- Vector2 dot_adv = dot_gl_font_rid.is_valid() ? _font_get_glyph_advance(dot_gl_font_rid, last_gl_font_size, dot_gl_idx) : Vector2();
+ int32_t dot_gl_idx = ((add_ellipsis || enforce_ellipsis) && dot_gl_font_rid.is_valid()) ? _font_get_glyph_index(dot_gl_font_rid, last_gl_font_size, (found_el_char ? sd->el_char : '.'), 0) : -1;
+ Vector2 dot_adv = ((add_ellipsis || enforce_ellipsis) && dot_gl_font_rid.is_valid()) ? _font_get_glyph_advance(dot_gl_font_rid, last_gl_font_size, dot_gl_idx) : Vector2();
int32_t whitespace_gl_idx = whitespace_gl_font_rid.is_valid() ? _font_get_glyph_index(whitespace_gl_font_rid, last_gl_font_size, ' ', 0) : -1;
Vector2 whitespace_adv = whitespace_gl_font_rid.is_valid() ? _font_get_glyph_advance(whitespace_gl_font_rid, last_gl_font_size, whitespace_gl_idx) : Vector2();
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index d30b2aae19..ae636a3151 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -4077,42 +4077,44 @@ void TextServerFallback::_shaped_text_overrun_trim_to_width(const RID &p_shaped_
// Find usable fonts, if fonts from the last glyph do not have required chars.
RID dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
- if (!_font_has_char(dot_gl_font_rid, sd->el_char)) {
- const Array &fonts = spans[spans.size() - 1].fonts;
- for (int i = 0; i < fonts.size(); i++) {
- if (_font_has_char(fonts[i], sd->el_char)) {
- dot_gl_font_rid = fonts[i];
- found_el_char = true;
- break;
- }
- }
- if (!found_el_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) {
- const char32_t u32str[] = { sd->el_char, 0 };
- RID rid = _find_sys_font_for_text(fonts[0], String(), spans[spans.size() - 1].language, u32str);
- if (rid.is_valid()) {
- dot_gl_font_rid = rid;
- found_el_char = true;
- }
- }
- } else {
- found_el_char = true;
- }
- if (!found_el_char) {
- bool found_dot_char = false;
- dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
- if (!_font_has_char(dot_gl_font_rid, '.')) {
+ if (add_ellipsis || enforce_ellipsis) {
+ if (!_font_has_char(dot_gl_font_rid, sd->el_char)) {
const Array &fonts = spans[spans.size() - 1].fonts;
for (int i = 0; i < fonts.size(); i++) {
- if (_font_has_char(fonts[i], '.')) {
+ if (_font_has_char(fonts[i], sd->el_char)) {
dot_gl_font_rid = fonts[i];
- found_dot_char = true;
+ found_el_char = true;
break;
}
}
- if (!found_dot_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) {
- RID rid = _find_sys_font_for_text(fonts[0], String(), spans[spans.size() - 1].language, ".");
+ if (!found_el_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) {
+ const char32_t u32str[] = { sd->el_char, 0 };
+ RID rid = _find_sys_font_for_text(fonts[0], String(), spans[spans.size() - 1].language, u32str);
if (rid.is_valid()) {
dot_gl_font_rid = rid;
+ found_el_char = true;
+ }
+ }
+ } else {
+ found_el_char = true;
+ }
+ if (!found_el_char) {
+ bool found_dot_char = false;
+ dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
+ if (!_font_has_char(dot_gl_font_rid, '.')) {
+ const Array &fonts = spans[spans.size() - 1].fonts;
+ for (int i = 0; i < fonts.size(); i++) {
+ if (_font_has_char(fonts[i], '.')) {
+ dot_gl_font_rid = fonts[i];
+ found_dot_char = true;
+ break;
+ }
+ }
+ if (!found_dot_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) {
+ RID rid = _find_sys_font_for_text(fonts[0], String(), spans[spans.size() - 1].language, ".");
+ if (rid.is_valid()) {
+ dot_gl_font_rid = rid;
+ }
}
}
}
@@ -4128,8 +4130,8 @@ void TextServerFallback::_shaped_text_overrun_trim_to_width(const RID &p_shaped_
}
}
- int32_t dot_gl_idx = dot_gl_font_rid.is_valid() ? _font_get_glyph_index(dot_gl_font_rid, last_gl_font_size, (found_el_char ? sd->el_char : '.'), 0) : -1;
- Vector2 dot_adv = dot_gl_font_rid.is_valid() ? _font_get_glyph_advance(dot_gl_font_rid, last_gl_font_size, dot_gl_idx) : Vector2();
+ int32_t dot_gl_idx = ((add_ellipsis || enforce_ellipsis) && dot_gl_font_rid.is_valid()) ? _font_get_glyph_index(dot_gl_font_rid, last_gl_font_size, (found_el_char ? sd->el_char : '.'), 0) : -1;
+ Vector2 dot_adv = ((add_ellipsis || enforce_ellipsis) && dot_gl_font_rid.is_valid()) ? _font_get_glyph_advance(dot_gl_font_rid, last_gl_font_size, dot_gl_idx) : Vector2();
int32_t whitespace_gl_idx = whitespace_gl_font_rid.is_valid() ? _font_get_glyph_index(whitespace_gl_font_rid, last_gl_font_size, ' ', 0) : -1;
Vector2 whitespace_adv = whitespace_gl_font_rid.is_valid() ? _font_get_glyph_advance(whitespace_gl_font_rid, last_gl_font_size, whitespace_gl_idx) : Vector2();
diff --git a/modules/vorbis/resource_importer_ogg_vorbis.h b/modules/vorbis/resource_importer_ogg_vorbis.h
index 59ae3378a0..f378b80694 100644
--- a/modules/vorbis/resource_importer_ogg_vorbis.h
+++ b/modules/vorbis/resource_importer_ogg_vorbis.h
@@ -65,6 +65,8 @@ public:
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
+ virtual bool can_import_threaded() const override { return true; }
+
ResourceImporterOggVorbis();
};
diff --git a/platform/SCsub b/platform/SCsub
index 7c9d07f6ef..248b4b88dd 100644
--- a/platform/SCsub
+++ b/platform/SCsub
@@ -60,7 +60,7 @@ register_platform_apis = env.CommandNoCache(
)
env.add_source_files(env.platform_sources, register_platform_apis)
for platform in env.platform_apis:
- env.add_source_files(env.platform_sources, f"{platform}/api/api.cpp")
+ env.add_source_files(env.platform_sources, f"{platform}/api/*.cpp")
lib = env.add_library("platform", env.platform_sources)
env.Prepend(LIBS=[lib])
diff --git a/platform/windows/SCsub b/platform/windows/SCsub
index 1d17e7b325..30df9df809 100644
--- a/platform/windows/SCsub
+++ b/platform/windows/SCsub
@@ -24,6 +24,7 @@ common_win = [
"gl_manager_windows_angle.cpp",
"wgl_detect_version.cpp",
"rendering_context_driver_vulkan_windows.cpp",
+ "drop_target_windows.cpp",
]
if env.msvc:
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index ddcd29adc9..e1109db24f 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -68,23 +68,23 @@ def can_build():
def get_mingw_bin_prefix(prefix, arch):
- if not prefix:
- mingw_bin_prefix = ""
- elif prefix[-1] != "/":
- mingw_bin_prefix = prefix + "/bin/"
- else:
- mingw_bin_prefix = prefix + "bin/"
+ bin_prefix = (os.path.normpath(os.path.join(prefix, "bin")) + os.sep) if prefix else ""
+ ARCH_PREFIXES = {
+ "x86_64": "x86_64-w64-mingw32-",
+ "x86_32": "i686-w64-mingw32-",
+ "arm32": "armv7-w64-mingw32-",
+ "arm64": "aarch64-w64-mingw32-",
+ }
+ arch_prefix = ARCH_PREFIXES[arch] if arch else ""
+ return bin_prefix + arch_prefix
- if arch == "x86_64":
- mingw_bin_prefix += "x86_64-w64-mingw32-"
- elif arch == "x86_32":
- mingw_bin_prefix += "i686-w64-mingw32-"
- elif arch == "arm32":
- mingw_bin_prefix += "armv7-w64-mingw32-"
- elif arch == "arm64":
- mingw_bin_prefix += "aarch64-w64-mingw32-"
- return mingw_bin_prefix
+def get_detected(env: "SConsEnvironment", tool: str) -> str:
+ checks = [
+ get_mingw_bin_prefix(env["mingw_prefix"], env["arch"]) + tool,
+ get_mingw_bin_prefix(env["mingw_prefix"], "") + tool,
+ ]
+ return str(env.Detect(checks))
def detect_build_env_arch():
@@ -245,41 +245,6 @@ def get_flags():
}
-def build_res_file(target, source, env: "SConsEnvironment"):
- arch_aliases = {
- "x86_32": "pe-i386",
- "x86_64": "pe-x86-64",
- "arm32": "armv7-w64-mingw32",
- "arm64": "aarch64-w64-mingw32",
- }
- cmdbase = "windres --include-dir . --target=" + arch_aliases[env["arch"]]
-
- mingw_bin_prefix = get_mingw_bin_prefix(env["mingw_prefix"], env["arch"])
-
- for x in range(len(source)):
- ok = True
- # Try prefixed executable (MinGW on Linux).
- cmd = mingw_bin_prefix + cmdbase + " -i " + str(source[x]) + " -o " + str(target[x])
- try:
- out = subprocess.Popen(cmd, shell=True, stderr=subprocess.PIPE).communicate()
- if len(out[1]):
- ok = False
- except Exception:
- ok = False
-
- # Try generic executable (MSYS2).
- if not ok:
- cmd = cmdbase + " -i " + str(source[x]) + " -o " + str(target[x])
- try:
- out = subprocess.Popen(cmd, shell=True, stderr=subprocess.PIPE).communicate()
- if len(out[1]):
- return -1
- except Exception:
- return -1
-
- return 0
-
-
def setup_msvc_manual(env: "SConsEnvironment"):
"""Running from VCVARS environment"""
@@ -361,6 +326,10 @@ def setup_mingw(env: "SConsEnvironment"):
print_error("No valid compilers found, use MINGW_PREFIX environment variable to set MinGW path.")
sys.exit(255)
+ env.Tool("mingw")
+ env.AppendUnique(CCFLAGS=env.get("ccflags", "").split())
+ env.AppendUnique(RCFLAGS=env.get("rcflags", "").split())
+
print("Using MinGW, arch %s" % (env["arch"]))
@@ -706,6 +675,13 @@ def configure_mingw(env: "SConsEnvironment"):
# https://www.scons.org/wiki/LongCmdLinesOnWin32
env.use_windows_spawn_fix()
+ # HACK: For some reason, Windows-native shells have their MinGW tools
+ # frequently fail as a result of parsing path separators incorrectly.
+ # For some other reason, this issue is circumvented entirely if the
+ # `mingw_prefix` bin is prepended to PATH.
+ if os.sep == "\\":
+ env.PrependENVPath("PATH", os.path.join(env["mingw_prefix"], "bin"))
+
# In case the command line to AR is too long, use a response file.
env["ARCOM_ORIG"] = env["ARCOM"]
env["ARCOM"] = "${TEMPFILE('$ARCOM_ORIG', '$ARCOMSTR')}"
@@ -743,9 +719,6 @@ def configure_mingw(env: "SConsEnvironment"):
## Compiler configuration
- if os.name != "nt":
- env["PROGSUFFIX"] = env["PROGSUFFIX"] + ".exe" # for linux cross-compilation
-
if env["arch"] == "x86_32":
if env["use_static_cpp"]:
env.Append(LINKFLAGS=["-static"])
@@ -760,29 +733,31 @@ def configure_mingw(env: "SConsEnvironment"):
env.Append(CCFLAGS=["-ffp-contract=off"])
- mingw_bin_prefix = get_mingw_bin_prefix(env["mingw_prefix"], env["arch"])
-
if env["use_llvm"]:
- env["CC"] = mingw_bin_prefix + "clang"
- env["CXX"] = mingw_bin_prefix + "clang++"
- if try_cmd("as --version", env["mingw_prefix"], env["arch"]):
- env["AS"] = mingw_bin_prefix + "as"
- env.Append(ASFLAGS=["-c"])
- if try_cmd("ar --version", env["mingw_prefix"], env["arch"]):
- env["AR"] = mingw_bin_prefix + "ar"
- if try_cmd("ranlib --version", env["mingw_prefix"], env["arch"]):
- env["RANLIB"] = mingw_bin_prefix + "ranlib"
+ env["CC"] = get_detected(env, "clang")
+ env["CXX"] = get_detected(env, "clang++")
+ env["AR"] = get_detected(env, "ar")
+ env["RANLIB"] = get_detected(env, "ranlib")
+ env.Append(ASFLAGS=["-c"])
env.extra_suffix = ".llvm" + env.extra_suffix
else:
- env["CC"] = mingw_bin_prefix + "gcc"
- env["CXX"] = mingw_bin_prefix + "g++"
- if try_cmd("as --version", env["mingw_prefix"], env["arch"]):
- env["AS"] = mingw_bin_prefix + "as"
- ar = "ar" if os.name == "nt" else "gcc-ar"
- if try_cmd(f"{ar} --version", env["mingw_prefix"], env["arch"]):
- env["AR"] = mingw_bin_prefix + ar
- if try_cmd("gcc-ranlib --version", env["mingw_prefix"], env["arch"]):
- env["RANLIB"] = mingw_bin_prefix + "gcc-ranlib"
+ env["CC"] = get_detected(env, "gcc")
+ env["CXX"] = get_detected(env, "g++")
+ env["AR"] = get_detected(env, "gcc-ar" if os.name != "nt" else "ar")
+ env["RANLIB"] = get_detected(env, "gcc-ranlib")
+
+ env["RC"] = get_detected(env, "windres")
+ ARCH_TARGETS = {
+ "x86_32": "pe-i386",
+ "x86_64": "pe-x86-64",
+ "arm32": "armv7-w64-mingw32",
+ "arm64": "aarch64-w64-mingw32",
+ }
+ env.AppendUnique(RCFLAGS=f"--target={ARCH_TARGETS[env['arch']]}")
+
+ env["AS"] = get_detected(env, "as")
+ env["OBJCOPY"] = get_detected(env, "objcopy")
+ env["STRIP"] = get_detected(env, "strip")
## LTO
@@ -924,9 +899,6 @@ def configure_mingw(env: "SConsEnvironment"):
env.Append(CPPDEFINES=["MINGW_ENABLED", ("MINGW_HAS_SECURE_API", 1)])
- # resrc
- env.Append(BUILDERS={"RES": env.Builder(action=build_res_file, suffix=".o", src_suffix=".rc")})
-
def configure(env: "SConsEnvironment"):
# Validate arch.
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index e252b26ddd..a6eab1bd29 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -30,6 +30,7 @@
#include "display_server_windows.h"
+#include "drop_target_windows.h"
#include "os_windows.h"
#include "wgl_detect_version.h"
@@ -1617,11 +1618,17 @@ void DisplayServerWindows::delete_sub_window(WindowID p_window) {
}
#endif
- if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[p_window].wtctx) {
- wintab_WTClose(windows[p_window].wtctx);
- windows[p_window].wtctx = nullptr;
+ if ((tablet_get_current_driver() == "wintab") && wintab_available && wd.wtctx) {
+ wintab_WTClose(wd.wtctx);
+ wd.wtctx = nullptr;
+ }
+
+ if (wd.drop_target != nullptr) {
+ RevokeDragDrop(wd.hWnd);
+ wd.drop_target->Release();
}
- DestroyWindow(windows[p_window].hWnd);
+
+ DestroyWindow(wd.hWnd);
windows.erase(p_window);
if (last_focused_window == p_window) {
@@ -1731,7 +1738,14 @@ void DisplayServerWindows::window_set_drop_files_callback(const Callable &p_call
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
- windows[p_window].drop_files_callback = p_callable;
+ WindowData &window_data = windows[p_window];
+
+ window_data.drop_files_callback = p_callable;
+
+ if (window_data.drop_target == nullptr) {
+ window_data.drop_target = memnew(DropTargetWindows(&window_data));
+ ERR_FAIL_COND(RegisterDragDrop(window_data.hWnd, window_data.drop_target) != S_OK);
+ }
}
void DisplayServerWindows::window_set_title(const String &p_title, WindowID p_window) {
@@ -5320,32 +5334,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
}
} break;
- case WM_DROPFILES: {
- HDROP hDropInfo = (HDROP)wParam;
- const int buffsize = 4096;
- WCHAR buf[buffsize];
-
- int fcount = DragQueryFileW(hDropInfo, 0xFFFFFFFF, nullptr, 0);
-
- Vector<String> files;
-
- for (int i = 0; i < fcount; i++) {
- DragQueryFileW(hDropInfo, i, buf, buffsize);
- String file = String::utf16((const char16_t *)buf);
- files.push_back(file);
- }
-
- if (files.size() && windows[window_id].drop_files_callback.is_valid()) {
- Variant v_files = files;
- const Variant *v_args[1] = { &v_files };
- Variant ret;
- Callable::CallError ce;
- windows[window_id].drop_files_callback.callp((const Variant **)&v_args, 1, ret, ce);
- if (ce.error != Callable::CallError::CALL_OK) {
- ERR_PRINT(vformat("Failed to execute drop files callback: %s.", Variant::get_callable_error_text(windows[window_id].drop_files_callback, v_args, 1, ce)));
- }
- }
- } break;
default: {
if (user_proc) {
return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam);
@@ -5450,7 +5438,7 @@ void DisplayServerWindows::_process_key_events() {
k->set_physical_keycode(physical_keycode);
k->set_key_label(key_label);
k->set_unicode(fix_unicode(unicode));
- if (k->get_unicode() && ke.altgr) {
+ if (k->get_unicode() && ke.altgr && windows[ke.window_id].ime_active) {
k->set_alt_pressed(false);
k->set_ctrl_pressed(false);
}
@@ -5526,7 +5514,7 @@ void DisplayServerWindows::_process_key_events() {
}
k->set_unicode(fix_unicode(unicode));
}
- if (k->get_unicode() && ke.altgr) {
+ if (k->get_unicode() && ke.altgr && windows[ke.window_id].ime_active) {
k->set_alt_pressed(false);
k->set_ctrl_pressed(false);
}
@@ -6174,6 +6162,8 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
FreeLibrary(comctl32);
}
+ OleInitialize(nullptr);
+
memset(&wc, 0, sizeof(WNDCLASSEXW));
wc.cbSize = sizeof(WNDCLASSEXW);
wc.style = CS_OWNDC | CS_DBLCLKS;
@@ -6606,6 +6596,12 @@ DisplayServerWindows::~DisplayServerWindows() {
wintab_WTClose(windows[MAIN_WINDOW_ID].wtctx);
windows[MAIN_WINDOW_ID].wtctx = nullptr;
}
+
+ if (windows[MAIN_WINDOW_ID].drop_target != nullptr) {
+ RevokeDragDrop(windows[MAIN_WINDOW_ID].hWnd);
+ windows[MAIN_WINDOW_ID].drop_target->Release();
+ }
+
DestroyWindow(windows[MAIN_WINDOW_ID].hWnd);
}
@@ -6637,4 +6633,6 @@ DisplayServerWindows::~DisplayServerWindows() {
if (tts) {
memdelete(tts);
}
+
+ OleUninitialize();
}
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index fc72e05b1d..0462d3f8fa 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -357,9 +357,13 @@ typedef enum _SHC_PROCESS_DPI_AWARENESS {
SHC_PROCESS_PER_MONITOR_DPI_AWARE = 2,
} SHC_PROCESS_DPI_AWARENESS;
+class DropTargetWindows;
+
class DisplayServerWindows : public DisplayServer {
// No need to register with GDCLASS, it's platform-specific and nothing is added.
+ friend class DropTargetWindows;
+
_THREAD_SAFE_CLASS_
// UXTheme API
@@ -521,6 +525,9 @@ class DisplayServerWindows : public DisplayServer {
Callable input_text_callback;
Callable drop_files_callback;
+ // OLE API
+ DropTargetWindows *drop_target = nullptr;
+
WindowID transient_parent = INVALID_WINDOW_ID;
HashSet<WindowID> transient_children;
diff --git a/platform/windows/drop_target_windows.cpp b/platform/windows/drop_target_windows.cpp
new file mode 100644
index 0000000000..d04924a9cf
--- /dev/null
+++ b/platform/windows/drop_target_windows.cpp
@@ -0,0 +1,375 @@
+/**************************************************************************/
+/* drop_target_windows.cpp */
+/**************************************************************************/
+/* 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. */
+/**************************************************************************/
+
+#include "drop_target_windows.h"
+
+#include "core/io/dir_access.h"
+#include "core/math/random_pcg.h"
+#include "core/os/time.h"
+
+#include <fileapi.h>
+
+// Helpers
+
+static String create_temp_dir() {
+ Char16String buf;
+ int bufsize = GetTempPathW(0, nullptr) + 1;
+ buf.resize(bufsize);
+ if (GetTempPathW(bufsize, (LPWSTR)buf.ptrw()) == 0) {
+ return "";
+ }
+
+ String tmp_dir = String::utf16((const char16_t *)buf.ptr());
+ RandomPCG gen(Time::get_singleton()->get_ticks_usec());
+
+ const int attempts = 4;
+
+ for (int i = 0; i < attempts; ++i) {
+ uint32_t rnd = gen.rand();
+ String dirname = "godot_tmp_" + String::num_uint64(rnd);
+ String res_dir = tmp_dir.path_join(dirname);
+ Char16String res_dir16 = res_dir.utf16();
+
+ if (CreateDirectoryW((LPCWSTR)res_dir16.ptr(), nullptr)) {
+ return res_dir;
+ }
+ }
+
+ return "";
+}
+
+static bool remove_dir_recursive(const String &p_dir) {
+ Ref<DirAccess> dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ if (dir_access->change_dir(p_dir) != OK) {
+ return false;
+ }
+ return dir_access->erase_contents_recursive() == OK;
+}
+
+static bool stream2file(IStream *p_stream, FILEDESCRIPTORW *p_desc, const String &p_path) {
+ if (DirAccess::make_dir_recursive_absolute(p_path.get_base_dir()) != OK) {
+ return false;
+ }
+
+ Char16String path16 = p_path.utf16();
+ DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
+
+ if (p_desc->dwFlags & FD_ATTRIBUTES) {
+ dwFlagsAndAttributes = p_desc->dwFileAttributes;
+ }
+
+ HANDLE file = CreateFileW(
+ (LPCWSTR)path16.ptr(),
+ GENERIC_WRITE,
+ 0,
+ nullptr,
+ CREATE_NEW,
+ dwFlagsAndAttributes,
+ nullptr);
+
+ if (!file) {
+ return false;
+ }
+
+ const int bufsize = 4096;
+ char buf[bufsize];
+ ULONG nread = 0;
+ DWORD nwritten = 0;
+ HRESULT read_result = S_OK;
+ bool result = true;
+
+ while (true) {
+ read_result = p_stream->Read(buf, bufsize, &nread);
+ if (read_result != S_OK && read_result != S_FALSE) {
+ result = false;
+ goto cleanup;
+ }
+
+ if (!nread) {
+ break;
+ }
+
+ while (nread > 0) {
+ if (!WriteFile(file, buf, nread, &nwritten, nullptr) || !nwritten) {
+ result = false;
+ goto cleanup;
+ }
+ nread -= nwritten;
+ }
+ }
+
+cleanup:
+ CloseHandle(file);
+ return result;
+}
+
+// DropTargetWindows
+
+bool DropTargetWindows::is_valid_filedescriptor() {
+ return cf_filedescriptor != 0 && cf_filecontents != 0;
+}
+
+HRESULT DropTargetWindows::handle_hdrop_format(Vector<String> *p_files, IDataObject *pDataObj) {
+ FORMATETC fmt = { CF_HDROP, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ STGMEDIUM stg;
+ HRESULT res = S_OK;
+
+ if (pDataObj->GetData(&fmt, &stg) != S_OK) {
+ return E_UNEXPECTED;
+ }
+
+ HDROP hDropInfo = (HDROP)GlobalLock(stg.hGlobal);
+
+ Char16String buf;
+
+ if (hDropInfo == nullptr) {
+ ReleaseStgMedium(&stg);
+ return E_UNEXPECTED;
+ }
+
+ int fcount = DragQueryFileW(hDropInfo, 0xFFFFFFFF, nullptr, 0);
+
+ for (int i = 0; i < fcount; i++) {
+ int buffsize = DragQueryFileW(hDropInfo, i, nullptr, 0);
+ buf.resize(buffsize + 1);
+ if (DragQueryFileW(hDropInfo, i, (LPWSTR)buf.ptrw(), buffsize + 1) == 0) {
+ res = E_UNEXPECTED;
+ goto cleanup;
+ }
+ String file = String::utf16((const char16_t *)buf.ptr());
+ p_files->push_back(file);
+ }
+
+cleanup:
+ GlobalUnlock(stg.hGlobal);
+ ReleaseStgMedium(&stg);
+
+ return res;
+}
+
+HRESULT DropTargetWindows::handle_filedescriptor_format(Vector<String> *p_files, IDataObject *pDataObj) {
+ FORMATETC fmt = { cf_filedescriptor, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ STGMEDIUM stg;
+ HRESULT res = S_OK;
+
+ if (pDataObj->GetData(&fmt, &stg) != S_OK) {
+ return E_UNEXPECTED;
+ }
+
+ FILEGROUPDESCRIPTORW *filegroup_desc = (FILEGROUPDESCRIPTORW *)GlobalLock(stg.hGlobal);
+
+ if (!filegroup_desc) {
+ ReleaseStgMedium(&stg);
+ return E_UNEXPECTED;
+ }
+
+ tmp_path = create_temp_dir();
+ Ref<DirAccess> dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ PackedStringArray copied;
+
+ if (dir_access->change_dir(tmp_path) != OK) {
+ res = E_UNEXPECTED;
+ goto cleanup;
+ }
+
+ for (int i = 0; i < (int)filegroup_desc->cItems; ++i) {
+ res = save_as_file(tmp_path, filegroup_desc->fgd + i, pDataObj, i);
+ if (res != S_OK) {
+ res = E_UNEXPECTED;
+ goto cleanup;
+ }
+ }
+
+ copied = dir_access->get_files();
+ for (const String &file : copied) {
+ p_files->push_back(tmp_path.path_join(file));
+ }
+
+ copied = dir_access->get_directories();
+ for (const String &dir : copied) {
+ p_files->push_back(tmp_path.path_join(dir));
+ }
+
+cleanup:
+ GlobalUnlock(filegroup_desc);
+ ReleaseStgMedium(&stg);
+ if (res != S_OK) {
+ remove_dir_recursive(tmp_path);
+ tmp_path.clear();
+ }
+ return res;
+}
+
+HRESULT DropTargetWindows::save_as_file(const String &p_out_dir, FILEDESCRIPTORW *p_file_desc, IDataObject *pDataObj, int p_file_idx) {
+ String relpath = String::utf16((const char16_t *)p_file_desc->cFileName);
+ String fullpath = p_out_dir.path_join(relpath);
+
+ if (p_file_desc->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ if (DirAccess::make_dir_recursive_absolute(fullpath) != OK) {
+ return E_UNEXPECTED;
+ }
+ return S_OK;
+ }
+
+ FORMATETC fmt = { cf_filecontents, nullptr, DVASPECT_CONTENT, p_file_idx, TYMED_ISTREAM };
+ STGMEDIUM stg;
+ HRESULT res = S_OK;
+
+ if (pDataObj->GetData(&fmt, &stg) != S_OK) {
+ return E_UNEXPECTED;
+ }
+
+ IStream *stream = stg.pstm;
+ if (stream == nullptr) {
+ res = E_UNEXPECTED;
+ goto cleanup;
+ }
+
+ if (!stream2file(stream, p_file_desc, fullpath)) {
+ res = E_UNEXPECTED;
+ goto cleanup;
+ }
+
+cleanup:
+ ReleaseStgMedium(&stg);
+ return res;
+}
+
+DropTargetWindows::DropTargetWindows(DisplayServerWindows::WindowData *p_window_data) :
+ ref_count(1), window_data(p_window_data) {
+ cf_filedescriptor = RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW);
+ cf_filecontents = RegisterClipboardFormat(CFSTR_FILECONTENTS);
+}
+
+HRESULT STDMETHODCALLTYPE DropTargetWindows::QueryInterface(REFIID riid, void **ppvObject) {
+ if (riid == IID_IUnknown || riid == IID_IDropTarget) {
+ *ppvObject = static_cast<IDropTarget *>(this);
+ AddRef();
+ return S_OK;
+ }
+ *ppvObject = nullptr;
+ return E_NOINTERFACE;
+}
+
+ULONG STDMETHODCALLTYPE DropTargetWindows::AddRef() {
+ return InterlockedIncrement(&ref_count);
+}
+
+ULONG STDMETHODCALLTYPE DropTargetWindows::Release() {
+ ULONG count = InterlockedDecrement(&ref_count);
+ if (count == 0) {
+ memfree(this);
+ }
+ return count;
+}
+
+HRESULT STDMETHODCALLTYPE DropTargetWindows::DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) {
+ (void)grfKeyState;
+ (void)pt;
+
+ FORMATETC hdrop_fmt = { CF_HDROP, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ FORMATETC filedesc_fmt = { cf_filedescriptor, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+
+ if (!window_data->drop_files_callback.is_valid()) {
+ *pdwEffect = DROPEFFECT_NONE;
+ } else if (pDataObj->QueryGetData(&hdrop_fmt) == S_OK) {
+ *pdwEffect = DROPEFFECT_COPY;
+ } else if (is_valid_filedescriptor() && pDataObj->QueryGetData(&filedesc_fmt) == S_OK) {
+ *pdwEffect = DROPEFFECT_COPY;
+ } else {
+ *pdwEffect = DROPEFFECT_NONE;
+ }
+
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE DropTargetWindows::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) {
+ (void)grfKeyState;
+ (void)pt;
+
+ *pdwEffect = DROPEFFECT_COPY;
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE DropTargetWindows::DragLeave() {
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE DropTargetWindows::Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) {
+ (void)grfKeyState;
+ (void)pt;
+
+ *pdwEffect = DROPEFFECT_NONE;
+
+ if (!window_data->drop_files_callback.is_valid()) {
+ return S_OK;
+ }
+
+ FORMATETC hdrop_fmt = { CF_HDROP, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ FORMATETC filedesc_fmt = { cf_filedescriptor, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ Vector<String> files;
+
+ if (pDataObj->QueryGetData(&hdrop_fmt) == S_OK) {
+ HRESULT res = handle_hdrop_format(&files, pDataObj);
+ if (res != S_OK) {
+ return res;
+ }
+ } else if (pDataObj->QueryGetData(&filedesc_fmt) == S_OK && is_valid_filedescriptor()) {
+ HRESULT res = handle_filedescriptor_format(&files, pDataObj);
+ if (res != S_OK) {
+ return res;
+ }
+ } else {
+ return E_UNEXPECTED;
+ }
+
+ if (!files.size()) {
+ return S_OK;
+ }
+
+ Variant v_files = files;
+ const Variant *v_args[1] = { &v_files };
+ Variant ret;
+ Callable::CallError ce;
+ window_data->drop_files_callback.callp((const Variant **)&v_args, 1, ret, ce);
+
+ if (!tmp_path.is_empty()) {
+ remove_dir_recursive(tmp_path);
+ tmp_path.clear();
+ }
+
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat("Failed to execute drop files callback: %s.", Variant::get_callable_error_text(window_data->drop_files_callback, v_args, 1, ce)));
+ return E_UNEXPECTED;
+ }
+
+ *pdwEffect = DROPEFFECT_COPY;
+ return S_OK;
+}
diff --git a/platform/windows/drop_target_windows.h b/platform/windows/drop_target_windows.h
new file mode 100644
index 0000000000..bd0e0270c6
--- /dev/null
+++ b/platform/windows/drop_target_windows.h
@@ -0,0 +1,77 @@
+/**************************************************************************/
+/* drop_target_windows.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 DROP_TARGET_WINDOWS_H
+#define DROP_TARGET_WINDOWS_H
+
+#include "display_server_windows.h"
+
+#include <shlobj.h>
+
+// Silence warning due to a COM API weirdness.
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
+#endif
+
+// https://learn.microsoft.com/en-us/windows/win32/api/ole2/nf-ole2-dodragdrop#remarks
+class DropTargetWindows : public IDropTarget {
+ LONG ref_count;
+ DisplayServerWindows::WindowData *window_data = nullptr;
+ CLIPFORMAT cf_filedescriptor = 0;
+ CLIPFORMAT cf_filecontents = 0;
+ String tmp_path;
+
+ bool is_valid_filedescriptor();
+ HRESULT handle_hdrop_format(Vector<String> *p_files, IDataObject *pDataObj);
+ HRESULT handle_filedescriptor_format(Vector<String> *p_files, IDataObject *pDataObj);
+ HRESULT save_as_file(const String &p_out_dir, FILEDESCRIPTORW *p_file_desc, IDataObject *pDataObj, int p_file_idx);
+
+public:
+ DropTargetWindows(DisplayServerWindows::WindowData *p_window_data);
+ virtual ~DropTargetWindows() {}
+
+ // IUnknown
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override;
+ ULONG STDMETHODCALLTYPE AddRef() override;
+ ULONG STDMETHODCALLTYPE Release() override;
+
+ // IDropTarget
+ HRESULT STDMETHODCALLTYPE DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) override;
+ HRESULT STDMETHODCALLTYPE DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) override;
+ HRESULT STDMETHODCALLTYPE DragLeave() override;
+ HRESULT STDMETHODCALLTYPE Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) override;
+};
+
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
+#endif // DROP_TARGET_WINDOWS_H
diff --git a/platform/windows/godot.natvis b/platform/windows/godot.natvis
index 14536fa130..fc34ad3cb3 100644
--- a/platform/windows/godot.natvis
+++ b/platform/windows/godot.natvis
@@ -1,5 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
+ <Type Name="Ref&lt;*&gt;">
+ <SmartPointer Usage="Minimal">reference</SmartPointer>
+ <DisplayString Condition="!reference">[empty]</DisplayString>
+ <DisplayString Condition="!!reference">{*reference}</DisplayString>
+ <Expand>
+ <Item Condition="!!reference" Name="[ptr]">reference</Item>
+ <Item Condition="!!reference" Name="[refcount]">reference->refcount.count.value</Item>
+ </Expand>
+ </Type>
+
<Type Name="Vector&lt;*&gt;">
<Expand>
<Item Name="[size]">_cowdata._ptr ? (((const unsigned long long *)(_cowdata._ptr))[-1]) : 0</Item>
@@ -91,6 +101,16 @@
<StringView Condition="_data &amp;&amp; !_data->cname">_data->name,s32b</StringView>
</Type>
+ <Type Name="HashSet&lt;*,*,*&gt;">
+ <Expand>
+ <Item Name="[size]">num_elements</Item>
+ <ArrayItems>
+ <Size>num_elements</Size>
+ <ValuePointer>($T1 *) keys._cowdata._ptr</ValuePointer>
+ </ArrayItems>
+ </Expand>
+ </Type>
+
<Type Name="HashMapElement&lt;*,*&gt;">
<DisplayString>{{Key = {($T1 *) &amp;data.key} Value = {($T2 *) &amp;data.value}}}</DisplayString>
<Expand>
diff --git a/platform/windows/platform_windows_builders.py b/platform/windows/platform_windows_builders.py
index 3fd9e1a581..2020e68748 100644
--- a/platform/windows/platform_windows_builders.py
+++ b/platform/windows/platform_windows_builders.py
@@ -2,23 +2,11 @@
import os
-from detect import get_mingw_bin_prefix, try_cmd
-
def make_debug_mingw(target, source, env):
dst = str(target[0])
# Force separate debug symbols if executable size is larger than 1.9 GB.
if env["separate_debug_symbols"] or os.stat(dst).st_size >= 2040109465:
- mingw_bin_prefix = get_mingw_bin_prefix(env["mingw_prefix"], env["arch"])
- if try_cmd("objcopy --version", env["mingw_prefix"], env["arch"]):
- os.system(mingw_bin_prefix + "objcopy --only-keep-debug {0} {0}.debugsymbols".format(dst))
- else:
- os.system("objcopy --only-keep-debug {0} {0}.debugsymbols".format(dst))
- if try_cmd("strip --version", env["mingw_prefix"], env["arch"]):
- os.system(mingw_bin_prefix + "strip --strip-debug --strip-unneeded {0}".format(dst))
- else:
- os.system("strip --strip-debug --strip-unneeded {0}".format(dst))
- if try_cmd("objcopy --version", env["mingw_prefix"], env["arch"]):
- os.system(mingw_bin_prefix + "objcopy --add-gnu-debuglink={0}.debugsymbols {0}".format(dst))
- else:
- os.system("objcopy --add-gnu-debuglink={0}.debugsymbols {0}".format(dst))
+ os.system("{0} --only-keep-debug {1} {1}.debugsymbols".format(env["OBJCOPY"], dst))
+ os.system("{0} --strip-debug --strip-unneeded {1}".format(env["STRIP"], dst))
+ os.system("{0} --add-gnu-debuglink={1}.debugsymbols {1}".format(env["OBJCOPY"], dst))
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index c1c992588b..abece303d1 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -183,17 +183,17 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const {
void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const {
for (uint32_t i = 0; i < bones.size(); i++) {
- const String prep = vformat("%s/%d/", PNAME("bones"), i);
- p_list->push_back(PropertyInfo(Variant::STRING, prep + PNAME("name"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
- p_list->push_back(PropertyInfo(Variant::INT, prep + PNAME("parent"), PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NO_EDITOR));
- p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + PNAME("rest"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
- p_list->push_back(PropertyInfo(Variant::BOOL, prep + PNAME("enabled"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
- p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + PNAME("position"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
- p_list->push_back(PropertyInfo(Variant::QUATERNION, prep + PNAME("rotation"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
- p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + PNAME("scale"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ const String prep = vformat("%s/%d/", "bones", i);
+ p_list->push_back(PropertyInfo(Variant::STRING, prep + "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + "rest", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::QUATERNION, prep + "rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
for (const KeyValue<StringName, Variant> &K : bones[i].metadata) {
- PropertyInfo pi = PropertyInfo(bones[i].metadata[K.key].get_type(), prep + PNAME("bone_meta/") + K.key, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR);
+ PropertyInfo pi = PropertyInfo(bones[i].metadata[K.key].get_type(), prep + "bone_meta/" + K.key, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR);
p_list->push_back(pi);
}
}
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 9967805134..3f979f7c20 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -128,6 +128,7 @@ bool LineEdit::has_ime_text() const {
void LineEdit::cancel_ime() {
if (!has_ime_text()) {
+ _close_ime_window();
return;
}
ime_text = String();
@@ -140,6 +141,7 @@ void LineEdit::cancel_ime() {
void LineEdit::apply_ime() {
if (!has_ime_text()) {
+ _close_ime_window();
return;
}
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 7a2570f400..b1918ff23f 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -3169,6 +3169,7 @@ bool TextEdit::has_ime_text() const {
void TextEdit::cancel_ime() {
if (!has_ime_text()) {
+ _close_ime_window();
return;
}
ime_text = String();
@@ -3181,6 +3182,7 @@ void TextEdit::cancel_ime() {
void TextEdit::apply_ime() {
if (!has_ime_text()) {
+ _close_ime_window();
return;
}
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index aab6f672f0..cfd93a5317 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -5362,14 +5362,16 @@ void Tree::scroll_to_item(TreeItem *p_item, bool p_center_on_item) {
int y_offset = get_item_offset(p_item);
if (y_offset != -1) {
- const int tbh = _get_title_button_height();
- y_offset -= tbh;
+ const int title_button_height = _get_title_button_height();
+ y_offset -= title_button_height;
const int cell_h = compute_item_height(p_item) + theme_cache.v_separation;
- int screen_h = area_size.height - tbh;
+ int screen_h = area_size.height - title_button_height;
if (p_center_on_item) {
- v_scroll->set_value(y_offset - (screen_h - cell_h) / 2.0f);
+ // This makes sure that centering the offset doesn't overflow.
+ const double v_scroll_value = y_offset - MAX((screen_h - cell_h) / 2.0, 0.0);
+ v_scroll->set_value(v_scroll_value);
} else {
if (cell_h > screen_h) { // Screen size is too small, maybe it was not resized yet.
v_scroll->set_value(y_offset);
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 6b1ce2b4ca..315ccaeb8c 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -512,6 +512,9 @@ void register_scene_types() {
GDREGISTER_CLASS(AnimationNodeStateMachine);
GDREGISTER_CLASS(AnimationNodeStateMachinePlayback);
+ GDREGISTER_INTERNAL_CLASS(AnimationNodeStartState);
+ GDREGISTER_INTERNAL_CLASS(AnimationNodeEndState);
+
GDREGISTER_CLASS(AnimationNodeSync);
GDREGISTER_CLASS(AnimationNodeStateMachineTransition);
GDREGISTER_CLASS(AnimationNodeOutput);
diff --git a/scene/resources/2d/tile_set.cpp b/scene/resources/2d/tile_set.cpp
index ca80486363..54a18cf67e 100644
--- a/scene/resources/2d/tile_set.cpp
+++ b/scene/resources/2d/tile_set.cpp
@@ -4220,10 +4220,10 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const {
// Tile Proxies.
// Note: proxies need to be set after sources are set.
- p_list->push_back(PropertyInfo(Variant::NIL, GNAME("Tile Proxies", ""), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
- p_list->push_back(PropertyInfo(Variant::ARRAY, PNAME("tile_proxies/source_level"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
- p_list->push_back(PropertyInfo(Variant::ARRAY, PNAME("tile_proxies/coords_level"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
- p_list->push_back(PropertyInfo(Variant::ARRAY, PNAME("tile_proxies/alternative_level"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::NIL, "Tile Proxies", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/source_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/coords_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/alternative_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
// Patterns.
for (unsigned int pattern_index = 0; pattern_index < patterns.size(); pattern_index++) {
@@ -4931,10 +4931,13 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const {
}
for (const KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) {
+ const String formatted_key = itos(E_alternative.key);
+
// Add a dummy property to show the alternative exists.
- tile_property_list.push_back(PropertyInfo(Variant::INT, vformat("%d", E_alternative.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ tile_property_list.push_back(PropertyInfo(Variant::INT, formatted_key, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
// Get the alternative tile's properties and append them to the list of properties.
+ const String alternative_property_info_prefix = formatted_key + '/';
List<PropertyInfo> alternative_property_list;
E_alternative.value->get_property_list(&alternative_property_list);
for (PropertyInfo &alternative_property_info : alternative_property_list) {
@@ -4943,14 +4946,15 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const {
if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value))) {
alternative_property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
- alternative_property_info.name = vformat("%s/%s", vformat("%d", E_alternative.key), alternative_property_info.name);
+ alternative_property_info.name = alternative_property_info_prefix + alternative_property_info.name;
tile_property_list.push_back(alternative_property_info);
}
}
// Add all alternative.
+ const String property_info_prefix = vformat("%d:%d/", E_tile.key.x, E_tile.key.y);
for (PropertyInfo &tile_property_info : tile_property_list) {
- tile_property_info.name = vformat("%s/%s", vformat("%d:%d", E_tile.key.x, E_tile.key.y), tile_property_info.name);
+ tile_property_info.name = property_info_prefix + tile_property_info.name;
p_list->push_back(tile_property_info);
}
}
diff --git a/scene/resources/3d/importer_mesh.cpp b/scene/resources/3d/importer_mesh.cpp
index e255cb077f..f040f04cd8 100644
--- a/scene/resources/3d/importer_mesh.cpp
+++ b/scene/resources/3d/importer_mesh.cpp
@@ -168,10 +168,56 @@ void ImporterMesh::set_surface_material(int p_surface, const Ref<Material> &p_ma
mesh.unref();
}
-void ImporterMesh::optimize_indices_for_cache() {
+template <typename T>
+static Vector<T> _remap_array(Vector<T> p_array, const Vector<uint32_t> &p_remap, uint32_t p_vertex_count) {
+ ERR_FAIL_COND_V(p_array.size() % p_remap.size() != 0, p_array);
+ int num_elements = p_array.size() / p_remap.size();
+ T *data = p_array.ptrw();
+ SurfaceTool::remap_vertex_func(data, data, p_remap.size(), sizeof(T) * num_elements, p_remap.ptr());
+ p_array.resize(p_vertex_count * num_elements);
+ return p_array;
+}
+
+static void _remap_arrays(Array &r_arrays, const Vector<uint32_t> &p_remap, uint32_t p_vertex_count) {
+ for (int i = 0; i < r_arrays.size(); i++) {
+ if (i == RS::ARRAY_INDEX) {
+ continue;
+ }
+
+ switch (r_arrays[i].get_type()) {
+ case Variant::NIL:
+ break;
+ case Variant::PACKED_VECTOR3_ARRAY:
+ r_arrays[i] = _remap_array<Vector3>(r_arrays[i], p_remap, p_vertex_count);
+ break;
+ case Variant::PACKED_VECTOR2_ARRAY:
+ r_arrays[i] = _remap_array<Vector2>(r_arrays[i], p_remap, p_vertex_count);
+ break;
+ case Variant::PACKED_FLOAT32_ARRAY:
+ r_arrays[i] = _remap_array<float>(r_arrays[i], p_remap, p_vertex_count);
+ break;
+ case Variant::PACKED_INT32_ARRAY:
+ r_arrays[i] = _remap_array<int32_t>(r_arrays[i], p_remap, p_vertex_count);
+ break;
+ case Variant::PACKED_BYTE_ARRAY:
+ r_arrays[i] = _remap_array<uint8_t>(r_arrays[i], p_remap, p_vertex_count);
+ break;
+ case Variant::PACKED_COLOR_ARRAY:
+ r_arrays[i] = _remap_array<Color>(r_arrays[i], p_remap, p_vertex_count);
+ break;
+ default:
+ ERR_FAIL_MSG("Unhandled array type.");
+ }
+ }
+}
+
+void ImporterMesh::optimize_indices() {
if (!SurfaceTool::optimize_vertex_cache_func) {
return;
}
+ if (!SurfaceTool::optimize_vertex_fetch_remap_func || !SurfaceTool::remap_vertex_func || !SurfaceTool::remap_index_func) {
+ return;
+ }
for (int i = 0; i < surfaces.size(); i++) {
if (surfaces[i].primitive != Mesh::PRIMITIVE_TRIANGLES) {
@@ -188,10 +234,48 @@ void ImporterMesh::optimize_indices_for_cache() {
continue;
}
+ // Optimize indices for vertex cache to establish final triangle order.
int *indices_ptr = indices.ptrw();
SurfaceTool::optimize_vertex_cache_func((unsigned int *)indices_ptr, (const unsigned int *)indices_ptr, index_count, vertex_count);
+ surfaces.write[i].arrays[RS::ARRAY_INDEX] = indices;
+
+ for (int j = 0; j < surfaces[i].lods.size(); ++j) {
+ Surface::LOD &lod = surfaces.write[i].lods.write[j];
+ int *lod_indices_ptr = lod.indices.ptrw();
+ SurfaceTool::optimize_vertex_cache_func((unsigned int *)lod_indices_ptr, (const unsigned int *)lod_indices_ptr, lod.indices.size(), vertex_count);
+ }
+ // Concatenate indices for all LODs in the order of coarse->fine; this establishes the effective order of vertices,
+ // and is important to optimize for vertex fetch (all GPUs) and shading (Mali GPUs)
+ PackedInt32Array merged_indices;
+ for (int j = surfaces[i].lods.size() - 1; j >= 0; --j) {
+ merged_indices.append_array(surfaces[i].lods[j].indices);
+ }
+ merged_indices.append_array(indices);
+
+ // Generate remap array that establishes optimal vertex order according to the order of indices above.
+ Vector<uint32_t> remap;
+ remap.resize(vertex_count);
+ unsigned int new_vertex_count = SurfaceTool::optimize_vertex_fetch_remap_func(remap.ptrw(), (const unsigned int *)merged_indices.ptr(), merged_indices.size(), vertex_count);
+
+ // We need to remap all vertex and index arrays in lockstep according to the remap.
+ SurfaceTool::remap_index_func((unsigned int *)indices_ptr, (const unsigned int *)indices_ptr, index_count, remap.ptr());
surfaces.write[i].arrays[RS::ARRAY_INDEX] = indices;
+
+ for (int j = 0; j < surfaces[i].lods.size(); ++j) {
+ Surface::LOD &lod = surfaces.write[i].lods.write[j];
+ int *lod_indices_ptr = lod.indices.ptrw();
+ SurfaceTool::remap_index_func((unsigned int *)lod_indices_ptr, (const unsigned int *)lod_indices_ptr, lod.indices.size(), remap.ptr());
+ }
+
+ _remap_arrays(surfaces.write[i].arrays, remap, new_vertex_count);
+ for (int j = 0; j < surfaces[i].blend_shape_data.size(); j++) {
+ _remap_arrays(surfaces.write[i].blend_shape_data.write[j].arrays, remap, new_vertex_count);
+ }
+ }
+
+ if (shadow_mesh.is_valid()) {
+ shadow_mesh->optimize_indices();
}
}
@@ -215,9 +299,6 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, Array p_bone_transf
if (!SurfaceTool::simplify_with_attrib_func) {
return;
}
- if (!SurfaceTool::optimize_vertex_cache_func) {
- return;
- }
LocalVector<Transform3D> bone_transform_vector;
for (int i = 0; i < p_bone_transform_array.size(); i++) {
@@ -431,12 +512,6 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, Array p_bone_transf
}
surfaces.write[i].lods.sort_custom<Surface::LODComparator>();
-
- for (int j = 0; j < surfaces.write[i].lods.size(); j++) {
- Surface::LOD &lod = surfaces.write[i].lods.write[j];
- unsigned int *lod_indices_ptr = (unsigned int *)lod.indices.ptrw();
- SurfaceTool::optimize_vertex_cache_func(lod_indices_ptr, lod_indices_ptr, lod.indices.size(), vertex_count);
- }
}
}
@@ -574,10 +649,6 @@ void ImporterMesh::create_shadow_mesh() {
index_wptr[j] = vertex_remap[index];
}
- if (SurfaceTool::optimize_vertex_cache_func && surfaces[i].primitive == Mesh::PRIMITIVE_TRIANGLES) {
- SurfaceTool::optimize_vertex_cache_func((unsigned int *)index_wptr, (const unsigned int *)index_wptr, index_count, new_vertices.size());
- }
-
new_surface[RS::ARRAY_INDEX] = new_indices;
// Make sure the same LODs as the full version are used.
@@ -596,10 +667,6 @@ void ImporterMesh::create_shadow_mesh() {
index_wptr[k] = vertex_remap[index];
}
- if (SurfaceTool::optimize_vertex_cache_func && surfaces[i].primitive == Mesh::PRIMITIVE_TRIANGLES) {
- SurfaceTool::optimize_vertex_cache_func((unsigned int *)index_wptr, (const unsigned int *)index_wptr, index_count, new_vertices.size());
- }
-
lods[surfaces[i].lods[j].distance] = new_indices;
}
}
diff --git a/scene/resources/3d/importer_mesh.h b/scene/resources/3d/importer_mesh.h
index 7776e78f11..2bdf759da6 100644
--- a/scene/resources/3d/importer_mesh.h
+++ b/scene/resources/3d/importer_mesh.h
@@ -113,7 +113,7 @@ public:
void set_surface_material(int p_surface, const Ref<Material> &p_material);
- void optimize_indices_for_cache();
+ void optimize_indices();
void generate_lods(float p_normal_merge_angle, Array p_skin_pose_transform_array);
diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp
index 6921885ee0..c230cf1b70 100644
--- a/scene/resources/surface_tool.cpp
+++ b/scene/resources/surface_tool.cpp
@@ -33,10 +33,10 @@
#define EQ_VERTEX_DIST 0.00001
SurfaceTool::OptimizeVertexCacheFunc SurfaceTool::optimize_vertex_cache_func = nullptr;
+SurfaceTool::OptimizeVertexFetchRemapFunc SurfaceTool::optimize_vertex_fetch_remap_func = nullptr;
SurfaceTool::SimplifyFunc SurfaceTool::simplify_func = nullptr;
SurfaceTool::SimplifyWithAttribFunc SurfaceTool::simplify_with_attrib_func = nullptr;
SurfaceTool::SimplifyScaleFunc SurfaceTool::simplify_scale_func = nullptr;
-SurfaceTool::SimplifySloppyFunc SurfaceTool::simplify_sloppy_func = nullptr;
SurfaceTool::GenerateRemapFunc SurfaceTool::generate_remap_func = nullptr;
SurfaceTool::RemapVertexFunc SurfaceTool::remap_vertex_func = nullptr;
SurfaceTool::RemapIndexFunc SurfaceTool::remap_index_func = nullptr;
diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h
index 3b18e082a1..68dc9e7198 100644
--- a/scene/resources/surface_tool.h
+++ b/scene/resources/surface_tool.h
@@ -90,14 +90,14 @@ public:
typedef void (*OptimizeVertexCacheFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, size_t vertex_count);
static OptimizeVertexCacheFunc optimize_vertex_cache_func;
+ typedef size_t (*OptimizeVertexFetchRemapFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, size_t vertex_count);
+ static OptimizeVertexFetchRemapFunc optimize_vertex_fetch_remap_func;
typedef size_t (*SimplifyFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const float *vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, unsigned int options, float *r_error);
static SimplifyFunc simplify_func;
typedef size_t (*SimplifyWithAttribFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const float *vertex_data, size_t vertex_count, size_t vertex_stride, const float *attributes, size_t attribute_stride, const float *attribute_weights, size_t attribute_count, const unsigned char *vertex_lock, size_t target_index_count, float target_error, unsigned int options, float *result_error);
static SimplifyWithAttribFunc simplify_with_attrib_func;
typedef float (*SimplifyScaleFunc)(const float *vertex_positions, size_t vertex_count, size_t vertex_positions_stride);
static SimplifyScaleFunc simplify_scale_func;
- typedef size_t (*SimplifySloppyFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const float *vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, float *out_result_error);
- static SimplifySloppyFunc simplify_sloppy_func;
typedef size_t (*GenerateRemapFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const void *vertices, size_t vertex_count, size_t vertex_size);
static GenerateRemapFunc generate_remap_func;
typedef void (*RemapVertexFunc)(void *destination, const void *vertices, size_t vertex_count, size_t vertex_size, const unsigned int *remap);
@@ -222,7 +222,9 @@ public:
void clear();
- LocalVector<Vertex> &get_vertex_array() { return vertex_array; }
+ LocalVector<Vertex> &get_vertex_array() {
+ return vertex_array;
+ }
void create_from_triangle_arrays(const Array &p_arrays);
void create_from_arrays(const Array &p_arrays, Mesh::PrimitiveType p_primitive_type = Mesh::PRIMITIVE_TRIANGLES);
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index 933e990920..f1bc83eb42 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -1921,13 +1921,13 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const {
}
for (const KeyValue<String, Varying> &E : varyings) {
- p_list->push_back(PropertyInfo(Variant::STRING, vformat("%s/%s", PNAME("varyings"), E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::STRING, vformat("%s/%s", "varyings", E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
}
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
for (const KeyValue<String, Variant> &E : preview_params) {
- p_list->push_back(PropertyInfo(Variant::STRING, vformat("%s/%s", PNAME("preview_params"), E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::STRING, vformat("%s/%s", "preview_params", E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
}
}
#endif // TOOLS_ENABLED
diff --git a/scu_builders.py b/scu_builders.py
index 6bf87b9163..e0bfffc320 100644
--- a/scu_builders.py
+++ b/scu_builders.py
@@ -259,10 +259,6 @@ def process_folder(folders, sought_exceptions=[], includes_per_scu=0, extension=
def generate_scu_files(max_includes_per_scu):
- print("=============================")
- print("Single Compilation Unit Build")
- print("=============================")
-
global _max_includes_per_scu
_max_includes_per_scu = max_includes_per_scu
diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp
index 14c4c3b7e1..63b2f28e74 100644
--- a/servers/audio_server.cpp
+++ b/servers/audio_server.cpp
@@ -371,10 +371,14 @@ void AudioServer::_mix_step() {
bus->soloed = false;
}
}
+ // This is legacy code from 3.x that allows video players and other audio sources that do not implement AudioStreamPlayback to output audio.
for (CallbackItem *ci : mix_callback_list) {
ci->callback(ci->userdata);
}
+ // Main mixing loop for audio streams.
+ // The basic idea here is to copy the samples returned by the AudioStreamPlayback's mix function into the audio buffers,
+ // while always maintaining a lookahead buffer of size LOOKAHEAD_BUFFER_SIZE to allow fade-outs for sudden stoppages.
for (AudioStreamPlaybackListNode *playback : playback_list) {
// Paused streams are no-ops. Don't even mix audio from the stream playback.
if (playback->state.load() == AudioStreamPlaybackListNode::PAUSED) {
@@ -385,22 +389,26 @@ void AudioServer::_mix_step() {
continue;
}
+ // If `fading_out` is true, we're in the process of fading out the stream playback.
+ // TODO: Currently this sets the volume of the stream to 0 which creates a linear interpolation between its previous volume and silence.
+ // A more punchy option for fading out could be to just use the lookahead buffer.
bool fading_out = playback->state.load() == AudioStreamPlaybackListNode::FADE_OUT_TO_DELETION || playback->state.load() == AudioStreamPlaybackListNode::FADE_OUT_TO_PAUSE;
AudioFrame *buf = mix_buffer.ptrw();
- // Copy the lookeahead buffer into the mix buffer.
+ // Copy the old contents of the lookahead buffer into the beginning of the mix buffer.
for (int i = 0; i < LOOKAHEAD_BUFFER_SIZE; i++) {
buf[i] = playback->lookahead[i];
}
- // Mix the audio stream
+ // Mix the audio stream.
unsigned int mixed_frames = playback->stream_playback->mix(&buf[LOOKAHEAD_BUFFER_SIZE], playback->pitch_scale.get(), buffer_size);
if (tag_used_audio_streams && playback->stream_playback->is_playing()) {
playback->stream_playback->tag_used_streams();
}
+ // Check to see if the stream has run out of samples.
if (mixed_frames != buffer_size) {
// We know we have at least the size of our lookahead buffer for fade-out purposes.
@@ -416,42 +424,52 @@ void AudioServer::_mix_step() {
new_state = AudioStreamPlaybackListNode::AWAITING_DELETION;
playback->state.store(new_state);
} else {
- // Move the last little bit of what we just mixed into our lookahead buffer.
+ // Move the last little bit of what we just mixed into our lookahead buffer for the next call to _mix_step.
for (int i = 0; i < LOOKAHEAD_BUFFER_SIZE; i++) {
playback->lookahead[i] = buf[buffer_size + i];
}
}
- AudioStreamPlaybackBusDetails *ptr = playback->bus_details.load();
- ERR_FAIL_NULL(ptr);
- // By putting null into the bus details pointers, we're taking ownership of their memory for the duration of this mix.
- AudioStreamPlaybackBusDetails bus_details = *ptr;
+ // Get the bus details for this playback. This contains information about which buses the playback is assigned to and the volume of the playback on each bus.
+ AudioStreamPlaybackBusDetails *bus_details_ptr = playback->bus_details.load();
+ ERR_FAIL_NULL(bus_details_ptr);
+ // Make a copy of the bus details so we can modify it without worrying about other threads.
+ AudioStreamPlaybackBusDetails bus_details = *bus_details_ptr;
// Mix to any active buses.
for (int idx = 0; idx < MAX_BUSES_PER_PLAYBACK; idx++) {
if (!bus_details.bus_active[idx]) {
continue;
}
+ // This is the AudioServer-internal index of the bus we're mixing to in this step of the loop. Not to be confused with `idx` which is an index into `AudioStreamPlaybackBusDetails` member var arrays.
int bus_idx = thread_find_bus_index(bus_details.bus[idx]);
+ // It's important to know whether or not this bus was active in the previous mix step of this stream. If it was, we need to perform volume interpolation to avoid pops.
int prev_bus_idx = -1;
for (int search_idx = 0; search_idx < MAX_BUSES_PER_PLAYBACK; search_idx++) {
if (!playback->prev_bus_details->bus_active[search_idx]) {
continue;
}
+ // If the StringNames of the buses match, we've found the previous bus index. This indicates that this playback mixed to `prev_bus_details->bus[prev_bus_index]` in the previous mix step, which gives us a way to look up the playback's previous volume.
if (playback->prev_bus_details->bus[search_idx].hash() == bus_details.bus[idx].hash()) {
prev_bus_idx = search_idx;
+ break;
}
}
+ // It's now time to mix to the bus. We do this by going through each channel of the bus and mixing to it.
+ // The channels correspond to output channels of the audio device, e.g. stereo or 5.1. To reduce needless nesting, this is done with a helper method named `_mix_step_for_channel`.
for (int channel_idx = 0; channel_idx < channel_count; channel_idx++) {
AudioFrame *channel_buf = thread_get_channel_mix_buffer(bus_idx, channel_idx);
+ // TODO: This `fading_out` check could be replaced with with an exponential fadeout of the samples from the lookahead buffer for more punchy results.
if (fading_out) {
bus_details.volume[idx][channel_idx] = AudioFrame(0, 0);
}
AudioFrame channel_vol = bus_details.volume[idx][channel_idx];
- AudioFrame prev_channel_vol = AudioFrame(0, 0);
+ // If this bus was not active in the previous mix step, we want to start playback at the full volume to avoid crushing transients.
+ AudioFrame prev_channel_vol = channel_vol;
+ // If this bus was active in the previous mix step, we need to interpolate between the previous volume and the current volume to avoid pops. Set `prev_channel_volume` accordingly.
if (prev_bus_idx != -1) {
prev_channel_vol = playback->prev_bus_details->volume[prev_bus_idx][channel_idx];
}
@@ -480,7 +498,7 @@ void AudioServer::_mix_step() {
for (int channel_idx = 0; channel_idx < channel_count; channel_idx++) {
AudioFrame *channel_buf = thread_get_channel_mix_buffer(bus_idx, channel_idx);
AudioFrame prev_channel_vol = playback->prev_bus_details->volume[idx][channel_idx];
- // Fade out to silence
+ // Fade out to silence. This could be replaced with an exponential fadeout of the samples from the lookahead buffer for more punchy results.
_mix_step_for_channel(channel_buf, buf, prev_channel_vol, AudioFrame(0, 0), playback->attenuation_filter_cutoff_hz.get(), playback->highshelf_gain.get(), &playback->filter_process[channel_idx * 2], &playback->filter_process[channel_idx * 2 + 1]);
}
}
@@ -501,15 +519,12 @@ void AudioServer::_mix_step() {
switch (playback->state.load()) {
case AudioStreamPlaybackListNode::AWAITING_DELETION:
case AudioStreamPlaybackListNode::FADE_OUT_TO_DELETION:
+ // Remove the playback from the list.
_delete_stream_playback_list_node(playback);
break;
case AudioStreamPlaybackListNode::FADE_OUT_TO_PAUSE: {
// Pause the stream.
- AudioStreamPlaybackListNode::PlaybackState old_state, new_state;
- do {
- old_state = playback->state.load();
- new_state = AudioStreamPlaybackListNode::PAUSED;
- } while (!playback->state.compare_exchange_strong(/* expected= */ old_state, new_state));
+ playback->state.store(AudioStreamPlaybackListNode::PAUSED);
} break;
case AudioStreamPlaybackListNode::PLAYING:
case AudioStreamPlaybackListNode::PAUSED:
@@ -518,13 +533,13 @@ void AudioServer::_mix_step() {
}
}
+ // Now that all of the buses have their audio sources mixed into them, we can process the effects and bus sends.
for (int i = buses.size() - 1; i >= 0; i--) {
- //go bus by bus
Bus *bus = buses[i];
for (int k = 0; k < bus->channels.size(); k++) {
if (bus->channels[k].active && !bus->channels[k].used) {
- //buffer was not used, but it's still active, so it must be cleaned
+ // Buffer was not used, but it's still active, so it must be cleaned.
AudioFrame *buf = bus->channels.write[k].buffer.ptrw();
for (uint32_t j = 0; j < buffer_size; j++) {
@@ -533,7 +548,7 @@ void AudioServer::_mix_step() {
}
}
- //process effects
+ // Process effects.
if (!bus->bypass) {
for (int j = 0; j < bus->effects.size(); j++) {
if (!bus->effects[j].enabled) {
@@ -551,7 +566,7 @@ void AudioServer::_mix_step() {
bus->channels.write[k].effect_instances.write[j]->process(bus->channels[k].buffer.ptr(), temp_buffer.write[k].ptrw(), buffer_size);
}
- //swap buffers, so internal buffer always has the right data
+ // Swap buffers, so internal buffer always has the right data.
for (int k = 0; k < bus->channels.size(); k++) {
if (!(buses[i]->channels[k].active || bus->channels[k].effect_instances[j]->process_silence())) {
continue;
@@ -565,17 +580,17 @@ void AudioServer::_mix_step() {
}
}
- //process send
+ // Process send.
Bus *send = nullptr;
if (i > 0) {
- //everything has a send save for master bus
+ // Everything has a send except for the master bus.
if (!bus_map.has(bus->send)) {
send = buses[0];
} else {
send = bus_map[bus->send];
- if (send->index_cache >= bus->index_cache) { //invalid, send to master
+ if (send->index_cache >= bus->index_cache) { // Invalid, send to master.
send = buses[0];
}
}
@@ -603,7 +618,7 @@ void AudioServer::_mix_step() {
}
}
- //apply volume and compute peak
+ // Apply volume and compute peak.
for (uint32_t j = 0; j < buffer_size; j++) {
buf[j] *= volume;
@@ -620,7 +635,7 @@ void AudioServer::_mix_step() {
bus->channels.write[k].peak_volume = AudioFrame(Math::linear_to_db(peak.left + AUDIO_PEAK_OFFSET), Math::linear_to_db(peak.right + AUDIO_PEAK_OFFSET));
if (!bus->channels[k].used) {
- //see if any audio is contained, because channel was not used
+ // See if any audio is contained, because channel was not used.
if (MAX(peak.right, peak.left) > Math::db_to_linear(channel_disable_threshold_db)) {
bus->channels.write[k].last_mix_with_audio = mix_frames;
@@ -631,7 +646,7 @@ void AudioServer::_mix_step() {
}
if (send) {
- //if not master bus, send
+ // If not master bus, send.
AudioFrame *target_buf = thread_get_channel_mix_buffer(send->index_cache, k);
for (uint32_t j = 0; j < buffer_size; j++) {
@@ -646,6 +661,7 @@ void AudioServer::_mix_step() {
}
void AudioServer::_mix_step_for_channel(AudioFrame *p_out_buf, AudioFrame *p_source_buf, AudioFrame p_vol_start, AudioFrame p_vol_final, float p_attenuation_filter_cutoff_hz, float p_highshelf_gain, AudioFilterSW::Processor *p_processor_l, AudioFilterSW::Processor *p_processor_r) {
+ // TODO: In the future it could be nice to replace all of these hardcoded effects with something a bit cleaner and more flexible, but for now this is what we do to support 3D audio players.
if (p_highshelf_gain != 0) {
AudioFilterSW filter;
filter.set_mode(AudioFilterSW::HIGHSHELF);
@@ -665,7 +681,7 @@ void AudioServer::_mix_step_for_channel(AudioFrame *p_out_buf, AudioFrame *p_sou
p_processor_r->update_coeffs(buffer_size);
for (unsigned int frame_idx = 0; frame_idx < buffer_size; frame_idx++) {
- // Make this buffer size invariant if buffer_size ever becomes a project setting.
+ // TODO: Make lerp speed buffer-size-invariant if buffer_size ever becomes a project setting to avoid very small buffer sizes causing pops due to too-fast lerps.
float lerp_param = (float)frame_idx / buffer_size;
AudioFrame vol = p_vol_final * lerp_param + (1 - lerp_param) * p_vol_start;
AudioFrame mixed = vol * p_source_buf[frame_idx];
@@ -676,7 +692,7 @@ void AudioServer::_mix_step_for_channel(AudioFrame *p_out_buf, AudioFrame *p_sou
} else {
for (unsigned int frame_idx = 0; frame_idx < buffer_size; frame_idx++) {
- // Make this buffer size invariant if buffer_size ever becomes a project setting.
+ // TODO: Make lerp speed buffer-size-invariant if buffer_size ever becomes a project setting to avoid very small buffer sizes causing pops due to too-fast lerps.
float lerp_param = (float)frame_idx / buffer_size;
p_out_buf[frame_idx] += (p_vol_final * lerp_param + (1 - lerp_param) * p_vol_start) * p_source_buf[frame_idx];
}
@@ -701,6 +717,7 @@ void AudioServer::_delete_stream_playback(Ref<AudioStreamPlayback> p_playback) {
}
void AudioServer::_delete_stream_playback_list_node(AudioStreamPlaybackListNode *p_playback_node) {
+ // Remove the playback from the list, registering a destructor to be run on the main thread.
playback_list.erase(p_playback_node, [](AudioStreamPlaybackListNode *p) {
delete p->prev_bus_details;
delete p->bus_details.load();
@@ -1471,7 +1488,9 @@ void AudioServer::init_channels_and_buffers() {
void AudioServer::init() {
channel_disable_threshold_db = GLOBAL_DEF_RST("audio/buses/channel_disable_threshold_db", -60.0);
channel_disable_frames = float(GLOBAL_DEF_RST(PropertyInfo(Variant::FLOAT, "audio/buses/channel_disable_time", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater"), 2.0)) * get_mix_rate();
- buffer_size = 512; //hardcoded for now
+ // TODO: Buffer size is hardcoded for now. This would be really nice to have as a project setting because currently it limits audio latency to an absolute minimum of 11ms with default mix rate, but there's some additional work required to make that happen. See TODOs in `_mix_step_for_channel`.
+ // When this becomes a project setting, it should be specified in milliseconds rather than raw sample count, because 512 samples at 192khz is shorter than it is at 48khz, for example.
+ buffer_size = 512;
init_channels_and_buffers();
diff --git a/servers/audio_server.h b/servers/audio_server.h
index 8978ad438e..9a80882dd7 100644
--- a/servers/audio_server.h
+++ b/servers/audio_server.h
@@ -271,6 +271,14 @@ private:
};
struct AudioStreamPlaybackListNode {
+ // The state machine for audio stream playbacks is as follows:
+ // 1. The playback is created and added to the playback list in the playing state.
+ // 2. The playback is (maybe) paused, and the state is set to FADE_OUT_TO_PAUSE.
+ // 2.1. The playback is mixed after being paused, and the audio server thread atomically sets the state to PAUSED after performing a brief fade-out.
+ // 3. The playback is (maybe) deleted, and the state is set to FADE_OUT_TO_DELETION.
+ // 3.1. The playback is mixed after being deleted, and the audio server thread atomically sets the state to AWAITING_DELETION after performing a brief fade-out.
+ // NOTE: The playback is not deallocated at this time because allocation and deallocation are not realtime-safe.
+ // 4. The playback is removed and deallocated on the main thread using the SafeList maybe_cleanup method.
enum PlaybackState {
PAUSED = 0, // Paused. Keep this stream playback around though so it can be restarted.
PLAYING = 1, // Playing. Fading may still be necessary if volume changes!
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index c4a732fef1..0dcdb90948 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -993,36 +993,43 @@ void RendererCanvasRenderRD::light_update_shadow(RID p_rid, int p_shadow_index,
Vector<Color> cc;
cc.push_back(Color(p_far, p_far, p_far, 1.0));
- for (int i = 0; i < 4; i++) {
- //make sure it remains orthogonal, makes easy to read angle later
+ Projection projection;
+ {
+ real_t fov = 90;
+ real_t nearp = p_near;
+ real_t farp = p_far;
+ real_t aspect = 1.0;
- //light.basis.scale(Vector3(to_light.elements[0].length(),to_light.elements[1].length(),1));
+ real_t ymax = nearp * Math::tan(Math::deg_to_rad(fov * 0.5));
+ real_t ymin = -ymax;
+ real_t xmin = ymin * aspect;
+ real_t xmax = ymax * aspect;
- Rect2i rect((state.shadow_texture_size / 4) * i, p_shadow_index * 2, (state.shadow_texture_size / 4), 2);
- RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(state.shadow_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, cc, 1.0, 0, rect);
+ projection.set_frustum(xmin, xmax, ymin, ymax, nearp, farp);
+ }
- Projection projection;
- {
- real_t fov = 90;
- real_t nearp = p_near;
- real_t farp = p_far;
- real_t aspect = 1.0;
+ // Precomputed:
+ // Vector3 cam_target = Basis::from_euler(Vector3(0, 0, Math_TAU * ((i + 3) / 4.0))).xform(Vector3(0, 1, 0));
+ // projection = projection * Projection(Transform3D().looking_at(cam_targets[i], Vector3(0, 0, -1)).affine_inverse());
+ const Projection projections[4] = {
+ projection * Projection(Vector4(0, 0, -1, 0), Vector4(1, 0, 0, 0), Vector4(0, -1, 0, 0), Vector4(0, 0, 0, 1)),
- real_t ymax = nearp * Math::tan(Math::deg_to_rad(fov * 0.5));
- real_t ymin = -ymax;
- real_t xmin = ymin * aspect;
- real_t xmax = ymax * aspect;
+ projection * Projection(Vector4(-1, 0, 0, 0), Vector4(0, 0, -1, 0), Vector4(0, -1, 0, 0), Vector4(0, 0, 0, 1)),
- projection.set_frustum(xmin, xmax, ymin, ymax, nearp, farp);
- }
+ projection * Projection(Vector4(0, 0, 1, 0), Vector4(-1, 0, 0, 0), Vector4(0, -1, 0, 0), Vector4(0, 0, 0, 1)),
+
+ projection * Projection(Vector4(1, 0, 0, 0), Vector4(0, 0, 1, 0), Vector4(0, -1, 0, 0), Vector4(0, 0, 0, 1))
+
+ };
- Vector3 cam_target = Basis::from_euler(Vector3(0, 0, Math_TAU * ((i + 3) / 4.0))).xform(Vector3(0, 1, 0));
- projection = projection * Projection(Transform3D().looking_at(cam_target, Vector3(0, 0, -1)).affine_inverse());
+ for (int i = 0; i < 4; i++) {
+ Rect2i rect((state.shadow_texture_size / 4) * i, p_shadow_index * 2, (state.shadow_texture_size / 4), 2);
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(state.shadow_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, cc, 1.0, 0, rect);
ShadowRenderPushConstant push_constant;
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
- push_constant.projection[y * 4 + x] = projection.columns[y][x];
+ push_constant.projection[y * 4 + x] = projections[i].columns[y][x];
}
}
static const Vector2 directions[4] = { Vector2(1, 0), Vector2(0, 1), Vector2(-1, 0), Vector2(0, -1) };
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 7fc4897935..b473d9e693 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -99,9 +99,14 @@ Files extracted from upstream source:
## certs
- Upstream: Mozilla, via https://github.com/bagder/ca-bundle
-- Version: git (c5a419971b1bec220368c619aaafd0b818aa119f, 2024)
+- Version: git (4d3fe6683f651d96be1bbef316b201e9b33b274d, 2024),
+ generated from mozilla-release changeset b8ea2342548b8571e58f9176d9555ccdb5ec199f
- License: MPL 2.0
+Files extracted from upstream source:
+
+- `ca-bundle.crt` renamed to `ca-certificates.crt`
+
## clipper2
diff --git a/thirdparty/certs/ca-certificates.crt b/thirdparty/certs/ca-certificates.crt
index f437587091..c559ab795b 100644
--- a/thirdparty/certs/ca-certificates.crt
+++ b/thirdparty/certs/ca-certificates.crt
@@ -1,7 +1,9 @@
##
## Bundle of CA Root Certificates
##
-## Certificate data from Mozilla as of: Mon Mar 11 15:15:21 2024 GMT
+## Certificate data from Mozilla as of: Sat Oct 19 21:26:09 2024 GMT
+##
+## Find updated versions here: https://curl.se/docs/caextract.html
##
## This is a bundle of X.509 certificates of public Certificate Authorities
## (CA). These were automatically extracted from Mozilla's root certificates
@@ -14,7 +16,7 @@
## Just configure this file as the SSLCACertificateFile.
##
## Conversion done with mk-ca-bundle.pl version 1.29.
-## SHA256: 4d96bd539f4719e9ace493757afbe4a23ee8579de1c97fbebc50bba3c12e8c1e
+## SHA256: 36105b01631f9fc03b1eca779b44a30a1a5890b9bf8dc07ccb001a07301e01cf
##
@@ -2600,36 +2602,6 @@ vLtoURMMA/cVi4RguYv/Uo7njLwcAjA8+RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+
CAezNIm8BZ/3Hobui3A=
-----END CERTIFICATE-----
-GLOBALTRUST 2020
-================
------BEGIN CERTIFICATE-----
-MIIFgjCCA2qgAwIBAgILWku9WvtPilv6ZeUwDQYJKoZIhvcNAQELBQAwTTELMAkGA1UEBhMCQVQx
-IzAhBgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVT
-VCAyMDIwMB4XDTIwMDIxMDAwMDAwMFoXDTQwMDYxMDAwMDAwMFowTTELMAkGA1UEBhMCQVQxIzAh
-BgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVTVCAy
-MDIwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAri5WrRsc7/aVj6B3GyvTY4+ETUWi
-D59bRatZe1E0+eyLinjF3WuvvcTfk0Uev5E4C64OFudBc/jbu9G4UeDLgztzOG53ig9ZYybNpyrO
-VPu44sB8R85gfD+yc/LAGbaKkoc1DZAoouQVBGM+uq/ufF7MpotQsjj3QWPKzv9pj2gOlTblzLmM
-CcpL3TGQlsjMH/1WljTbjhzqLL6FLmPdqqmV0/0plRPwyJiT2S0WR5ARg6I6IqIoV6Lr/sCMKKCm
-fecqQjuCgGOlYx8ZzHyyZqjC0203b+J+BlHZRYQfEs4kUmSFC0iAToexIiIwquuuvuAC4EDosEKA
-A1GqtH6qRNdDYfOiaxaJSaSjpCuKAsR49GiKweR6NrFvG5Ybd0mN1MkGco/PU+PcF4UgStyYJ9OR
-JitHHmkHr96i5OTUawuzXnzUJIBHKWk7buis/UDr2O1xcSvy6Fgd60GXIsUf1DnQJ4+H4xj04KlG
-DfV0OoIu0G4skaMxXDtG6nsEEFZegB31pWXogvziB4xiRfUg3kZwhqG8k9MedKZssCz3AwyIDMvU
-clOGvGBG85hqwvG/Q/lwIHfKN0F5VVJjjVsSn8VoxIidrPIwq7ejMZdnrY8XD2zHc+0klGvIg5rQ
-mjdJBKuxFshsSUktq6HQjJLyQUp5ISXbY9e2nKd+Qmn7OmMCAwEAAaNjMGEwDwYDVR0TAQH/BAUw
-AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFNwuH9FhN3nkq9XVsxJxaD1qaJwiMB8GA1Ud
-IwQYMBaAFNwuH9FhN3nkq9XVsxJxaD1qaJwiMA0GCSqGSIb3DQEBCwUAA4ICAQCR8EICaEDuw2jA
-VC/f7GLDw56KoDEoqoOOpFaWEhCGVrqXctJUMHytGdUdaG/7FELYjQ7ztdGl4wJCXtzoRlgHNQIw
-4Lx0SsFDKv/bGtCwr2zD/cuz9X9tAy5ZVp0tLTWMstZDFyySCstd6IwPS3BD0IL/qMy/pJTAvoe9
-iuOTe8aPmxadJ2W8esVCgmxcB9CpwYhgROmYhRZf+I/KARDOJcP5YBugxZfD0yyIMaK9MOzQ0MAS
-8cE54+X1+NZK3TTN+2/BT+MAi1bikvcoskJ3ciNnxz8RFbLEAwW+uxF7Cr+obuf/WEPPm2eggAe2
-HcqtbepBEX4tdJP7wry+UUTF72glJ4DjyKDUEuzZpTcdN3y0kcra1LGWge9oXHYQSa9+pTeAsRxS
-vTOBTI/53WXZFM2KJVj04sWDpQmQ1GwUY7VA3+vA/MRYfg0UFodUJ25W5HCEuGwyEn6CMUO+1918
-oa2u1qsgEu8KwxCMSZY13At1XrFP1U80DhEgB3VDRemjEdqso5nCtnkn4rnvyOL2NSl6dPrFf4IF
-YqYK6miyeUcGbvJXqBUzxvd4Sj1Ce2t+/vdG6tHrju+IaFvowdlxfv1k7/9nR4hYJS8+hge9+6jl
-gqispdNpQ80xiEmEU5LAsTkbOYMBMMTyqfrQA71yN2BWHzZ8vTmR9W0Nv3vXkg==
------END CERTIFICATE-----
-
ANF Secure Server Root CA
=========================
-----BEGIN CERTIFICATE-----
@@ -3579,3 +3551,116 @@ wPfc5+pbrrLMtTWGS9DiP7bY+A4A7l3j941Y/8+LN+ljX273CXE2whJdV/LItM3z7gLfEdxquVeE
HVlNjM7IDiPCtyaaEBRx/pOyiriA8A4QntOoUAw3gi/q4Iqd4Sw5/7W0cwDk90imc6y/st53BIe0
o82bNSQ3+pCTE4FCxpgmdTdmQRCsu/WU48IxK63nI1bMNSWSs1A=
-----END CERTIFICATE-----
+
+FIRMAPROFESIONAL CA ROOT-A WEB
+==============================
+-----BEGIN CERTIFICATE-----
+MIICejCCAgCgAwIBAgIQMZch7a+JQn81QYehZ1ZMbTAKBggqhkjOPQQDAzBuMQswCQYDVQQGEwJF
+UzEcMBoGA1UECgwTRmlybWFwcm9mZXNpb25hbCBTQTEYMBYGA1UEYQwPVkFURVMtQTYyNjM0MDY4
+MScwJQYDVQQDDB5GSVJNQVBST0ZFU0lPTkFMIENBIFJPT1QtQSBXRUIwHhcNMjIwNDA2MDkwMTM2
+WhcNNDcwMzMxMDkwMTM2WjBuMQswCQYDVQQGEwJFUzEcMBoGA1UECgwTRmlybWFwcm9mZXNpb25h
+bCBTQTEYMBYGA1UEYQwPVkFURVMtQTYyNjM0MDY4MScwJQYDVQQDDB5GSVJNQVBST0ZFU0lPTkFM
+IENBIFJPT1QtQSBXRUIwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARHU+osEaR3xyrq89Zfe9MEkVz6
+iMYiuYMQYneEMy3pA4jU4DP37XcsSmDq5G+tbbT4TIqk5B/K6k84Si6CcyvHZpsKjECcfIr28jlg
+st7L7Ljkb+qbXbdTkBgyVcUgt5SjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUk+FD
+Y1w8ndYn81LsF7Kpryz3dvgwHQYDVR0OBBYEFJPhQ2NcPJ3WJ/NS7Beyqa8s93b4MA4GA1UdDwEB
+/wQEAwIBBjAKBggqhkjOPQQDAwNoADBlAjAdfKR7w4l1M+E7qUW/Runpod3JIha3RxEL2Jq68cgL
+cFBTApFwhVmpHqTm6iMxoAACMQD94vizrxa5HnPEluPBMBnYfubDl94cT7iJLzPrSA8Z94dGXSaQ
+pYXFuXqUPoeovQA=
+-----END CERTIFICATE-----
+
+TWCA CYBER Root CA
+==================
+-----BEGIN CERTIFICATE-----
+MIIFjTCCA3WgAwIBAgIQQAE0jMIAAAAAAAAAATzyxjANBgkqhkiG9w0BAQwFADBQMQswCQYDVQQG
+EwJUVzESMBAGA1UEChMJVEFJV0FOLUNBMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJUV0NB
+IENZQkVSIFJvb3QgQ0EwHhcNMjIxMTIyMDY1NDI5WhcNNDcxMTIyMTU1OTU5WjBQMQswCQYDVQQG
+EwJUVzESMBAGA1UEChMJVEFJV0FOLUNBMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJUV0NB
+IENZQkVSIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDG+Moe2Qkgfh1s
+Ts6P40czRJzHyWmqOlt47nDSkvgEs1JSHWdyKKHfi12VCv7qze33Kc7wb3+szT3vsxxFavcokPFh
+V8UMxKNQXd7UtcsZyoC5dc4pztKFIuwCY8xEMCDa6pFbVuYdHNWdZsc/34bKS1PE2Y2yHer43CdT
+o0fhYcx9tbD47nORxc5zb87uEB8aBs/pJ2DFTxnk684iJkXXYJndzk834H/nY62wuFm40AZoNWDT
+Nq5xQwTxaWV4fPMf88oon1oglWa0zbfuj3ikRRjpJi+NmykosaS3Om251Bw4ckVYsV7r8Cibt4LK
+/c/WMw+f+5eesRycnupfXtuq3VTpMCEobY5583WSjCb+3MX2w7DfRFlDo7YDKPYIMKoNM+HvnKkH
+IuNZW0CP2oi3aQiotyMuRAlZN1vH4xfyIutuOVLF3lSnmMlLIJXcRolftBL5hSmO68gnFSDAS9TM
+fAxsNAwmmyYxpjyn9tnQS6Jk/zuZQXLB4HCX8SS7K8R0IrGsayIyJNN4KsDAoS/xUgXJP+92ZuJF
+2A09rZXIx4kmyA+upwMu+8Ff+iDhcK2wZSA3M2Cw1a/XDBzCkHDXShi8fgGwsOsVHkQGzaRP6AzR
+wyAQ4VRlnrZR0Bp2a0JaWHY06rc3Ga4udfmW5cFZ95RXKSWNOkyrTZpB0F8mAwIDAQABo2MwYTAO
+BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBSdhWEUfMFib5do5E83
+QOGt4A1WNzAdBgNVHQ4EFgQUnYVhFHzBYm+XaORPN0DhreANVjcwDQYJKoZIhvcNAQEMBQADggIB
+AGSPesRiDrWIzLjHhg6hShbNcAu3p4ULs3a2D6f/CIsLJc+o1IN1KriWiLb73y0ttGlTITVX1olN
+c79pj3CjYcya2x6a4CD4bLubIp1dhDGaLIrdaqHXKGnK/nZVekZn68xDiBaiA9a5F/gZbG0jAn/x
+X9AKKSM70aoK7akXJlQKTcKlTfjF/biBzysseKNnTKkHmvPfXvt89YnNdJdhEGoHK4Fa0o635yDR
+IG4kqIQnoVesqlVYL9zZyvpoBJ7tRCT5dEA7IzOrg1oYJkK2bVS1FmAwbLGg+LhBoF1JSdJlBTrq
+/p1hvIbZv97Tujqxf36SNI7JAG7cmL3c7IAFrQI932XtCwP39xaEBDG6k5TY8hL4iuO/Qq+n1M0R
+FxbIQh0UqEL20kCGoE8jypZFVmAGzbdVAaYBlGX+bgUJurSkquLvWL69J1bY73NxW0Qz8ppy6rBe
+Pm6pUlvscG21h483XjyMnM7k8M4MZ0HMzvaAq07MTFb1wWFZk7Q+ptq4NxKfKjLji7gh7MMrZQzv
+It6IKTtM1/r+t+FHvpw+PoP7UV31aPcuIYXcv/Fa4nzXxeSDwWrruoBa3lwtcHb4yOWHh8qgnaHl
+IhInD0Q9HWzq1MKLL295q39QpsQZp6F6t5b5wR9iWqJDB0BeJsas7a5wFsWqynKKTbDPAYsDP27X
+-----END CERTIFICATE-----
+
+SecureSign Root CA12
+====================
+-----BEGIN CERTIFICATE-----
+MIIDcjCCAlqgAwIBAgIUZvnHwa/swlG07VOX5uaCwysckBYwDQYJKoZIhvcNAQELBQAwUTELMAkG
+A1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28uLCBMdGQuMR0wGwYDVQQDExRT
+ZWN1cmVTaWduIFJvb3QgQ0ExMjAeFw0yMDA0MDgwNTM2NDZaFw00MDA0MDgwNTM2NDZaMFExCzAJ
+BgNVBAYTAkpQMSMwIQYDVQQKExpDeWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMU
+U2VjdXJlU2lnbiBSb290IENBMTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6OcE3
+emhFKxS06+QT61d1I02PJC0W6K6OyX2kVzsqdiUzg2zqMoqUm048luT9Ub+ZyZN+v/mtp7JIKwcc
+J/VMvHASd6SFVLX9kHrko+RRWAPNEHl57muTH2SOa2SroxPjcf59q5zdJ1M3s6oYwlkm7Fsf0uZl
+fO+TvdhYXAvA42VvPMfKWeP+bl+sg779XSVOKik71gurFzJ4pOE+lEa+Ym6b3kaosRbnhW70CEBF
+EaCeVESE99g2zvVQR9wsMJvuwPWW0v4JhscGWa5Pro4RmHvzC1KqYiaqId+OJTN5lxZJjfU+1Uef
+NzFJM3IFTQy2VYzxV4+Kh9GtxRESOaCtAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P
+AQH/BAQDAgEGMB0GA1UdDgQWBBRXNPN0zwRL1SXm8UC2LEzZLemgrTANBgkqhkiG9w0BAQsFAAOC
+AQEAPrvbFxbS8hQBICw4g0utvsqFepq2m2um4fylOqyttCg6r9cBg0krY6LdmmQOmFxv3Y67ilQi
+LUoT865AQ9tPkbeGGuwAtEGBpE/6aouIs3YIcipJQMPTw4WJmBClnW8Zt7vPemVV2zfrPIpyMpce
+mik+rY3moxtt9XUa5rBouVui7mlHJzWhhpmA8zNL4WukJsPvdFlseqJkth5Ew1DgDzk9qTPxpfPS
+vWKErI4cqc1avTc7bgoitPQV55FYxTpE05Uo2cBl6XLK0A+9H7MV2anjpEcJnuDLN/v9vZfVvhga
+aaI5gdka9at/yOPiZwud9AzqVN/Ssq+xIvEg37xEHA==
+-----END CERTIFICATE-----
+
+SecureSign Root CA14
+====================
+-----BEGIN CERTIFICATE-----
+MIIFcjCCA1qgAwIBAgIUZNtaDCBO6Ncpd8hQJ6JaJ90t8sswDQYJKoZIhvcNAQEMBQAwUTELMAkG
+A1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28uLCBMdGQuMR0wGwYDVQQDExRT
+ZWN1cmVTaWduIFJvb3QgQ0ExNDAeFw0yMDA0MDgwNzA2MTlaFw00NTA0MDgwNzA2MTlaMFExCzAJ
+BgNVBAYTAkpQMSMwIQYDVQQKExpDeWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMU
+U2VjdXJlU2lnbiBSb290IENBMTQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDF0nqh
+1oq/FjHQmNE6lPxauG4iwWL3pwon71D2LrGeaBLwbCRjOfHw3xDG3rdSINVSW0KZnvOgvlIfX8xn
+bacuUKLBl422+JX1sLrcneC+y9/3OPJH9aaakpUqYllQC6KxNedlsmGy6pJxaeQp8E+BgQQ8sqVb
+1MWoWWd7VRxJq3qdwudzTe/NCcLEVxLbAQ4jeQkHO6Lo/IrPj8BGJJw4J+CDnRugv3gVEOuGTgpa
+/d/aLIJ+7sr2KeH6caH3iGicnPCNvg9JkdjqOvn90Ghx2+m1K06Ckm9mH+Dw3EzsytHqunQG+bOE
+kJTRX45zGRBdAuVwpcAQ0BB8b8VYSbSwbprafZX1zNoCr7gsfXmPvkPx+SgojQlD+Ajda8iLLCSx
+jVIHvXiby8posqTdDEx5YMaZ0ZPxMBoH064iwurO8YQJzOAUbn8/ftKChazcqRZOhaBgy/ac18iz
+ju3Gm5h1DVXoX+WViwKkrkMpKBGk5hIwAUt1ax5mnXkvpXYvHUC0bcl9eQjs0Wq2XSqypWa9a4X0
+dFbD9ed1Uigspf9mR6XU/v6eVL9lfgHWMI+lNpyiUBzuOIABSMbHdPTGrMNASRZhdCyvjG817XsY
+AFs2PJxQDcqSMxDxJklt33UkN4Ii1+iW/RVLApY+B3KVfqs9TC7XyvDf4Fg/LS8EmjijAQIDAQAB
+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUBpOjCl4oaTeq
+YR3r6/wtbyPk86AwDQYJKoZIhvcNAQEMBQADggIBAJaAcgkGfpzMkwQWu6A6jZJOtxEaCnFxEM0E
+rX+lRVAQZk5KQaID2RFPeje5S+LGjzJmdSX7684/AykmjbgWHfYfM25I5uj4V7Ibed87hwriZLoA
+ymzvftAj63iP/2SbNDefNWWipAA9EiOWWF3KY4fGoweITedpdopTzfFP7ELyk+OZpDc8h7hi2/Ds
+Hzc/N19DzFGdtfCXwreFamgLRB7lUe6TzktuhsHSDCRZNhqfLJGP4xjblJUK7ZGqDpncllPjYYPG
+FrojutzdfhrGe0K22VoF3Jpf1d+42kd92jjbrDnVHmtsKheMYc2xbXIBw8MgAGJoFjHVdqqGuw6q
+nsb58Nn4DSEC5MUoFlkRudlpcyqSeLiSV5sI8jrlL5WwWLdrIBRtFO8KvH7YVdiI2i/6GaX7i+B/
+OfVyK4XELKzvGUWSTLNhB9xNH27SgRNcmvMSZ4PPmz+Ln52kuaiWA3rF7iDeM9ovnhp6dB7h7sxa
+OgTdsxoEqBRjrLdHEoOabPXm6RUVkRqEGQ6UROcSjiVbgGcZ3GOTEAtlLor6CZpO2oYofaphNdgO
+pygau1LgePhsumywbrmHXumZNTfxPWQrqaA0k89jL9WB365jJ6UeTo3cKXhZ+PmhIIynJkBugnLN
+eLLIjzwec+fBH7/PzqUqm9tEZDKgu39cJRNItX+S
+-----END CERTIFICATE-----
+
+SecureSign Root CA15
+====================
+-----BEGIN CERTIFICATE-----
+MIICIzCCAamgAwIBAgIUFhXHw9hJp75pDIqI7fBw+d23PocwCgYIKoZIzj0EAwMwUTELMAkGA1UE
+BhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28uLCBMdGQuMR0wGwYDVQQDExRTZWN1
+cmVTaWduIFJvb3QgQ0ExNTAeFw0yMDA0MDgwODMyNTZaFw00NTA0MDgwODMyNTZaMFExCzAJBgNV
+BAYTAkpQMSMwIQYDVQQKExpDeWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMUU2Vj
+dXJlU2lnbiBSb290IENBMTUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQLUHSNZDKZmbPSYAi4Io5G
+dCx4wCtELW1fHcmuS1Iggz24FG1Th2CeX2yF2wYUleDHKP+dX+Sq8bOLbe1PL0vJSpSRZHX+AezB
+2Ot6lHhWGENfa4HL9rzatAy2KZMIaY+jQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD
+AgEGMB0GA1UdDgQWBBTrQciu/NWeUUj1vYv0hyCTQSvT9DAKBggqhkjOPQQDAwNoADBlAjEA2S6J
+fl5OpBEHvVnCB96rMjhTKkZEBhd6zlHp4P9mLQlO4E/0BdGF9jVg3PVys0Z9AjBEmEYagoUeYWmJ
+SwdLZrWeqrqgHkHZAXQ6bkU6iYAZezKYVWOr62Nuk22rGwlgMU4=
+-----END CERTIFICATE-----