summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/actions/godot-cache-restore/action.yml (renamed from .github/actions/godot-cache/action.yml)9
-rw-r--r--.github/actions/godot-cache-save/action.yml17
-rw-r--r--.github/workflows/android_builds.yml10
-rw-r--r--.github/workflows/ios_builds.yml8
-rw-r--r--.github/workflows/linux_builds.yml10
-rw-r--r--.github/workflows/macos_builds.yml10
-rw-r--r--.github/workflows/web_builds.yml10
-rw-r--r--.github/workflows/windows_builds.yml10
-rw-r--r--core/debugger/remote_debugger.cpp3
-rw-r--r--core/extension/gdextension.cpp2
-rw-r--r--core/extension/gdextension_interface.h6
-rw-r--r--core/input/input.cpp16
-rw-r--r--core/input/input.h6
-rw-r--r--core/io/marshalls.cpp18
-rw-r--r--core/object/object.cpp2
-rw-r--r--core/os/os.h2
-rw-r--r--doc/classes/@GlobalScope.xml4
-rw-r--r--doc/classes/AABB.xml2
-rw-r--r--doc/classes/CanvasItem.xml1
-rw-r--r--doc/classes/DisplayServer.xml1
-rw-r--r--doc/classes/EditorSettings.xml10
-rw-r--r--doc/classes/LightmapGI.xml6
-rw-r--r--doc/classes/Node.xml2
-rw-r--r--doc/classes/Parallax2D.xml1
-rw-r--r--doc/classes/ProjectSettings.xml6
-rw-r--r--doc/classes/RenderingDevice.xml8
-rw-r--r--doc/classes/Resource.xml7
-rw-r--r--doc/classes/Skeleton3D.xml17
-rw-r--r--doc/classes/TileSetScenesCollectionSource.xml26
-rw-r--r--drivers/d3d12/rendering_device_driver_d3d12.cpp33
-rw-r--r--drivers/d3d12/rendering_device_driver_d3d12.h5
-rw-r--r--drivers/gles3/storage/material_storage.cpp2
-rw-r--r--drivers/gles3/storage/texture_storage.cpp4
-rw-r--r--editor/code_editor.cpp11
-rw-r--r--editor/editor_file_system.cpp30
-rw-r--r--editor/editor_inspector.cpp4
-rw-r--r--editor/editor_node.cpp9
-rw-r--r--editor/editor_run_native.cpp4
-rw-r--r--editor/editor_settings.cpp10
-rw-r--r--editor/export/editor_export_platform.cpp16
-rw-r--r--editor/gui/editor_bottom_panel.cpp13
-rw-r--r--editor/gui/editor_spin_slider.cpp4
-rw-r--r--editor/gui/editor_spin_slider.h2
-rw-r--r--editor/import/3d/resource_importer_scene.cpp8
-rw-r--r--editor/import_dock.cpp28
-rw-r--r--editor/plugins/curve_editor_plugin.cpp6
-rw-r--r--editor/plugins/lightmap_gi_editor_plugin.cpp3
-rw-r--r--editor/plugins/script_text_editor.cpp11
-rw-r--r--editor/plugins/tiles/tile_map_layer_editor.cpp3
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp5
-rw-r--r--editor/project_manager.cpp8
-rw-r--r--editor/scene_tree_dock.cpp9
-rw-r--r--editor/themes/editor_theme_manager.cpp2
-rw-r--r--main/main.cpp20
-rw-r--r--main/main.h1
-rw-r--r--misc/extension_api_validation/4.2-stable.expected7
-rw-r--r--modules/enet/doc_classes/ENetConnection.xml12
-rw-r--r--modules/fbx/editor/editor_scene_importer_fbx2gltf.cpp7
-rw-r--r--modules/fbx/editor/editor_scene_importer_ufbx.cpp19
-rw-r--r--modules/gdscript/gdscript_compiler.cpp13
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp4
-rw-r--r--modules/gdscript/gdscript_vm.cpp4
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/simple_setter_chain_call_setter.gd13
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/simple_setter_chain_call_setter.out4
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/single_underscore_node_name.gd15
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/single_underscore_node_name.out1
-rw-r--r--modules/gltf/editor/editor_scene_importer_blend.cpp3
-rw-r--r--modules/gltf/editor/editor_scene_importer_gltf.cpp12
-rw-r--r--modules/interactive_music/audio_stream_interactive.cpp2
-rw-r--r--modules/interactive_music/register_types.cpp6
-rw-r--r--modules/lightmapper_rd/lightmapper_rd.cpp16
-rw-r--r--modules/openxr/editor/openxr_action_map_editor.cpp35
-rw-r--r--platform/android/display_server_android.cpp1
-rw-r--r--platform/android/export/export_plugin.cpp1
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt12
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.kt33
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotLib.java7
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt5
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java14
-rw-r--r--platform/android/java_godot_lib_jni.cpp9
-rw-r--r--platform/android/java_godot_lib_jni.h2
-rw-r--r--platform/ios/export/export_plugin.cpp1
-rw-r--r--platform/linuxbsd/wayland/display_server_wayland.cpp2
-rw-r--r--platform/linuxbsd/wayland/wayland_thread.cpp2
-rw-r--r--platform/web/audio_driver_web.cpp14
-rw-r--r--platform/windows/display_server_windows.cpp214
-rw-r--r--platform/windows/display_server_windows.h64
-rw-r--r--scene/3d/lightmap_gi.cpp6
-rw-r--r--scene/3d/lightmap_gi.h1
-rw-r--r--scene/3d/lightmapper.h5
-rw-r--r--scene/3d/skeleton_3d.cpp10
-rw-r--r--scene/3d/skeleton_3d.h2
-rw-r--r--scene/audio/audio_stream_player_internal.cpp4
-rw-r--r--scene/gui/aspect_ratio_container.cpp2
-rw-r--r--scene/gui/center_container.cpp2
-rw-r--r--scene/gui/code_edit.cpp32
-rw-r--r--scene/gui/code_edit.h1
-rw-r--r--scene/gui/control.cpp2
-rw-r--r--scene/gui/flow_container.cpp2
-rw-r--r--scene/gui/split_container.cpp9
-rw-r--r--scene/gui/split_container.h2
-rw-r--r--scene/gui/text_edit.cpp2
-rw-r--r--scene/main/instance_placeholder.cpp15
-rw-r--r--scene/resources/2d/navigation_polygon.cpp4
-rw-r--r--scene/resources/visual_shader.cpp2
-rw-r--r--servers/audio/audio_stream.h1
-rw-r--r--servers/audio_server.cpp12
-rw-r--r--servers/audio_server.h2
-rw-r--r--servers/physics_server_2d_wrap_mt.h4
-rw-r--r--servers/physics_server_3d_wrap_mt.h4
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp2
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp4
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h1
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp2
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp12
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_inc.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl10
-rw-r--r--servers/rendering/rendering_device.cpp11
-rw-r--r--servers/rendering/shader_language.cpp14
-rw-r--r--thirdparty/README.md2
-rw-r--r--thirdparty/thorvg/inc/config.h2
-rw-r--r--thirdparty/thorvg/inc/thorvg.h9
-rw-r--r--thirdparty/thorvg/src/common/tvgLines.cpp2
-rw-r--r--thirdparty/thorvg/src/common/tvgLock.h11
-rw-r--r--thirdparty/thorvg/src/common/tvgMath.cpp14
-rw-r--r--thirdparty/thorvg/src/common/tvgMath.h3
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp29
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp4
-rw-r--r--thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp4
-rw-r--r--thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp14
-rw-r--r--thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp5
-rw-r--r--thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp103
-rw-r--r--thirdparty/thorvg/src/renderer/tvgPaint.cpp30
-rw-r--r--thirdparty/thorvg/src/renderer/tvgPaint.h3
-rw-r--r--thirdparty/thorvg/src/renderer/tvgPicture.h8
-rw-r--r--thirdparty/thorvg/src/renderer/tvgRender.cpp27
-rw-r--r--thirdparty/thorvg/src/renderer/tvgRender.h23
-rw-r--r--thirdparty/thorvg/src/renderer/tvgShape.h4
-rwxr-xr-xthirdparty/thorvg/update-thorvg.sh2
141 files changed, 1032 insertions, 492 deletions
diff --git a/.github/actions/godot-cache/action.yml b/.github/actions/godot-cache-restore/action.yml
index 13142e7ed1..eb955affef 100644
--- a/.github/actions/godot-cache/action.yml
+++ b/.github/actions/godot-cache-restore/action.yml
@@ -1,5 +1,5 @@
-name: Setup Godot build cache
-description: Setup Godot build cache.
+name: Restore Godot build cache
+description: Restore Godot build cache.
inputs:
cache-name:
description: The cache base name (job name by default).
@@ -10,9 +10,8 @@ inputs:
runs:
using: "composite"
steps:
- # Upload cache on completion and check it out now.
- - name: Load SCons cache directory
- uses: actions/cache@v4
+ - name: Restore SCons cache directory
+ uses: actions/cache/restore@v4
with:
path: ${{inputs.scons-cache}}
key: ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
diff --git a/.github/actions/godot-cache-save/action.yml b/.github/actions/godot-cache-save/action.yml
new file mode 100644
index 0000000000..b7cbf91f94
--- /dev/null
+++ b/.github/actions/godot-cache-save/action.yml
@@ -0,0 +1,17 @@
+name: Save Godot build cache
+description: Save Godot build cache.
+inputs:
+ cache-name:
+ description: The cache base name (job name by default).
+ default: "${{github.job}}"
+ scons-cache:
+ description: The SCons cache path.
+ default: "${{github.workspace}}/.scons-cache/"
+runs:
+ using: "composite"
+ steps:
+ - name: Save SCons cache directory
+ uses: actions/cache/save@v4
+ with:
+ path: ${{inputs.scons-cache}}
+ key: ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
diff --git a/.github/workflows/android_builds.yml b/.github/workflows/android_builds.yml
index f99a31179e..ee75d53282 100644
--- a/.github/workflows/android_builds.yml
+++ b/.github/workflows/android_builds.yml
@@ -49,8 +49,8 @@ jobs:
distribution: temurin
java-version: 17
- - name: Setup Godot build cache
- uses: ./.github/actions/godot-cache
+ - name: Restore Godot build cache
+ uses: ./.github/actions/godot-cache-restore
with:
cache-name: ${{ matrix.cache-name }}
continue-on-error: true
@@ -66,6 +66,12 @@ jobs:
target: ${{ matrix.target }}
tests: ${{ matrix.tests }}
+ - name: Save Godot build cache
+ uses: ./.github/actions/godot-cache-save
+ with:
+ cache-name: ${{ matrix.cache-name }}
+ continue-on-error: true
+
- name: Generate Godot templates
if: matrix.target == 'template_release'
run: |
diff --git a/.github/workflows/ios_builds.yml b/.github/workflows/ios_builds.yml
index 0546f43acc..8da6d1311e 100644
--- a/.github/workflows/ios_builds.yml
+++ b/.github/workflows/ios_builds.yml
@@ -22,8 +22,8 @@ jobs:
with:
submodules: recursive
- - name: Setup Godot build cache
- uses: ./.github/actions/godot-cache
+ - name: Restore Godot build cache
+ uses: ./.github/actions/godot-cache-restore
continue-on-error: true
- name: Setup Python and SCons
@@ -37,5 +37,9 @@ jobs:
target: template_release
tests: false
+ - name: Save Godot build cache
+ uses: ./.github/actions/godot-cache-save
+ continue-on-error: true
+
- name: Upload artifact
uses: ./.github/actions/upload-artifact
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml
index 6b98256110..8eb6e3afd9 100644
--- a/.github/workflows/linux_builds.yml
+++ b/.github/workflows/linux_builds.yml
@@ -111,8 +111,8 @@ jobs:
sudo rm -rf /usr/local/lib/android
echo "Disk usage after:" && df -h
- - name: Setup Godot build cache
- uses: ./.github/actions/godot-cache
+ - name: Restore Godot build cache
+ uses: ./.github/actions/godot-cache-restore
with:
cache-name: ${{ matrix.cache-name }}
continue-on-error: true
@@ -140,6 +140,12 @@ jobs:
target: ${{ matrix.target }}
tests: ${{ matrix.tests }}
+ - name: Save Godot build cache
+ uses: ./.github/actions/godot-cache-save
+ with:
+ cache-name: ${{ matrix.cache-name }}
+ continue-on-error: true
+
- name: Generate C# glue
if: ${{ matrix.build-mono }}
run: |
diff --git a/.github/workflows/macos_builds.yml b/.github/workflows/macos_builds.yml
index badcb688d1..f564a496e2 100644
--- a/.github/workflows/macos_builds.yml
+++ b/.github/workflows/macos_builds.yml
@@ -37,8 +37,8 @@ jobs:
with:
submodules: recursive
- - name: Setup Godot build cache
- uses: ./.github/actions/godot-cache
+ - name: Restore Godot build cache
+ uses: ./.github/actions/godot-cache-restore
with:
cache-name: ${{ matrix.cache-name }}
continue-on-error: true
@@ -66,6 +66,12 @@ jobs:
target: ${{ matrix.target }}
tests: ${{ matrix.tests }}
+ - name: Save Godot build cache
+ uses: ./.github/actions/godot-cache-save
+ with:
+ cache-name: ${{ matrix.cache-name }}
+ continue-on-error: true
+
- name: Prepare artifact
run: |
lipo -create ./bin/godot.macos.${{ matrix.target }}.x86_64 ./bin/godot.macos.${{ matrix.target }}.arm64 -output ./bin/godot.macos.${{ matrix.target }}.universal
diff --git a/.github/workflows/web_builds.yml b/.github/workflows/web_builds.yml
index 1eb7b901cd..de8339bf1b 100644
--- a/.github/workflows/web_builds.yml
+++ b/.github/workflows/web_builds.yml
@@ -52,8 +52,8 @@ jobs:
run: |
emcc -v
- - name: Setup Godot build cache
- uses: ./.github/actions/godot-cache
+ - name: Restore Godot build cache
+ uses: ./.github/actions/godot-cache-restore
with:
cache-name: ${{ matrix.cache-name }}
continue-on-error: true
@@ -69,6 +69,12 @@ jobs:
target: ${{ matrix.target }}
tests: ${{ matrix.tests }}
+ - name: Save Godot build cache
+ uses: ./.github/actions/godot-cache-save
+ with:
+ cache-name: ${{ matrix.cache-name }}
+ continue-on-error: true
+
- name: Upload artifact
uses: ./.github/actions/upload-artifact
if: ${{ matrix.artifact }}
diff --git a/.github/workflows/windows_builds.yml b/.github/workflows/windows_builds.yml
index 7a3347d49c..8c93179448 100644
--- a/.github/workflows/windows_builds.yml
+++ b/.github/workflows/windows_builds.yml
@@ -42,8 +42,8 @@ jobs:
with:
submodules: recursive
- - name: Setup Godot build cache
- uses: ./.github/actions/godot-cache
+ - name: Restore Godot build cache
+ uses: ./.github/actions/godot-cache-restore
with:
cache-name: ${{ matrix.cache-name }}
continue-on-error: true
@@ -76,6 +76,12 @@ jobs:
target: ${{ matrix.target }}
tests: ${{ matrix.tests }}
+ - name: Save Godot build cache
+ uses: ./.github/actions/godot-cache-save
+ with:
+ cache-name: ${{ matrix.cache-name }}
+ continue-on-error: true
+
- name: Prepare artifact
run: |
Remove-Item bin/* -Include *.exp,*.lib,*.pdb -Force
diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp
index bd30da3047..e2ed7245a2 100644
--- a/core/debugger/remote_debugger.cpp
+++ b/core/debugger/remote_debugger.cpp
@@ -39,6 +39,7 @@
#include "core/io/resource_loader.h"
#include "core/object/script_language.h"
#include "core/os/os.h"
+#include "servers/display_server.h"
class RemoteDebugger::PerformanceProfiler : public EngineProfiler {
Object *performance = nullptr;
@@ -539,7 +540,7 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
OS::get_singleton()->delay_usec(10000);
if (Thread::get_caller_id() == Thread::get_main_id()) {
// If this is a busy loop on the main thread, events still need to be processed.
- OS::get_singleton()->process_and_drop_events();
+ DisplayServer::get_singleton()->force_process_and_drop_events();
}
}
}
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp
index 47628e4ea0..8e2366fc95 100644
--- a/core/extension/gdextension.cpp
+++ b/core/extension/gdextension.cpp
@@ -795,7 +795,7 @@ Error GDExtension::open_library(const String &p_path, const String &p_entry_symb
// because that's what we want to check to see if it's changed.
library_path = actual_lib_path.get_base_dir().path_join(p_path.get_file());
} else {
- library_path = p_path;
+ library_path = actual_lib_path;
}
ERR_FAIL_COND_V_MSG(err == ERR_FILE_NOT_FOUND, err, "GDExtension dynamic library not found: " + abs_path);
diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h
index d6c1df9c00..fce377f967 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/gdextension_interface.h
@@ -2800,12 +2800,16 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClassVirtualMethod)(G
*
* Registers an integer constant on an extension class in the ClassDB.
*
+ * Note about registering bitfield values (if p_is_bitfield is true): even though p_constant_value is signed, language bindings are
+ * advised to treat bitfields as uint64_t, since this is generally clearer and can prevent mistakes like using -1 for setting all bits.
+ * Language APIs should thus provide an abstraction that registers bitfields (uint64_t) separately from regular constants (int64_t).
+ *
* @param p_library A pointer the library received by the GDExtension's entry point function.
* @param p_class_name A pointer to a StringName with the class name.
* @param p_enum_name A pointer to a StringName with the enum name.
* @param p_constant_name A pointer to a StringName with the constant name.
* @param p_constant_value The constant value.
- * @param p_is_bitfield Whether or not this is a bit field.
+ * @param p_is_bitfield Whether or not this constant is part of a bitfield.
*/
typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClassIntegerConstant)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_enum_name, GDExtensionConstStringNamePtr p_constant_name, GDExtensionInt p_constant_value, GDExtensionBool p_is_bitfield);
diff --git a/core/input/input.cpp b/core/input/input.cpp
index ec0303df06..91378591b0 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -890,8 +890,9 @@ void Input::action_press(const StringName &p_action, float p_strength) {
// Create or retrieve existing action.
ActionState &action_state = action_states[p_action];
+ // As input may come in part way through a physics tick, the earliest we can react to it is the next physics tick.
if (!action_state.cache.pressed) {
- action_state.pressed_physics_frame = Engine::get_singleton()->get_physics_frames();
+ action_state.pressed_physics_frame = Engine::get_singleton()->get_physics_frames() + 1;
action_state.pressed_process_frame = Engine::get_singleton()->get_process_frames();
}
action_state.exact = true;
@@ -908,7 +909,8 @@ void Input::action_release(const StringName &p_action) {
action_state.cache.pressed = 0;
action_state.cache.strength = 0.0;
action_state.cache.raw_strength = 0.0;
- action_state.released_physics_frame = Engine::get_singleton()->get_physics_frames();
+ // As input may come in part way through a physics tick, the earliest we can react to it is the next physics tick.
+ action_state.released_physics_frame = Engine::get_singleton()->get_physics_frames() + 1;
action_state.released_process_frame = Engine::get_singleton()->get_process_frames();
action_state.device_states.clear();
action_state.exact = true;
@@ -1023,7 +1025,7 @@ void Input::parse_input_event(const Ref<InputEvent> &p_event) {
if (buffered_events.is_empty() || !buffered_events.back()->get()->accumulate(p_event)) {
buffered_events.push_back(p_event);
}
- } else if (use_input_buffering) {
+ } else if (agile_input_event_flushing) {
buffered_events.push_back(p_event);
} else {
_parse_input_event_impl(p_event, false);
@@ -1054,12 +1056,12 @@ void Input::flush_buffered_events() {
}
}
-bool Input::is_using_input_buffering() {
- return use_input_buffering;
+bool Input::is_agile_input_event_flushing() {
+ return agile_input_event_flushing;
}
-void Input::set_use_input_buffering(bool p_enable) {
- use_input_buffering = p_enable;
+void Input::set_agile_input_event_flushing(bool p_enable) {
+ agile_input_event_flushing = p_enable;
}
void Input::set_use_accumulated_input(bool p_enable) {
diff --git a/core/input/input.h b/core/input/input.h
index 4daea0c9e8..89e48f53d7 100644
--- a/core/input/input.h
+++ b/core/input/input.h
@@ -128,7 +128,7 @@ private:
bool emulate_touch_from_mouse = false;
bool emulate_mouse_from_touch = false;
- bool use_input_buffering = false;
+ bool agile_input_event_flushing = false;
bool use_accumulated_input = true;
int mouse_from_touch_index = -1;
@@ -367,8 +367,8 @@ public:
void flush_frame_parsed_events();
#endif
void flush_buffered_events();
- bool is_using_input_buffering();
- void set_use_input_buffering(bool p_enable);
+ bool is_agile_input_event_flushing();
+ void set_agile_input_event_flushing(bool p_enable);
void set_use_accumulated_input(bool p_enable);
bool is_using_accumulated_input();
diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp
index c0d18d0120..67469de5cc 100644
--- a/core/io/marshalls.cpp
+++ b/core/io/marshalls.cpp
@@ -1315,10 +1315,12 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
if (array.is_typed()) {
Ref<Script> script = array.get_typed_script();
if (script.is_valid()) {
- header |= HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT;
+ header |= p_full_objects ? HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT : HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME;
} else if (array.get_typed_class_name() != StringName()) {
header |= HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME;
} else {
+ // No need to check `p_full_objects` since for `Variant::OBJECT`
+ // `array.get_typed_class_name()` should be non-empty.
header |= HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN;
}
}
@@ -1783,12 +1785,18 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
Variant variant = array.get_typed_script();
Ref<Script> script = variant;
if (script.is_valid()) {
- String path = script->get_path();
- ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://"), ERR_UNAVAILABLE, "Failed to encode a path to a custom script for an array type.");
- _encode_string(path, buf, r_len);
+ if (p_full_objects) {
+ String path = script->get_path();
+ ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://"), ERR_UNAVAILABLE, "Failed to encode a path to a custom script for an array type.");
+ _encode_string(path, buf, r_len);
+ } else {
+ _encode_string(EncodedObjectAsID::get_class_static(), buf, r_len);
+ }
} else if (array.get_typed_class_name() != StringName()) {
- _encode_string(array.get_typed_class_name(), buf, r_len);
+ _encode_string(p_full_objects ? array.get_typed_class_name().operator String() : EncodedObjectAsID::get_class_static(), buf, r_len);
} else {
+ // No need to check `p_full_objects` since for `Variant::OBJECT`
+ // `array.get_typed_class_name()` should be non-empty.
if (buf) {
encode_uint32(array.get_typed_builtin(), buf);
buf += 4;
diff --git a/core/object/object.cpp b/core/object/object.cpp
index 97a3a405b9..e4d1a8fc9a 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -763,7 +763,7 @@ Variant Object::callp(const StringName &p_method, const Variant **p_args, int p_
}
if (is_ref_counted()) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- ERR_FAIL_V_MSG(Variant(), "Can't 'free' a reference.");
+ ERR_FAIL_V_MSG(Variant(), "Can't free a RefCounted object.");
}
if (_lock_index.get() > 1) {
diff --git a/core/os/os.h b/core/os/os.h
index 63cc6ed50e..91e0ce9379 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -328,8 +328,6 @@ public:
virtual void benchmark_end_measure(const String &p_context, const String &p_what);
virtual void benchmark_dump();
- virtual void process_and_drop_events() {}
-
virtual Error setup_remote_filesystem(const String &p_server_host, int p_port, const String &p_password, String &r_project_path);
enum PreferredTextureFormat {
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index bcab80ea94..2cd3a51722 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -2939,10 +2939,10 @@
The property is not stored, and does not display in the editor. This is the default for non-exported properties.
</constant>
<constant name="PROPERTY_USAGE_STORAGE" value="2" enum="PropertyUsageFlags" is_bitfield="true">
- The property is serialized and saved in the scene file (default).
+ The property is serialized and saved in the scene file (default for exported properties).
</constant>
<constant name="PROPERTY_USAGE_EDITOR" value="4" enum="PropertyUsageFlags" is_bitfield="true">
- The property is shown in the [EditorInspector] (default).
+ The property is shown in the [EditorInspector] (default for exported properties).
</constant>
<constant name="PROPERTY_USAGE_INTERNAL" value="8" enum="PropertyUsageFlags" is_bitfield="true">
The property is excluded from the class reference.
diff --git a/doc/classes/AABB.xml b/doc/classes/AABB.xml
index 427d38d421..77c74e17da 100644
--- a/doc/classes/AABB.xml
+++ b/doc/classes/AABB.xml
@@ -170,7 +170,7 @@
<method name="get_shortest_axis" qualifiers="const">
<return type="Vector3" />
<description>
- Returns the shortest normaalized axis of this bounding box's [member size], as a [Vector3] ([constant Vector3.RIGHT], [constant Vector3.UP], or [constant Vector3.BACK]).
+ Returns the shortest normalized axis of this bounding box's [member size], as a [Vector3] ([constant Vector3.RIGHT], [constant Vector3.UP], or [constant Vector3.BACK]).
[codeblocks]
[gdscript]
var box = AABB(Vector3(0, 0, 0), Vector3(2, 4, 8))
diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml
index 8bee6c3470..186ee1b9c4 100644
--- a/doc/classes/CanvasItem.xml
+++ b/doc/classes/CanvasItem.xml
@@ -525,6 +525,7 @@
<return type="bool" />
<description>
Returns [code]true[/code] if the node is present in the [SceneTree], its [member visible] property is [code]true[/code] and all its ancestors are also visible. If any ancestor is hidden, this node will not be visible in the scene tree, and is therefore not drawn (see [method _draw]).
+ Visibility is checked only in parent nodes that inherit from [CanvasItem], [CanvasLayer], and [Window]. If the parent is of any other type (such as [Node], [AnimationPlayer], or [Node3D]), it is assumed to be visible.
</description>
</method>
<method name="make_canvas_position_local" qualifiers="const">
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index ff1c390b3c..62556ed38f 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -20,6 +20,7 @@
<return type="Image" />
<description>
Returns the user's clipboard as an image if possible.
+ [b]Note:[/b] This method uses the copied pixel data, e.g. from a image editing software or a web browser, not an image file copied from file explorer.
</description>
</method>
<method name="clipboard_get_primary" qualifiers="const">
diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml
index c7ff543b66..e4dab85038 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -596,6 +596,16 @@
The path to the directory containing the Open Image Denoise (OIDN) executable, used optionally for denoising lightmaps. It can be downloaded from [url=https://www.openimagedenoise.org/downloads.html]openimagedenoise.org[/url].
To enable this feature for your specific project, use [member ProjectSettings.rendering/lightmapping/denoising/denoiser].
</member>
+ <member name="input/buffering/agile_event_flushing" type="bool" setter="" getter="">
+ If [code]true[/code], input events will be flushed just before every idle and physics frame.
+ If [code]false[/code], these events will be flushed only once per process frame, between iterations of the engine.
+ Enabling this setting can greatly improve input responsiveness, especially in devices that struggle to run at the project's intended frame rate.
+ </member>
+ <member name="input/buffering/use_accumulated_input" type="bool" setter="" getter="">
+ If [code]true[/code], similar input events sent by the operating system are accumulated. When input accumulation is enabled, all input events generated during a frame will be merged and emitted when the frame is done rendering. Therefore, this limits the number of input method calls per second to the rendering FPS.
+ Input accumulation can be disabled to get slightly more precise/reactive input at the cost of increased CPU usage.
+ [b]Note:[/b] Input accumulation is [i]enabled[/i] by default.
+ </member>
<member name="interface/editor/accept_dialog_cancel_ok_buttons" type="int" setter="" getter="">
How to position the Cancel and OK buttons in the editor's [AcceptDialog]s. Different platforms have different standard behaviors for this, which can be overridden using this setting. This is useful if you use Godot both on Windows and macOS/Linux and your Godot muscle memory is stronger than your OS specific one.
- [b]Auto[/b] follows the platform convention: Cancel first on macOS and Linux, OK first on Windows.
diff --git a/doc/classes/LightmapGI.xml b/doc/classes/LightmapGI.xml
index 6fb15e4d21..e7d44411ef 100644
--- a/doc/classes/LightmapGI.xml
+++ b/doc/classes/LightmapGI.xml
@@ -137,6 +137,12 @@
<constant name="BAKE_ERROR_TEXTURE_SIZE_TOO_SMALL" value="9" enum="BakeError">
Lightmap baking failed as the maximum texture size is too small to fit some of the meshes marked for baking.
</constant>
+ <constant name="BAKE_ERROR_LIGHTMAP_TOO_SMALL" value="10" enum="BakeError">
+ Lightmap baking failed as the lightmap is too small.
+ </constant>
+ <constant name="BAKE_ERROR_ATLAS_TOO_SMALL" value="11" enum="BakeError">
+ Lightmap baking failed as the lightmap was unable to fit into an atlas.
+ </constant>
<constant name="ENVIRONMENT_MODE_DISABLED" value="0" enum="EnvironmentMode">
Ignore environment lighting when baking lightmaps.
</constant>
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index 2662b34aec..c54219c056 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -1187,7 +1187,7 @@
</constant>
<constant name="NOTIFICATION_WM_GO_BACK_REQUEST" value="1007">
Notification received from the OS when a go back request is sent (e.g. pressing the "Back" button on Android).
- Implemented only on iOS.
+ Implemented only on Android.
</constant>
<constant name="NOTIFICATION_WM_SIZE_CHANGED" value="1008">
Notification received when the window is resized.
diff --git a/doc/classes/Parallax2D.xml b/doc/classes/Parallax2D.xml
index 472aeb0bd3..cfd282a723 100644
--- a/doc/classes/Parallax2D.xml
+++ b/doc/classes/Parallax2D.xml
@@ -8,6 +8,7 @@
[b]Note:[/b] Any changes to this node's position made after it enters the scene tree will be overridden if [member ignore_camera_scroll] is [code]false[/code] or [member screen_offset] is modified.
</description>
<tutorials>
+ <link title="2D Parallax">$DOCS_URL/tutorials/2d/2d_parallax.html</link>
</tutorials>
<members>
<member name="autoscroll" type="Vector2" setter="set_autoscroll" getter="get_autoscroll" default="Vector2(0, 0)">
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index b0f421e932..4f7f372864 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -1395,12 +1395,6 @@
Enabling this can greatly improve the responsiveness to input, specially in devices that need to run multiple physics frames per visible (process) frame, because they can't run at the target frame rate.
[b]Note:[/b] Currently implemented only on Android.
</member>
- <member name="input_devices/buffering/android/use_accumulated_input" type="bool" setter="" getter="" default="true">
- If [code]true[/code], multiple input events will be accumulated into a single input event when possible.
- </member>
- <member name="input_devices/buffering/android/use_input_buffering" type="bool" setter="" getter="" default="true">
- If [code]true[/code], input events will be buffered prior to being dispatched.
- </member>
<member name="input_devices/compatibility/legacy_just_pressed_behavior" type="bool" setter="" getter="" default="false">
If [code]true[/code], [method Input.is_action_just_pressed] and [method Input.is_action_just_released] will only return [code]true[/code] if the action is still in the respective state, i.e. an action that is pressed [i]and[/i] released on the same frame will be missed.
If [code]false[/code], no input will be lost.
diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml
index 3c1061dee9..876f229d0c 100644
--- a/doc/classes/RenderingDevice.xml
+++ b/doc/classes/RenderingDevice.xml
@@ -1444,10 +1444,10 @@
VRAM-compressed signed red/green channel data format with normalized value. Values are in the [code][-1.0, 1.0][/code] range. The format's precision is 8 bits of red channel and 8 bits of green channel. Using BC5 texture compression (also known as S3TC RGTC).
</constant>
<constant name="DATA_FORMAT_BC6H_UFLOAT_BLOCK" value="142" enum="DataFormat">
- VRAM-compressed unsigned red/green/blue channel data format with the floating-point value stored as-is. The format's precision is 8 bits of red channel and 8 bits of green channel. Using BC6H texture compression (also known as BPTC HDR).
+ VRAM-compressed unsigned red/green/blue channel data format with the floating-point value stored as-is. The format's precision is between 10 and 13 bits for the red/green/blue channels. Using BC6H texture compression (also known as BPTC HDR).
</constant>
<constant name="DATA_FORMAT_BC6H_SFLOAT_BLOCK" value="143" enum="DataFormat">
- VRAM-compressed signed red/green/blue channel data format with the floating-point value stored as-is. The format's precision is between 4 and 7 bits for the red/green/blue channels and between 0 and 8 bits for the alpha channel. Using BC7 texture compression (also known as BPTC HDR).
+ VRAM-compressed signed red/green/blue channel data format with the floating-point value stored as-is. The format's precision is between 10 and 13 bits for the red/green/blue channels. Using BC6H texture compression (also known as BPTC HDR).
</constant>
<constant name="DATA_FORMAT_BC7_UNORM_BLOCK" value="144" enum="DataFormat">
VRAM-compressed unsigned red/green/blue/alpha channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range. The format's precision is between 4 and 7 bits for the red/green/blue channels and between 0 and 8 bits for the alpha channel. Also known as BPTC LDR.
@@ -1477,13 +1477,13 @@
11-bit VRAM-compressed unsigned red channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range. Using ETC2 texture compression.
</constant>
<constant name="DATA_FORMAT_EAC_R11_SNORM_BLOCK" value="153" enum="DataFormat">
- 11-bit VRAM-compressed signed red channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range. Using ETC2 texture compression.
+ 11-bit VRAM-compressed signed red channel data format with normalized value. Values are in the [code][-1.0, 1.0][/code] range. Using ETC2 texture compression.
</constant>
<constant name="DATA_FORMAT_EAC_R11G11_UNORM_BLOCK" value="154" enum="DataFormat">
11-bit VRAM-compressed unsigned red/green channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range. Using ETC2 texture compression.
</constant>
<constant name="DATA_FORMAT_EAC_R11G11_SNORM_BLOCK" value="155" enum="DataFormat">
- 11-bit VRAM-compressed signed red/green channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range. Using ETC2 texture compression.
+ 11-bit VRAM-compressed signed red/green channel data format with normalized value. Values are in the [code][-1.0, 1.0][/code] range. Using ETC2 texture compression.
</constant>
<constant name="DATA_FORMAT_ASTC_4x4_UNORM_BLOCK" value="156" enum="DataFormat">
VRAM-compressed unsigned floating-point data format with normalized value, packed in 4×4 blocks (highest quality). Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
diff --git a/doc/classes/Resource.xml b/doc/classes/Resource.xml
index cec936ac3e..74d083594f 100644
--- a/doc/classes/Resource.xml
+++ b/doc/classes/Resource.xml
@@ -40,8 +40,11 @@
<param index="0" name="subresources" type="bool" default="false" />
<description>
Duplicates this resource, returning a new resource with its [code]export[/code]ed or [constant PROPERTY_USAGE_STORAGE] properties copied from the original.
- If [param subresources] is [code]false[/code], a shallow copy is returned; nested resources within subresources are not duplicated and are shared from the original resource. If [param subresources] is [code]true[/code], a deep copy is returned; nested subresources will be duplicated and are not shared.
- Subresource properties with the [constant PROPERTY_USAGE_ALWAYS_DUPLICATE] flag are always duplicated even with [param subresources] set to [code]false[/code], and properties with the [constant PROPERTY_USAGE_NEVER_DUPLICATE] flag are never duplicated even with [param subresources] set to [code]true[/code].
+ If [param subresources] is [code]false[/code], a shallow copy is returned; nested resources within subresources are not duplicated and are shared with the original resource (with one exception; see below). If [param subresources] is [code]true[/code], a deep copy is returned; nested subresources will be duplicated and are not shared (with two exceptions; see below).
+ [param subresources] is usually respected, with the following exceptions:
+ - Subresource properties with the [constant PROPERTY_USAGE_ALWAYS_DUPLICATE] flag are always duplicated.
+ - Subresource properties with the [constant PROPERTY_USAGE_NEVER_DUPLICATE] flag are never duplicated.
+ - Subresources inside [Array] and [Dictionary] properties are never duplicated.
[b]Note:[/b] For custom resources, this method will fail if [method Object._init] has been defined with required parameters.
</description>
</method>
diff --git a/doc/classes/Skeleton3D.xml b/doc/classes/Skeleton3D.xml
index 5829a787a1..cc3f61e1b2 100644
--- a/doc/classes/Skeleton3D.xml
+++ b/doc/classes/Skeleton3D.xml
@@ -57,11 +57,6 @@
Force updates the bone transform for the bone at [param bone_idx] and all of its children.
</description>
</method>
- <method name="get_animate_physical_bones" qualifiers="const" deprecated="">
- <return type="bool" />
- <description>
- </description>
- </method>
<method name="get_bone_children" qualifiers="const">
<return type="PackedInt32Array" />
<param index="0" name="bone_idx" type="int" />
@@ -239,14 +234,6 @@
Sets all bone poses to rests.
</description>
</method>
- <method name="set_animate_physical_bones" deprecated="">
- <return type="void" />
- <param index="0" name="enabled" type="bool" />
- <description>
- This method exists for compatibility with old structures in which the [Skeleton3D] does not have a [PhysicalBoneSimulator3D] as a child, but directly has [PhysicalBone3D]s as children.
- In case you need to raycast to it without running [method physical_bones_start_simulation], call this method with [code]enabled == true[/code].
- </description>
- </method>
<method name="set_bone_enabled">
<return type="void" />
<param index="0" name="bone_idx" type="int" />
@@ -342,6 +329,10 @@
</method>
</methods>
<members>
+ <member name="animate_physical_bones" type="bool" setter="set_animate_physical_bones" getter="get_animate_physical_bones" default="true" deprecated="">
+ If you follow the recommended workflow and explicitly have [PhysicalBoneSimulator3D] as a child of [Skeleton3D], you can control whether it is affected by raycasting without running [method physical_bones_start_simulation], by its [member SkeletonModifier3D.active].
+ However, for old (deprecated) configurations, [Skeleton3D] has an internal virtual [PhysicalBoneSimulator3D] for compatibility. This property controls the internal virtual [PhysicalBoneSimulator3D]'s [member SkeletonModifier3D.active].
+ </member>
<member name="modifier_callback_mode_process" type="int" setter="set_modifier_callback_mode_process" getter="get_modifier_callback_mode_process" enum="Skeleton3D.ModifierCallbackModeProcess" default="1">
Sets the processing timing for the Modifier.
</member>
diff --git a/doc/classes/TileSetScenesCollectionSource.xml b/doc/classes/TileSetScenesCollectionSource.xml
index 9d2742b844..40ad269e83 100644
--- a/doc/classes/TileSetScenesCollectionSource.xml
+++ b/doc/classes/TileSetScenesCollectionSource.xml
@@ -6,6 +6,32 @@
<description>
When placed on a [TileMap], tiles from [TileSetScenesCollectionSource] will automatically instantiate an associated scene at the cell's position in the TileMap.
Scenes are instantiated as children of the [TileMap] when it enters the tree. If you add/remove a scene tile in the [TileMap] that is already inside the tree, the [TileMap] will automatically instantiate/free the scene accordingly.
+ [b]Note:[/b] Scene tiles all occupy one tile slot and instead use alternate tile ID to identify scene index. [method TileSetSource.get_tiles_count] will always return [code]1[/code]. Use [method get_scene_tiles_count] to get a number of scenes in a [TileSetScenesCollectionSource].
+ Use this code if you want to find the scene path at a given tile in [TileMapLayer]:
+ [codeblocks]
+ [gdscript]
+ var source_id = tile_map_layer.get_cell_source_id(Vector2i(x, y))
+ if source_id &gt; -1:
+ var scene_source = tile_map_layer.tile_set.get_source(source_id)
+ if scene_source is TileSetScenesCollectionSource:
+ var alt_id = tile_map_layer.get_cell_alternative_tile(Vector2i(x, y))
+ # The assigned PackedScene.
+ var scene = scene_source.get_scene_tile_scene(alt_id)
+ [/gdscript]
+ [csharp]
+ int sourceId = tileMapLayer.GetCellSourceId(new Vector2I(x, y));
+ if (sourceId &gt; -1)
+ {
+ TileSetSource source = tileMapLayer.TileSet.GetSource(sourceId);
+ if (source is TileSetScenesCollectionSource sceneSource)
+ {
+ int altId = tileMapLayer.GetCellAlternativeTile(new Vector2I(x, y));
+ // The assigned PackedScene.
+ PackedScene scene = sceneSource.GetSceneTileScene(altId);
+ }
+ }
+ [/csharp]
+ [/codeblocks]
</description>
<tutorials>
</tutorials>
diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp
index 4f3cd56b07..122585e595 100644
--- a/drivers/d3d12/rendering_device_driver_d3d12.cpp
+++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp
@@ -5335,10 +5335,12 @@ void RenderingDeviceDriverD3D12::command_bind_render_pipeline(CommandBufferID p_
cmd_buf_info->cmd_list->OMSetBlendFactor(pso_extra_info.dyn_params.blend_constant.components);
cmd_buf_info->cmd_list->OMSetStencilRef(pso_extra_info.dyn_params.stencil_reference);
- ComPtr<ID3D12GraphicsCommandList1> command_list_1;
- cmd_buf_info->cmd_list->QueryInterface(command_list_1.GetAddressOf());
- if (command_list_1) {
- command_list_1->OMSetDepthBounds(pso_extra_info.dyn_params.depth_bounds_min, pso_extra_info.dyn_params.depth_bounds_max);
+ if (misc_features_support.depth_bounds_supported) {
+ ComPtr<ID3D12GraphicsCommandList1> command_list_1;
+ cmd_buf_info->cmd_list->QueryInterface(command_list_1.GetAddressOf());
+ if (command_list_1) {
+ command_list_1->OMSetDepthBounds(pso_extra_info.dyn_params.depth_bounds_min, pso_extra_info.dyn_params.depth_bounds_max);
+ }
}
cmd_buf_info->render_pass_state.vf_info = pso_extra_info.vf_info;
@@ -5728,8 +5730,15 @@ RDD::PipelineID RenderingDeviceDriverD3D12::render_pipeline_create(
(&pipeline_desc.DepthStencilState)->BackFace.StencilDepthFailOp = RD_TO_D3D12_STENCIL_OP[p_depth_stencil_state.back_op.depth_fail];
(&pipeline_desc.DepthStencilState)->BackFace.StencilFunc = RD_TO_D3D12_COMPARE_OP[p_depth_stencil_state.back_op.compare];
- pso_extra_info.dyn_params.depth_bounds_min = p_depth_stencil_state.enable_depth_range ? p_depth_stencil_state.depth_range_min : 0.0f;
- pso_extra_info.dyn_params.depth_bounds_max = p_depth_stencil_state.enable_depth_range ? p_depth_stencil_state.depth_range_max : 1.0f;
+ if (misc_features_support.depth_bounds_supported) {
+ pso_extra_info.dyn_params.depth_bounds_min = p_depth_stencil_state.enable_depth_range ? p_depth_stencil_state.depth_range_min : 0.0f;
+ pso_extra_info.dyn_params.depth_bounds_max = p_depth_stencil_state.enable_depth_range ? p_depth_stencil_state.depth_range_max : 1.0f;
+ } else {
+ if (p_depth_stencil_state.enable_depth_range) {
+ WARN_PRINT_ONCE("Depth bounds test is not supported by the GPU driver.");
+ }
+ }
+
pso_extra_info.dyn_params.stencil_reference = p_depth_stencil_state.front_op.reference;
}
@@ -6441,6 +6450,12 @@ Error RenderingDeviceDriverD3D12::_check_capabilities() {
subgroup_capabilities.wave_ops_supported = options1.WaveOps;
}
+ D3D12_FEATURE_DATA_D3D12_OPTIONS2 options2 = {};
+ res = device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS2, &options2, sizeof(options2));
+ if (SUCCEEDED(res)) {
+ misc_features_support.depth_bounds_supported = options2.DepthBoundsTestSupported;
+ }
+
D3D12_FEATURE_DATA_D3D12_OPTIONS3 options3 = {};
res = device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS3, &options3, sizeof(options3));
if (SUCCEEDED(res)) {
@@ -6526,6 +6541,12 @@ Error RenderingDeviceDriverD3D12::_check_capabilities() {
print_verbose(String("- D3D12 16-bit ops supported: ") + (shader_capabilities.native_16bit_ops ? "yes" : "no"));
+ if (misc_features_support.depth_bounds_supported) {
+ print_verbose("- Depth bounds test supported");
+ } else {
+ print_verbose("- Depth bounds test not supported");
+ }
+
return OK;
}
diff --git a/drivers/d3d12/rendering_device_driver_d3d12.h b/drivers/d3d12/rendering_device_driver_d3d12.h
index 34e4840247..61b1498755 100644
--- a/drivers/d3d12/rendering_device_driver_d3d12.h
+++ b/drivers/d3d12/rendering_device_driver_d3d12.h
@@ -139,6 +139,10 @@ class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver {
bool enhanced_barriers_supported = false;
};
+ struct MiscFeaturesSupport {
+ bool depth_bounds_supported = false;
+ };
+
RenderingContextDriverD3D12 *context_driver = nullptr;
RenderingContextDriver::Device context_device;
ComPtr<IDXGIAdapter> adapter;
@@ -154,6 +158,7 @@ class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver {
StorageBufferCapabilities storage_buffer_capabilities;
FormatCapabilities format_capabilities;
BarrierCapabilities barrier_capabilities;
+ MiscFeaturesSupport misc_features_support;
String pipeline_cache_id;
class DescriptorsHeap {
diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp
index 21790d29b5..1aaf840b30 100644
--- a/drivers/gles3/storage/material_storage.cpp
+++ b/drivers/gles3/storage/material_storage.cpp
@@ -1280,7 +1280,7 @@ MaterialStorage::MaterialStorage() {
actions.renames["NODE_POSITION_WORLD"] = "model_matrix[3].xyz";
actions.renames["CAMERA_POSITION_WORLD"] = "scene_data.inv_view_matrix[3].xyz";
- actions.renames["CAMERA_DIRECTION_WORLD"] = "scene_data.view_matrix[3].xyz";
+ actions.renames["CAMERA_DIRECTION_WORLD"] = "scene_data.inv_view_matrix[2].xyz";
actions.renames["CAMERA_VISIBLE_LAYERS"] = "scene_data.camera_visible_layers";
actions.renames["NODE_POSITION_VIEW"] = "(scene_data.view_matrix * model_matrix)[3].xyz";
diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp
index 2dcf623995..9138045653 100644
--- a/drivers/gles3/storage/texture_storage.cpp
+++ b/drivers/gles3/storage/texture_storage.cpp
@@ -1030,10 +1030,8 @@ Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const {
if (texture->compressed) {
glPixelStorei(GL_PACK_ALIGNMENT, 4);
glGetCompressedTexImage(texture->target, i, &w[ofs]);
-
} else {
glPixelStorei(GL_PACK_ALIGNMENT, 1);
-
glGetTexImage(texture->target, i, texture->gl_format_cache, texture->gl_type_cache, &w[ofs]);
}
}
@@ -2123,7 +2121,7 @@ void TextureStorage::_update_render_target(RenderTarget *rt) {
texture->layers = 1;
}
texture->gl_format_cache = rt->color_format;
- texture->gl_type_cache = GL_UNSIGNED_BYTE;
+ texture->gl_type_cache = !rt->hdr ? GL_UNSIGNED_BYTE : GL_FLOAT; // to set HDR format size to 8 and keep 4 for LDR format
texture->gl_internal_format_cache = rt->color_internal_format;
texture->tex_id = rt->color;
texture->width = rt->size.x;
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index a9d37a6a99..d24b1edd70 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -1431,12 +1431,21 @@ Point2i CodeTextEditor::get_error_pos() const {
void CodeTextEditor::goto_error() {
if (!error->get_text().is_empty()) {
+ int corrected_column = error_column;
+
+ const String line_text = text_editor->get_line(error_line);
+ const int indent_size = text_editor->get_indent_size();
+ if (indent_size > 1) {
+ const int tab_count = line_text.length() - line_text.lstrip("\t").length();
+ corrected_column -= tab_count * (indent_size - 1);
+ }
+
if (text_editor->get_line_count() != error_line) {
text_editor->unfold_line(error_line);
}
text_editor->remove_secondary_carets();
text_editor->set_caret_line(error_line);
- text_editor->set_caret_column(error_column);
+ text_editor->set_caret_column(corrected_column);
text_editor->center_viewport_to_caret();
}
}
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index 4664defa59..54a28d9cb1 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -435,11 +435,6 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo
return true;
}
- if (!ResourceFormatImporter::get_singleton()->are_import_settings_valid(p_path)) {
- //reimport settings are not valid, reimport
- return true;
- }
-
Error err;
Ref<FileAccess> f = FileAccess::open(p_path + ".import", FileAccess::READ, &err);
@@ -1594,7 +1589,10 @@ bool EditorFileSystem::_find_file(const String &p_file, EditorFileSystemDirector
}
if (idx == -1) {
- //does not exist, create i guess?
+ // Only create a missing directory in memory when it exists on disk.
+ if (!dir->dir_exists(fs->get_path().path_join(path[i]))) {
+ return false;
+ }
EditorFileSystemDirectory *efsd = memnew(EditorFileSystemDirectory);
efsd->name = path[i];
@@ -2445,16 +2443,14 @@ Error EditorFileSystem::_reimport_file(const String &p_file, const HashMap<Strin
Variant meta;
Error err = importer->import(p_file, base_path, params, &import_variants, &gen_files, &meta);
- ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_UNRECOGNIZED, "Error importing '" + p_file + "'.");
-
- //as import is complete, save the .import file
+ // As import is complete, save the .import file.
Vector<String> dest_paths;
{
Ref<FileAccess> f = FileAccess::open(p_file + ".import", FileAccess::WRITE);
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_FILE_CANT_OPEN, "Cannot open file from path '" + p_file + ".import'.");
- //write manually, as order matters ([remap] has to go first for performance).
+ // Write manually, as order matters ([remap] has to go first for performance).
f->store_line("[remap]");
f->store_line("");
f->store_line("importer=\"" + importer->get_importer_name() + "\"");
@@ -2470,7 +2466,7 @@ Error EditorFileSystem::_reimport_file(const String &p_file, const HashMap<Strin
uid = ResourceUID::get_singleton()->create_id();
}
- f->store_line("uid=\"" + ResourceUID::get_singleton()->id_to_text(uid) + "\""); //store in readable format
+ f->store_line("uid=\"" + ResourceUID::get_singleton()->id_to_text(uid) + "\""); // Store in readable format.
if (err == OK) {
if (importer->get_save_extension().is_empty()) {
@@ -2525,13 +2521,14 @@ Error EditorFileSystem::_reimport_file(const String &p_file, const HashMap<Strin
for (int i = 0; i < dest_paths.size(); i++) {
dp.push_back(dest_paths[i]);
}
- f->store_line("dest_files=" + Variant(dp).get_construct_string() + "\n");
+ f->store_line("dest_files=" + Variant(dp).get_construct_string());
}
+ f->store_line("");
f->store_line("[params]");
f->store_line("");
- //store options in provided order, to avoid file changing. Order is also important because first match is accepted first.
+ // Store options in provided order, to avoid file changing. Order is also important because first match is accepted first.
for (const ResourceImporter::ImportOption &E : opts) {
String base = E.option.name;
@@ -2555,7 +2552,7 @@ Error EditorFileSystem::_reimport_file(const String &p_file, const HashMap<Strin
// Update cpos, newly created files could've changed the index of the reimported p_file.
_find_file(p_file, &fs, cpos);
- //update modified times, to avoid reimport
+ // Update modified times, to avoid reimport.
fs->files[cpos]->modified_time = FileAccess::get_modified_time(p_file);
fs->files[cpos]->import_modified_time = FileAccess::get_modified_time(p_file + ".import");
fs->files[cpos]->deps = _get_dependencies(p_file);
@@ -2569,8 +2566,8 @@ Error EditorFileSystem::_reimport_file(const String &p_file, const HashMap<Strin
ResourceUID::get_singleton()->add_id(uid, p_file);
}
- //if file is currently up, maybe the source it was loaded from changed, so import math must be updated for it
- //to reload properly
+ // If file is currently up, maybe the source it was loaded from changed, so import math must be updated for it
+ // to reload properly.
Ref<Resource> r = ResourceCache::get_ref(p_file);
if (r.is_valid()) {
if (!r->get_import_path().is_empty()) {
@@ -2584,6 +2581,7 @@ Error EditorFileSystem::_reimport_file(const String &p_file, const HashMap<Strin
print_verbose(vformat("EditorFileSystem: \"%s\" import took %d ms.", p_file, OS::get_singleton()->get_ticks_msec() - start_time));
+ ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_UNRECOGNIZED, "Error importing '" + p_file + "'.");
return OK;
}
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 467a19ebb1..199383c391 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -3579,7 +3579,9 @@ void EditorInspector::edit(Object *p_object) {
next_object = p_object; // Some plugins need to know the next edited object when clearing the inspector.
if (object) {
- object->disconnect(CoreStringName(property_list_changed), callable_mp(this, &EditorInspector::_changed_callback));
+ if (likely(Variant(object).get_validated_object())) {
+ object->disconnect(CoreStringName(property_list_changed), callable_mp(this, &EditorInspector::_changed_callback));
+ }
_clear();
}
per_array_page.clear();
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index fd49920c6b..4479eeb66a 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -6425,7 +6425,6 @@ EditorNode::EditorNode() {
// No scripting by default if in editor (except for tool).
ScriptServer::set_scripting_enabled(false);
- Input::get_singleton()->set_use_accumulated_input(true);
if (!DisplayServer::get_singleton()->is_touchscreen_available()) {
// Only if no touchscreen ui hint, disable emulation just in case.
Input::get_singleton()->set_emulate_touch_from_mouse(false);
@@ -6475,6 +6474,14 @@ EditorNode::EditorNode() {
}
{
+ bool agile_input_event_flushing = EDITOR_GET("input/buffering/agile_event_flushing");
+ bool use_accumulated_input = EDITOR_GET("input/buffering/use_accumulated_input");
+
+ Input::get_singleton()->set_agile_input_event_flushing(agile_input_event_flushing);
+ Input::get_singleton()->set_use_accumulated_input(use_accumulated_input);
+ }
+
+ {
int display_scale = EDITOR_GET("interface/editor/display_scale");
switch (display_scale) {
diff --git a/editor/editor_run_native.cpp b/editor/editor_run_native.cpp
index 548eac1737..5d378820ae 100644
--- a/editor/editor_run_native.cpp
+++ b/editor/editor_run_native.cpp
@@ -58,12 +58,12 @@ void EditorRunNative::_notification(int p_what) {
for (int j = 0; j < EditorExport::get_singleton()->get_export_platform_count(); j++) {
if (eep->get_name() == EditorExport::get_singleton()->get_export_platform(j)->get_name()) {
platform_idx = j;
+ break;
}
}
int dc = MIN(eep->get_options_count(), 9000);
- bool needs_templates;
String error;
- if (dc > 0 && preset->is_runnable() && eep->can_export(preset, error, needs_templates)) {
+ if (dc > 0 && preset->is_runnable()) {
popup->add_icon_item(eep->get_run_icon(), eep->get_name(), -1);
popup->set_item_disabled(-1, true);
for (int j = 0; j < dc; j++) {
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 5d3cc80da9..4107048375 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -474,11 +474,6 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/vsync_mode", 1, "Disabled,Enabled,Adaptive,Mailbox")
EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/update_continuously", false, "")
-#ifdef ANDROID_ENABLED
- EDITOR_SETTING_USAGE(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/android/use_accumulated_input", true, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
- EDITOR_SETTING_USAGE(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/android/use_input_buffering", true, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
-#endif
-
// Inspector
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "interface/inspector/max_array_dictionary_items_per_page", 20, "10,100,1")
EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/inspector/show_low_level_opentype_features", false, "")
@@ -526,11 +521,13 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
// Touchscreen
bool has_touchscreen_ui = DisplayServer::get_singleton()->is_touchscreen_available();
EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/touchscreen/increase_scrollbar_touch_area", has_touchscreen_ui, "")
+ set_restart_if_changed("interface/touchscreen/increase_scrollbar_touch_area", true);
EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/touchscreen/enable_long_press_as_right_click", has_touchscreen_ui, "")
set_restart_if_changed("interface/touchscreen/enable_long_press_as_right_click", true);
EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/touchscreen/enable_pan_and_scale_gestures", has_touchscreen_ui, "")
set_restart_if_changed("interface/touchscreen/enable_pan_and_scale_gestures", true);
EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "interface/touchscreen/scale_gizmo_handles", has_touchscreen_ui ? 3 : 1, "1,5,1")
+ set_restart_if_changed("interface/touchscreen/scale_gizmo_handles", true);
// Scene tabs
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/scene_tabs/display_close_button", 1, "Never,If Tab Active,Always"); // TabBar::CloseButtonDisplayPolicy
@@ -866,6 +863,9 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
/* Extra config */
+ EDITOR_SETTING_USAGE(Variant::BOOL, PROPERTY_HINT_NONE, "input/buffering/agile_event_flushing", false, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
+ EDITOR_SETTING_USAGE(Variant::BOOL, PROPERTY_HINT_NONE, "input/buffering/use_accumulated_input", true, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
+
// TRANSLATORS: Project Manager here refers to the tool used to create/manage Godot projects.
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "project_manager/sorting_order", 0, "Last Edited,Name,Path")
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "project_manager/directory_naming_convention", 1, "No convention,kebab-case,snake_case,camelCase,PascalCase,Title Case")
diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp
index c0646dc572..0768ae128b 100644
--- a/editor/export/editor_export_platform.cpp
+++ b/editor/export/editor_export_platform.cpp
@@ -1229,8 +1229,12 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
config->set_value("remap", "path", export_path);
// Erase useless sections.
- config->erase_section("deps");
- config->erase_section("params");
+ if (config->has_section("deps")) {
+ config->erase_section("deps");
+ }
+ if (config->has_section("params")) {
+ config->erase_section("params");
+ }
String import_text = config->encode_to_text();
CharString cs = import_text.utf8();
@@ -1294,8 +1298,12 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
}
// Erase useless sections.
- config->erase_section("deps");
- config->erase_section("params");
+ if (config->has_section("deps")) {
+ config->erase_section("deps");
+ }
+ if (config->has_section("params")) {
+ config->erase_section("params");
+ }
String import_text = config->encode_to_text();
CharString cs = import_text.utf8();
diff --git a/editor/gui/editor_bottom_panel.cpp b/editor/gui/editor_bottom_panel.cpp
index 3e74a3c94e..3cb95a3926 100644
--- a/editor/gui/editor_bottom_panel.cpp
+++ b/editor/gui/editor_bottom_panel.cpp
@@ -188,10 +188,11 @@ Button *EditorBottomPanel::add_item(String p_text, Control *p_item, const Ref<Sh
}
void EditorBottomPanel::remove_item(Control *p_item) {
+ bool was_visible = false;
for (int i = 0; i < items.size(); i++) {
if (items[i].control == p_item) {
if (p_item->is_visible_in_tree()) {
- _switch_to_item(false, i);
+ was_visible = true;
}
item_vbox->remove_child(items[i].control);
button_hbox->remove_child(items[i].button);
@@ -200,6 +201,16 @@ void EditorBottomPanel::remove_item(Control *p_item) {
break;
}
}
+
+ if (was_visible) {
+ // Open the first panel to ensure that if the removed dock was visible, the bottom
+ // panel will not collapse.
+ _switch_to_item(true, 0);
+ } else if (last_opened_control == p_item) {
+ // When a dock is removed by plugins, it might not have been visible, and it
+ // might have been the last_opened_control. We need to make sure to reset the last opened control.
+ last_opened_control = items[0].control;
+ }
}
void EditorBottomPanel::make_item_visible(Control *p_item, bool p_visible) {
diff --git a/editor/gui/editor_spin_slider.cpp b/editor/gui/editor_spin_slider.cpp
index 6c85641d3a..4e8d6d63bf 100644
--- a/editor/gui/editor_spin_slider.cpp
+++ b/editor/gui/editor_spin_slider.cpp
@@ -36,6 +36,10 @@
#include "editor/editor_settings.h"
#include "editor/themes/editor_scale.h"
+bool EditorSpinSlider::is_text_field() const {
+ return true;
+}
+
String EditorSpinSlider::get_tooltip(const Point2 &p_pos) const {
if (!read_only && grabber->is_visible()) {
Key key = (OS::get_singleton()->has_feature("macos") || OS::get_singleton()->has_feature("web_macos") || OS::get_singleton()->has_feature("web_ios")) ? Key::META : Key::CTRL;
diff --git a/editor/gui/editor_spin_slider.h b/editor/gui/editor_spin_slider.h
index a999f5c48f..a0c0685629 100644
--- a/editor/gui/editor_spin_slider.h
+++ b/editor/gui/editor_spin_slider.h
@@ -96,6 +96,8 @@ protected:
void _focus_entered();
public:
+ virtual bool is_text_field() const override;
+
String get_tooltip(const Point2 &p_pos) const override;
String get_text_value() const;
diff --git a/editor/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp
index 2db3fb1f07..5ee4322a22 100644
--- a/editor/import/3d/resource_importer_scene.cpp
+++ b/editor/import/3d/resource_importer_scene.cpp
@@ -291,14 +291,18 @@ bool ResourceImporterScene::get_option_visibility(const String &p_path, const St
for (int i = 0; i < post_importer_plugins.size(); i++) {
Variant ret = post_importer_plugins.write[i]->get_option_visibility(p_path, animation_importer, p_option, p_options);
if (ret.get_type() == Variant::BOOL) {
- return ret;
+ if (!ret) {
+ return false;
+ }
}
}
for (Ref<EditorSceneFormatImporter> importer : scene_importers) {
Variant ret = importer->get_option_visibility(p_path, animation_importer, p_option, p_options);
if (ret.get_type() == Variant::BOOL) {
- return ret;
+ if (!ret) {
+ return false;
+ }
}
}
diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp
index 846456ea55..a8f8e9ef11 100644
--- a/editor/import_dock.cpp
+++ b/editor/import_dock.cpp
@@ -431,31 +431,39 @@ void ImportDock::_importer_selected(int i_idx) {
void ImportDock::_preset_selected(int p_idx) {
int item_id = preset->get_popup()->get_item_id(p_idx);
+ String setting_name = "importer_defaults/" + params->importer->get_importer_name();
switch (item_id) {
case ITEM_SET_AS_DEFAULT: {
- Dictionary d;
+ Dictionary import_settings;
+ // When import settings already exist, we will update these settings
+ // to ensure that the dictionary retains settings that are not displayed in the
+ // editor. For Scene, the dictionary is the same for FBX, GLTF, and Blender, but each
+ // file type has some different settings.
+ if (ProjectSettings::get_singleton()->has_setting(setting_name)) {
+ import_settings = GLOBAL_GET(setting_name);
+ }
for (const PropertyInfo &E : params->properties) {
- d[E.name] = params->values[E.name];
+ import_settings[E.name] = params->values[E.name];
}
- ProjectSettings::get_singleton()->set("importer_defaults/" + params->importer->get_importer_name(), d);
+ ProjectSettings::get_singleton()->set(setting_name, import_settings);
ProjectSettings::get_singleton()->save();
_update_preset_menu();
} break;
case ITEM_LOAD_DEFAULT: {
- ERR_FAIL_COND(!ProjectSettings::get_singleton()->has_setting("importer_defaults/" + params->importer->get_importer_name()));
+ ERR_FAIL_COND(!ProjectSettings::get_singleton()->has_setting(setting_name));
- Dictionary d = GLOBAL_GET("importer_defaults/" + params->importer->get_importer_name());
- List<Variant> v;
- d.get_key_list(&v);
+ Dictionary import_settings = GLOBAL_GET(setting_name);
+ List<Variant> keys;
+ import_settings.get_key_list(&keys);
if (params->checking) {
params->checked.clear();
}
- for (const Variant &E : v) {
- params->values[E] = d[E];
+ for (const Variant &E : keys) {
+ params->values[E] = import_settings[E];
if (params->checking) {
params->checked.insert(E);
}
@@ -463,7 +471,7 @@ void ImportDock::_preset_selected(int p_idx) {
params->update();
} break;
case ITEM_CLEAR_DEFAULT: {
- ProjectSettings::get_singleton()->set("importer_defaults/" + params->importer->get_importer_name(), Variant());
+ ProjectSettings::get_singleton()->set(setting_name, Variant());
ProjectSettings::get_singleton()->save();
_update_preset_menu();
} break;
diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp
index e37619aa20..041778d634 100644
--- a/editor/plugins/curve_editor_plugin.cpp
+++ b/editor/plugins/curve_editor_plugin.cpp
@@ -118,12 +118,6 @@ void CurveEdit::_notification(int p_what) {
queue_redraw();
}
} break;
- case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- if (!EditorSettings::get_singleton()->check_changed_settings_in_group("interface/touchscreen")) {
- break;
- }
- [[fallthrough]];
- }
case NOTIFICATION_THEME_CHANGED: {
float gizmo_scale = EDITOR_GET("interface/touchscreen/scale_gizmo_handles");
point_radius = Math::round(BASE_POINT_RADIUS * get_theme_default_base_scale() * gizmo_scale);
diff --git a/editor/plugins/lightmap_gi_editor_plugin.cpp b/editor/plugins/lightmap_gi_editor_plugin.cpp
index 2289105d78..1c17d99d0d 100644
--- a/editor/plugins/lightmap_gi_editor_plugin.cpp
+++ b/editor/plugins/lightmap_gi_editor_plugin.cpp
@@ -110,6 +110,9 @@ void LightmapGIEditorPlugin::_bake_select_file(const String &p_file) {
case LightmapGI::BAKE_ERROR_LIGHTMAP_TOO_SMALL: {
EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images. Make sure all meshes selected to bake have `lightmap_size_hint` value set high enough, and `texel_scale` value of LightmapGI is not too low."));
} break;
+ case LightmapGI::BAKE_ERROR_ATLAS_TOO_SMALL: {
+ EditorNode::get_singleton()->show_warning(TTR("Failed fitting a lightmap image into an atlas. This should never happen and should be reported."));
+ } break;
default: {
} break;
}
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 96127ec93e..070471f3f3 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -317,7 +317,16 @@ void ScriptTextEditor::_error_clicked(const Variant &p_line) {
if (!scr.is_valid()) {
EditorNode::get_singleton()->show_warning(TTR("Could not load file at:") + "\n\n" + path, TTR("Error!"));
} else {
- ScriptEditor::get_singleton()->edit(scr, line, column);
+ int corrected_column = column;
+
+ const String line_text = code_editor->get_text_editor()->get_line(line);
+ const int indent_size = code_editor->get_text_editor()->get_indent_size();
+ if (indent_size > 1) {
+ const int tab_count = line_text.length() - line_text.lstrip("\t").length();
+ corrected_column -= tab_count * (indent_size - 1);
+ }
+
+ ScriptEditor::get_singleton()->edit(scr, line, corrected_column);
}
}
}
diff --git a/editor/plugins/tiles/tile_map_layer_editor.cpp b/editor/plugins/tiles/tile_map_layer_editor.cpp
index 4a59530159..b76e673414 100644
--- a/editor/plugins/tiles/tile_map_layer_editor.cpp
+++ b/editor/plugins/tiles/tile_map_layer_editor.cpp
@@ -163,7 +163,10 @@ void TileMapLayerEditorTilesPlugin::_update_tile_set_sources_list() {
int old_source = -1;
if (old_current > -1) {
old_source = sources_list->get_item_metadata(old_current);
+ } else {
+ old_source = sources_list->get_meta("old_source", -1);
}
+ sources_list->set_meta("old_source", old_source);
sources_list->clear();
TileMapLayer *edited_layer = _get_edited_layer();
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 2f36198b23..22a4c49c4d 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -6499,8 +6499,9 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("LessThan (<)", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Less Than (<)")), { VisualShaderNodeCompare::FUNC_LESS_THAN }, VisualShaderNode::PORT_TYPE_BOOLEAN));
add_options.push_back(AddOption("LessThanEqual (<=)", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Less Than or Equal (<=)")), { VisualShaderNodeCompare::FUNC_LESS_THAN_EQUAL }, VisualShaderNode::PORT_TYPE_BOOLEAN));
add_options.push_back(AddOption("NotEqual (!=)", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Not Equal (!=)")), { VisualShaderNodeCompare::FUNC_NOT_EQUAL }, VisualShaderNode::PORT_TYPE_BOOLEAN));
- add_options.push_back(AddOption("Switch (==)", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated 3D vector if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
- add_options.push_back(AddOption("Switch2D (==)", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated 2D vector if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("SwitchVector2D (==)", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated 2D vector if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("SwitchVector3D (==)", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated 3D vector if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("SwitchVector4D (==)", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated 4D vector if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
add_options.push_back(AddOption("SwitchBool (==)", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated boolean if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_BOOLEAN }, VisualShaderNode::PORT_TYPE_BOOLEAN));
add_options.push_back(AddOption("SwitchFloat (==)", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated floating-point scalar if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_FLOAT }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("SwitchInt (==)", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated integer scalar if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_INT }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 52d7b14b65..59f45ef5db 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -1053,6 +1053,14 @@ ProjectManager::ProjectManager() {
}
EditorSettings::get_singleton()->set_optimize_save(false); // Just write settings as they come.
+ {
+ bool agile_input_event_flushing = EDITOR_GET("input/buffering/agile_event_flushing");
+ bool use_accumulated_input = EDITOR_GET("input/buffering/use_accumulated_input");
+
+ Input::get_singleton()->set_agile_input_event_flushing(agile_input_event_flushing);
+ Input::get_singleton()->set_use_accumulated_input(use_accumulated_input);
+ }
+
int display_scale = EDITOR_GET("interface/editor/display_scale");
switch (display_scale) {
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 25de9facb2..10e290df05 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -149,7 +149,8 @@ void SceneTreeDock::input(const Ref<InputEvent> &p_event) {
void SceneTreeDock::shortcut_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
- if (get_viewport()->gui_get_focus_owner() && get_viewport()->gui_get_focus_owner()->is_text_field()) {
+ Control *focus_owner = get_viewport()->gui_get_focus_owner();
+ if (focus_owner && focus_owner->is_text_field()) {
return;
}
@@ -158,7 +159,11 @@ void SceneTreeDock::shortcut_input(const Ref<InputEvent> &p_event) {
}
if (ED_IS_SHORTCUT("scene_tree/rename", p_event)) {
- _tool_selected(TOOL_RENAME);
+ // Prevent renaming if a button is focused
+ // to avoid conflict with Enter shortcut on macOS
+ if (!focus_owner || !Object::cast_to<BaseButton>(focus_owner)) {
+ _tool_selected(TOOL_RENAME);
+ }
#ifdef MODULE_REGEX_ENABLED
} else if (ED_IS_SHORTCUT("scene_tree/batch_rename", p_event)) {
_tool_selected(TOOL_BATCH_RENAME);
diff --git a/editor/themes/editor_theme_manager.cpp b/editor/themes/editor_theme_manager.cpp
index 03752656c0..a63b6d4e14 100644
--- a/editor/themes/editor_theme_manager.cpp
+++ b/editor/themes/editor_theme_manager.cpp
@@ -2652,8 +2652,6 @@ bool EditorThemeManager::is_generated_theme_outdated() {
EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor/font") ||
EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor/main_font") ||
EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor/code_font") ||
- EditorSettings::get_singleton()->check_changed_settings_in_group("interface/touchscreen/increase_scrollbar_touch_area") ||
- EditorSettings::get_singleton()->check_changed_settings_in_group("interface/touchscreen/scale_gizmo_handles") ||
EditorSettings::get_singleton()->check_changed_settings_in_group("editors/visual_editors") ||
EditorSettings::get_singleton()->check_changed_settings_in_group("text_editor/theme") ||
EditorSettings::get_singleton()->check_changed_settings_in_group("text_editor/help/help") ||
diff --git a/main/main.cpp b/main/main.cpp
index 32eb32142d..1cabe43065 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -2516,7 +2516,6 @@ error:
memdelete(message_queue);
}
- OS::get_singleton()->benchmark_end_measure("Startup", "Core");
OS::get_singleton()->benchmark_end_measure("Startup", "Main::Setup");
#if defined(STEAMAPI_ENABLED)
@@ -2917,8 +2916,6 @@ Error Main::setup2(bool p_show_boot_logo) {
MAIN_PRINT("Main: Clear Color");
DisplayServer::set_early_window_clear_color_override(false);
- RenderingServer::get_singleton()->set_default_clear_color(
- GLOBAL_GET("rendering/environment/defaults/default_clear_color"));
GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/config/icon", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg"), String());
GLOBAL_DEF(PropertyInfo(Variant::STRING, "application/config/macos_native_icon", PROPERTY_HINT_FILE, "*.icns"), String());
@@ -2928,7 +2925,8 @@ Error Main::setup2(bool p_show_boot_logo) {
Input *id = Input::get_singleton();
if (id) {
- agile_input_event_flushing = GLOBAL_DEF("input_devices/buffering/agile_event_flushing", false);
+ bool agile_input_event_flushing = GLOBAL_DEF("input_devices/buffering/agile_event_flushing", false);
+ id->set_agile_input_event_flushing(agile_input_event_flushing);
if (bool(GLOBAL_DEF_BASIC("input_devices/pointing/emulate_touch_from_mouse", false)) &&
!(editor || project_manager)) {
@@ -2941,8 +2939,6 @@ Error Main::setup2(bool p_show_boot_logo) {
id->set_emulate_mouse_from_touch(bool(GLOBAL_DEF_BASIC("input_devices/pointing/emulate_mouse_from_touch", true)));
}
- GLOBAL_DEF("input_devices/buffering/android/use_accumulated_input", true);
- GLOBAL_DEF("input_devices/buffering/android/use_input_buffering", true);
GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_long_press_as_right_click", false);
GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_pan_and_scale_gestures", false);
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "input_devices/pointing/android/rotary_input_scroll_axis", PROPERTY_HINT_ENUM, "Horizontal,Vertical"), 1);
@@ -3219,6 +3215,8 @@ void Main::setup_boot_logo() {
}
#endif
}
+ RenderingServer::get_singleton()->set_default_clear_color(
+ GLOBAL_GET("rendering/environment/defaults/default_clear_color"));
}
String Main::get_rendering_driver_name() {
@@ -3979,7 +3977,6 @@ uint32_t Main::hide_print_fps_attempts = 3;
uint32_t Main::frame = 0;
bool Main::force_redraw_requested = false;
int Main::iterating = 0;
-bool Main::agile_input_event_flushing = false;
bool Main::is_iterating() {
return iterating > 0;
@@ -4040,7 +4037,7 @@ bool Main::iteration() {
NavigationServer3D::get_singleton()->sync();
for (int iters = 0; iters < advance.physics_steps; ++iters) {
- if (Input::get_singleton()->is_using_input_buffering() && agile_input_event_flushing) {
+ if (Input::get_singleton()->is_agile_input_event_flushing()) {
Input::get_singleton()->flush_buffered_events();
}
@@ -4097,7 +4094,7 @@ bool Main::iteration() {
Engine::get_singleton()->_in_physics = false;
}
- if (Input::get_singleton()->is_using_input_buffering() && agile_input_event_flushing) {
+ if (Input::get_singleton()->is_agile_input_event_flushing()) {
Input::get_singleton()->flush_buffered_events();
}
@@ -4169,11 +4166,6 @@ bool Main::iteration() {
iterating--;
- // Needed for OSs using input buffering regardless accumulation (like Android)
- if (Input::get_singleton()->is_using_input_buffering() && !agile_input_event_flushing) {
- Input::get_singleton()->flush_buffered_events();
- }
-
if (movie_writer) {
movie_writer->add_frame();
}
diff --git a/main/main.h b/main/main.h
index b1cfcd3c2d..6dd2ff7d7a 100644
--- a/main/main.h
+++ b/main/main.h
@@ -58,7 +58,6 @@ class Main {
static uint32_t frame;
static bool force_redraw_requested;
static int iterating;
- static bool agile_input_event_flushing;
public:
static bool is_cmdline_tool();
diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected
index 4b0d22a1aa..50695cb0b1 100644
--- a/misc/extension_api_validation/4.2-stable.expected
+++ b/misc/extension_api_validation/4.2-stable.expected
@@ -254,13 +254,6 @@ Validate extension JSON: API was removed: classes/VisualShaderNodeComment/method
Validate extension JSON: API was removed: classes/VisualShaderNodeComment/properties/title
-GH-87888
---------
-Validate extension JSON: API was removed: classes/Skeleton3D/properties/animate_physical_bones
-
-These base class is changed to SkeletonModifier3D which is processed by Skeleton3D with the assumption that it is Skeleton3D's child.
-
-
GH-90575
--------
Validate extension JSON: API was removed: classes/BoneAttachment3D/methods/on_bone_pose_update
diff --git a/modules/enet/doc_classes/ENetConnection.xml b/modules/enet/doc_classes/ENetConnection.xml
index ebd1577172..84534fec06 100644
--- a/modules/enet/doc_classes/ENetConnection.xml
+++ b/modules/enet/doc_classes/ENetConnection.xml
@@ -51,7 +51,7 @@
<param index="3" name="data" type="int" default="0" />
<description>
Initiates a connection to a foreign [param address] using the specified [param port] and allocating the requested [param channels]. Optional [param data] can be passed during connection in the form of a 32 bit integer.
- [b]Note:[/b] You must call either [method create_host] or [method create_host_bound] before calling this method.
+ [b]Note:[/b] You must call either [method create_host] or [method create_host_bound] on both ends before calling this method.
</description>
</method>
<method name="create_host">
@@ -61,7 +61,9 @@
<param index="2" name="in_bandwidth" type="int" default="0" />
<param index="3" name="out_bandwidth" type="int" default="0" />
<description>
- Create an ENetHost that will allow up to [param max_peers] connected peers, each allocating up to [param max_channels] channels, optionally limiting bandwidth to [param in_bandwidth] and [param out_bandwidth].
+ Creates an ENetHost that allows up to [param max_peers] connected peers, each allocating up to [param max_channels] channels, optionally limiting bandwidth to [param in_bandwidth] and [param out_bandwidth] (if greater than zero).
+ This method binds a random available dynamic UDP port on the host machine at the [i]unspecified[/i] address. Use [method create_host_bound] to specify the address and port.
+ [b]Note:[/b] It is necessary to create a host in both client and server in order to establish a connection.
</description>
</method>
<method name="create_host_bound">
@@ -73,7 +75,8 @@
<param index="4" name="in_bandwidth" type="int" default="0" />
<param index="5" name="out_bandwidth" type="int" default="0" />
<description>
- Create an ENetHost like [method create_host] which is also bound to the given [param bind_address] and [param bind_port].
+ Creates an ENetHost bound to the given [param bind_address] and [param bind_port] that allows up to [param max_peers] connected peers, each allocating up to [param max_channels] channels, optionally limiting bandwidth to [param in_bandwidth] and [param out_bandwidth] (if greater than zero).
+ [b]Note:[/b] It is necessary to create a host in both client and server in order to establish a connection.
</description>
</method>
<method name="destroy">
@@ -141,8 +144,9 @@
<return type="Array" />
<param index="0" name="timeout" type="int" default="0" />
<description>
- Waits for events on the specified host and shuttles packets between the host and its peers, with the given [param timeout] (in milliseconds). The returned [Array] will have 4 elements. An [enum EventType], the [ENetPacketPeer] which generated the event, the event associated data (if any), the event associated channel (if any). If the generated event is [constant EVENT_RECEIVE], the received packet will be queued to the associated [ENetPacketPeer].
+ Waits for events on this connection and shuttles packets between the host and its peers, with the given [param timeout] (in milliseconds). The returned [Array] will have 4 elements. An [enum EventType], the [ENetPacketPeer] which generated the event, the event associated data (if any), the event associated channel (if any). If the generated event is [constant EVENT_RECEIVE], the received packet will be queued to the associated [ENetPacketPeer].
Call this function regularly to handle connections, disconnections, and to receive new packets.
+ [b]Note:[/b] This method must be called on both ends involved in the event (sending and receiving hosts).
</description>
</method>
<method name="socket_send">
diff --git a/modules/fbx/editor/editor_scene_importer_fbx2gltf.cpp b/modules/fbx/editor/editor_scene_importer_fbx2gltf.cpp
index afb63246e4..daa4db37df 100644
--- a/modules/fbx/editor/editor_scene_importer_fbx2gltf.cpp
+++ b/modules/fbx/editor/editor_scene_importer_fbx2gltf.cpp
@@ -126,10 +126,9 @@ Node *EditorSceneFormatImporterFBX2GLTF::import_scene(const String &p_path, uint
Variant EditorSceneFormatImporterFBX2GLTF::get_option_visibility(const String &p_path, bool p_for_animation,
const String &p_option, const HashMap<StringName, Variant> &p_options) {
- if (p_option == "fbx/embedded_image_handling") {
- return false;
- }
- if (p_options.has("fbx/importer") && int(p_options["fbx/importer"]) == EditorSceneFormatImporterUFBX::FBX_IMPORTER_FBX2GLTF && p_option == "fbx/embedded_image_handling") {
+ // Remove all the FBX options except for 'fbx/importer' if the importer is fbx2gltf.
+ // These options are available only for ufbx.
+ if (p_option.begins_with("fbx/") && p_option != "fbx/importer" && p_options.has("fbx/importer") && int(p_options["fbx/importer"]) == EditorSceneFormatImporterUFBX::FBX_IMPORTER_FBX2GLTF) {
return false;
}
return true;
diff --git a/modules/fbx/editor/editor_scene_importer_ufbx.cpp b/modules/fbx/editor/editor_scene_importer_ufbx.cpp
index fb5e324390..4d5f220539 100644
--- a/modules/fbx/editor/editor_scene_importer_ufbx.cpp
+++ b/modules/fbx/editor/editor_scene_importer_ufbx.cpp
@@ -76,9 +76,6 @@ Node *EditorSceneFormatImporterUFBX::import_scene(const String &p_path, uint32_t
if (p_options.has(SNAME("nodes/import_as_skeleton_bones")) ? (bool)p_options[SNAME("nodes/import_as_skeleton_bones")] : false) {
state->set_import_as_skeleton_bones(true);
}
- if (p_options.has(SNAME("nodes/import_as_skeleton_bones")) ? (bool)p_options[SNAME("nodes/import_as_skeleton_bones")] : false) {
- state->set_import_as_skeleton_bones(true);
- }
p_flags |= EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS;
state->set_bake_fps(p_options["animation/fps"]);
Error err = fbx->append_from_file(path, state, p_flags, p_path.get_base_dir());
@@ -93,21 +90,17 @@ Node *EditorSceneFormatImporterUFBX::import_scene(const String &p_path, uint32_t
Variant EditorSceneFormatImporterUFBX::get_option_visibility(const String &p_path, bool p_for_animation,
const String &p_option, const HashMap<StringName, Variant> &p_options) {
- String file_extension = p_path.get_extension().to_lower();
- if (file_extension != "fbx" && p_option.begins_with("fbx/")) {
- return false;
- }
- if ((file_extension != "gltf" && file_extension != "glb") && p_option.begins_with("gltf/")) {
- return false;
- }
return true;
}
void EditorSceneFormatImporterUFBX::get_import_options(const String &p_path,
List<ResourceImporter::ImportOption> *r_options) {
- r_options->push_back(ResourceImporterScene::ImportOption(PropertyInfo(Variant::INT, "fbx/importer", PROPERTY_HINT_ENUM, "ufbx,FBX2glTF"), FBX_IMPORTER_UFBX));
- r_options->push_back(ResourceImporterScene::ImportOption(PropertyInfo(Variant::BOOL, "fbx/allow_geometry_helper_nodes"), false));
- r_options->push_back(ResourceImporterScene::ImportOption(PropertyInfo(Variant::INT, "fbx/embedded_image_handling", PROPERTY_HINT_ENUM, "Discard All Textures,Extract Textures,Embed as Basis Universal,Embed as Uncompressed", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), FBXState::HANDLE_BINARY_EXTRACT_TEXTURES));
+ // Returns all the options when path is empty because that means it's for the Project Settings.
+ if (p_path.is_empty() || p_path.get_extension().to_lower() == "fbx") {
+ r_options->push_back(ResourceImporterScene::ImportOption(PropertyInfo(Variant::INT, "fbx/importer", PROPERTY_HINT_ENUM, "ufbx,FBX2glTF"), FBX_IMPORTER_UFBX));
+ r_options->push_back(ResourceImporterScene::ImportOption(PropertyInfo(Variant::BOOL, "fbx/allow_geometry_helper_nodes"), false));
+ r_options->push_back(ResourceImporterScene::ImportOption(PropertyInfo(Variant::INT, "fbx/embedded_image_handling", PROPERTY_HINT_ENUM, "Discard All Textures,Extract Textures,Embed as Basis Universal,Embed as Uncompressed", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), FBXState::HANDLE_BINARY_EXTRACT_TEXTURES));
+ }
}
void EditorSceneFormatImporterUFBX::handle_compatibility_options(HashMap<StringName, Variant> &p_import_params) const {
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 5469dad3f7..b0ac4aa800 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -1064,12 +1064,22 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
// Get at (potential) root stack pos, so it can be returned.
GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, chain.back()->get()->base);
+
if (r_error) {
return GDScriptCodeGenerator::Address();
}
GDScriptCodeGenerator::Address prev_base = base;
+ // In case the base has a setter, don't use the address directly, as we want to call that setter.
+ // So use a temp value instead and call the setter at the end.
+ GDScriptCodeGenerator::Address base_temp;
+ if (base.mode == GDScriptCodeGenerator::Address::MEMBER && member_property_has_setter && !member_property_is_in_setter) {
+ base_temp = codegen.add_temporary(base.type);
+ gen->write_assign(base_temp, base);
+ prev_base = base_temp;
+ }
+
struct ChainInfo {
bool is_named = false;
GDScriptCodeGenerator::Address base;
@@ -1218,6 +1228,9 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
gen->write_end_jump_if_shared();
}
}
+ } else if (base_temp.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ // Save the temp value back to the base by calling its setter.
+ gen->write_call(GDScriptCodeGenerator::Address(), base, member_property_setter_function, { assigned });
}
if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 5b1639e250..404b61fb40 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -574,7 +574,9 @@ GDScriptTokenizer::Token GDScriptTokenizerText::potential_identifier() {
if (len == 1 && _peek(-1) == '_') {
// Lone underscore.
- return make_token(Token::UNDERSCORE);
+ Token token = make_token(Token::UNDERSCORE);
+ token.literal = "_";
+ return token;
}
String name(_start, len);
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index 912367764b..5eab8a6306 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -1807,7 +1807,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
} else if (methodstr == "free") {
if (err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
if (base->is_ref_counted()) {
- err_text = "Attempted to free a reference.";
+ err_text = "Attempted to free a RefCounted object.";
OPCODE_BREAK;
} else if (base->get_type() == Variant::OBJECT) {
err_text = "Attempted to free a locked object (calling or emitting).";
@@ -1898,7 +1898,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
} else if (methodstr == "free") {
if (err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
if (base->is_ref_counted()) {
- err_text = "Attempted to free a reference.";
+ err_text = "Attempted to free a RefCounted object.";
OPCODE_BREAK;
} else if (base->get_type() == Variant::OBJECT) {
err_text = "Attempted to free a locked object (calling or emitting).";
diff --git a/modules/gdscript/tests/scripts/runtime/features/simple_setter_chain_call_setter.gd b/modules/gdscript/tests/scripts/runtime/features/simple_setter_chain_call_setter.gd
new file mode 100644
index 0000000000..9e27a500bf
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/simple_setter_chain_call_setter.gd
@@ -0,0 +1,13 @@
+# https://github.com/godotengine/godot/issues/85952
+
+var vec: Vector2 = Vector2.ZERO:
+ set(new_vec):
+ prints("setting vec from", vec, "to", new_vec)
+ if new_vec == Vector2(1, 1):
+ vec = new_vec
+
+func test():
+ vec.x = 2
+ vec.y = 2
+
+ prints("vec is", vec)
diff --git a/modules/gdscript/tests/scripts/runtime/features/simple_setter_chain_call_setter.out b/modules/gdscript/tests/scripts/runtime/features/simple_setter_chain_call_setter.out
new file mode 100644
index 0000000000..31b3b3a3a8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/simple_setter_chain_call_setter.out
@@ -0,0 +1,4 @@
+GDTEST_OK
+setting vec from (0, 0) to (2, 0)
+setting vec from (0, 0) to (0, 2)
+vec is (0, 0)
diff --git a/modules/gdscript/tests/scripts/runtime/features/single_underscore_node_name.gd b/modules/gdscript/tests/scripts/runtime/features/single_underscore_node_name.gd
new file mode 100644
index 0000000000..b07c40b6da
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/single_underscore_node_name.gd
@@ -0,0 +1,15 @@
+extends Node
+
+func test() -> void:
+ var node1 := Node.new()
+ node1.name = "_"
+ var node2 := Node.new()
+ node2.name = "Child"
+ var node3 := Node.new()
+ node3.name = "Child"
+
+ add_child(node1)
+ node1.add_child(node2)
+ add_child(node3)
+
+ assert(get_node("_/Child") == $_/Child)
diff --git a/modules/gdscript/tests/scripts/runtime/features/single_underscore_node_name.out b/modules/gdscript/tests/scripts/runtime/features/single_underscore_node_name.out
new file mode 100644
index 0000000000..d73c5eb7cd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/single_underscore_node_name.out
@@ -0,0 +1 @@
+GDTEST_OK
diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp
index b474128fd6..f70e440781 100644
--- a/modules/gltf/editor/editor_scene_importer_blend.cpp
+++ b/modules/gltf/editor/editor_scene_importer_blend.cpp
@@ -332,7 +332,8 @@ Variant EditorSceneFormatImporterBlend::get_option_visibility(const String &p_pa
}
void EditorSceneFormatImporterBlend::get_import_options(const String &p_path, List<ResourceImporter::ImportOption> *r_options) {
- if (p_path.get_extension().to_lower() != "blend") {
+ // Returns all the options when path is empty because that means it's for the Project Settings.
+ if (!p_path.is_empty() && p_path.get_extension().to_lower() != "blend") {
return;
}
#define ADD_OPTION_BOOL(PATH, VALUE) \
diff --git a/modules/gltf/editor/editor_scene_importer_gltf.cpp b/modules/gltf/editor/editor_scene_importer_gltf.cpp
index b38c64de01..ce7e17d361 100644
--- a/modules/gltf/editor/editor_scene_importer_gltf.cpp
+++ b/modules/gltf/editor/editor_scene_importer_gltf.cpp
@@ -84,8 +84,12 @@ Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t
void EditorSceneFormatImporterGLTF::get_import_options(const String &p_path,
List<ResourceImporter::ImportOption> *r_options) {
- r_options->push_back(ResourceImporterScene::ImportOption(PropertyInfo(Variant::INT, "gltf/naming_version", PROPERTY_HINT_ENUM, "Godot 4.1 or 4.0,Godot 4.2 or later"), 1));
- r_options->push_back(ResourceImporterScene::ImportOption(PropertyInfo(Variant::INT, "gltf/embedded_image_handling", PROPERTY_HINT_ENUM, "Discard All Textures,Extract Textures,Embed as Basis Universal,Embed as Uncompressed", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), GLTFState::HANDLE_BINARY_EXTRACT_TEXTURES));
+ String file_extension = p_path.get_extension().to_lower();
+ // Returns all the options when path is empty because that means it's for the Project Settings.
+ if (p_path.is_empty() || file_extension == "gltf" || file_extension == "glb") {
+ r_options->push_back(ResourceImporterScene::ImportOption(PropertyInfo(Variant::INT, "gltf/naming_version", PROPERTY_HINT_ENUM, "Godot 4.1 or 4.0,Godot 4.2 or later"), 1));
+ r_options->push_back(ResourceImporterScene::ImportOption(PropertyInfo(Variant::INT, "gltf/embedded_image_handling", PROPERTY_HINT_ENUM, "Discard All Textures,Extract Textures,Embed as Basis Universal,Embed as Uncompressed", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), GLTFState::HANDLE_BINARY_EXTRACT_TEXTURES));
+ }
}
void EditorSceneFormatImporterGLTF::handle_compatibility_options(HashMap<StringName, Variant> &p_import_params) const {
@@ -98,10 +102,6 @@ void EditorSceneFormatImporterGLTF::handle_compatibility_options(HashMap<StringN
Variant EditorSceneFormatImporterGLTF::get_option_visibility(const String &p_path, bool p_for_animation,
const String &p_option, const HashMap<StringName, Variant> &p_options) {
- String file_extension = p_path.get_extension().to_lower();
- if ((file_extension != "gltf" && file_extension != "glb") && p_option.begins_with("gltf/")) {
- return false;
- }
return true;
}
diff --git a/modules/interactive_music/audio_stream_interactive.cpp b/modules/interactive_music/audio_stream_interactive.cpp
index 01764d66ed..d7762295eb 100644
--- a/modules/interactive_music/audio_stream_interactive.cpp
+++ b/modules/interactive_music/audio_stream_interactive.cpp
@@ -976,6 +976,8 @@ void AudioStreamPlaybackInteractive::switch_to_clip_by_name(const StringName &p_
return;
}
+ ERR_FAIL_COND_MSG(stream.is_null(), "Attempted to switch while not playing back any stream.");
+
for (int i = 0; i < stream->get_clip_count(); i++) {
if (stream->get_clip_name(i) == p_name) {
switch_request = i;
diff --git a/modules/interactive_music/register_types.cpp b/modules/interactive_music/register_types.cpp
index 5baea13f81..6175ea6493 100644
--- a/modules/interactive_music/register_types.cpp
+++ b/modules/interactive_music/register_types.cpp
@@ -42,11 +42,11 @@
void initialize_interactive_music_module(ModuleInitializationLevel p_level) {
if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) {
GDREGISTER_CLASS(AudioStreamPlaylist);
- GDREGISTER_VIRTUAL_CLASS(AudioStreamPlaybackPlaylist);
+ GDREGISTER_ABSTRACT_CLASS(AudioStreamPlaybackPlaylist);
GDREGISTER_CLASS(AudioStreamInteractive);
- GDREGISTER_VIRTUAL_CLASS(AudioStreamPlaybackInteractive);
+ GDREGISTER_ABSTRACT_CLASS(AudioStreamPlaybackInteractive);
GDREGISTER_CLASS(AudioStreamSynchronized);
- GDREGISTER_VIRTUAL_CLASS(AudioStreamPlaybackSynchronized);
+ GDREGISTER_ABSTRACT_CLASS(AudioStreamPlaybackSynchronized);
}
#ifdef TOOLS_ENABLED
if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp
index 7ac7bd8088..33b0b0d015 100644
--- a/modules/lightmapper_rd/lightmapper_rd.cpp
+++ b/modules/lightmapper_rd/lightmapper_rd.cpp
@@ -233,14 +233,14 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_
MeshInstance &mi = mesh_instances.write[m_i];
Size2i s = Size2i(mi.data.albedo_on_uv2->get_width(), mi.data.albedo_on_uv2->get_height());
sizes.push_back(s);
- atlas_size = atlas_size.max(s + Size2i(2, 2));
+ atlas_size = atlas_size.max(s + Size2i(2, 2).maxi(p_denoiser_range));
}
int max = nearest_power_of_2_templated(atlas_size.width);
max = MAX(max, nearest_power_of_2_templated(atlas_size.height));
if (max > p_max_texture_size) {
- return BAKE_ERROR_LIGHTMAP_TOO_SMALL;
+ return BAKE_ERROR_TEXTURE_EXCEEDS_MAX_SIZE;
}
if (p_step_function) {
@@ -254,19 +254,27 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_
int best_atlas_memory = 0x7FFFFFFF;
Vector<Vector3i> best_atlas_offsets;
- //determine best texture array atlas size by bruteforce fitting
+ // Determine best texture array atlas size by bruteforce fitting.
while (atlas_size.x <= p_max_texture_size && atlas_size.y <= p_max_texture_size) {
Vector<Vector2i> source_sizes;
Vector<int> source_indices;
source_sizes.resize(sizes.size());
source_indices.resize(sizes.size());
for (int i = 0; i < source_indices.size(); i++) {
- source_sizes.write[i] = sizes[i] + Vector2i(2, 2).maxi(p_denoiser_range); // Add padding between lightmaps
+ source_sizes.write[i] = sizes[i] + Vector2i(2, 2).maxi(p_denoiser_range); // Add padding between lightmaps.
source_indices.write[i] = i;
}
Vector<Vector3i> atlas_offsets;
atlas_offsets.resize(source_sizes.size());
+ // Ensure the sizes can all fit into a single atlas layer.
+ // This should always happen, and this check is only in place to prevent an infinite loop.
+ for (int i = 0; i < source_sizes.size(); i++) {
+ if (source_sizes[i] > atlas_size) {
+ return BAKE_ERROR_ATLAS_TOO_SMALL;
+ }
+ }
+
int slices = 0;
while (source_sizes.size() > 0) {
diff --git a/modules/openxr/editor/openxr_action_map_editor.cpp b/modules/openxr/editor/openxr_action_map_editor.cpp
index 51e6c3e277..a353073f21 100644
--- a/modules/openxr/editor/openxr_action_map_editor.cpp
+++ b/modules/openxr/editor/openxr_action_map_editor.cpp
@@ -248,32 +248,27 @@ void OpenXRActionMapEditor::_on_interaction_profile_selected(const String p_path
void OpenXRActionMapEditor::_load_action_map(const String p_path, bool p_create_new_if_missing) {
Error err = OK;
- action_map = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err);
- if (err != OK) {
- if ((err == ERR_FILE_NOT_FOUND || err == ERR_CANT_OPEN) && p_create_new_if_missing) {
- action_map.instantiate();
- action_map->create_default_action_sets();
-
- // Save it immediately
- err = ResourceSaver::save(action_map, p_path);
- if (err != OK) {
- // Show warning but continue.
- EditorNode::get_singleton()->show_warning(vformat(TTR("Error saving file %s: %s"), edited_path, error_names[err]));
- } else {
- // Reload so it's cached.
- action_map = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err);
- if (err != OK) {
- // Show warning but continue.
- EditorNode::get_singleton()->show_warning(vformat(TTR("Error reloading file %s: %s"), edited_path, error_names[err]));
- }
- }
- } else {
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ if (da->file_exists(p_path)) {
+ action_map = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err);
+ if (err != OK) {
EditorNode::get_singleton()->show_warning(vformat(TTR("Error loading %s: %s."), edited_path, error_names[err]));
edited_path = "";
header_label->set_text("");
return;
}
+ } else if (p_create_new_if_missing) {
+ action_map.instantiate();
+ action_map->create_default_action_sets();
+ action_map->set_path(p_path);
+
+ // Save it immediately
+ err = ResourceSaver::save(action_map, p_path);
+ if (err != OK) {
+ // Show warning but continue.
+ EditorNode::get_singleton()->show_warning(vformat(TTR("Error saving file %s: %s"), edited_path, error_names[err]));
+ }
}
edited_path = p_path;
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp
index 06b304dcde..8dc0e869d0 100644
--- a/platform/android/display_server_android.cpp
+++ b/platform/android/display_server_android.cpp
@@ -651,7 +651,6 @@ DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, Dis
#endif
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
- Input::get_singleton()->set_use_input_buffering(true); // Needed because events will come directly from the UI thread
r_error = OK;
}
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 479158c91f..a0da9019c6 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -441,6 +441,7 @@ void EditorExportPlatformAndroid::_update_preset_status() {
} else {
has_runnable_preset.clear();
}
+ devices_changed.set();
}
#endif
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
index dad397de61..5515347bd6 100644
--- a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
@@ -117,10 +117,6 @@ open class GodotEditor : GodotActivity() {
val longPressEnabled = enableLongPressGestures()
val panScaleEnabled = enablePanAndScaleGestures()
- val useInputBuffering = useInputBuffering()
- val useAccumulatedInput = useAccumulatedInput()
- GodotLib.updateInputDispatchSettings(useAccumulatedInput, useInputBuffering)
-
checkForProjectPermissionsToEnable()
runOnUiThread {
@@ -128,7 +124,6 @@ open class GodotEditor : GodotActivity() {
godotFragment?.godot?.renderView?.inputHandler?.apply {
enableLongPress(longPressEnabled)
enablePanningAndScalingGestures(panScaleEnabled)
- enableInputDispatchToRenderThread(!useInputBuffering && !useAccumulatedInput)
}
}
}
@@ -280,13 +275,6 @@ open class GodotEditor : GodotActivity() {
java.lang.Boolean.parseBoolean(GodotLib.getEditorSetting("interface/touchscreen/enable_pan_and_scale_gestures"))
/**
- * Use input buffering for the Godot Android editor.
- */
- protected open fun useInputBuffering() = java.lang.Boolean.parseBoolean(GodotLib.getEditorSetting("interface/editor/android/use_input_buffering"))
-
- protected open fun useAccumulatedInput() = java.lang.Boolean.parseBoolean(GodotLib.getEditorSetting("interface/editor/android/use_accumulated_input"))
-
- /**
* Whether we should launch the new godot instance in an adjacent window
* @see https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_LAUNCH_ADJACENT
*/
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt
index f50b5577c3..2bcfba559c 100644
--- a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt
@@ -45,10 +45,6 @@ class GodotGame : GodotEditor() {
override fun enablePanAndScaleGestures() = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_pan_and_scale_gestures"))
- override fun useInputBuffering() = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/buffering/android/use_input_buffering"))
-
- override fun useAccumulatedInput() = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/buffering/android/use_accumulated_input"))
-
override fun checkForProjectPermissionsToEnable() {
// Nothing to do.. by the time we get here, the project permissions will have already
// been requested by the Editor window.
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
index c188a97ca5..7e2a44ab39 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
@@ -628,26 +628,19 @@ class Godot(private val context: Context) : SensorEventListener {
private fun onGodotSetupCompleted() {
Log.v(TAG, "OnGodotSetupCompleted")
- if (!isEditorBuild()) {
- // These properties are defined after Godot setup completion, so we retrieve them here.
- val longPressEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_long_press_as_right_click"))
- val panScaleEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_pan_and_scale_gestures"))
- val rotaryInputAxisValue = GodotLib.getGlobal("input_devices/pointing/android/rotary_input_scroll_axis")
-
- val useInputBuffering = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/buffering/android/use_input_buffering"))
- val useAccumulatedInput = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/buffering/android/use_accumulated_input"))
- GodotLib.updateInputDispatchSettings(useAccumulatedInput, useInputBuffering)
-
- runOnUiThread {
- renderView?.inputHandler?.apply {
- enableLongPress(longPressEnabled)
- enablePanningAndScalingGestures(panScaleEnabled)
- enableInputDispatchToRenderThread(!useInputBuffering && !useAccumulatedInput)
- try {
- setRotaryInputAxis(Integer.parseInt(rotaryInputAxisValue))
- } catch (e: NumberFormatException) {
- Log.w(TAG, e)
- }
+ // These properties are defined after Godot setup completion, so we retrieve them here.
+ val longPressEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_long_press_as_right_click"))
+ val panScaleEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_pan_and_scale_gestures"))
+ val rotaryInputAxisValue = GodotLib.getGlobal("input_devices/pointing/android/rotary_input_scroll_axis")
+
+ runOnUiThread {
+ renderView?.inputHandler?.apply {
+ enableLongPress(longPressEnabled)
+ enablePanningAndScalingGestures(panScaleEnabled)
+ try {
+ setRotaryInputAxis(Integer.parseInt(rotaryInputAxisValue))
+ } catch (e: NumberFormatException) {
+ Log.w(TAG, e)
}
}
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
index 37e889daf7..909daf05c9 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
@@ -242,9 +242,8 @@ public class GodotLib {
public static native void onRendererPaused();
/**
- * Invoked on the GL thread to update the input dispatch settings
- * @param useAccumulatedInput True to use accumulated input, false otherwise
- * @param useInputBuffering True to use input buffering, false otherwise
+ * @return true if input must be dispatched from the render thread. If false, input is
+ * dispatched from the UI thread.
*/
- public static native void updateInputDispatchSettings(boolean useAccumulatedInput, boolean useInputBuffering);
+ public static native boolean shouldDispatchInputToRenderThread();
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt
index 4cd3bd8db9..2929a0a0b0 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt
@@ -76,7 +76,10 @@ internal class GodotGestureHandler(private val inputHandler: GodotInputHandler)
}
override fun onLongPress(event: MotionEvent) {
- contextClickRouter(event)
+ val toolType = GodotInputHandler.getEventToolType(event)
+ if (toolType != MotionEvent.TOOL_TYPE_MOUSE) {
+ contextClickRouter(event)
+ }
}
private fun contextClickRouter(event: MotionEvent) {
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
index 889618914d..273774a33d 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
@@ -77,8 +77,6 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
private int rotaryInputAxis = ROTARY_INPUT_VERTICAL_AXIS;
- private boolean dispatchInputToRenderThread = false;
-
public GodotInputHandler(GodotRenderView godotView) {
final Context context = godotView.getView().getContext();
mRenderView = godotView;
@@ -111,19 +109,11 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
}
/**
- * Specifies whether input should be dispatch on the UI thread or on the Render thread.
- * @param enable true to dispatch input on the Render thread, false to dispatch input on the UI thread
- */
- public void enableInputDispatchToRenderThread(boolean enable) {
- this.dispatchInputToRenderThread = enable;
- }
-
- /**
* @return true if input must be dispatched from the render thread. If false, input is
* dispatched from the UI thread.
*/
private boolean shouldDispatchInputToRenderThread() {
- return dispatchInputToRenderThread;
+ return GodotLib.shouldDispatchInputToRenderThread();
}
/**
@@ -472,7 +462,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
return button;
}
- private static int getEventToolType(MotionEvent event) {
+ static int getEventToolType(MotionEvent event) {
return event.getPointerCount() > 0 ? event.getToolType(0) : MotionEvent.TOOL_TYPE_UNKNOWN;
}
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index 87d4281c5a..11e897facf 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -550,10 +550,11 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIE
}
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_updateInputDispatchSettings(JNIEnv *env, jclass clazz, jboolean p_use_accumulated_input, jboolean p_use_input_buffering) {
- if (Input::get_singleton()) {
- Input::get_singleton()->set_use_accumulated_input(p_use_accumulated_input);
- Input::get_singleton()->set_use_input_buffering(p_use_input_buffering);
+JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_shouldDispatchInputToRenderThread(JNIEnv *env, jclass clazz) {
+ Input *input = Input::get_singleton();
+ if (input) {
+ return !input->is_agile_input_event_flushing();
}
+ return false;
}
}
diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h
index 852c475e7e..d027da31fa 100644
--- a/platform/android/java_godot_lib_jni.h
+++ b/platform/android/java_godot_lib_jni.h
@@ -69,7 +69,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResu
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onNightModeChanged(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIEnv *env, jclass clazz);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_updateInputDispatchSettings(JNIEnv *env, jclass clazz, jboolean p_use_accumulated_input, jboolean p_use_input_buffering);
+JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_shouldDispatchInputToRenderThread(JNIEnv *env, jclass clazz);
}
#endif // JAVA_GODOT_LIB_JNI_H
diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp
index 490f73a36d..5b65c8b485 100644
--- a/platform/ios/export/export_plugin.cpp
+++ b/platform/ios/export/export_plugin.cpp
@@ -2971,6 +2971,7 @@ void EditorExportPlatformIOS::_update_preset_status() {
} else {
has_runnable_preset.clear();
}
+ devices_changed.set();
}
#endif
diff --git a/platform/linuxbsd/wayland/display_server_wayland.cpp b/platform/linuxbsd/wayland/display_server_wayland.cpp
index adc9beed66..93096fcdcc 100644
--- a/platform/linuxbsd/wayland/display_server_wayland.cpp
+++ b/platform/linuxbsd/wayland/display_server_wayland.cpp
@@ -1238,7 +1238,7 @@ void DisplayServerWayland::process_events() {
} else {
try_suspend();
}
- } else if (wayland_thread.get_reset_frame()) {
+ } else if (!wayland_thread.is_suspended() || wayland_thread.get_reset_frame()) {
// At last, a sign of life! We're no longer suspended.
suspended = false;
}
diff --git a/platform/linuxbsd/wayland/wayland_thread.cpp b/platform/linuxbsd/wayland/wayland_thread.cpp
index 7bdc75db29..0ba49f900e 100644
--- a/platform/linuxbsd/wayland/wayland_thread.cpp
+++ b/platform/linuxbsd/wayland/wayland_thread.cpp
@@ -3245,6 +3245,8 @@ void WaylandThread::window_create(DisplayServer::WindowID p_window_id, int p_wid
zxdg_exported_v1_add_listener(ws.xdg_exported, &xdg_exported_listener, &ws);
}
+ wl_surface_commit(ws.wl_surface);
+
// Wait for the surface to be configured before continuing.
wl_display_roundtrip(wl_display);
}
diff --git a/platform/web/audio_driver_web.cpp b/platform/web/audio_driver_web.cpp
index b24c6cb1fd..22487d2756 100644
--- a/platform/web/audio_driver_web.cpp
+++ b/platform/web/audio_driver_web.cpp
@@ -65,19 +65,7 @@ void AudioDriverWeb::_sample_playback_finished_callback(const char *p_playback_o
return;
}
- Object *player_object = ObjectDB::get_instance(playback->player_id);
- if (player_object == nullptr) {
- return;
- }
- Node *player = Object::cast_to<Node>(player_object);
- if (player == nullptr) {
- return;
- }
-
- const StringName finished = SNAME("finished");
- if (player->has_signal(finished)) {
- player->emit_signal(finished);
- }
+ AudioServer::get_singleton()->stop_sample_playback(playback);
}
void AudioDriverWeb::_audio_driver_process(int p_from, int p_samples) {
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 750e8bb54c..f29048b16d 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -2911,24 +2911,67 @@ Key DisplayServerWindows::keyboard_get_label_from_physical(Key p_keycode) const
return p_keycode;
}
-String _get_full_layout_name_from_registry(HKL p_layout) {
- String id = "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\" + String::num_int64((int64_t)p_layout, 16, false).lpad(8, "0");
+String DisplayServerWindows::_get_keyboard_layout_display_name(const String &p_klid) const {
String ret;
+ HKEY key;
+ if (RegOpenKeyW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts", &key) != ERROR_SUCCESS) {
+ return String();
+ }
- HKEY hkey;
- WCHAR layout_text[1024];
- memset(layout_text, 0, 1024 * sizeof(WCHAR));
-
- if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, (LPCWSTR)(id.utf16().get_data()), 0, KEY_QUERY_VALUE, &hkey) != ERROR_SUCCESS) {
- return ret;
+ WCHAR buffer[MAX_PATH] = {};
+ DWORD buffer_size = MAX_PATH;
+ if (RegGetValueW(key, (LPCWSTR)p_klid.utf16().get_data(), L"Layout Display Name", RRF_RT_REG_SZ, nullptr, buffer, &buffer_size) == ERROR_SUCCESS) {
+ if (load_indirect_string) {
+ if (load_indirect_string(buffer, buffer, buffer_size, nullptr) == S_OK) {
+ ret = String::utf16((const char16_t *)buffer, buffer_size);
+ }
+ }
+ } else {
+ if (RegGetValueW(key, (LPCWSTR)p_klid.utf16().get_data(), L"Layout Text", RRF_RT_REG_SZ, nullptr, buffer, &buffer_size) == ERROR_SUCCESS) {
+ ret = String::utf16((const char16_t *)buffer, buffer_size);
+ }
}
- DWORD buffer = 1024;
- DWORD vtype = REG_SZ;
- if (RegQueryValueExW(hkey, L"Layout Text", nullptr, &vtype, (LPBYTE)layout_text, &buffer) == ERROR_SUCCESS) {
- ret = String::utf16((const char16_t *)layout_text);
+ RegCloseKey(key);
+ return ret;
+}
+
+String DisplayServerWindows::_get_klid(HKL p_hkl) const {
+ String ret;
+
+ WORD device = HIWORD(p_hkl);
+ if ((device & 0xf000) == 0xf000) {
+ WORD layout_id = device & 0x0fff;
+
+ HKEY key;
+ if (RegOpenKeyW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts", &key) != ERROR_SUCCESS) {
+ return String();
+ }
+
+ DWORD index = 0;
+ wchar_t klid_buffer[KL_NAMELENGTH];
+ DWORD klid_buffer_size = KL_NAMELENGTH;
+ while (RegEnumKeyExW(key, index, klid_buffer, &klid_buffer_size, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS) {
+ wchar_t layout_id_buf[MAX_PATH] = {};
+ DWORD layout_id_size = MAX_PATH;
+ if (RegGetValueW(key, klid_buffer, L"Layout Id", RRF_RT_REG_SZ, nullptr, layout_id_buf, &layout_id_size) == ERROR_SUCCESS) {
+ if (layout_id == String::utf16((char16_t *)layout_id_buf, layout_id_size).hex_to_int()) {
+ ret = String::utf16((const char16_t *)klid_buffer, klid_buffer_size).lpad(8, "0");
+ break;
+ }
+ }
+ klid_buffer_size = KL_NAMELENGTH;
+ ++index;
+ }
+
+ RegCloseKey(key);
+ } else {
+ if (device == 0) {
+ device = LOWORD(p_hkl);
+ }
+ ret = (String::num_uint64((uint64_t)device, 16, false)).lpad(8, "0");
}
- RegCloseKey(hkey);
+
return ret;
}
@@ -2940,7 +2983,7 @@ String DisplayServerWindows::keyboard_get_layout_name(int p_index) const {
HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL));
GetKeyboardLayoutList(layout_count, layouts);
- String ret = _get_full_layout_name_from_registry(layouts[p_index]); // Try reading full name from Windows registry, fallback to locale name if failed (e.g. on Wine).
+ String ret = _get_keyboard_layout_display_name(_get_klid(layouts[p_index])); // Try reading full name from Windows registry, fallback to locale name if failed (e.g. on Wine).
if (ret.is_empty()) {
WCHAR buf[LOCALE_NAME_MAX_LENGTH];
memset(buf, 0, LOCALE_NAME_MAX_LENGTH * sizeof(WCHAR));
@@ -4161,6 +4204,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
mm->set_relative_screen_position(mm->get_relative());
old_x = mm->get_position().x;
old_y = mm->get_position().y;
+
if (windows[window_id].window_focused || window_get_active_popup() == window_id) {
Input::get_singleton()->parse_input_event(mm);
}
@@ -4187,13 +4231,118 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
break;
}
+ pointer_button[GET_POINTERID_WPARAM(wParam)] = MouseButton::NONE;
windows[window_id].block_mm = true;
return 0;
} break;
case WM_POINTERLEAVE: {
+ pointer_button[GET_POINTERID_WPARAM(wParam)] = MouseButton::NONE;
windows[window_id].block_mm = false;
return 0;
} break;
+ case WM_POINTERDOWN:
+ case WM_POINTERUP: {
+ if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {
+ break;
+ }
+
+ if ((tablet_get_current_driver() != "winink") || !winink_available) {
+ break;
+ }
+
+ Ref<InputEventMouseButton> mb;
+ mb.instantiate();
+ mb->set_window_id(window_id);
+
+ BitField<MouseButtonMask> last_button_state = 0;
+ if (IS_POINTER_FIRSTBUTTON_WPARAM(wParam)) {
+ last_button_state.set_flag(MouseButtonMask::LEFT);
+ mb->set_button_index(MouseButton::LEFT);
+ }
+ if (IS_POINTER_SECONDBUTTON_WPARAM(wParam)) {
+ last_button_state.set_flag(MouseButtonMask::RIGHT);
+ mb->set_button_index(MouseButton::RIGHT);
+ }
+ if (IS_POINTER_THIRDBUTTON_WPARAM(wParam)) {
+ last_button_state.set_flag(MouseButtonMask::MIDDLE);
+ mb->set_button_index(MouseButton::MIDDLE);
+ }
+ if (IS_POINTER_FOURTHBUTTON_WPARAM(wParam)) {
+ last_button_state.set_flag(MouseButtonMask::MB_XBUTTON1);
+ mb->set_button_index(MouseButton::MB_XBUTTON1);
+ }
+ if (IS_POINTER_FIFTHBUTTON_WPARAM(wParam)) {
+ last_button_state.set_flag(MouseButtonMask::MB_XBUTTON2);
+ mb->set_button_index(MouseButton::MB_XBUTTON2);
+ }
+ mb->set_button_mask(last_button_state);
+
+ const BitField<WinKeyModifierMask> &mods = _get_mods();
+ mb->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));
+ mb->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));
+ mb->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));
+ mb->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));
+
+ POINT coords; // Client coords.
+ coords.x = GET_X_LPARAM(lParam);
+ coords.y = GET_Y_LPARAM(lParam);
+
+ // Note: Handle popup closing here, since mouse event is not emulated and hook will not be called.
+ uint64_t delta = OS::get_singleton()->get_ticks_msec() - time_since_popup;
+ if (delta > 250) {
+ Point2i pos = Point2i(coords.x, coords.y) - _get_screens_origin();
+ List<WindowID>::Element *C = nullptr;
+ List<WindowID>::Element *E = popup_list.back();
+ // Find top popup to close.
+ while (E) {
+ // Popup window area.
+ Rect2i win_rect = Rect2i(window_get_position_with_decorations(E->get()), window_get_size_with_decorations(E->get()));
+ // Area of the parent window, which responsible for opening sub-menu.
+ Rect2i safe_rect = window_get_popup_safe_rect(E->get());
+ if (win_rect.has_point(pos)) {
+ break;
+ } else if (safe_rect != Rect2i() && safe_rect.has_point(pos)) {
+ break;
+ } else {
+ C = E;
+ E = E->prev();
+ }
+ }
+ if (C) {
+ _send_window_event(windows[C->get()], DisplayServerWindows::WINDOW_EVENT_CLOSE_REQUEST);
+ }
+ }
+
+ int64_t pen_id = GET_POINTERID_WPARAM(wParam);
+ if (uMsg == WM_POINTERDOWN) {
+ mb->set_pressed(true);
+ if (pointer_down_time.has(pen_id) && (pointer_prev_button[pen_id] == mb->get_button_index()) && (ABS(coords.y - pointer_last_pos[pen_id].y) < GetSystemMetrics(SM_CYDOUBLECLK)) && GetMessageTime() - pointer_down_time[pen_id] < (LONG)GetDoubleClickTime()) {
+ mb->set_double_click(true);
+ pointer_down_time[pen_id] = 0;
+ } else {
+ pointer_down_time[pen_id] = GetMessageTime();
+ pointer_prev_button[pen_id] = mb->get_button_index();
+ pointer_last_pos[pen_id] = Vector2(coords.x, coords.y);
+ }
+ pointer_button[pen_id] = mb->get_button_index();
+ } else {
+ if (!pointer_button.has(pen_id)) {
+ return 0;
+ }
+ mb->set_pressed(false);
+ mb->set_button_index(pointer_button[pen_id]);
+ pointer_button[pen_id] = MouseButton::NONE;
+ }
+
+ ScreenToClient(windows[window_id].hWnd, &coords);
+
+ mb->set_position(Vector2(coords.x, coords.y));
+ mb->set_global_position(Vector2(coords.x, coords.y));
+
+ Input::get_singleton()->parse_input_event(mb);
+
+ return 0;
+ } break;
case WM_POINTERUPDATE: {
if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {
break;
@@ -4271,7 +4420,23 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
mm->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));
mm->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));
- mm->set_button_mask(mouse_get_button_state());
+ BitField<MouseButtonMask> last_button_state = 0;
+ if (IS_POINTER_FIRSTBUTTON_WPARAM(wParam)) {
+ last_button_state.set_flag(MouseButtonMask::LEFT);
+ }
+ if (IS_POINTER_SECONDBUTTON_WPARAM(wParam)) {
+ last_button_state.set_flag(MouseButtonMask::RIGHT);
+ }
+ if (IS_POINTER_THIRDBUTTON_WPARAM(wParam)) {
+ last_button_state.set_flag(MouseButtonMask::MIDDLE);
+ }
+ if (IS_POINTER_FOURTHBUTTON_WPARAM(wParam)) {
+ last_button_state.set_flag(MouseButtonMask::MB_XBUTTON1);
+ }
+ if (IS_POINTER_FIFTHBUTTON_WPARAM(wParam)) {
+ last_button_state.set_flag(MouseButtonMask::MB_XBUTTON2);
+ }
+ mm->set_button_mask(last_button_state);
POINT coords; // Client coords.
coords.x = GET_X_LPARAM(lParam);
@@ -4442,6 +4607,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
mm->set_position(mm->get_position() - window_get_position(receiving_window_id) + window_get_position(window_id));
mm->set_global_position(mm->get_position());
}
+
Input::get_singleton()->parse_input_event(mm);
} break;
@@ -5247,6 +5413,9 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
::DwmSetWindowAttribute(wd.hWnd, use_legacy_dark_mode_before_20H1 ? DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 : DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));
}
+ RECT real_client_rect;
+ GetClientRect(wd.hWnd, &real_client_rect);
+
#ifdef RD_ENABLED
if (rendering_context) {
union {
@@ -5276,7 +5445,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
return INVALID_WINDOW_ID;
}
- rendering_context->window_set_size(id, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top);
+ rendering_context->window_set_size(id, real_client_rect.right - real_client_rect.left, real_client_rect.bottom - real_client_rect.top);
rendering_context->window_set_vsync_mode(id, p_vsync_mode);
wd.context_created = true;
}
@@ -5284,7 +5453,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
#ifdef GLES3_ENABLED
if (gl_manager_native) {
- if (gl_manager_native->window_create(id, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) != OK) {
+ if (gl_manager_native->window_create(id, wd.hWnd, hInstance, real_client_rect.right - real_client_rect.left, real_client_rect.bottom - real_client_rect.top) != OK) {
memdelete(gl_manager_native);
gl_manager_native = nullptr;
windows.erase(id);
@@ -5294,7 +5463,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
}
if (gl_manager_angle) {
- if (gl_manager_angle->window_create(id, nullptr, wd.hWnd, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) != OK) {
+ if (gl_manager_angle->window_create(id, nullptr, wd.hWnd, real_client_rect.right - real_client_rect.left, real_client_rect.bottom - real_client_rect.top) != OK) {
memdelete(gl_manager_angle);
gl_manager_angle = nullptr;
windows.erase(id);
@@ -5433,6 +5602,9 @@ GetPointerPenInfoPtr DisplayServerWindows::win8p_GetPointerPenInfo = nullptr;
LogicalToPhysicalPointForPerMonitorDPIPtr DisplayServerWindows::win81p_LogicalToPhysicalPointForPerMonitorDPI = nullptr;
PhysicalToLogicalPointForPerMonitorDPIPtr DisplayServerWindows::win81p_PhysicalToLogicalPointForPerMonitorDPI = nullptr;
+// Shell API,
+SHLoadIndirectStringPtr DisplayServerWindows::load_indirect_string = nullptr;
+
Vector2i _get_device_ids(const String &p_device_name) {
if (p_device_name.is_empty()) {
return Vector2i();
@@ -5606,6 +5778,12 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
FreeLibrary(nt_lib);
}
+ // Load Shell API.
+ HMODULE shellapi_lib = LoadLibraryW(L"shlwapi.dll");
+ if (shellapi_lib) {
+ load_indirect_string = (SHLoadIndirectStringPtr)GetProcAddress(shellapi_lib, "SHLoadIndirectString");
+ }
+
// Load UXTheme, available on Windows 10+ only.
if (os_ver.dwBuildNumber >= 10240) {
HMODULE ux_theme_lib = LoadLibraryW(L"uxtheme.dll");
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index c2f4de7d81..650f0412ea 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -207,6 +207,50 @@ typedef UINT32 PEN_MASK;
#define POINTER_MESSAGE_FLAG_FIRSTBUTTON 0x00000010
#endif
+#ifndef POINTER_MESSAGE_FLAG_SECONDBUTTON
+#define POINTER_MESSAGE_FLAG_SECONDBUTTON 0x00000020
+#endif
+
+#ifndef POINTER_MESSAGE_FLAG_THIRDBUTTON
+#define POINTER_MESSAGE_FLAG_THIRDBUTTON 0x00000040
+#endif
+
+#ifndef POINTER_MESSAGE_FLAG_FOURTHBUTTON
+#define POINTER_MESSAGE_FLAG_FOURTHBUTTON 0x00000080
+#endif
+
+#ifndef POINTER_MESSAGE_FLAG_FIFTHBUTTON
+#define POINTER_MESSAGE_FLAG_FIFTHBUTTON 0x00000100
+#endif
+
+#ifndef IS_POINTER_FLAG_SET_WPARAM
+#define IS_POINTER_FLAG_SET_WPARAM(wParam, flag) (((DWORD)HIWORD(wParam) & (flag)) == (flag))
+#endif
+
+#ifndef IS_POINTER_FIRSTBUTTON_WPARAM
+#define IS_POINTER_FIRSTBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FIRSTBUTTON)
+#endif
+
+#ifndef IS_POINTER_SECONDBUTTON_WPARAM
+#define IS_POINTER_SECONDBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_SECONDBUTTON)
+#endif
+
+#ifndef IS_POINTER_THIRDBUTTON_WPARAM
+#define IS_POINTER_THIRDBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_THIRDBUTTON)
+#endif
+
+#ifndef IS_POINTER_FOURTHBUTTON_WPARAM
+#define IS_POINTER_FOURTHBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FOURTHBUTTON)
+#endif
+
+#ifndef IS_POINTER_FIFTHBUTTON_WPARAM
+#define IS_POINTER_FIFTHBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FIFTHBUTTON)
+#endif
+
+#ifndef GET_POINTERID_WPARAM
+#define GET_POINTERID_WPARAM(wParam) (LOWORD(wParam))
+#endif
+
#if WINVER < 0x0602
enum tagPOINTER_INPUT_TYPE {
PT_POINTER = 0x00000001,
@@ -274,10 +318,19 @@ typedef struct tagPOINTER_PEN_INFO {
#define WM_POINTERLEAVE 0x024A
#endif
+#ifndef WM_POINTERDOWN
+#define WM_POINTERDOWN 0x0246
+#endif
+
+#ifndef WM_POINTERUP
+#define WM_POINTERUP 0x0247
+#endif
+
typedef BOOL(WINAPI *GetPointerTypePtr)(uint32_t p_id, POINTER_INPUT_TYPE *p_type);
typedef BOOL(WINAPI *GetPointerPenInfoPtr)(uint32_t p_id, POINTER_PEN_INFO *p_pen_info);
typedef BOOL(WINAPI *LogicalToPhysicalPointForPerMonitorDPIPtr)(HWND hwnd, LPPOINT lpPoint);
typedef BOOL(WINAPI *PhysicalToLogicalPointForPerMonitorDPIPtr)(HWND hwnd, LPPOINT lpPoint);
+typedef HRESULT(WINAPI *SHLoadIndirectStringPtr)(PCWSTR pszSource, PWSTR pszOutBuf, UINT cchOutBuf, void **ppvReserved);
typedef struct {
BYTE bWidth; // Width, in pixels, of the image
@@ -328,6 +381,9 @@ class DisplayServerWindows : public DisplayServer {
static LogicalToPhysicalPointForPerMonitorDPIPtr win81p_LogicalToPhysicalPointForPerMonitorDPI;
static PhysicalToLogicalPointForPerMonitorDPIPtr win81p_PhysicalToLogicalPointForPerMonitorDPI;
+ // Shell API
+ static SHLoadIndirectStringPtr load_indirect_string;
+
void _update_tablet_ctx(const String &p_old_driver, const String &p_new_driver);
String tablet_driver;
Vector<String> tablet_drivers;
@@ -479,6 +535,11 @@ class DisplayServerWindows : public DisplayServer {
IndicatorID indicator_id_counter = 0;
HashMap<IndicatorID, IndicatorData> indicators;
+ HashMap<int64_t, MouseButton> pointer_prev_button;
+ HashMap<int64_t, MouseButton> pointer_button;
+ HashMap<int64_t, LONG> pointer_down_time;
+ HashMap<int64_t, Vector2> pointer_last_pos;
+
void _send_window_event(const WindowData &wd, WindowEvent p_event);
void _get_window_style(bool p_main_window, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_maximized, bool p_maximized_fs, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex);
@@ -531,6 +592,9 @@ class DisplayServerWindows : public DisplayServer {
Error _file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback, bool p_options_in_cb);
+ String _get_keyboard_layout_display_name(const String &p_klid) const;
+ String _get_klid(HKL p_hkl) const;
+
public:
LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT MouseProc(int code, WPARAM wParam, LPARAM lParam);
diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp
index ef492a6994..77c5d5a499 100644
--- a/scene/3d/lightmap_gi.cpp
+++ b/scene/3d/lightmap_gi.cpp
@@ -1104,10 +1104,12 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
Lightmapper::BakeError bake_err = lightmapper->bake(Lightmapper::BakeQuality(bake_quality), use_denoiser, denoiser_strength, denoiser_range, bounces, bounce_indirect_energy, bias, max_texture_size, directional, use_texture_for_bounces, Lightmapper::GenerateProbes(gen_probes), environment_image, environment_transform, _lightmap_bake_step_function, &bsud, exposure_normalization);
- if (bake_err == Lightmapper::BAKE_ERROR_LIGHTMAP_TOO_SMALL) {
+ if (bake_err == Lightmapper::BAKE_ERROR_TEXTURE_EXCEEDS_MAX_SIZE) {
return BAKE_ERROR_TEXTURE_SIZE_TOO_SMALL;
} else if (bake_err == Lightmapper::BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES) {
return BAKE_ERROR_MESHES_INVALID;
+ } else if (bake_err == Lightmapper::BAKE_ERROR_ATLAS_TOO_SMALL) {
+ return BAKE_ERROR_ATLAS_TOO_SMALL;
}
// POSTBAKE: Save Textures.
@@ -1711,6 +1713,8 @@ void LightmapGI::_bind_methods() {
BIND_ENUM_CONSTANT(BAKE_ERROR_CANT_CREATE_IMAGE);
BIND_ENUM_CONSTANT(BAKE_ERROR_USER_ABORTED);
BIND_ENUM_CONSTANT(BAKE_ERROR_TEXTURE_SIZE_TOO_SMALL);
+ BIND_ENUM_CONSTANT(BAKE_ERROR_LIGHTMAP_TOO_SMALL);
+ BIND_ENUM_CONSTANT(BAKE_ERROR_ATLAS_TOO_SMALL);
BIND_ENUM_CONSTANT(ENVIRONMENT_MODE_DISABLED);
BIND_ENUM_CONSTANT(ENVIRONMENT_MODE_SCENE);
diff --git a/scene/3d/lightmap_gi.h b/scene/3d/lightmap_gi.h
index 527667177b..67480132b6 100644
--- a/scene/3d/lightmap_gi.h
+++ b/scene/3d/lightmap_gi.h
@@ -143,6 +143,7 @@ public:
BAKE_ERROR_USER_ABORTED,
BAKE_ERROR_TEXTURE_SIZE_TOO_SMALL,
BAKE_ERROR_LIGHTMAP_TOO_SMALL,
+ BAKE_ERROR_ATLAS_TOO_SMALL,
};
enum EnvironmentMode {
diff --git a/scene/3d/lightmapper.h b/scene/3d/lightmapper.h
index 39181ad9a2..9aa8ef8ccb 100644
--- a/scene/3d/lightmapper.h
+++ b/scene/3d/lightmapper.h
@@ -143,9 +143,10 @@ public:
};
enum BakeError {
- BAKE_ERROR_LIGHTMAP_TOO_SMALL,
+ BAKE_OK,
+ BAKE_ERROR_TEXTURE_EXCEEDS_MAX_SIZE,
BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES,
- BAKE_OK
+ BAKE_ERROR_ATLAS_TOO_SMALL,
};
enum BakeQuality {
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index b5263add48..3d24b3bbe9 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -301,6 +301,7 @@ void Skeleton3D::setup_simulator() {
sim->is_compat = true;
sim->set_active(false); // Don't run unneeded process.
add_child(simulator);
+ set_animate_physical_bones(animate_physical_bones);
}
#endif // _DISABLE_DEPRECATED
@@ -1097,6 +1098,9 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("physical_bones_start_simulation", "bones"), &Skeleton3D::physical_bones_start_simulation_on, DEFVAL(Array()));
ClassDB::bind_method(D_METHOD("physical_bones_add_collision_exception", "exception"), &Skeleton3D::physical_bones_add_collision_exception);
ClassDB::bind_method(D_METHOD("physical_bones_remove_collision_exception", "exception"), &Skeleton3D::physical_bones_remove_collision_exception);
+
+ ADD_GROUP("Deprecated", "");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "animate_physical_bones"), "set_animate_physical_bones", "get_animate_physical_bones");
#endif // _DISABLE_DEPRECATED
}
@@ -1136,19 +1140,15 @@ Node *Skeleton3D::get_simulator() {
}
void Skeleton3D::set_animate_physical_bones(bool p_enabled) {
+ animate_physical_bones = p_enabled;
PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(simulator);
if (!sim) {
return;
}
- animate_physical_bones = p_enabled;
sim->set_active(animate_physical_bones || sim->is_simulating_physics());
}
bool Skeleton3D::get_animate_physical_bones() const {
- PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(simulator);
- if (!sim) {
- return false;
- }
return animate_physical_bones;
}
diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h
index 2d70aafcad..a009383f45 100644
--- a/scene/3d/skeleton_3d.h
+++ b/scene/3d/skeleton_3d.h
@@ -67,7 +67,7 @@ class Skeleton3D : public Node3D {
GDCLASS(Skeleton3D, Node3D);
#ifndef DISABLE_DEPRECATED
- bool animate_physical_bones = false;
+ bool animate_physical_bones = true;
Node *simulator = nullptr;
void setup_simulator();
#endif // _DISABLE_DEPRECATED
diff --git a/scene/audio/audio_stream_player_internal.cpp b/scene/audio/audio_stream_player_internal.cpp
index 6653e01f25..36c14e03d5 100644
--- a/scene/audio/audio_stream_player_internal.cpp
+++ b/scene/audio/audio_stream_player_internal.cpp
@@ -152,7 +152,6 @@ Ref<AudioStreamPlayback> AudioStreamPlayerInternal::play_basic() {
Ref<AudioSamplePlayback> sample_playback;
sample_playback.instantiate();
sample_playback->stream = stream;
- sample_playback->player_id = node->get_instance_id();
stream_playback->set_sample_playback(sample_playback);
}
} else if (!stream->is_meta_stream()) {
@@ -262,9 +261,6 @@ void AudioStreamPlayerInternal::seek(float p_seconds) {
void AudioStreamPlayerInternal::stop() {
for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
AudioServer::get_singleton()->stop_playback_stream(playback);
- if (_is_sample() && playback->get_sample_playback().is_valid()) {
- AudioServer::get_singleton()->stop_sample_playback(playback->get_sample_playback());
- }
}
stream_playbacks.clear();
diff --git a/scene/gui/aspect_ratio_container.cpp b/scene/gui/aspect_ratio_container.cpp
index 1663a7d602..e9c178e423 100644
--- a/scene/gui/aspect_ratio_container.cpp
+++ b/scene/gui/aspect_ratio_container.cpp
@@ -35,7 +35,7 @@
Size2 AspectRatioContainer::get_minimum_size() const {
Size2 ms;
for (int i = 0; i < get_child_count(); i++) {
- Control *c = as_sortable_control(get_child(i));
+ Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE);
if (!c) {
continue;
}
diff --git a/scene/gui/center_container.cpp b/scene/gui/center_container.cpp
index 1af33d2814..5dc336ceb5 100644
--- a/scene/gui/center_container.cpp
+++ b/scene/gui/center_container.cpp
@@ -36,7 +36,7 @@ Size2 CenterContainer::get_minimum_size() const {
}
Size2 ms;
for (int i = 0; i < get_child_count(); i++) {
- Control *c = as_sortable_control(get_child(i));
+ Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE);
if (!c) {
continue;
}
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index c3c4b1d3fb..00b9a3478a 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -251,6 +251,27 @@ void CodeEdit::_notification(int p_what) {
}
void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
+ Ref<InputEventPanGesture> pan_gesture = p_gui_input;
+ if (pan_gesture.is_valid() && code_completion_active && code_completion_rect.has_point(pan_gesture->get_position())) {
+ const real_t delta = pan_gesture->get_delta().y;
+ code_completion_pan_offset += delta;
+ if (code_completion_pan_offset <= -1.0) {
+ if (code_completion_current_selected > 0) {
+ code_completion_current_selected--;
+ code_completion_force_item_center = -1;
+ queue_redraw();
+ }
+ code_completion_pan_offset += 1.0f;
+ } else if (code_completion_pan_offset >= +1.0) {
+ if (code_completion_current_selected < code_completion_options.size() - 1) {
+ code_completion_current_selected++;
+ code_completion_force_item_center = -1;
+ queue_redraw();
+ }
+ code_completion_pan_offset -= 1.0f;
+ }
+ }
+
Ref<InputEventMouseButton> mb = p_gui_input;
if (mb.is_valid()) {
// Ignore mouse clicks in IME input mode, let TextEdit handle it.
@@ -285,6 +306,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
if (code_completion_current_selected > 0) {
code_completion_current_selected--;
code_completion_force_item_center = -1;
+ code_completion_pan_offset = 0.0f;
queue_redraw();
}
} break;
@@ -292,6 +314,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
if (code_completion_current_selected < code_completion_options.size() - 1) {
code_completion_current_selected++;
code_completion_force_item_center = -1;
+ code_completion_pan_offset = 0.0f;
queue_redraw();
}
} break;
@@ -301,6 +324,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
}
code_completion_current_selected = CLAMP(code_completion_line_ofs + (mb->get_position().y - code_completion_rect.position.y) / get_line_height(), 0, code_completion_options.size() - 1);
+ code_completion_pan_offset = 0.0f;
if (mb->is_double_click()) {
confirm_code_completion();
}
@@ -472,6 +496,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
code_completion_current_selected = code_completion_options.size() - 1;
}
code_completion_force_item_center = -1;
+ code_completion_pan_offset = 0.0f;
queue_redraw();
accept_event();
return;
@@ -483,6 +508,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
code_completion_current_selected = 0;
}
code_completion_force_item_center = -1;
+ code_completion_pan_offset = 0.0f;
queue_redraw();
accept_event();
return;
@@ -490,6 +516,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
if (k->is_action("ui_page_up", true)) {
code_completion_current_selected = MAX(0, code_completion_current_selected - theme_cache.code_completion_max_lines);
code_completion_force_item_center = -1;
+ code_completion_pan_offset = 0.0f;
queue_redraw();
accept_event();
return;
@@ -497,6 +524,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
if (k->is_action("ui_page_down", true)) {
code_completion_current_selected = MIN(code_completion_options.size() - 1, code_completion_current_selected + theme_cache.code_completion_max_lines);
code_completion_force_item_center = -1;
+ code_completion_pan_offset = 0.0f;
queue_redraw();
accept_event();
return;
@@ -2119,6 +2147,7 @@ void CodeEdit::set_code_completion_selected_index(int p_index) {
ERR_FAIL_INDEX(p_index, code_completion_options.size());
code_completion_current_selected = p_index;
code_completion_force_item_center = -1;
+ code_completion_pan_offset = 0.0f;
queue_redraw();
}
@@ -3244,6 +3273,7 @@ void CodeEdit::_update_scroll_selected_line(float p_mouse_y) {
code_completion_current_selected = (int)(percent * (code_completion_options.size() - 1));
code_completion_force_item_center = -1;
+ code_completion_pan_offset = 0.0f;
}
void CodeEdit::_filter_code_completion_candidates_impl() {
@@ -3305,6 +3335,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
if (_should_reset_selected_option_for_new_options(code_completion_options_new)) {
code_completion_current_selected = 0;
+ code_completion_pan_offset = 0.0f;
}
code_completion_options = code_completion_options_new;
@@ -3520,6 +3551,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
code_completion_options_new.sort_custom<CodeCompletionOptionCompare>();
if (_should_reset_selected_option_for_new_options(code_completion_options_new)) {
code_completion_current_selected = 0;
+ code_completion_pan_offset = 0.0f;
}
code_completion_options = code_completion_options_new;
diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h
index 56f8cce548..580435f65e 100644
--- a/scene/gui/code_edit.h
+++ b/scene/gui/code_edit.h
@@ -214,6 +214,7 @@ private:
int code_completion_longest_line = 0;
Rect2i code_completion_rect;
Rect2i code_completion_scroll_rect;
+ float code_completion_pan_offset = 0.0f;
HashSet<char32_t> code_completion_prefixes;
List<ScriptLanguage::CodeCompletionOption> code_completion_option_submitted;
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 1d53edbfa6..d169e82e5d 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -696,7 +696,7 @@ void Control::_update_canvas_item_transform() {
// We use a little workaround to avoid flickering when moving the pivot with _edit_set_pivot()
if (is_inside_tree() && Math::abs(Math::sin(data.rotation * 4.0f)) < 0.00001f && get_viewport()->is_snap_controls_to_pixels_enabled()) {
- xform[2] = xform[2].round();
+ xform[2] = (xform[2] + Vector2(0.5, 0.5)).floor();
}
RenderingServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), xform);
diff --git a/scene/gui/flow_container.cpp b/scene/gui/flow_container.cpp
index f77d66fe98..eedcd473fb 100644
--- a/scene/gui/flow_container.cpp
+++ b/scene/gui/flow_container.cpp
@@ -250,7 +250,7 @@ Size2 FlowContainer::get_minimum_size() const {
Size2i minimum;
for (int i = 0; i < get_child_count(); i++) {
- Control *c = as_sortable_control(get_child(i));
+ Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE);
if (!c) {
continue;
}
diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp
index 925600756a..8ab9b4c1cd 100644
--- a/scene/gui/split_container.cpp
+++ b/scene/gui/split_container.cpp
@@ -122,11 +122,11 @@ void SplitContainerDragger::_notification(int p_what) {
}
}
-Control *SplitContainer::_get_sortable_child(int p_idx) const {
+Control *SplitContainer::_get_sortable_child(int p_idx, SortableVisbilityMode p_visibility_mode) const {
int idx = 0;
for (int i = 0; i < get_child_count(false); i++) {
- Control *c = as_sortable_control(get_child(i, false));
+ Control *c = as_sortable_control(get_child(i, false), p_visibility_mode);
if (!c) {
continue;
}
@@ -258,7 +258,8 @@ Size2 SplitContainer::get_minimum_size() const {
int sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(theme_cache.separation, vertical ? g->get_height() : g->get_width()) : 0;
for (int i = 0; i < 2; i++) {
- if (!_get_sortable_child(i)) {
+ Control *child = _get_sortable_child(i, SortableVisbilityMode::VISIBLE);
+ if (!child) {
break;
}
@@ -270,7 +271,7 @@ Size2 SplitContainer::get_minimum_size() const {
}
}
- Size2 ms = _get_sortable_child(i)->get_combined_minimum_size();
+ Size2 ms = child->get_combined_minimum_size();
if (vertical) {
minimum.height += ms.height;
diff --git a/scene/gui/split_container.h b/scene/gui/split_container.h
index 95f26f5e0b..db870554c2 100644
--- a/scene/gui/split_container.h
+++ b/scene/gui/split_container.h
@@ -82,7 +82,7 @@ private:
Ref<Texture2D> _get_grabber_icon() const;
void _compute_middle_sep(bool p_clamp);
void _resort();
- Control *_get_sortable_child(int p_idx) const;
+ Control *_get_sortable_child(int p_idx, SortableVisbilityMode p_visibility_mode = SortableVisbilityMode::VISIBLE_IN_TREE) const;
protected:
bool is_fixed = false;
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 9cc59f1def..a51ef143fa 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -8143,7 +8143,7 @@ void TextEdit::_update_gutter_width() {
/* Syntax highlighting. */
Dictionary TextEdit::_get_line_syntax_highlighting(int p_line) {
- return syntax_highlighter.is_null() && !setting_text ? Dictionary() : syntax_highlighter->get_line_syntax_highlighting(p_line);
+ return (syntax_highlighter.is_null() || setting_text) ? Dictionary() : syntax_highlighter->get_line_syntax_highlighting(p_line);
}
/* Deprecated. */
diff --git a/scene/main/instance_placeholder.cpp b/scene/main/instance_placeholder.cpp
index f36bbe9395..29166f3d92 100644
--- a/scene/main/instance_placeholder.cpp
+++ b/scene/main/instance_placeholder.cpp
@@ -161,12 +161,12 @@ void InstancePlaceholder::set_value_on_instance(InstancePlaceholder *p_placehold
}
switch (current_type) {
- case Variant::Type::NIL:
- if (placeholder_type != Variant::Type::NODE_PATH) {
+ case Variant::Type::NIL: {
+ Ref<Resource> resource = p_set.value;
+ if (placeholder_type != Variant::Type::NODE_PATH && !resource.is_valid()) {
break;
}
- // If it's nil but we have a NodePath, we guess what works.
-
+ // If it's nil but we have a NodePath or a Resource, we guess what works.
p_instance->set(p_set.name, p_set.value, &is_valid);
if (is_valid) {
break;
@@ -174,13 +174,15 @@ void InstancePlaceholder::set_value_on_instance(InstancePlaceholder *p_placehold
p_instance->set(p_set.name, try_get_node(p_placeholder, p_instance, p_set.value), &is_valid);
break;
- case Variant::Type::OBJECT:
+ }
+ case Variant::Type::OBJECT: {
if (placeholder_type != Variant::Type::NODE_PATH) {
break;
}
// Easiest case, we want a node, but we have a deferred NodePath.
p_instance->set(p_set.name, try_get_node(p_placeholder, p_instance, p_set.value));
break;
+ }
case Variant::Type::ARRAY: {
// If we have reached here it means our array types don't match,
// so we will convert the placeholder array into the correct type
@@ -209,9 +211,10 @@ void InstancePlaceholder::set_value_on_instance(InstancePlaceholder *p_placehold
}
break;
}
- default:
+ default: {
WARN_PRINT(vformat("Property '%s' with type '%s' could not be set when creating instance of '%s'.", p_set.name, Variant::get_type_name(current_type), p_placeholder->get_name()));
break;
+ }
}
}
diff --git a/scene/resources/2d/navigation_polygon.cpp b/scene/resources/2d/navigation_polygon.cpp
index a845809bf2..4a290db86b 100644
--- a/scene/resources/2d/navigation_polygon.cpp
+++ b/scene/resources/2d/navigation_polygon.cpp
@@ -193,6 +193,10 @@ void NavigationPolygon::set_data(const Vector<Vector2> &p_vertices, const Vector
for (int i = 0; i < p_polygons.size(); i++) {
polygons.write[i].indices = p_polygons[i];
}
+ {
+ MutexLock lock(navigation_mesh_generation);
+ navigation_mesh.unref();
+ }
}
void NavigationPolygon::get_data(Vector<Vector2> &r_vertices, Vector<Vector<int>> &r_polygons) {
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index bedac4e933..3b38db6237 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -3897,6 +3897,7 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "Roughness", "ROUGHNESS" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "Point Size", "POINT_SIZE" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "Model View Matrix", "MODELVIEW_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "Projection Matrix", "PROJECTION_MATRIX" },
////////////////////////////////////////////////////////////////////////
// Node3D, Fragment.
////////////////////////////////////////////////////////////////////////
@@ -3961,6 +3962,7 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = {
////////////////////////////////////////////////////////////////////////
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Light", "LIGHT.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "Light Alpha", "LIGHT.a" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Shadow Modulate", "SHADOW_MODULATE.rgb" },
////////////////////////////////////////////////////////////////////////
// Sky, Sky.
diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h
index 9149109381..0ca4777d5c 100644
--- a/servers/audio/audio_stream.h
+++ b/servers/audio/audio_stream.h
@@ -49,7 +49,6 @@ class AudioSamplePlayback : public RefCounted {
public:
Ref<AudioStream> stream;
- ObjectID player_id;
float offset = 0.0f;
Vector<AudioFrame> volume_vector;
StringName bus;
diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp
index fefb8bfd41..54840adcae 100644
--- a/servers/audio_server.cpp
+++ b/servers/audio_server.cpp
@@ -1220,6 +1220,12 @@ void AudioServer::start_playback_stream(Ref<AudioStreamPlayback> p_playback, con
void AudioServer::stop_playback_stream(Ref<AudioStreamPlayback> p_playback) {
ERR_FAIL_COND(p_playback.is_null());
+ // Handle sample playback.
+ if (p_playback->get_is_sample() && p_playback->get_sample_playback().is_valid()) {
+ AudioServer::get_singleton()->stop_sample_playback(p_playback->get_sample_playback());
+ return;
+ }
+
AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback);
if (!playback_node) {
return;
@@ -1358,6 +1364,10 @@ void AudioServer::set_playback_highshelf_params(Ref<AudioStreamPlayback> p_playb
bool AudioServer::is_playback_active(Ref<AudioStreamPlayback> p_playback) {
ERR_FAIL_COND_V(p_playback.is_null(), false);
+ if (p_playback->get_is_sample() && p_playback->get_sample_playback().is_valid()) {
+ return sample_playback_list.has(p_playback->get_sample_playback());
+ }
+
AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback);
if (!playback_node) {
return false;
@@ -1818,11 +1828,13 @@ void AudioServer::unregister_sample(const Ref<AudioSample> &p_sample) {
void AudioServer::start_sample_playback(const Ref<AudioSamplePlayback> &p_playback) {
ERR_FAIL_COND_MSG(p_playback.is_null(), "Parameter p_playback is null.");
AudioDriver::get_singleton()->start_sample_playback(p_playback);
+ sample_playback_list.ordered_insert(p_playback);
}
void AudioServer::stop_sample_playback(const Ref<AudioSamplePlayback> &p_playback) {
ERR_FAIL_COND_MSG(p_playback.is_null(), "Parameter p_playback is null.");
AudioDriver::get_singleton()->stop_sample_playback(p_playback);
+ sample_playback_list.erase(p_playback);
}
void AudioServer::set_sample_playback_pause(const Ref<AudioSamplePlayback> &p_playback, bool p_paused) {
diff --git a/servers/audio_server.h b/servers/audio_server.h
index 4825e24336..84c091e320 100644
--- a/servers/audio_server.h
+++ b/servers/audio_server.h
@@ -329,6 +329,8 @@ private:
friend class AudioDriver;
void _driver_process(int p_frames, int32_t *p_buffer);
+ LocalVector<Ref<AudioStreamPlayback>> sample_playback_list;
+
protected:
static void _bind_methods();
diff --git a/servers/physics_server_2d_wrap_mt.h b/servers/physics_server_2d_wrap_mt.h
index f0c36a906f..5c757264b3 100644
--- a/servers/physics_server_2d_wrap_mt.h
+++ b/servers/physics_server_2d_wrap_mt.h
@@ -45,7 +45,11 @@
#endif
#ifdef DEBUG_ENABLED
+#ifdef DEV_ENABLED
#define MAIN_THREAD_SYNC_WARN WARN_PRINT("Call to " + String(__FUNCTION__) + " causing PhysicsServer2D synchronizations on every frame. This significantly affects performance.");
+#else
+#define MAIN_THREAD_SYNC_WARN
+#endif
#endif
class PhysicsServer2DWrapMT : public PhysicsServer2D {
diff --git a/servers/physics_server_3d_wrap_mt.h b/servers/physics_server_3d_wrap_mt.h
index 0909c46b55..2fd39546a5 100644
--- a/servers/physics_server_3d_wrap_mt.h
+++ b/servers/physics_server_3d_wrap_mt.h
@@ -44,7 +44,11 @@
#endif
#ifdef DEBUG_ENABLED
+#ifdef DEV_ENABLED
#define MAIN_THREAD_SYNC_WARN WARN_PRINT("Call to " + String(__FUNCTION__) + " causing PhysicsServer3D synchronizations on every frame. This significantly affects performance.");
+#else
+#define MAIN_THREAD_SYNC_WARN
+#endif
#endif
class PhysicsServer3DWrapMT : public PhysicsServer3D {
diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
index 9e0dacc1f2..42e1f7b6dc 100644
--- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
@@ -640,7 +640,7 @@ void SceneShaderForwardClustered::init(const String p_defines) {
actions.renames["NODE_POSITION_WORLD"] = "read_model_matrix[3].xyz";
actions.renames["CAMERA_POSITION_WORLD"] = "scene_data.inv_view_matrix[3].xyz";
- actions.renames["CAMERA_DIRECTION_WORLD"] = "scene_data.view_matrix[3].xyz";
+ actions.renames["CAMERA_DIRECTION_WORLD"] = "scene_data.inv_view_matrix[2].xyz";
actions.renames["CAMERA_VISIBLE_LAYERS"] = "scene_data.camera_visible_layers";
actions.renames["NODE_POSITION_VIEW"] = "(scene_data.view_matrix * read_model_matrix)[3].xyz";
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
index af190207db..c03dd96062 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -2058,6 +2058,10 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr
uint32_t base_spec_constants = p_params->spec_constant_base_flags;
+ if (bool(inst->flags_cache & INSTANCE_DATA_FLAG_MULTIMESH)) {
+ base_spec_constants |= 1 << SPEC_CONSTANT_IS_MULTIMESH;
+ }
+
SceneState::PushConstant push_constant;
push_constant.base_index = i + p_params->element_offset;
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
index b0fe552449..34260bd701 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
@@ -81,6 +81,7 @@ private:
SPEC_CONSTANT_DISABLE_DECALS = 13,
SPEC_CONSTANT_DISABLE_FOG = 14,
SPEC_CONSTANT_USE_DEPTH_FOG = 16,
+ SPEC_CONSTANT_IS_MULTIMESH = 17,
};
diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
index dd722cc2dd..cf661bb8f4 100644
--- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
@@ -544,7 +544,7 @@ void SceneShaderForwardMobile::init(const String p_defines) {
actions.renames["NODE_POSITION_WORLD"] = "read_model_matrix[3].xyz";
actions.renames["CAMERA_POSITION_WORLD"] = "scene_data.inv_view_matrix[3].xyz";
- actions.renames["CAMERA_DIRECTION_WORLD"] = "scene_data.view_matrix[3].xyz";
+ actions.renames["CAMERA_DIRECTION_WORLD"] = "scene_data.inv_view_matrix[2].xyz";
actions.renames["CAMERA_VISIBLE_LAYERS"] = "scene_data.camera_visible_layers";
actions.renames["NODE_POSITION_VIEW"] = "(scene_data.view_matrix * read_model_matrix)[3].xyz";
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index fa8cf9c028..e58e45f13e 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -509,11 +509,16 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
current_repeat = RenderingServer::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED;
}
+ Color modulated = rect->modulate * base_color;
+ if (use_linear_colors) {
+ modulated = modulated.srgb_to_linear();
+ }
+
//bind pipeline
if (rect->flags & CANVAS_RECT_LCD) {
RID pipeline = pipeline_variants->variants[light_mode][PIPELINE_VARIANT_QUAD_LCD_BLEND].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format);
RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline);
- RD::get_singleton()->draw_list_set_blend_constants(p_draw_list, rect->modulate);
+ RD::get_singleton()->draw_list_set_blend_constants(p_draw_list, modulated);
} else {
RID pipeline = pipeline_variants->variants[light_mode][PIPELINE_VARIANT_QUAD].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format);
RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline);
@@ -582,11 +587,6 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
push_constant.flags |= FLAGS_USE_LCD;
}
- Color modulated = rect->modulate * base_color;
- if (use_linear_colors) {
- modulated = modulated.srgb_to_linear();
- }
-
push_constant.modulation[0] = modulated.r;
push_constant.modulation[1] = modulated.g;
push_constant.modulation[2] = modulated.b;
diff --git a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl
index 8618f083b3..d629f2738d 100644
--- a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl
@@ -90,7 +90,7 @@ void main() {
if (sc_multiview) {
view_dir = normalize(vertex + scene_data.eye_offset[params.view_index].xyz);
} else {
- view_dir = normalize(vertex);
+ view_dir = params.orthogonal ? vec3(0.0, 0.0, -1.0) : normalize(vertex);
}
vec3 ray_dir = normalize(reflect(view_dir, normal));
diff --git a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_inc.glsl b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_inc.glsl
index 26405ab040..9b692824a1 100644
--- a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_inc.glsl
@@ -20,7 +20,7 @@ vec3 reconstructCSPosition(vec2 screen_pos, float z) {
return pos.xyz;
} else {
if (params.orthogonal) {
- return vec3((screen_pos.xy * params.proj_info.xy + params.proj_info.zw), z);
+ return vec3(-(screen_pos.xy * params.proj_info.xy + params.proj_info.zw), z);
} else {
return vec3((screen_pos.xy * params.proj_info.xy + params.proj_info.zw) * z, z);
}
diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
index 1637326b48..a64b2e10ea 100644
--- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
@@ -76,6 +76,10 @@ void axis_angle_to_tbn(vec3 axis, float angle, out vec3 tangent, out vec3 binorm
normal = omc_axis.zzz * axis + vec3(-s_axis.y, s_axis.x, c);
}
+/* Spec Constants */
+
+layout(constant_id = 17) const bool sc_is_multimesh = false;
+
/* Varyings */
layout(location = 0) highp out vec3 vertex_interp;
@@ -178,8 +182,6 @@ void main() {
color_interp = color_attrib;
#endif
- bool is_multimesh = bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_MULTIMESH);
-
mat4 model_matrix = instances.data[draw_call.instance_index].transform;
mat4 inv_view_matrix = scene_data.inv_view_matrix;
#ifdef USE_DOUBLE_PRECISION
@@ -203,7 +205,7 @@ void main() {
mat4 matrix;
mat4 read_model_matrix = model_matrix;
- if (is_multimesh) {
+ if (sc_is_multimesh) {
//multimesh, instances are for it
#ifdef USE_PARTICLE_TRAILS
@@ -399,7 +401,7 @@ void main() {
// Then we combine the translations from the model matrix and the view matrix using emulated doubles.
// We add the result to the vertex and ignore the final lost precision.
vec3 model_origin = model_matrix[3].xyz;
- if (is_multimesh) {
+ if (sc_is_multimesh) {
vertex = mat3(matrix) * vertex;
model_origin = double_add_vec3(model_origin, model_precision, matrix[3].xyz, vec3(0.0), model_precision);
}
diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp
index c65d3bec95..2784e22429 100644
--- a/servers/rendering/rendering_device.cpp
+++ b/servers/rendering/rendering_device.cpp
@@ -1617,9 +1617,6 @@ Vector<uint8_t> RenderingDevice::texture_get_data(RID p_texture, uint32_t p_laye
thread_local LocalVector<RDD::BufferTextureCopyRegion> command_buffer_texture_copy_regions_vector;
command_buffer_texture_copy_regions_vector.clear();
- uint32_t block_w = 0, block_h = 0;
- get_compressed_image_format_block_dimensions(tex->format, block_w, block_h);
-
uint32_t w = tex->width;
uint32_t h = tex->height;
uint32_t d = tex->depth;
@@ -1635,8 +1632,8 @@ Vector<uint8_t> RenderingDevice::texture_get_data(RID p_texture, uint32_t p_laye
copy_region.texture_region_size.z = d;
command_buffer_texture_copy_regions_vector.push_back(copy_region);
- w = MAX(block_w, w >> 1);
- h = MAX(block_h, h >> 1);
+ w = (w >> 1);
+ h = (h >> 1);
d = MAX(1u, d >> 1);
}
@@ -1653,6 +1650,10 @@ Vector<uint8_t> RenderingDevice::texture_get_data(RID p_texture, uint32_t p_laye
const uint8_t *read_ptr = driver->buffer_map(tmp_buffer);
ERR_FAIL_NULL_V(read_ptr, Vector<uint8_t>());
+ uint32_t block_w = 0;
+ uint32_t block_h = 0;
+ get_compressed_image_format_block_dimensions(tex->format, block_w, block_h);
+
Vector<uint8_t> buffer_data;
uint32_t tight_buffer_size = get_image_format_required_size(tex->format, tex->width, tex->height, tex->depth, tex->mipmaps);
buffer_data.resize(tight_buffer_size);
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 0b1595d988..e35fde406f 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -5548,10 +5548,16 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
}
if (is_sampler_type(call_function->arguments[i].type)) {
- //let's see where our argument comes from
- ERR_CONTINUE(n->type != Node::NODE_TYPE_VARIABLE); //bug? this should always be a variable
- VariableNode *vn = static_cast<VariableNode *>(n);
- StringName varname = vn->name;
+ // Let's see where our argument comes from.
+ StringName varname;
+ if (n->type == Node::NODE_TYPE_VARIABLE) {
+ VariableNode *vn = static_cast<VariableNode *>(n);
+ varname = vn->name;
+ } else if (n->type == Node::NODE_TYPE_ARRAY) {
+ ArrayNode *an = static_cast<ArrayNode *>(n);
+ varname = an->name;
+ }
+
if (shader->uniforms.has(varname)) {
//being sampler, this either comes from a uniform
ShaderNode::Uniform *u = &shader->uniforms[varname];
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 5bc76026c7..9a56f6baa4 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -882,7 +882,7 @@ instead of `miniz.h` as an external dependency.
## thorvg
- Upstream: https://github.com/thorvg/thorvg
-- Version: 0.14.1 (70b2f2dad158316dd08166d613b425248b36fd27, 2024)
+- Version: 0.14.2 (f6c4d8a94e0b2194fe911d6e19a550683055dd50, 2024)
- License: MIT
Files extracted from upstream source:
diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h
index 4be7e3936d..8c185ccbca 100644
--- a/thirdparty/thorvg/inc/config.h
+++ b/thirdparty/thorvg/inc/config.h
@@ -15,5 +15,5 @@
// For internal debugging:
//#define THORVG_LOG_ENABLED
-#define THORVG_VERSION_STRING "0.14.1"
+#define THORVG_VERSION_STRING "0.14.2"
#endif
diff --git a/thirdparty/thorvg/inc/thorvg.h b/thirdparty/thorvg/inc/thorvg.h
index 20f1942a57..47414d851a 100644
--- a/thirdparty/thorvg/inc/thorvg.h
+++ b/thirdparty/thorvg/inc/thorvg.h
@@ -281,6 +281,9 @@ public:
* The rotational axis passes through the point on the object with zero coordinates.
*
* @param[in] degree The value of the angle in degrees.
+ *
+ * @retval Result::InsufficientCondition in case a custom transform is applied.
+ * @see Paint::transform()
*/
Result rotate(float degree) noexcept;
@@ -288,6 +291,9 @@ public:
* @brief Sets the scale value of the object.
*
* @param[in] factor The value of the scaling factor. The default value is 1.
+ *
+ * @retval Result::InsufficientCondition in case a custom transform is applied.
+ * @see Paint::transform()
*/
Result scale(float factor) noexcept;
@@ -299,6 +305,9 @@ public:
*
* @param[in] x The value of the horizontal shift.
* @param[in] y The value of the vertical shift.
+ *
+ * @retval Result::InsufficientCondition in case a custom transform is applied.
+ * @see Paint::transform()
*/
Result translate(float x, float y) noexcept;
diff --git a/thirdparty/thorvg/src/common/tvgLines.cpp b/thirdparty/thorvg/src/common/tvgLines.cpp
index da48f9eae2..9d704900a5 100644
--- a/thirdparty/thorvg/src/common/tvgLines.cpp
+++ b/thirdparty/thorvg/src/common/tvgLines.cpp
@@ -238,7 +238,7 @@ float bezAngleAt(const Bezier& bz, float t)
pt.x *= 3;
pt.y *= 3;
- return mathRad2Deg(atan2(pt.x, pt.y));
+ return mathRad2Deg(mathAtan2(pt.y, pt.x));
}
diff --git a/thirdparty/thorvg/src/common/tvgLock.h b/thirdparty/thorvg/src/common/tvgLock.h
index d8bf7269f6..59f68d0d44 100644
--- a/thirdparty/thorvg/src/common/tvgLock.h
+++ b/thirdparty/thorvg/src/common/tvgLock.h
@@ -28,6 +28,7 @@
#define _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR
#include <mutex>
+#include "tvgTaskScheduler.h"
namespace tvg {
@@ -42,13 +43,17 @@ namespace tvg {
ScopedLock(Key& k)
{
- k.mtx.lock();
- key = &k;
+ if (TaskScheduler::threads() > 0) {
+ k.mtx.lock();
+ key = &k;
+ }
}
~ScopedLock()
{
- key->mtx.unlock();
+ if (TaskScheduler::threads() > 0) {
+ key->mtx.unlock();
+ }
}
};
diff --git a/thirdparty/thorvg/src/common/tvgMath.cpp b/thirdparty/thorvg/src/common/tvgMath.cpp
index e99ec46681..c56b32249f 100644
--- a/thirdparty/thorvg/src/common/tvgMath.cpp
+++ b/thirdparty/thorvg/src/common/tvgMath.cpp
@@ -22,6 +22,20 @@
#include "tvgMath.h"
+//see: https://en.wikipedia.org/wiki/Remez_algorithm
+float mathAtan2(float y, float x)
+{
+ if (y == 0.0f && x == 0.0f) return 0.0f;
+
+ auto a = std::min(fabsf(x), fabsf(y)) / std::max(fabsf(x), fabsf(y));
+ auto s = a * a;
+ auto r = ((-0.0464964749f * s + 0.15931422f) * s - 0.327622764f) * s * a + a;
+ if (fabsf(y) > fabsf(x)) r = 1.57079637f - r;
+ if (x < 0) r = 3.14159274f - r;
+ if (y < 0) return -r;
+ return r;
+}
+
bool mathInverse(const Matrix* m, Matrix* out)
{
diff --git a/thirdparty/thorvg/src/common/tvgMath.h b/thirdparty/thorvg/src/common/tvgMath.h
index 3555885c8e..668260c689 100644
--- a/thirdparty/thorvg/src/common/tvgMath.h
+++ b/thirdparty/thorvg/src/common/tvgMath.h
@@ -42,6 +42,7 @@
/* General functions */
/************************************************************************/
+float mathAtan2(float y, float x);
static inline float mathDeg2Rad(float degree)
{
@@ -79,7 +80,7 @@ bool operator==(const Matrix& lhs, const Matrix& rhs);
static inline bool mathRightAngle(const Matrix* m)
{
- auto radian = fabsf(atan2f(m->e21, m->e11));
+ auto radian = fabsf(mathAtan2(m->e21, m->e11));
if (radian < FLOAT_EPSILON || mathEqual(radian, MATH_PI2) || mathEqual(radian, MATH_PI)) return true;
return false;
}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
index ae784ccfd7..8fbf3816ea 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
@@ -647,9 +647,9 @@ static bool _hslToRgb(float hue, float saturation, float brightness, uint8_t* re
}
}
- *red = static_cast<uint8_t>(ceil(_red * 255.0f));
- *green = static_cast<uint8_t>(ceil(_green * 255.0f));
- *blue = static_cast<uint8_t>(ceil(_blue * 255.0f));
+ *red = (uint8_t)nearbyint(_red * 255.0f);
+ *green = (uint8_t)nearbyint(_green * 255.0f);
+ *blue = (uint8_t)nearbyint(_blue * 255.0f);
return true;
}
@@ -3254,19 +3254,34 @@ static void _clonePostponedNodes(Array<SvgNodeIdPair>* cloneNodes, SvgNode* doc)
}
-static void _svgLoaderParserXmlClose(SvgLoaderData* loader, const char* content)
+static void _svgLoaderParserXmlClose(SvgLoaderData* loader, const char* content, unsigned int length)
{
+ const char* itr = nullptr;
+ int sz = length;
+ char tagName[20] = "";
+
content = _skipSpace(content, nullptr);
+ itr = content;
+ while ((itr != nullptr) && *itr != '>') itr++;
+
+ if (itr) {
+ sz = itr - content;
+ while ((sz > 0) && (isspace(content[sz - 1]))) sz--;
+ if ((unsigned int)sz >= sizeof(tagName)) sz = sizeof(tagName) - 1;
+ strncpy(tagName, content, sz);
+ tagName[sz] = '\0';
+ }
+ else return;
for (unsigned int i = 0; i < sizeof(groupTags) / sizeof(groupTags[0]); i++) {
- if (!strncmp(content, groupTags[i].tag, groupTags[i].sz - 1)) {
+ if (!strncmp(tagName, groupTags[i].tag, sz)) {
loader->stack.pop();
break;
}
}
for (unsigned int i = 0; i < sizeof(graphicsTags) / sizeof(graphicsTags[0]); i++) {
- if (!strncmp(content, graphicsTags[i].tag, graphicsTags[i].sz - 1)) {
+ if (!strncmp(tagName, graphicsTags[i].tag, sz)) {
loader->currentGraphicsNode = nullptr;
loader->stack.pop();
break;
@@ -3437,7 +3452,7 @@ static bool _svgLoaderParser(void* data, SimpleXMLType type, const char* content
break;
}
case SimpleXMLType::Close: {
- _svgLoaderParserXmlClose(loader, content);
+ _svgLoaderParserXmlClose(loader, content, length);
break;
}
case SimpleXMLType::Data:
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
index 63e9ce53c8..115e81aee1 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
@@ -194,10 +194,10 @@ void _pathAppendArcTo(Array<PathCommand>* cmds, Array<Point>* pts, Point* cur, P
//We dont' use arccos (as per w3c doc), see
//http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm
//Note: atan2 (0.0, 1.0) == 0.0
- at = atan2(((y1p - cyp) / ry), ((x1p - cxp) / rx));
+ at = mathAtan2(((y1p - cyp) / ry), ((x1p - cxp) / rx));
theta1 = (at < 0.0f) ? 2.0f * MATH_PI + at : at;
- nat = atan2(((-y1p - cyp) / ry), ((-x1p - cxp) / rx));
+ nat = mathAtan2(((-y1p - cyp) / ry), ((-x1p - cxp) / rx));
deltaTheta = (nat < at) ? 2.0f * MATH_PI - at + nat : nat - at;
if (sweep) {
diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp
index c162945501..e1d41a0d52 100644
--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp
+++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp
@@ -114,8 +114,8 @@ bool imagePrepare(SwImage* image, const RenderMesh* mesh, const Matrix* transfor
//Fast track: Non-transformed image but just shifted.
if (image->direct) {
- image->ox = -static_cast<int32_t>(round(transform->e13));
- image->oy = -static_cast<int32_t>(round(transform->e23));
+ image->ox = -static_cast<int32_t>(nearbyint(transform->e13));
+ image->oy = -static_cast<int32_t>(nearbyint(transform->e23));
//Figure out the scale factor by transform matrix
} else {
auto scaleX = sqrtf((transform->e11 * transform->e11) + (transform->e21 * transform->e21));
diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp
index ad5a2b7371..ae158c836a 100644
--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp
+++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp
@@ -164,8 +164,8 @@ void mathRotate(SwPoint& pt, SwFixed angle)
auto cosv = cosf(radian);
auto sinv = sinf(radian);
- pt.x = SwCoord(roundf((v.x * cosv - v.y * sinv) * 64.0f));
- pt.y = SwCoord(roundf((v.x * sinv + v.y * cosv) * 64.0f));
+ pt.x = SwCoord(nearbyint((v.x * cosv - v.y * sinv) * 64.0f));
+ pt.y = SwCoord(nearbyint((v.x * sinv + v.y * cosv) * 64.0f));
}
@@ -179,7 +179,7 @@ SwFixed mathTan(SwFixed angle)
SwFixed mathAtan(const SwPoint& pt)
{
if (pt.zero()) return 0;
- return SwFixed(atan2f(TO_FLOAT(pt.y), TO_FLOAT(pt.x)) * (180.0f / MATH_PI) * 65536.0f);
+ return SwFixed(mathAtan2(TO_FLOAT(pt.y), TO_FLOAT(pt.x)) * (180.0f / MATH_PI) * 65536.0f);
}
@@ -309,10 +309,10 @@ bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, S
//the rasterization region has to be rearranged.
//https://github.com/Samsung/thorvg/issues/916
if (fastTrack) {
- renderRegion.min.x = static_cast<SwCoord>(round(xMin / 64.0f));
- renderRegion.max.x = static_cast<SwCoord>(round(xMax / 64.0f));
- renderRegion.min.y = static_cast<SwCoord>(round(yMin / 64.0f));
- renderRegion.max.y = static_cast<SwCoord>(round(yMax / 64.0f));
+ renderRegion.min.x = static_cast<SwCoord>(nearbyint(xMin / 64.0f));
+ renderRegion.max.x = static_cast<SwCoord>(nearbyint(xMax / 64.0f));
+ renderRegion.min.y = static_cast<SwCoord>(nearbyint(yMin / 64.0f));
+ renderRegion.max.y = static_cast<SwCoord>(nearbyint(yMax / 64.0f));
} else {
renderRegion.min.x = xMin >> 6;
renderRegion.max.x = (xMax + 63) >> 6;
diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp
index b3507acdc3..042d1e2b44 100644
--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp
+++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp
@@ -383,7 +383,8 @@ static bool _rasterMattedRect(SwSurface* surface, const SwBBox& region, uint8_t
auto dst = &buffer[y * surface->stride];
auto cmp = &cbuffer[y * surface->compositor->image.stride * csize];
for (uint32_t x = 0; x < w; ++x, ++dst, cmp += csize) {
- *dst = INTERPOLATE(color, *dst, alpha(cmp));
+ auto tmp = ALPHA_BLEND(color, alpha(cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
}
}
//8bits grayscale
@@ -674,7 +675,7 @@ static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g,
auto sy = (y) * itransform->e22 + itransform->e23 - 0.49f; \
if (sy <= -0.5f || (uint32_t)(sy + 0.5f) >= image->h) continue; \
if (scaleMethod == _interpDownScaler) { \
- auto my = (int32_t)round(sy); \
+ auto my = (int32_t)nearbyint(sy); \
miny = my - (int32_t)sampleSize; \
if (miny < 0) miny = 0; \
maxy = my + (int32_t)sampleSize; \
diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp
index 1e5c4ef409..25c6cd90b9 100644
--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp
+++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp
@@ -197,7 +197,6 @@
/* Internal Class Implementation */
/************************************************************************/
-constexpr auto MAX_SPANS = 256;
constexpr auto PIXEL_BITS = 8; //must be at least 6 bits!
constexpr auto ONE_PIXEL = (1L << PIXEL_BITS);
@@ -240,10 +239,6 @@ struct RleWorker
SwOutline* outline;
- SwSpan spans[MAX_SPANS];
- int spansCnt;
- int ySpan;
-
int bandSize;
int bandShoot;
@@ -301,26 +296,6 @@ static inline SwCoord HYPOT(SwPoint pt)
return ((pt.x > pt.y) ? (pt.x + (3 * pt.y >> 3)) : (pt.y + (3 * pt.x >> 3)));
}
-static void _genSpan(SwRleData* rle, const SwSpan* spans, uint32_t count)
-{
- auto newSize = rle->size + count;
-
- /* allocate enough memory for new spans */
- /* alloc is required to prevent free and reallocation */
- /* when the rle needs to be regenerated because of attribute change. */
- if (rle->alloc < newSize) {
- rle->alloc = (newSize * 2);
- //OPTIMIZE: use mempool!
- rle->spans = static_cast<SwSpan*>(realloc(rle->spans, rle->alloc * sizeof(SwSpan)));
- }
-
- //copy the new spans to the allocated memory
- SwSpan* lastSpan = rle->spans + rle->size;
- memcpy(lastSpan, spans, count * sizeof(SwSpan));
-
- rle->size = newSize;
-}
-
static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoord acount)
{
@@ -344,25 +319,26 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor
if (coverage > 255) coverage = 255;
}
+ if (coverage == 0) return;
+
//span has ushort coordinates. check limit overflow
if (x >= SHRT_MAX) {
- TVGERR("SW_ENGINE", "X-coordiante overflow!");
- x = SHRT_MAX;
+ TVGERR("SW_ENGINE", "X-coordinate overflow!");
+ return;
}
if (y >= SHRT_MAX) {
- TVGERR("SW_ENGINE", "Y Coordiante overflow!");
- y = SHRT_MAX;
+ TVGERR("SW_ENGINE", "Y-coordinate overflow!");
+ return;
}
- if (coverage > 0) {
- if (!rw.antiAlias) coverage = 255;
- auto count = rw.spansCnt;
- auto span = rw.spans + count - 1;
+ auto rle = rw.rle;
- //see whether we can add this span to the current list
- if ((count > 0) && (rw.ySpan == y) &&
- (span->x + span->len == x) && (span->coverage == coverage)) {
+ if (!rw.antiAlias) coverage = 255;
+ //see whether we can add this span to the current list
+ if (rle->size > 0) {
+ auto span = rle->spans + rle->size - 1;
+ if ((span->coverage == coverage) && (span->y == y) && (span->x + span->len == x)) {
//Clip x range
SwCoord xOver = 0;
if (x + acount >= rw.cellMax.x) xOver -= (x + acount - rw.cellMax.x);
@@ -372,35 +348,35 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor
span->len += (acount + xOver);
return;
}
+ }
- if (count >= MAX_SPANS) {
- _genSpan(rw.rle, rw.spans, count);
- rw.spansCnt = 0;
- rw.ySpan = 0;
- span = rw.spans;
- } else {
- ++span;
+ //span pool is full, grow it.
+ if (rle->size >= rle->alloc) {
+ auto newSize = (rle->size > 0) ? (rle->size * 2) : 256;
+ if (rle->alloc < newSize) {
+ rle->alloc = newSize;
+ rle->spans = static_cast<SwSpan*>(realloc(rle->spans, rle->alloc * sizeof(SwSpan)));
}
+ }
- //Clip x range
- SwCoord xOver = 0;
- if (x + acount >= rw.cellMax.x) xOver -= (x + acount - rw.cellMax.x);
- if (x < rw.cellMin.x) {
- xOver -= (rw.cellMin.x - x);
- x = rw.cellMin.x;
- }
+ //Clip x range
+ SwCoord xOver = 0;
+ if (x + acount >= rw.cellMax.x) xOver -= (x + acount - rw.cellMax.x);
+ if (x < rw.cellMin.x) {
+ xOver -= (rw.cellMin.x - x);
+ x = rw.cellMin.x;
+ }
- //Nothing to draw
- if (acount + xOver <= 0) return;
+ //Nothing to draw
+ if (acount + xOver <= 0) return;
- //add a span to the current list
- span->x = x;
- span->y = y;
- span->len = (acount + xOver);
- span->coverage = coverage;
- ++rw.spansCnt;
- rw.ySpan = y;
- }
+ //add a span to the current list
+ auto span = rle->spans + rle->size;
+ span->x = x;
+ span->y = y;
+ span->len = (acount + xOver);
+ span->coverage = coverage;
+ rle->size++;
}
@@ -408,9 +384,6 @@ static void _sweep(RleWorker& rw)
{
if (rw.cellsCnt == 0) return;
- rw.spansCnt = 0;
- rw.ySpan = 0;
-
for (int y = 0; y < rw.yCnt; ++y) {
auto cover = 0;
auto x = 0;
@@ -427,8 +400,6 @@ static void _sweep(RleWorker& rw)
if (cover != 0) _horizLine(rw, x, y, cover * (ONE_PIXEL * 2), rw.cellXCnt - x);
}
-
- if (rw.spansCnt > 0) _genSpan(rw.rle, rw.spans, rw.spansCnt);
}
@@ -926,7 +897,6 @@ SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& ren
rw.cellMax = renderRegion.max;
rw.cellXCnt = rw.cellMax.x - rw.cellMin.x;
rw.cellYCnt = rw.cellMax.y - rw.cellMin.y;
- rw.ySpan = 0;
rw.outline = const_cast<SwOutline*>(outline);
rw.bandSize = rw.bufferSize / (sizeof(Cell) * 2); //bandSize: 256
rw.bandShoot = 0;
@@ -1019,7 +989,6 @@ SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& ren
error:
free(rw.rle);
- rw.rle = nullptr;
return nullptr;
}
diff --git a/thirdparty/thorvg/src/renderer/tvgPaint.cpp b/thirdparty/thorvg/src/renderer/tvgPaint.cpp
index ff0f75dc0f..0ce6540f20 100644
--- a/thirdparty/thorvg/src/renderer/tvgPaint.cpp
+++ b/thirdparty/thorvg/src/renderer/tvgPaint.cpp
@@ -87,7 +87,7 @@ static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Ren
if (ptsCnt != 4) return Result::InsufficientCondition;
- if (rTransform) rTransform->update();
+ if (rTransform && (cmpTarget->pImpl->renderFlag & RenderUpdateFlag::Transform)) rTransform->update();
//No rotation and no skewing, still can try out clipping the rect region.
auto tryClip = false;
@@ -181,13 +181,14 @@ Paint* Paint::Impl::duplicate()
bool Paint::Impl::rotate(float degree)
{
if (rTransform) {
+ if (rTransform->overriding) return false;
if (mathEqual(degree, rTransform->degree)) return true;
} else {
if (mathZero(degree)) return true;
rTransform = new RenderTransform();
}
rTransform->degree = degree;
- if (!rTransform->overriding) renderFlag |= RenderUpdateFlag::Transform;
+ renderFlag |= RenderUpdateFlag::Transform;
return true;
}
@@ -196,13 +197,14 @@ bool Paint::Impl::rotate(float degree)
bool Paint::Impl::scale(float factor)
{
if (rTransform) {
+ if (rTransform->overriding) return false;
if (mathEqual(factor, rTransform->scale)) return true;
} else {
if (mathEqual(factor, 1.0f)) return true;
rTransform = new RenderTransform();
}
rTransform->scale = factor;
- if (!rTransform->overriding) renderFlag |= RenderUpdateFlag::Transform;
+ renderFlag |= RenderUpdateFlag::Transform;
return true;
}
@@ -211,14 +213,15 @@ bool Paint::Impl::scale(float factor)
bool Paint::Impl::translate(float x, float y)
{
if (rTransform) {
- if (mathEqual(x, rTransform->x) && mathEqual(y, rTransform->y)) return true;
+ if (rTransform->overriding) return false;
+ if (mathEqual(x, rTransform->m.e13) && mathEqual(y, rTransform->m.e23)) return true;
} else {
if (mathZero(x) && mathZero(y)) return true;
rTransform = new RenderTransform();
}
- rTransform->x = x;
- rTransform->y = y;
- if (!rTransform->overriding) renderFlag |= RenderUpdateFlag::Transform;
+ rTransform->m.e13 = x;
+ rTransform->m.e23 = y;
+ renderFlag |= RenderUpdateFlag::Transform;
return true;
}
@@ -263,10 +266,7 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const RenderTransform* pT
this->renderer = renderer;
}
- if (renderFlag & RenderUpdateFlag::Transform) {
- if (!rTransform) return nullptr;
- rTransform->update();
- }
+ if (renderFlag & RenderUpdateFlag::Transform) rTransform->update();
/* 1. Composition Pre Processing */
RenderData trd = nullptr; //composite target render data
@@ -390,28 +390,28 @@ Paint :: ~Paint()
Result Paint::rotate(float degree) noexcept
{
if (pImpl->rotate(degree)) return Result::Success;
- return Result::FailedAllocation;
+ return Result::InsufficientCondition;
}
Result Paint::scale(float factor) noexcept
{
if (pImpl->scale(factor)) return Result::Success;
- return Result::FailedAllocation;
+ return Result::InsufficientCondition;
}
Result Paint::translate(float x, float y) noexcept
{
if (pImpl->translate(x, y)) return Result::Success;
- return Result::FailedAllocation;
+ return Result::InsufficientCondition;
}
Result Paint::transform(const Matrix& m) noexcept
{
if (pImpl->transform(m)) return Result::Success;
- return Result::FailedAllocation;
+ return Result::InsufficientCondition;
}
diff --git a/thirdparty/thorvg/src/renderer/tvgPaint.h b/thirdparty/thorvg/src/renderer/tvgPaint.h
index c7eb68b198..bc07ab52ab 100644
--- a/thirdparty/thorvg/src/renderer/tvgPaint.h
+++ b/thirdparty/thorvg/src/renderer/tvgPaint.h
@@ -87,7 +87,6 @@ namespace tvg
if (!rTransform) {
if (mathIdentity(&m)) return true;
rTransform = new RenderTransform();
- if (!rTransform) return false;
}
rTransform->override(m);
renderFlag |= RenderUpdateFlag::Transform;
@@ -98,7 +97,7 @@ namespace tvg
Matrix* transform()
{
if (rTransform) {
- rTransform->update();
+ if (renderFlag & RenderUpdateFlag::Transform) rTransform->update();
return &rTransform->m;
}
return nullptr;
diff --git a/thirdparty/thorvg/src/renderer/tvgPicture.h b/thirdparty/thorvg/src/renderer/tvgPicture.h
index 91c16eb44e..bd7021218a 100644
--- a/thirdparty/thorvg/src/renderer/tvgPicture.h
+++ b/thirdparty/thorvg/src/renderer/tvgPicture.h
@@ -92,18 +92,19 @@ struct Picture::Impl
RenderData update(RenderMethod* renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
{
- auto flag = load();
+ auto flag = static_cast<RenderUpdateFlag>(pFlag | load());
if (surface) {
+ if (flag == RenderUpdateFlag::None) return rd;
auto transform = resizeTransform(pTransform);
- rd = renderer->prepare(surface, &rm, rd, &transform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | flag));
+ rd = renderer->prepare(surface, &rm, rd, &transform, clips, opacity, flag);
} else if (paint) {
if (resizing) {
loader->resize(paint, w, h);
resizing = false;
}
needComp = needComposition(opacity) ? true : false;
- rd = paint->pImpl->update(renderer, pTransform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | flag), clipper);
+ rd = paint->pImpl->update(renderer, pTransform, clips, opacity, flag, clipper);
}
return rd;
}
@@ -200,6 +201,7 @@ struct Picture::Impl
if (loader) {
dup->loader = loader;
++dup->loader->sharing;
+ PP(ret)->renderFlag |= RenderUpdateFlag::Image;
}
dup->surface = surface;
diff --git a/thirdparty/thorvg/src/renderer/tvgRender.cpp b/thirdparty/thorvg/src/renderer/tvgRender.cpp
index 9c779f7c00..82145b9aa4 100644
--- a/thirdparty/thorvg/src/renderer/tvgRender.cpp
+++ b/thirdparty/thorvg/src/renderer/tvgRender.cpp
@@ -32,6 +32,20 @@
/* External Class Implementation */
/************************************************************************/
+uint32_t RenderMethod::ref()
+{
+ ScopedLock lock(key);
+ return (++refCnt);
+}
+
+
+uint32_t RenderMethod::unref()
+{
+ ScopedLock lock(key);
+ return (--refCnt);
+}
+
+
void RenderTransform::override(const Matrix& m)
{
this->m = m;
@@ -43,13 +57,18 @@ void RenderTransform::update()
{
if (overriding) return;
- mathIdentity(&m);
+ m.e11 = 1.0f;
+ m.e12 = 0.0f;
- mathScale(&m, scale, scale);
+ m.e21 = 0.0f;
+ m.e22 = 1.0f;
- mathRotate(&m, degree);
+ m.e31 = 0.0f;
+ m.e32 = 0.0f;
+ m.e33 = 1.0f;
- mathTranslate(&m, x, y);
+ mathScale(&m, scale, scale);
+ mathRotate(&m, degree);
}
diff --git a/thirdparty/thorvg/src/renderer/tvgRender.h b/thirdparty/thorvg/src/renderer/tvgRender.h
index a915d68fec..ff55748033 100644
--- a/thirdparty/thorvg/src/renderer/tvgRender.h
+++ b/thirdparty/thorvg/src/renderer/tvgRender.h
@@ -112,9 +112,7 @@ struct RenderRegion
struct RenderTransform
{
- Matrix m; //3x3 Matrix Elements
- float x = 0.0f;
- float y = 0.0f;
+ Matrix m;
float degree = 0.0f; //rotation degree
float scale = 1.0f; //scale factor
bool overriding = false; //user transform?
@@ -122,7 +120,11 @@ struct RenderTransform
void update();
void override(const Matrix& m);
- RenderTransform() {}
+ RenderTransform()
+ {
+ m.e13 = m.e23 = 0.0f;
+ }
+
RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs);
};
@@ -246,17 +248,8 @@ private:
Key key;
public:
- uint32_t ref()
- {
- ScopedLock lock(key);
- return (++refCnt);
- }
-
- uint32_t unref()
- {
- ScopedLock lock(key);
- return (--refCnt);
- }
+ uint32_t ref();
+ uint32_t unref();
virtual ~RenderMethod() {}
virtual RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) = 0;
diff --git a/thirdparty/thorvg/src/renderer/tvgShape.h b/thirdparty/thorvg/src/renderer/tvgShape.h
index ecc58b6cc0..ebc0b304ab 100644
--- a/thirdparty/thorvg/src/renderer/tvgShape.h
+++ b/thirdparty/thorvg/src/renderer/tvgShape.h
@@ -96,7 +96,9 @@ struct Shape::Impl
}
RenderData update(RenderMethod* renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
- {
+ {
+ if (static_cast<RenderUpdateFlag>(pFlag | flag) == RenderUpdateFlag::None) return rd;
+
if ((needComp = needComposition(opacity))) {
/* Overriding opacity value. If this scene is half-translucent,
It must do intermeidate composition with that opacity value. */
diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh
index e964e5ab6d..1a68daf3c5 100755
--- a/thirdparty/thorvg/update-thorvg.sh
+++ b/thirdparty/thorvg/update-thorvg.sh
@@ -1,6 +1,6 @@
#!/bin/bash -e
-VERSION=0.14.1
+VERSION=0.14.2
cd thirdparty/thorvg/ || true
rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/