summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/CODEOWNERS64
-rw-r--r--.github/workflows/windows_builds.yml27
-rw-r--r--core/config/project_settings.cpp19
-rw-r--r--core/io/resource_format_binary.cpp38
-rw-r--r--core/math/geometry_2d.cpp12
-rw-r--r--doc/classes/EditorContextMenuPlugin.xml18
-rw-r--r--doc/classes/Environment.xml3
-rw-r--r--doc/classes/PackedVector2Array.xml2
-rw-r--r--drivers/d3d12/SCsub6
-rw-r--r--drivers/d3d12/dxil_hash.cpp2
-rw-r--r--drivers/d3d12/rendering_device_driver_d3d12.cpp9
-rw-r--r--drivers/egl/egl_manager.cpp65
-rw-r--r--drivers/egl/egl_manager.h7
-rw-r--r--drivers/gles3/rasterizer_gles3.cpp21
-rw-r--r--drivers/gles3/rasterizer_gles3.h10
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp1
-rw-r--r--drivers/gles3/shaders/scene.glsl2
-rw-r--r--drivers/gles3/storage/material_storage.cpp1
-rw-r--r--drivers/gles3/storage/mesh_storage.cpp6
-rw-r--r--drivers/gles3/storage/mesh_storage.h2
-rw-r--r--drivers/metal/rendering_device_driver_metal.mm3
-rw-r--r--editor/animation_track_editor.cpp13
-rw-r--r--editor/editor_node.cpp37
-rw-r--r--editor/editor_node.h2
-rw-r--r--editor/editor_paths.cpp16
-rw-r--r--editor/editor_properties.cpp11
-rw-r--r--editor/editor_properties.h2
-rw-r--r--editor/editor_resource_picker.cpp34
-rw-r--r--editor/editor_resource_picker.h2
-rw-r--r--editor/editor_settings.cpp23
-rw-r--r--editor/export/export_template_manager.cpp3
-rw-r--r--editor/gui/editor_file_dialog.cpp44
-rw-r--r--editor/gui/editor_quick_open_dialog.cpp2
-rw-r--r--editor/multi_node_edit.cpp18
-rw-r--r--editor/plugins/editor_context_menu_plugin.cpp22
-rw-r--r--editor/plugins/editor_context_menu_plugin.h2
-rw-r--r--editor/plugins/editor_preview_plugins.cpp9
-rw-r--r--editor/plugins/font_config_plugin.cpp8
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp56
-rw-r--r--editor/plugins/node_3d_editor_plugin.h2
-rw-r--r--editor/plugins/theme_editor_plugin.cpp78
-rw-r--r--editor/plugins/theme_editor_plugin.h1
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp5
-rw-r--r--editor/project_manager/project_dialog.cpp15
-rw-r--r--editor/project_settings_editor.cpp6
-rwxr-xr-xmisc/scripts/install_vulkan_sdk_macos.sh16
-rw-r--r--modules/bcdec/image_decompress_bcdec.cpp35
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp15
-rw-r--r--modules/gdscript/gdscript_editor.cpp14
-rw-r--r--modules/gdscript/tests/scripts/.editorconfig2
-rw-r--r--modules/mono/editor/bindings_generator.cpp69
-rw-r--r--modules/mono/editor/bindings_generator.h3
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs68
-rw-r--r--modules/noise/doc_classes/FastNoiseLite.xml4
-rw-r--r--platform/ios/detect.py4
-rw-r--r--platform/macos/detect.py4
-rw-r--r--platform/windows/detect.py7
-rw-r--r--scene/2d/skeleton_2d.cpp44
-rw-r--r--scene/2d/skeleton_2d.h2
-rw-r--r--scene/3d/node_3d.cpp22
-rw-r--r--scene/3d/skeleton_3d.cpp223
-rw-r--r--scene/3d/skeleton_3d.h13
-rw-r--r--scene/gui/graph_edit.cpp4
-rw-r--r--scene/gui/scroll_container.cpp2
-rw-r--r--scene/gui/tree.cpp8
-rw-r--r--scene/main/scene_tree.cpp16
-rw-r--r--scene/resources/material.cpp9
-rw-r--r--scene/resources/resource_format_text.cpp76
-rw-r--r--servers/rendering/renderer_canvas_cull.cpp139
-rw-r--r--servers/rendering/renderer_canvas_cull.h18
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp5
-rw-r--r--servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp7
-rw-r--r--servers/rendering/renderer_rd/storage_rd/mesh_storage.h2
-rw-r--r--servers/rendering/shader_compiler.cpp4
-rw-r--r--servers/rendering/shader_language.cpp4
76 files changed, 1008 insertions, 562 deletions
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 748d787b86..68bd4bc4ce 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -2,7 +2,7 @@
# Each line is a file pattern followed by one or more owners.
# Owners can be @users, @org/teams or emails
-# Buildsystem
+# Buildsystem (Before everything to be overwritten)
* @godotengine/buildsystem
@@ -17,10 +17,11 @@
# Doc
/doc/ @godotengine/documentation
-**/doc_classes/ @godotengine/documentation
# Drivers
+/drivers/ @godotengine/_systems
+
## Audio
/drivers/alsa/ @godotengine/audio
/drivers/alsamidi/ @godotengine/audio
@@ -34,7 +35,9 @@
## Rendering
/drivers/d3d12/ @godotengine/rendering
/drivers/dummy/ @godotengine/rendering
+/drivers/egl/ @godotengine/rendering
/drivers/gles3/ @godotengine/rendering
+/drivers/metal/ @godotengine/rendering
/drivers/spirv-reflect/ @godotengine/rendering
/drivers/vulkan/ @godotengine/rendering
@@ -47,18 +50,19 @@
# Editor
-/editor/*debugger* @godotengine/debugger
+/editor/ @godotengine/_editor
+/editor/**/*2d* @godotengine/2d-editor
+/editor/**/*3d* @godotengine/3d-editor
+/editor/**/*code* @godotengine/script-editor
+/editor/**/*debugger* @godotengine/debugger
+/editor/**/*dock* @godotengine/docks
+/editor/**/*script* @godotengine/script-editor
+/editor/**/*shader* @godotengine/shaders
+/editor/debugger/ @godotengine/debugger
/editor/gui/ @godotengine/usability @godotengine/gui-nodes
/editor/icons/ @godotengine/usability
/editor/import/ @godotengine/import
-/editor/plugins/*2d_*.* @godotengine/2d-editor
-/editor/plugins/*3d_*.* @godotengine/3d-editor
-/editor/plugins/script_*.* @godotengine/script-editor
-/editor/plugins/*shader*.* @godotengine/shaders
/editor/themes/ @godotengine/usability @godotengine/gui-nodes
-/editor/code_editor.* @godotengine/script-editor
-/editor/*dock*.* @godotengine/docks
-/editor/*shader*.* @godotengine/shaders
# Main
@@ -71,9 +75,16 @@
# Modules
+/modules/ @godotengine/_engine
+/modules/**/doc_classes/ @godotengine/_engine @godotengine/documentation
+/modules/**/editor/ @godotengine/_engine @godotengine/_editor
+/modules/**/icons/ @godotengine/_engine @godotengine/usability
+/modules/**/tests/ @godotengine/_engine @godotengine/tests
+
## Audio (+ video)
/modules/interactive_music/ @godotengine/audio
/modules/interactive_music/doc_classes/ @godotengine/audio @godotengine/documentation
+/modules/interactive_music/editor/ @godotengine/audio @godotengine/_editor
/modules/minimp3/ @godotengine/audio
/modules/minimp3/doc_classes/ @godotengine/audio @godotengine/documentation
/modules/ogg/ @godotengine/audio
@@ -93,8 +104,10 @@
/modules/etcpak/ @godotengine/import
/modules/fbx/ @godotengine/import
/modules/fbx/doc_classes/ @godotengine/import @godotengine/documentation
+/modules/fbx/editor/ @godotengine/import @godotengine/_editor
/modules/gltf/ @godotengine/import
/modules/gltf/doc_classes/ @godotengine/import @godotengine/documentation
+/modules/gltf/editor/ @godotengine/import @godotengine/_editor
/modules/gltf/tests/ @godotengine/import @godotengine/tests
/modules/hdr/ @godotengine/import
/modules/jpg/ @godotengine/import
@@ -112,12 +125,14 @@
/modules/mbedtls/tests/ @godotengine/network @godotengine/tests
/modules/multiplayer/ @godotengine/network
/modules/multiplayer/doc_classes/ @godotengine/network @godotengine/documentation
+/modules/multiplayer/editor/ @godotengine/network @godotengine/_editor
/modules/upnp/ @godotengine/network
/modules/upnp/doc_classes/ @godotengine/network @godotengine/documentation
/modules/webrtc/ @godotengine/network
/modules/webrtc/doc_classes/ @godotengine/network @godotengine/documentation
/modules/websocket/ @godotengine/network
/modules/websocket/doc_classes/ @godotengine/network @godotengine/documentation
+/modules/websocket/editor/ @godotengine/network @godotengine/_editor
## Physics
/modules/godot_physics_2d/ @godotengine/physics
@@ -134,12 +149,14 @@
## Scripting
/modules/gdscript/ @godotengine/gdscript
/modules/gdscript/doc_classes/ @godotengine/gdscript @godotengine/documentation
+/modules/gdscript/editor/ @godotengine/gdscript @godotengine/_editor
/modules/gdscript/icons/ @godotengine/gdscript @godotengine/usability
/modules/gdscript/tests/ @godotengine/gdscript @godotengine/tests
/modules/jsonrpc/ @godotengine/gdscript @godotengine/network
/modules/jsonrpc/tests @godotengine/gdscript @godotengine/network @godotengine/tests
/modules/mono/ @godotengine/dotnet
/modules/mono/doc_classes/ @godotengine/dotnet @godotengine/documentation
+/modules/mono/editor/ @godotengine/dotnet @godotengine/_editor
/modules/mono/icons/ @godotengine/dotnet @godotengine/usability
## Text
@@ -156,19 +173,24 @@
/modules/mobile_vr/doc_classes/ @godotengine/xr @godotengine/documentation
/modules/openxr/ @godotengine/xr
/modules/openxr/doc_classes/ @godotengine/xr @godotengine/documentation
+/modules/openxr/editor/ @godotengine/xr @godotengine/_editor
/modules/webxr/ @godotengine/xr
/modules/webxr/doc_classes/ @godotengine/xr @godotengine/documentation
## Misc
/modules/csg/ @godotengine/3d-nodes
/modules/csg/doc_classes/ @godotengine/3d-nodes @godotengine/documentation
+/modules/csg/editor/ @godotengine/3d-nodes @godotengine/_editor
/modules/csg/icons/ @godotengine/3d-nodes @godotengine/usability
-/modules/navigation/ @godotengine/navigation
/modules/gridmap/ @godotengine/3d-nodes
/modules/gridmap/doc_classes/ @godotengine/3d-nodes @godotengine/documentation
+/modules/gridmap/editor/ @godotengine/3d-nodes @godotengine/_editor
/modules/gridmap/icons/ @godotengine/3d-nodes @godotengine/usability
+/modules/navigation/ @godotengine/navigation
+/modules/navigation/editor/ @godotengine/navigation @godotengine/_editor
/modules/noise/ @godotengine/core
/modules/noise/doc_classes/ @godotengine/core @godotengine/documentation
+/modules/noise/editor/ @godotengine/core @godotengine/_editor
/modules/noise/icons/ @godotengine/core @godotengine/usability
/modules/noise/tests/ @godotengine/core @godotengine/tests
/modules/regex/ @godotengine/core
@@ -180,6 +202,7 @@
# Platform
+/platform/ @godotengine/_platforms
/platform/android/ @godotengine/android
/platform/android/doc_classes/ @godotengine/android @godotengine/documentation
/platform/ios/ @godotengine/ios
@@ -195,6 +218,7 @@
# Scene
+/scene/ @godotengine/_systems @godotengine/core
/scene/2d/ @godotengine/2d-nodes
/scene/2d/physics/ @godotengine/2d-nodes @godotengine/physics
/scene/3d/ @godotengine/3d-nodes
@@ -213,14 +237,16 @@
# Servers
-/servers/audio* @godotengine/audio
-/servers/camera* @godotengine/xr
-/servers/display_server.* @godotengine/_platforms
-/servers/navigation_server*.* @godotengine/navigation
-/servers/physics* @godotengine/physics
-/servers/rendering* @godotengine/rendering
-/servers/text_server.* @godotengine/gui-nodes
-/servers/xr* @godotengine/xr
+/servers/ @godotengine/_systems
+/servers/**/audio* @godotengine/audio
+/servers/**/camera* @godotengine/xr
+/servers/**/debugger* @godotengine/debugger
+/servers/**/display* @godotengine/_platforms
+/servers/**/navigation* @godotengine/navigation
+/servers/**/physics* @godotengine/physics
+/servers/**/rendering* @godotengine/rendering
+/servers/**/text* @godotengine/gui-nodes
+/servers/**/xr* @godotengine/xr
# Tests
diff --git a/.github/workflows/windows_builds.yml b/.github/workflows/windows_builds.yml
index 95e3d4a553..384284b604 100644
--- a/.github/workflows/windows_builds.yml
+++ b/.github/workflows/windows_builds.yml
@@ -30,7 +30,7 @@ jobs:
# Skip debug symbols, they're way too big with MSVC.
sconsflags: debug_symbols=no vsproj=yes vsproj_gen_only=no windows_subsystem=console
bin: ./bin/godot.windows.editor.x86_64.exe
- artifact: true
+ compiler: msvc
- name: Editor w/ clang-cl (target=editor, tests=yes, use_llvm=yes)
cache-name: windows-editor-clang
@@ -38,6 +38,7 @@ jobs:
tests: true
sconsflags: debug_symbols=no windows_subsystem=console use_llvm=yes
bin: ./bin/godot.windows.editor.x86_64.llvm.exe
+ compiler: clang
- name: Template (target=template_release, tests=yes)
cache-name: windows-template
@@ -45,7 +46,16 @@ jobs:
tests: true
sconsflags: debug_symbols=no
bin: ./bin/godot.windows.template_release.x86_64.console.exe
- artifact: true
+ compiler: msvc
+
+ - name: Template w/ GCC (target=template_release, tests=yes, use_mingw=yes)
+ cache-name: windows-template-gcc
+ # MinGW takes MUCH longer to compile; save time by only targeting Template.
+ target: template_release
+ tests: true
+ sconsflags: debug_symbols=no use_mingw=yes
+ bin: ./bin/godot.windows.template_release.x86_64.console.exe
+ compiler: gcc
steps:
- name: Checkout
@@ -69,16 +79,21 @@ jobs:
uses: dsaltares/fetch-gh-release-asset@1.1.2
with:
repo: godotengine/godot-angle-static
- version: tags/chromium/6029
- file: Windows.6029-1.MSVC_17.x86_64.x86_32.zip
+ version: tags/chromium/6601.2
+ file: godot-angle-static-x86_64-${{ matrix.compiler == 'gcc' && 'gcc' || 'msvc' }}-release.zip
target: angle/angle.zip
- name: Extract pre-built ANGLE static libraries
run: Expand-Archive -Force angle/angle.zip ${{ github.workspace }}/
- name: Setup MSVC problem matcher
+ if: matrix.compiler == 'msvc'
uses: ammaraskar/msvc-problem-matcher@master
+ - name: Setup GCC problem matcher
+ if: matrix.compiler != 'msvc'
+ uses: ammaraskar/gcc-problem-matcher@master
+
- name: Compilation
uses: ./.github/actions/godot-build
with:
@@ -94,12 +109,12 @@ jobs:
continue-on-error: true
- name: Prepare artifact
- if: ${{ matrix.artifact }}
+ if: matrix.compiler == 'msvc'
run: |
Remove-Item bin/* -Include *.exp,*.lib,*.pdb -Force
- name: Upload artifact
- if: ${{ matrix.artifact }}
+ if: matrix.compiler == 'msvc'
uses: ./.github/actions/upload-artifact
with:
name: ${{ matrix.cache-name }}
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 562bde978e..01f15f9c8e 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -348,7 +348,6 @@ bool ProjectSettings::_get(const StringName &p_name, Variant &r_ret) const {
_THREAD_SAFE_METHOD_
if (!props.has(p_name)) {
- WARN_PRINT("Property not found: " + String(p_name));
return false;
}
r_ret = props[p_name].variant;
@@ -1168,22 +1167,16 @@ bool ProjectSettings::is_project_loaded() const {
}
bool ProjectSettings::_property_can_revert(const StringName &p_name) const {
- if (!props.has(p_name)) {
- return false;
- }
-
- return props[p_name].initial != props[p_name].variant;
+ return props.has(p_name);
}
bool ProjectSettings::_property_get_revert(const StringName &p_name, Variant &r_property) const {
- if (!props.has(p_name)) {
- return false;
+ const RBMap<StringName, ProjectSettings::VariantContainer>::Element *value = props.find(p_name);
+ if (value) {
+ r_property = value->value().initial.duplicate();
+ return true;
}
-
- // Duplicate so that if value is array or dictionary, changing the setting will not change the stored initial value.
- r_property = props[p_name].initial.duplicate();
-
- return true;
+ return false;
}
void ProjectSettings::set_setting(const String &p_setting, const Variant &p_value) {
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index 3bfa022382..109999d612 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -845,29 +845,27 @@ Error ResourceLoaderBinary::load() {
}
}
- if (ClassDB::has_property(res->get_class_name(), name)) {
- if (value.get_type() == Variant::ARRAY) {
- Array set_array = value;
- bool is_get_valid = false;
- Variant get_value = res->get(name, &is_get_valid);
- if (is_get_valid && get_value.get_type() == Variant::ARRAY) {
- Array get_array = get_value;
- if (!set_array.is_same_typed(get_array)) {
- value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script());
- }
+ if (value.get_type() == Variant::ARRAY) {
+ Array set_array = value;
+ bool is_get_valid = false;
+ Variant get_value = res->get(name, &is_get_valid);
+ if (is_get_valid && get_value.get_type() == Variant::ARRAY) {
+ Array get_array = get_value;
+ if (!set_array.is_same_typed(get_array)) {
+ value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script());
}
}
+ }
- if (value.get_type() == Variant::DICTIONARY) {
- Dictionary set_dict = value;
- bool is_get_valid = false;
- Variant get_value = res->get(name, &is_get_valid);
- if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) {
- Dictionary get_dict = get_value;
- if (!set_dict.is_same_typed(get_dict)) {
- value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(),
- get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script());
- }
+ if (value.get_type() == Variant::DICTIONARY) {
+ Dictionary set_dict = value;
+ bool is_get_valid = false;
+ Variant get_value = res->get(name, &is_get_valid);
+ if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) {
+ Dictionary get_dict = get_value;
+ if (!set_dict.is_same_typed(get_dict)) {
+ value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(),
+ get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script());
}
}
}
diff --git a/core/math/geometry_2d.cpp b/core/math/geometry_2d.cpp
index d60619b27f..a49826958a 100644
--- a/core/math/geometry_2d.cpp
+++ b/core/math/geometry_2d.cpp
@@ -35,7 +35,8 @@
#define STB_RECT_PACK_IMPLEMENTATION
#include "thirdparty/misc/stb_rect_pack.h"
-#define PRECISION 5 // Based on CMP_EPSILON.
+const int clipper_precision = 5; // Based on CMP_EPSILON.
+const double clipper_scale = Math::pow(10.0, clipper_precision);
Vector<Vector<Vector2>> Geometry2D::decompose_polygon_in_convex(const Vector<Point2> &polygon) {
Vector<Vector<Vector2>> decomp;
@@ -224,7 +225,7 @@ Vector<Vector<Point2>> Geometry2D::_polypaths_do_operation(PolyBooleanOperation
path_b[i] = PointD(p_polypath_b[i].x, p_polypath_b[i].y);
}
- ClipperD clp(PRECISION); // Scale points up internally to attain the desired precision.
+ ClipperD clp(clipper_precision); // Scale points up internally to attain the desired precision.
clp.PreserveCollinear(false); // Remove redundant vertices.
if (is_a_open) {
clp.AddOpenSubject({ path_a });
@@ -298,9 +299,10 @@ Vector<Vector<Point2>> Geometry2D::_polypath_offset(const Vector<Point2> &p_poly
}
// Inflate/deflate.
- PathsD paths = InflatePaths({ polypath }, p_delta, jt, et, 2.0, PRECISION, 0.0);
- // Here the miter_limit = 2.0 and arc_tolerance = 0.0 are Clipper2 defaults,
- // and the PRECISION is used to scale points up internally, to attain the desired precision.
+ PathsD paths = InflatePaths({ polypath }, p_delta, jt, et, 2.0, clipper_precision, 0.25 * clipper_scale);
+ // Here the points are scaled up internally and
+ // the arc_tolerance is scaled accordingly
+ // to attain the desired precision.
Vector<Vector<Point2>> polypaths;
for (PathsD::size_type i = 0; i < paths.size(); ++i) {
diff --git a/doc/classes/EditorContextMenuPlugin.xml b/doc/classes/EditorContextMenuPlugin.xml
index 71c4ca0f9b..fb90a2a5cd 100644
--- a/doc/classes/EditorContextMenuPlugin.xml
+++ b/doc/classes/EditorContextMenuPlugin.xml
@@ -47,6 +47,24 @@
[/codeblock]
</description>
</method>
+ <method name="add_context_submenu_item">
+ <return type="void" />
+ <param index="0" name="name" type="String" />
+ <param index="1" name="menu" type="PopupMenu" />
+ <param index="2" name="icon" type="Texture2D" default="null" />
+ <description>
+ Add a submenu to the context menu of the plugin's specified slot. The submenu is not automatically handled, you need to connect to its signals yourself. Also the submenu is freed on every popup, so provide a new [PopupMenu] every time.
+ [codeblock]
+ func _popup_menu(paths):
+ var popup_menu = PopupMenu.new()
+ popup_menu.add_item("Blue")
+ popup_menu.add_item("White")
+ popup_menu.id_pressed.connect(_on_color_submenu_option)
+
+ add_context_menu_item("Set Node Color", popup_menu)
+ [/codeblock]
+ </description>
+ </method>
<method name="add_menu_shortcut">
<return type="void" />
<param index="0" name="shortcut" type="Shortcut" />
diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml
index de3295fbe0..1779408a4d 100644
--- a/doc/classes/Environment.xml
+++ b/doc/classes/Environment.xml
@@ -81,8 +81,9 @@
The background mode. See [enum BGMode] for possible values.
</member>
<member name="fog_aerial_perspective" type="float" setter="set_fog_aerial_perspective" getter="get_fog_aerial_perspective" default="0.0">
- If set above [code]0.0[/code] (exclusive), blends between the fog's color and the color of the background [Sky]. This has a small performance cost when set above [code]0.0[/code]. Must have [member background_mode] set to [constant BG_SKY].
+ If set above [code]0.0[/code] (exclusive), blends between the fog's color and the color of the background [Sky], as read from the radiance cubemap. This has a small performance cost when set above [code]0.0[/code]. Must have [member background_mode] set to [constant BG_SKY].
This is useful to simulate [url=https://en.wikipedia.org/wiki/Aerial_perspective]aerial perspective[/url] in large scenes with low density fog. However, it is not very useful for high-density fog, as the sky will shine through. When set to [code]1.0[/code], the fog color comes completely from the [Sky]. If set to [code]0.0[/code], aerial perspective is disabled.
+ Notice that this does not sample the [Sky] directly, but rather the radiance cubemap. The cubemap is sampled at a mipmap level depending on the depth of the rendered pixel; the farther away, the higher the resolution of the sampled mipmap. This results in the actual color being a blurred version of the sky, with more blur closer to the camera. The highest mipmap resolution is used at a depth of [member Camera3D.far].
</member>
<member name="fog_density" type="float" setter="set_fog_density" getter="get_fog_density" default="0.01">
The fog density to be used. This is demonstrated in different ways depending on the [member fog_mode] mode chosen:
diff --git a/doc/classes/PackedVector2Array.xml b/doc/classes/PackedVector2Array.xml
index da41812e0b..4d487b6dc2 100644
--- a/doc/classes/PackedVector2Array.xml
+++ b/doc/classes/PackedVector2Array.xml
@@ -5,7 +5,7 @@
</brief_description>
<description>
An array specifically designed to hold [Vector2]. Packs data tightly, so it saves memory for large array sizes.
- [b]Differences between packed arrays, typed arrays, and untyped arrays:[/b] Packed arrays are generally faster to iterate on and modify compared to a typed array of the same type (e.g. [PackedVector3Array] versus [code]Array[Vector2][/code]). Also, packed arrays consume less memory. As a downside, packed arrays are less flexible as they don't offer as many convenience methods such as [method Array.map]. Typed arrays are in turn faster to iterate on and modify than untyped arrays.
+ [b]Differences between packed arrays, typed arrays, and untyped arrays:[/b] Packed arrays are generally faster to iterate on and modify compared to a typed array of the same type (e.g. [PackedVector2Array] versus [code]Array[Vector2][/code]). Also, packed arrays consume less memory. As a downside, packed arrays are less flexible as they don't offer as many convenience methods such as [method Array.map]. Typed arrays are in turn faster to iterate on and modify than untyped arrays.
[b]Note:[/b] Packed arrays are always passed by reference. To get a copy of an array that can be modified independently of the original array, use [method duplicate]. This is [i]not[/i] the case for built-in properties and methods. The returned packed array of these are a copies, and changing it will [i]not[/i] affect the original value. To update a built-in property you need to modify the returned array, and then assign it to the property again.
</description>
<tutorials>
diff --git a/drivers/d3d12/SCsub b/drivers/d3d12/SCsub
index b6ceed23ac..beeb13398e 100644
--- a/drivers/d3d12/SCsub
+++ b/drivers/d3d12/SCsub
@@ -4,6 +4,8 @@ from misc.utility.scons_hints import *
import os
from pathlib import Path
+import methods
+
Import("env")
env_d3d12_rdd = env.Clone()
@@ -139,6 +141,10 @@ else:
extra_defines += [
"HAVE_STRUCT_TIMESPEC",
]
+ if methods.using_gcc(env) and methods.get_compiler_version(env)["major"] < 13:
+ # `region` & `endregion` not recognized as valid pragmas.
+ env_d3d12_rdd.Append(CCFLAGS=["-Wno-unknown-pragmas"])
+ env.Append(CCFLAGS=["-Wno-unknown-pragmas"])
# This is needed since rendering_device_d3d12.cpp needs to include some Mesa internals.
env_d3d12_rdd.Prepend(CPPPATH=mesa_private_inc_paths)
diff --git a/drivers/d3d12/dxil_hash.cpp b/drivers/d3d12/dxil_hash.cpp
index f94a4a30df..e08492c9ea 100644
--- a/drivers/d3d12/dxil_hash.cpp
+++ b/drivers/d3d12/dxil_hash.cpp
@@ -96,7 +96,7 @@ void compute_dxil_hash(const BYTE *pData, UINT byteCount, BYTE *pOutHash) {
UINT NextEndState = bTwoRowsPadding ? N - 2 : N - 1;
const BYTE *pCurrData = pData;
for (UINT i = 0; i < N; i++, offset += 64, pCurrData += 64) {
- UINT x[16];
+ UINT x[16] = {};
const UINT *pX;
if (i == NextEndState) {
if (!bTwoRowsPadding && i == N - 1) {
diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp
index 8271d4b7e3..0ef88e7d52 100644
--- a/drivers/d3d12/rendering_device_driver_d3d12.cpp
+++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp
@@ -1348,7 +1348,14 @@ RDD::TextureID RenderingDeviceDriverD3D12::texture_create(const TextureFormat &p
}
tex_info->states_ptr = &tex_info->owner_info.states;
tex_info->format = p_format.format;
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#endif
tex_info->desc = *(CD3DX12_RESOURCE_DESC *)&resource_desc;
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
tex_info->base_layer = 0;
tex_info->layers = resource_desc.ArraySize();
tex_info->base_mip = 0;
@@ -6578,8 +6585,6 @@ static Error create_command_signature(ID3D12Device *device, D3D12_INDIRECT_ARGUM
Error RenderingDeviceDriverD3D12::_initialize_frames(uint32_t p_frame_count) {
Error err;
- D3D12MA::ALLOCATION_DESC allocation_desc = {};
- allocation_desc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
//CD3DX12_RESOURCE_DESC resource_desc = CD3DX12_RESOURCE_DESC::Buffer(D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT);
uint32_t resource_descriptors_per_frame = GLOBAL_GET("rendering/rendering_device/d3d12/max_resource_descriptors_per_frame");
diff --git a/drivers/egl/egl_manager.cpp b/drivers/egl/egl_manager.cpp
index 4477ba7752..603dfadd4b 100644
--- a/drivers/egl/egl_manager.cpp
+++ b/drivers/egl/egl_manager.cpp
@@ -30,6 +30,8 @@
#include "egl_manager.h"
+#include "drivers/gles3/rasterizer_gles3.h"
+
#ifdef EGL_ENABLED
#if defined(EGL_STATIC)
@@ -51,6 +53,16 @@ extern "C" EGLAPI EGLDisplay EGLAPIENTRY eglGetPlatformDisplayEXT(EGLenum platfo
#define GLAD_EGL_EXT_platform_base 0
#endif
+#ifdef WINDOWS_ENABLED
+// Unofficial ANGLE extension: EGL_ANGLE_surface_orientation
+#ifndef EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE
+#define EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE 0x33A7
+#define EGL_SURFACE_ORIENTATION_ANGLE 0x33A8
+#define EGL_SURFACE_ORIENTATION_INVERT_X_ANGLE 0x0001
+#define EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE 0x0002
+#endif
+#endif
+
// Creates and caches a GLDisplay. Returns -1 on error.
int EGLManager::_get_gldisplay_id(void *p_display) {
// Look for a cached GLDisplay.
@@ -115,6 +127,18 @@ int EGLManager::_get_gldisplay_id(void *p_display) {
}
#endif
+#ifdef WINDOWS_ENABLED
+ String client_extensions_string = eglQueryString(new_gldisplay.egl_display, EGL_EXTENSIONS);
+ if (eglGetError() == EGL_SUCCESS) {
+ Vector<String> egl_extensions = client_extensions_string.split(" ");
+
+ if (egl_extensions.has("EGL_ANGLE_surface_orientation")) {
+ new_gldisplay.has_EGL_ANGLE_surface_orientation = true;
+ print_verbose("EGL: EGL_ANGLE_surface_orientation is supported.");
+ }
+ }
+#endif
+
displays.push_back(new_gldisplay);
// Return the new GLDisplay's ID.
@@ -237,8 +261,29 @@ Error EGLManager::window_create(DisplayServer::WindowID p_window_id, void *p_dis
GLWindow &glwindow = windows[p_window_id];
glwindow.gldisplay_id = gldisplay_id;
+ Vector<EGLAttrib> egl_attribs;
+
+#ifdef WINDOWS_ENABLED
+ if (gldisplay.has_EGL_ANGLE_surface_orientation) {
+ EGLint optimal_orientation;
+ if (eglGetConfigAttrib(gldisplay.egl_display, gldisplay.egl_config, EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE, &optimal_orientation)) {
+ // We only need to support inverting Y for optimizing ANGLE on D3D11.
+ if (optimal_orientation & EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE && !(optimal_orientation & EGL_SURFACE_ORIENTATION_INVERT_X_ANGLE)) {
+ egl_attribs.push_back(EGL_SURFACE_ORIENTATION_ANGLE);
+ egl_attribs.push_back(EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE);
+ }
+ } else {
+ ERR_PRINT(vformat("Failed to get EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE, error: 0x%08X", eglGetError()));
+ }
+ }
+
+ if (!egl_attribs.is_empty()) {
+ egl_attribs.push_back(EGL_NONE);
+ }
+#endif
+
if (GLAD_EGL_VERSION_1_5) {
- glwindow.egl_surface = eglCreatePlatformWindowSurface(gldisplay.egl_display, gldisplay.egl_config, p_native_window, nullptr);
+ glwindow.egl_surface = eglCreatePlatformWindowSurface(gldisplay.egl_display, gldisplay.egl_config, p_native_window, egl_attribs.ptr());
} else {
EGLNativeWindowType *native_window_type = (EGLNativeWindowType *)p_native_window;
glwindow.egl_surface = eglCreateWindowSurface(gldisplay.egl_display, gldisplay.egl_config, *native_window_type, nullptr);
@@ -250,6 +295,20 @@ Error EGLManager::window_create(DisplayServer::WindowID p_window_id, void *p_dis
glwindow.initialized = true;
+#ifdef WINDOWS_ENABLED
+ if (gldisplay.has_EGL_ANGLE_surface_orientation) {
+ EGLint orientation;
+ if (eglQuerySurface(gldisplay.egl_display, glwindow.egl_surface, EGL_SURFACE_ORIENTATION_ANGLE, &orientation)) {
+ if (orientation & EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE && !(orientation & EGL_SURFACE_ORIENTATION_INVERT_X_ANGLE)) {
+ glwindow.flipped_y = true;
+ print_verbose("EGL: Using optimal surface orientation: Invert Y");
+ }
+ } else {
+ ERR_PRINT(vformat("Failed to get EGL_SURFACE_ORIENTATION_ANGLE, error: 0x%08X", eglGetError()));
+ }
+ }
+#endif
+
window_make_current(p_window_id);
return OK;
@@ -316,6 +375,10 @@ void EGLManager::window_make_current(DisplayServer::WindowID p_window_id) {
GLDisplay &current_display = displays[current_window->gldisplay_id];
eglMakeCurrent(current_display.egl_display, current_window->egl_surface, current_window->egl_surface, current_display.egl_context);
+
+#ifdef WINDOWS_ENABLED
+ RasterizerGLES3::set_screen_flipped_y(glwindow.flipped_y);
+#endif
}
void EGLManager::set_use_vsync(bool p_use) {
diff --git a/drivers/egl/egl_manager.h b/drivers/egl/egl_manager.h
index a4502c0687..f1b3dc99b7 100644
--- a/drivers/egl/egl_manager.h
+++ b/drivers/egl/egl_manager.h
@@ -53,11 +53,18 @@ private:
EGLDisplay egl_display = EGL_NO_DISPLAY;
EGLContext egl_context = EGL_NO_CONTEXT;
EGLConfig egl_config = nullptr;
+
+#ifdef WINDOWS_ENABLED
+ bool has_EGL_ANGLE_surface_orientation = false;
+#endif
};
// EGL specific window data.
struct GLWindow {
bool initialized = false;
+#ifdef WINDOWS_ENABLED
+ bool flipped_y = false;
+#endif
// An handle to the GLDisplay associated with this window.
int gldisplay_id = -1;
diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp
index e79f1db08d..6e508c6ebf 100644
--- a/drivers/gles3/rasterizer_gles3.cpp
+++ b/drivers/gles3/rasterizer_gles3.cpp
@@ -86,6 +86,10 @@
#define strcpy strcpy_s
#endif
+#ifdef WINDOWS_ENABLED
+bool RasterizerGLES3::screen_flipped_y = false;
+#endif
+
bool RasterizerGLES3::gles_over_gl = true;
void RasterizerGLES3::begin_frame(double frame_step) {
@@ -389,6 +393,12 @@ void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, Display
flip_y = false;
}
+#ifdef WINDOWS_ENABLED
+ if (screen_flipped_y) {
+ flip_y = !flip_y;
+ }
+#endif
+
GLuint read_fbo = 0;
glGenFramebuffers(1, &read_fbo);
glBindFramebuffer(GL_READ_FRAMEBUFFER, read_fbo);
@@ -485,9 +495,14 @@ void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_c
screenrect.position += ((Size2(win_size.width, win_size.height) - screenrect.size) / 2.0).floor();
}
- // Flip Y.
- screenrect.position.y = win_size.y - screenrect.position.y;
- screenrect.size.y = -screenrect.size.y;
+#ifdef WINDOWS_ENABLED
+ if (!screen_flipped_y)
+#endif
+ {
+ // Flip Y.
+ screenrect.position.y = win_size.y - screenrect.position.y;
+ screenrect.size.y = -screenrect.size.y;
+ }
// Normalize texture coordinates to window size.
screenrect.position /= win_size;
diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h
index 92454e014e..6765d8b4d5 100644
--- a/drivers/gles3/rasterizer_gles3.h
+++ b/drivers/gles3/rasterizer_gles3.h
@@ -58,6 +58,10 @@ private:
double time_total = 0.0;
bool flip_xy_workaround = false;
+#ifdef WINDOWS_ENABLED
+ static bool screen_flipped_y;
+#endif
+
static bool gles_over_gl;
protected:
@@ -118,6 +122,12 @@ public:
low_end = true;
}
+#ifdef WINDOWS_ENABLED
+ static void set_screen_flipped_y(bool p_flipped) {
+ screen_flipped_y = p_flipped;
+ }
+#endif
+
_ALWAYS_INLINE_ uint64_t get_frame_number() const { return frame; }
_ALWAYS_INLINE_ double get_frame_delta_time() const { return delta; }
_ALWAYS_INLINE_ double get_total_time() const { return time_total; }
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index c2d1784958..a73f14c796 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -3443,6 +3443,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
}
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::MODEL_FLAGS, inst->flags_cache, shader->version, instance_variant, spec_constants);
+ material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::INSTANCE_OFFSET, uint32_t(inst->shader_uniforms_offset), shader->version, instance_variant, spec_constants);
if (p_pass_mode == PASS_MODE_MATERIAL) {
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::UV_OFFSET, p_params->uv_offset, shader->version, instance_variant, spec_constants);
diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl
index 9ce10a4488..fcfbeddb9e 100644
--- a/drivers/gles3/shaders/scene.glsl
+++ b/drivers/gles3/shaders/scene.glsl
@@ -430,6 +430,7 @@ uniform highp mat4 world_transform;
uniform highp vec3 compressed_aabb_position;
uniform highp vec3 compressed_aabb_size;
uniform highp vec4 uv_scale;
+uniform highp uint instance_offset;
uniform highp uint model_flags;
@@ -1201,6 +1202,7 @@ ivec2 multiview_uv(ivec2 uv) {
uniform highp mat4 world_transform;
uniform mediump float opaque_prepass_threshold;
uniform highp uint model_flags;
+uniform highp uint instance_offset;
#if defined(RENDER_MATERIAL)
layout(location = 0) out vec4 albedo_output_buffer;
diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp
index 5dca149d99..684f179492 100644
--- a/drivers/gles3/storage/material_storage.cpp
+++ b/drivers/gles3/storage/material_storage.cpp
@@ -1379,6 +1379,7 @@ MaterialStorage::MaterialStorage() {
actions.check_multiview_samplers = RasterizerGLES3::get_singleton()->is_xr_enabled();
actions.global_buffer_array_variable = "global_shader_uniforms";
+ actions.instance_uniform_index_variable = "instance_offset";
shaders.compiler_scene.initialize(actions);
}
diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp
index 5058554659..73d95d75ba 100644
--- a/drivers/gles3/storage/mesh_storage.cpp
+++ b/drivers/gles3/storage/mesh_storage.cpp
@@ -2201,7 +2201,7 @@ void MeshStorage::skeleton_allocate_data(RID p_skeleton, int p_bones, bool p_2d_
glBindTexture(GL_TEXTURE_2D, 0);
GLES3::Utilities::get_singleton()->texture_allocated_data(skeleton->transforms_texture, skeleton->data.size() * sizeof(float), "Skeleton transforms texture");
- memset(skeleton->data.ptrw(), 0, skeleton->data.size() * sizeof(float));
+ memset(skeleton->data.ptr(), 0, skeleton->data.size() * sizeof(float));
_skeleton_make_dirty(skeleton);
}
@@ -2232,7 +2232,7 @@ void MeshStorage::skeleton_bone_set_transform(RID p_skeleton, int p_bone, const
ERR_FAIL_INDEX(p_bone, skeleton->size);
ERR_FAIL_COND(skeleton->use_2d);
- float *dataptr = skeleton->data.ptrw() + p_bone * 12;
+ float *dataptr = skeleton->data.ptr() + p_bone * 12;
dataptr[0] = p_transform.basis.rows[0][0];
dataptr[1] = p_transform.basis.rows[0][1];
@@ -2284,7 +2284,7 @@ void MeshStorage::skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, con
ERR_FAIL_INDEX(p_bone, skeleton->size);
ERR_FAIL_COND(!skeleton->use_2d);
- float *dataptr = skeleton->data.ptrw() + p_bone * 8;
+ float *dataptr = skeleton->data.ptr() + p_bone * 8;
dataptr[0] = p_transform.columns[0][0];
dataptr[1] = p_transform.columns[1][0];
diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h
index 31858cd372..8615b89a30 100644
--- a/drivers/gles3/storage/mesh_storage.h
+++ b/drivers/gles3/storage/mesh_storage.h
@@ -214,7 +214,7 @@ struct Skeleton {
bool use_2d = false;
int size = 0;
int height = 0;
- Vector<float> data;
+ LocalVector<float> data;
bool dirty = false;
Skeleton *dirty_list = nullptr;
diff --git a/drivers/metal/rendering_device_driver_metal.mm b/drivers/metal/rendering_device_driver_metal.mm
index 0f7faaddf0..a4a408356a 100644
--- a/drivers/metal/rendering_device_driver_metal.mm
+++ b/drivers/metal/rendering_device_driver_metal.mm
@@ -3671,7 +3671,8 @@ void RenderingDeviceDriverMetal::set_object_name(ObjectType p_type, ID p_driver_
uint64_t RenderingDeviceDriverMetal::get_resource_native_handle(DriverResource p_type, ID p_driver_id) {
switch (p_type) {
case DRIVER_RESOURCE_LOGICAL_DEVICE: {
- return 0;
+ uintptr_t devicePtr = (uintptr_t)(__bridge void *)device;
+ return (uint64_t)devicePtr;
}
case DRIVER_RESOURCE_PHYSICAL_DEVICE: {
return 0;
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index c02efc445f..f8d35f2112 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -4279,7 +4279,18 @@ void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_p
// Let's build a node path.
String path = root->get_path_to(p_node, true);
- Variant value = p_node->get(p_property);
+ // Get the value from the subpath.
+ Variant value = p_node;
+ Vector<String> property_path = p_property.split(":");
+ for (const String &E : property_path) {
+ if (value.get_type() == Variant::OBJECT) {
+ Object *obj = value;
+ value = obj->get(E);
+ } else {
+ value = Variant();
+ break;
+ }
+ }
if (Object::cast_to<AnimationPlayer>(p_node) && p_property == "current_animation") {
if (p_node == AnimationPlayerEditor::get_singleton()->get_player()) {
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 314661591e..95b3c30d1b 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -1983,7 +1983,7 @@ void EditorNode::try_autosave() {
editor_data.save_editor_external_data();
}
-void EditorNode::restart_editor() {
+void EditorNode::restart_editor(bool p_goto_project_manager) {
exiting = true;
if (project_run_bar->is_playing()) {
@@ -1991,22 +1991,25 @@ void EditorNode::restart_editor() {
}
String to_reopen;
- if (get_tree()->get_edited_scene_root()) {
+ if (!p_goto_project_manager && get_tree()->get_edited_scene_root()) {
to_reopen = get_tree()->get_edited_scene_root()->get_scene_file_path();
}
_exit_editor(EXIT_SUCCESS);
List<String> args;
-
for (const String &a : Main::get_forwardable_cli_arguments(Main::CLI_SCOPE_TOOL)) {
args.push_back(a);
}
- args.push_back("--path");
- args.push_back(ProjectSettings::get_singleton()->get_resource_path());
+ if (p_goto_project_manager) {
+ args.push_back("--project-manager");
+ } else {
+ args.push_back("--path");
+ args.push_back(ProjectSettings::get_singleton()->get_resource_path());
- args.push_back("-e");
+ args.push_back("-e");
+ }
if (!to_reopen.is_empty()) {
args.push_back(to_reopen);
@@ -2387,7 +2390,7 @@ void EditorNode::hide_unused_editors(const Object *p_editing_owner) {
// This is to sweep properties that were removed from the inspector.
List<ObjectID> to_remove;
for (KeyValue<ObjectID, HashSet<EditorPlugin *>> &kv : active_plugins) {
- const Object *context = ObjectDB::get_instance(kv.key);
+ Object *context = ObjectDB::get_instance(kv.key);
if (context) {
// In case of self-owning plugins, they are disabled here if they can auto hide.
const EditorPlugin *self_owning = Object::cast_to<EditorPlugin>(context);
@@ -2396,7 +2399,7 @@ void EditorNode::hide_unused_editors(const Object *p_editing_owner) {
}
}
- if (!context) {
+ if (!context || context->call(SNAME("_should_stop_editing"))) {
to_remove.push_back(kv.key);
for (EditorPlugin *plugin : kv.value) {
if (plugin->can_auto_hide()) {
@@ -3402,23 +3405,7 @@ void EditorNode::_discard_changes(const String &p_str) {
} break;
case RUN_PROJECT_MANAGER: {
- project_run_bar->stop_playing();
- _exit_editor(EXIT_SUCCESS);
- String exec = OS::get_singleton()->get_executable_path();
-
- List<String> args;
- for (const String &a : Main::get_forwardable_cli_arguments(Main::CLI_SCOPE_TOOL)) {
- args.push_back(a);
- }
-
- String exec_base_dir = exec.get_base_dir();
- if (!exec_base_dir.is_empty()) {
- args.push_back("--path");
- args.push_back(exec_base_dir);
- }
- args.push_back("--project-manager");
-
- OS::get_singleton()->set_restart_on_exit(true, args);
+ restart_editor(true);
} break;
case RELOAD_CURRENT_PROJECT: {
restart_editor();
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 55caed4bb4..696caf857c 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -926,7 +926,7 @@ public:
void save_scene_list(const HashSet<String> &p_scene_paths);
void save_before_run();
void try_autosave();
- void restart_editor();
+ void restart_editor(bool p_goto_project_manager = false);
void unload_editor_addons();
void dim_editor(bool p_dimming);
diff --git a/editor/editor_paths.cpp b/editor/editor_paths.cpp
index ff869f8a8a..883116bab6 100644
--- a/editor/editor_paths.cpp
+++ b/editor/editor_paths.cpp
@@ -257,22 +257,6 @@ EditorPaths::EditorPaths() {
}
}
- // Check that `.editorconfig` file exists.
- String project_editorconfig_path = "res://.editorconfig";
- if (!FileAccess::exists(project_editorconfig_path)) {
- Ref<FileAccess> f = FileAccess::open(project_editorconfig_path, FileAccess::WRITE);
- if (f.is_valid()) {
- f->store_line("root = true");
- f->store_line("");
- f->store_line("[*]");
- f->store_line("charset = utf-8");
- f->close();
- } else {
- ERR_PRINT("Failed to create file " + project_editorconfig_path.quote() + ".");
- }
- FileAccess::set_hidden_attribute(project_editorconfig_path, true);
- }
-
Engine::get_singleton()->set_shader_cache_path(project_data_dir);
// Editor metadata dir.
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index 897e7835fd..c5a35e466c 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -3179,6 +3179,10 @@ void EditorPropertyResource::_update_preferred_shader() {
}
}
+bool EditorPropertyResource::_should_stop_editing() const {
+ return !resource_picker->is_toggle_pressed();
+}
+
void EditorPropertyResource::_viewport_selected(const NodePath &p_path) {
Node *to_node = get_node(p_path);
if (!Object::cast_to<Viewport>(to_node)) {
@@ -3353,13 +3357,18 @@ void EditorPropertyResource::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_EXIT_TREE: {
const EditorInspector *ei = get_parent_inspector();
- if (ei && !ei->is_main_editor_inspector()) {
+ const EditorInspector *main_ei = InspectorDock::get_inspector_singleton();
+ if (ei && main_ei && ei != main_ei && !main_ei->is_ancestor_of(ei)) {
fold_resource();
}
} break;
}
}
+void EditorPropertyResource::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_should_stop_editing"), &EditorPropertyResource::_should_stop_editing);
+}
+
EditorPropertyResource::EditorPropertyResource() {
use_sub_inspector = bool(EDITOR_GET("interface/inspector/open_resources_in_current_inspector"));
has_borders = true;
diff --git a/editor/editor_properties.h b/editor/editor_properties.h
index 2ec78cdb44..004630da3e 100644
--- a/editor/editor_properties.h
+++ b/editor/editor_properties.h
@@ -683,10 +683,12 @@ class EditorPropertyResource : public EditorProperty {
void _open_editor_pressed();
void _update_preferred_shader();
+ bool _should_stop_editing() const;
protected:
virtual void _set_read_only(bool p_read_only) override;
void _notification(int p_what);
+ static void _bind_methods();
public:
virtual void update_property() override;
diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp
index e4ae2a6202..0f0287718c 100644
--- a/editor/editor_resource_picker.cpp
+++ b/editor/editor_resource_picker.cpp
@@ -132,6 +132,11 @@ void EditorResourcePicker::_resource_selected() {
emit_signal(SNAME("resource_selected"), edited_resource, false);
}
+void EditorResourcePicker::_resource_changed() {
+ emit_signal(SNAME("resource_changed"), edited_resource);
+ _update_resource();
+}
+
void EditorResourcePicker::_file_selected(const String &p_path) {
Ref<Resource> loaded_resource = ResourceLoader::load(p_path);
ERR_FAIL_COND_MSG(loaded_resource.is_null(), "Cannot load resource from path '" + p_path + "'.");
@@ -167,8 +172,7 @@ void EditorResourcePicker::_file_selected(const String &p_path) {
}
edited_resource = loaded_resource;
- emit_signal(SNAME("resource_changed"), edited_resource);
- _update_resource();
+ _resource_changed();
}
void EditorResourcePicker::_resource_saved(Object *p_resource) {
@@ -353,8 +357,7 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
case OBJ_MENU_CLEAR: {
edited_resource = Ref<Resource>();
- emit_signal(SNAME("resource_changed"), edited_resource);
- _update_resource();
+ _resource_changed();
} break;
case OBJ_MENU_MAKE_UNIQUE: {
@@ -366,8 +369,7 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
ERR_FAIL_COND(unique_resource.is_null()); // duplicate() may fail.
edited_resource = unique_resource;
- emit_signal(SNAME("resource_changed"), edited_resource);
- _update_resource();
+ _resource_changed();
} break;
case OBJ_MENU_MAKE_UNIQUE_RECURSIVE: {
@@ -432,9 +434,7 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
_edit_menu_cbk(OBJ_MENU_MAKE_UNIQUE);
return;
}
-
- emit_signal(SNAME("resource_changed"), edited_resource);
- _update_resource();
+ _resource_changed();
} break;
case OBJ_MENU_SHOW_IN_FILE_SYSTEM: {
@@ -453,8 +453,7 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
ERR_FAIL_INDEX(to_type, conversions.size());
edited_resource = conversions[to_type]->convert(edited_resource);
- emit_signal(SNAME("resource_changed"), edited_resource);
- _update_resource();
+ _resource_changed();
break;
}
@@ -481,8 +480,7 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
// Prevent freeing of the object until the end of the update of the resource (GH-88286).
Ref<Resource> old_edited_resource = edited_resource;
edited_resource = Ref<Resource>(resp);
- emit_signal(SNAME("resource_changed"), edited_resource);
- _update_resource();
+ _resource_changed();
} break;
}
}
@@ -778,8 +776,7 @@ void EditorResourcePicker::drop_data_fw(const Point2 &p_point, const Variant &p_
}
edited_resource = dropped_resource;
- emit_signal(SNAME("resource_changed"), edited_resource);
- _update_resource();
+ _resource_changed();
}
}
@@ -952,6 +949,10 @@ void EditorResourcePicker::set_toggle_pressed(bool p_pressed) {
assign_button->set_pressed(p_pressed);
}
+bool EditorResourcePicker::is_toggle_pressed() const {
+ return assign_button->is_pressed();
+}
+
void EditorResourcePicker::set_editable(bool p_editable) {
editable = p_editable;
assign_button->set_disabled(!editable && !edited_resource.is_valid());
@@ -1046,8 +1047,7 @@ void EditorResourcePicker::_duplicate_selected_resources() {
if (meta.size() == 1) { // Root.
edited_resource = unique_resource;
- emit_signal(SNAME("resource_changed"), edited_resource);
- _update_resource();
+ _resource_changed();
} else {
Array parent_meta = item->get_parent()->get_metadata(0);
Ref<Resource> parent = parent_meta[0];
diff --git a/editor/editor_resource_picker.h b/editor/editor_resource_picker.h
index c39d9af764..0a32dea3ed 100644
--- a/editor/editor_resource_picker.h
+++ b/editor/editor_resource_picker.h
@@ -86,6 +86,7 @@ class EditorResourcePicker : public HBoxContainer {
void _update_resource_preview(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, ObjectID p_obj);
void _resource_selected();
+ void _resource_changed();
void _file_selected(const String &p_path);
void _resource_saved(Object *p_resource);
@@ -134,6 +135,7 @@ public:
void set_toggle_mode(bool p_enable);
bool is_toggle_mode() const;
void set_toggle_pressed(bool p_pressed);
+ bool is_toggle_pressed() const;
void set_editable(bool p_editable);
bool is_editable() const;
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index ceaffb64c4..12a7c3a2ff 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -221,7 +221,6 @@ bool EditorSettings::_get(const StringName &p_name, Variant &r_ret) const {
const VariantContainer *v = props.getptr(p_name);
if (!v) {
- WARN_PRINT("EditorSettings::_get - Property not found: " + String(p_name));
return false;
}
r_ret = v->variant;
@@ -1425,24 +1424,20 @@ Variant _EDITOR_GET(const String &p_setting) {
}
bool EditorSettings::_property_can_revert(const StringName &p_name) const {
- if (!props.has(p_name)) {
- return false;
- }
-
- if (!props[p_name].has_default_value) {
- return false;
+ const VariantContainer *property = props.getptr(p_name);
+ if (property) {
+ return property->has_default_value;
}
-
- return props[p_name].initial != props[p_name].variant;
+ return false;
}
bool EditorSettings::_property_get_revert(const StringName &p_name, Variant &r_property) const {
- if (!props.has(p_name) || !props[p_name].has_default_value) {
- return false;
+ const VariantContainer *value = props.getptr(p_name);
+ if (value && value->has_default_value) {
+ r_property = value->initial;
+ return true;
}
-
- r_property = props[p_name].initial;
- return true;
+ return false;
}
void EditorSettings::add_property_hint(const PropertyInfo &p_hint) {
diff --git a/editor/export/export_template_manager.cpp b/editor/export/export_template_manager.cpp
index 5360b6fb60..2309319376 100644
--- a/editor/export/export_template_manager.cpp
+++ b/editor/export/export_template_manager.cpp
@@ -34,6 +34,7 @@
#include "core/io/json.h"
#include "core/io/zip_io.h"
#include "core/version.h"
+#include "editor/editor_file_system.h"
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
#include "editor/editor_settings.h"
@@ -876,7 +877,7 @@ Error ExportTemplateManager::install_android_template_from_file(const String &p_
ProgressDialog::get_singleton()->end_task("uncompress_src");
unzClose(pkg);
-
+ EditorFileSystem::get_singleton()->scan_changes();
return OK;
}
diff --git a/editor/gui/editor_file_dialog.cpp b/editor/gui/editor_file_dialog.cpp
index a63c3f7848..77d0ba7a60 100644
--- a/editor/gui/editor_file_dialog.cpp
+++ b/editor/gui/editor_file_dialog.cpp
@@ -1580,7 +1580,7 @@ void EditorFileDialog::_favorite_move_down() {
}
void EditorFileDialog::_update_favorites() {
- bool res = (access == ACCESS_RESOURCES);
+ bool access_resources = (access == ACCESS_RESOURCES);
String current = get_current_dir();
favorites->clear();
@@ -1596,8 +1596,11 @@ void EditorFileDialog::_update_favorites() {
for (int i = 0; i < favorited.size(); i++) {
String name = favorited[i];
- bool cres = name.begins_with("res://");
- if (cres != res || !name.ends_with("/")) {
+ if (access_resources != name.begins_with("res://")) {
+ continue;
+ }
+
+ if (!name.ends_with("/")) {
continue;
}
@@ -1609,7 +1612,7 @@ void EditorFileDialog::_update_favorites() {
}
// Compute favorite display text.
- if (res && name == "res://") {
+ if (access_resources && name == "res://") {
if (name == current) {
current_favorite = favorited_paths.size();
}
@@ -1620,7 +1623,7 @@ void EditorFileDialog::_update_favorites() {
if (name == current || name == current + "/") {
current_favorite = favorited_paths.size();
}
- name = name.substr(0, name.length() - 1);
+ name = name.trim_suffix("/");
name = name.get_file();
favorited_paths.append(favorited[i]);
favorited_names.append(name);
@@ -1647,7 +1650,7 @@ void EditorFileDialog::_update_favorites() {
}
void EditorFileDialog::_favorite_pressed() {
- bool res = (access == ACCESS_RESOURCES);
+ bool access_resources = (access == ACCESS_RESOURCES);
String cd = get_current_dir();
if (!cd.ends_with("/")) {
@@ -1657,13 +1660,12 @@ void EditorFileDialog::_favorite_pressed() {
Vector<String> favorited = EditorSettings::get_singleton()->get_favorites();
bool found = false;
- for (int i = 0; i < favorited.size(); i++) {
- bool cres = favorited[i].begins_with("res://");
- if (cres != res) {
+ for (const String &name : favorited) {
+ if (access_resources != name.begins_with("res://")) {
continue;
}
- if (favorited[i] == cd) {
+ if (name == cd) {
found = true;
break;
}
@@ -1683,31 +1685,30 @@ void EditorFileDialog::_favorite_pressed() {
void EditorFileDialog::_update_recent() {
recent->clear();
- bool res = (access == ACCESS_RESOURCES);
+ bool access_resources = (access == ACCESS_RESOURCES);
Vector<String> recentd = EditorSettings::get_singleton()->get_recent_dirs();
Vector<String> recentd_paths;
Vector<String> recentd_names;
+ bool modified = false;
for (int i = 0; i < recentd.size(); i++) {
- bool cres = recentd[i].begins_with("res://");
- if (cres != res) {
+ String name = recentd[i];
+ if (access_resources != name.begins_with("res://")) {
continue;
}
- if (!dir_access->dir_exists(recentd[i])) {
+ if (!dir_access->dir_exists(name)) {
// Remove invalid directory from the list of Recent directories.
recentd.remove_at(i--);
+ modified = true;
continue;
}
// Compute recent directory display text.
- String name = recentd[i];
- if (res && name == "res://") {
+ if (access_resources && name == "res://") {
name = "/";
} else {
- if (name.ends_with("/")) {
- name = name.substr(0, name.length() - 1);
- }
+ name = name.trim_suffix("/");
name = name.get_file();
}
recentd_paths.append(recentd[i]);
@@ -1721,7 +1722,10 @@ void EditorFileDialog::_update_recent() {
recent->set_item_metadata(-1, recentd_paths[i]);
recent->set_item_icon_modulate(-1, get_dir_icon_color(recentd_paths[i]));
}
- EditorSettings::get_singleton()->set_recent_dirs(recentd);
+
+ if (modified) {
+ EditorSettings::get_singleton()->set_recent_dirs(recentd);
+ }
}
void EditorFileDialog::_recent_selected(int p_idx) {
diff --git a/editor/gui/editor_quick_open_dialog.cpp b/editor/gui/editor_quick_open_dialog.cpp
index 83b11e7022..94a5ff94a3 100644
--- a/editor/gui/editor_quick_open_dialog.cpp
+++ b/editor/gui/editor_quick_open_dialog.cpp
@@ -709,7 +709,7 @@ void QuickOpenResultContainer::_notification(int p_what) {
file_details_path->add_theme_color_override(SceneStringName(font_color), text_color);
no_results_label->add_theme_color_override(SceneStringName(font_color), text_color);
- panel_container->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("QuickOpenBackgroundPanel"), EditorStringName(EditorStyles)));
+ panel_container->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));
if (content_display_mode == QuickOpenDisplayMode::LIST) {
display_mode_toggle->set_icon(get_editor_theme_icon(SNAME("FileThumbnail")));
diff --git a/editor/multi_node_edit.cpp b/editor/multi_node_edit.cpp
index 45786c0ab5..ef6a9c9a88 100644
--- a/editor/multi_node_edit.cpp
+++ b/editor/multi_node_edit.cpp
@@ -186,25 +186,9 @@ bool MultiNodeEdit::_property_can_revert(const StringName &p_name) const {
}
if (ClassDB::has_property(get_edited_class_name(), p_name)) {
- StringName class_name;
for (const NodePath &E : nodes) {
Node *node = es->get_node_or_null(E);
- if (!node) {
- continue;
- }
-
- class_name = node->get_class_name();
- }
-
- Variant default_value = ClassDB::class_get_default_property_value(class_name, p_name);
- for (const NodePath &E : nodes) {
- Node *node = es->get_node_or_null(E);
- if (!node) {
- continue;
- }
-
- if (node->get(p_name) != default_value) {
- // A node that doesn't have the default value has been found, so show the revert button.
+ if (node) {
return true;
}
}
diff --git a/editor/plugins/editor_context_menu_plugin.cpp b/editor/plugins/editor_context_menu_plugin.cpp
index 0648327fab..b635816bd9 100644
--- a/editor/plugins/editor_context_menu_plugin.cpp
+++ b/editor/plugins/editor_context_menu_plugin.cpp
@@ -67,10 +67,21 @@ void EditorContextMenuPlugin::add_context_menu_item_from_shortcut(const String &
context_menu_items.insert(p_name, item);
}
+void EditorContextMenuPlugin::add_context_submenu_item(const String &p_name, PopupMenu *p_menu, const Ref<Texture2D> &p_texture) {
+ ERR_FAIL_NULL(p_menu);
+
+ ContextMenuItem item;
+ item.item_name = p_name;
+ item.icon = p_texture;
+ item.submenu = p_menu;
+ context_menu_items.insert(p_name, item);
+}
+
void EditorContextMenuPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_menu_shortcut", "shortcut", "callback"), &EditorContextMenuPlugin::add_menu_shortcut);
ClassDB::bind_method(D_METHOD("add_context_menu_item", "name", "callback", "icon"), &EditorContextMenuPlugin::add_context_menu_item, DEFVAL(Ref<Texture2D>()));
ClassDB::bind_method(D_METHOD("add_context_menu_item_from_shortcut", "name", "shortcut", "icon"), &EditorContextMenuPlugin::add_context_menu_item_from_shortcut, DEFVAL(Ref<Texture2D>()));
+ ClassDB::bind_method(D_METHOD("add_context_submenu_item", "name", "menu", "icon"), &EditorContextMenuPlugin::add_context_submenu_item, DEFVAL(Ref<Texture2D>()));
GDVIRTUAL_BIND(_popup_menu, "paths");
@@ -117,12 +128,17 @@ void EditorContextMenuPluginManager::add_options_from_plugins(PopupMenu *p_popup
EditorContextMenuPlugin::ContextMenuItem &item = E.value;
item.id = id;
- if (item.icon.is_valid()) {
- p_popup->add_icon_item(item.icon, item.item_name, id);
- p_popup->set_item_icon_max_width(-1, icon_size);
+ if (item.submenu) {
+ p_popup->add_submenu_node_item(item.item_name, item.submenu, id);
} else {
p_popup->add_item(item.item_name, id);
}
+
+ if (item.icon.is_valid()) {
+ p_popup->set_item_icon(-1, item.icon);
+ p_popup->set_item_icon_max_width(-1, icon_size);
+ }
+
if (item.shortcut.is_valid()) {
p_popup->set_item_shortcut(-1, item.shortcut, true);
}
diff --git a/editor/plugins/editor_context_menu_plugin.h b/editor/plugins/editor_context_menu_plugin.h
index 0232d254ba..86c67dedda 100644
--- a/editor/plugins/editor_context_menu_plugin.h
+++ b/editor/plugins/editor_context_menu_plugin.h
@@ -65,6 +65,7 @@ public:
Callable callable;
Ref<Texture2D> icon;
Ref<Shortcut> shortcut;
+ PopupMenu *submenu = nullptr;
};
HashMap<String, ContextMenuItem> context_menu_items;
HashMap<Ref<Shortcut>, Callable> context_menu_shortcuts;
@@ -80,6 +81,7 @@ public:
void add_menu_shortcut(const Ref<Shortcut> &p_shortcut, const Callable &p_callable);
void add_context_menu_item(const String &p_name, const Callable &p_callable, const Ref<Texture2D> &p_texture);
void add_context_menu_item_from_shortcut(const String &p_name, const Ref<Shortcut> &p_shortcut, const Ref<Texture2D> &p_texture);
+ void add_context_submenu_item(const String &p_name, PopupMenu *p_menu, const Ref<Texture2D> &p_texture);
};
VARIANT_ENUM_CAST(EditorContextMenuPlugin::ContextMenuSlot);
diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp
index a2c36b1f3c..9a53f07a3f 100644
--- a/editor/plugins/editor_preview_plugins.cpp
+++ b/editor/plugins/editor_preview_plugins.cpp
@@ -112,9 +112,13 @@ Ref<Texture2D> EditorTexturePreviewPlugin::generate(const Ref<Resource> &p_from,
return Ref<Texture2D>();
}
- const int mid_depth = (tex_3d->get_depth() - 1) / 2;
-
Vector<Ref<Image>> data = tex_3d->get_data();
+ if (data.size() != tex_3d->get_depth()) {
+ return Ref<Texture2D>();
+ }
+
+ // Use the middle slice for the thumbnail.
+ const int mid_depth = (tex_3d->get_depth() - 1) / 2;
if (!data.is_empty() && data[mid_depth].is_valid()) {
img = data[mid_depth]->duplicate();
}
@@ -124,6 +128,7 @@ Ref<Texture2D> EditorTexturePreviewPlugin::generate(const Ref<Resource> &p_from,
return Ref<Texture2D>();
}
+ // Use the middle slice for the thumbnail.
const int mid_layer = (tex_lyr->get_layers() - 1) / 2;
Ref<Image> data = tex_lyr->get_layer_data(mid_layer);
diff --git a/editor/plugins/font_config_plugin.cpp b/editor/plugins/font_config_plugin.cpp
index ec9513363d..6366d20539 100644
--- a/editor/plugins/font_config_plugin.cpp
+++ b/editor/plugins/font_config_plugin.cpp
@@ -121,13 +121,8 @@ bool EditorPropertyFontOTObject::_property_can_revert(const StringName &p_name)
if (name.begins_with("keys")) {
int key = name.get_slicec('/', 1).to_int();
- if (defaults_dict.has(key) && dict.has(key)) {
- int value = dict[key];
- Vector3i range = defaults_dict[key];
- return range.z != value;
- }
+ return defaults_dict.has(key) && dict.has(key);
}
-
return false;
}
@@ -142,7 +137,6 @@ bool EditorPropertyFontOTObject::_property_get_revert(const StringName &p_name,
return true;
}
}
-
return false;
}
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index fe7301975f..0cf194b7fe 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -4255,8 +4255,31 @@ Vector3 Node3DEditorViewport::_get_instance_position(const Point2 &p_pos) const
ray_params.to = world_pos + world_ray * camera->get_far();
PhysicsDirectSpaceState3D::RayResult result;
- if (ss->intersect_ray(ray_params, result)) {
- return result.position;
+ if (ss->intersect_ray(ray_params, result) && preview_node->get_child_count() > 0) {
+ // Calculate an offset for the `preview_node` such that the its bounding box is on top of and touching the contact surface's plane.
+
+ // Use the Gram-Schmidt process to get an orthonormal Basis aligned with the surface normal.
+ const Vector3 bb_basis_x = result.normal;
+ Vector3 bb_basis_y = Vector3(0, 1, 0);
+ bb_basis_y = bb_basis_y - bb_basis_y.project(bb_basis_x);
+ if (bb_basis_y.is_zero_approx()) {
+ bb_basis_y = Vector3(0, 0, 1);
+ bb_basis_y = bb_basis_y - bb_basis_y.project(bb_basis_x);
+ }
+ bb_basis_y = bb_basis_y.normalized();
+ const Vector3 bb_basis_z = bb_basis_x.cross(bb_basis_y);
+ const Basis bb_basis = Basis(bb_basis_x, bb_basis_y, bb_basis_z);
+
+ // This normal-aligned Basis allows us to create an AABB that can fit on the surface plane as snugly as possible.
+ const Transform3D bb_transform = Transform3D(bb_basis, preview_node->get_transform().origin);
+ const AABB preview_node_bb = _calculate_spatial_bounds(preview_node, true, &bb_transform);
+ // The x-axis's alignment with the surface normal also makes it trivial to get the distance from `preview_node`'s origin at (0, 0, 0) to the correct AABB face.
+ const float offset_distance = -preview_node_bb.position.x;
+
+ // `result_offset` is in global space.
+ const Vector3 result_offset = result.position + result.normal * offset_distance;
+
+ return result_offset;
}
const bool is_orthogonal = camera->get_projection() == Camera3D::PROJECTION_ORTHOGONAL;
@@ -4284,18 +4307,21 @@ Vector3 Node3DEditorViewport::_get_instance_position(const Point2 &p_pos) const
return world_pos + world_ray * FALLBACK_DISTANCE;
}
-AABB Node3DEditorViewport::_calculate_spatial_bounds(const Node3D *p_parent, const Node3D *p_top_level_parent) {
+AABB Node3DEditorViewport::_calculate_spatial_bounds(const Node3D *p_parent, bool p_omit_top_level, const Transform3D *p_bounds_orientation) {
AABB bounds;
- if (!p_top_level_parent) {
- p_top_level_parent = p_parent;
+ Transform3D bounds_orientation;
+ if (p_bounds_orientation) {
+ bounds_orientation = *p_bounds_orientation;
+ } else {
+ bounds_orientation = p_parent->get_global_transform();
}
if (!p_parent) {
return AABB(Vector3(-0.2, -0.2, -0.2), Vector3(0.4, 0.4, 0.4));
}
- Transform3D xform_to_top_level_parent_space = p_top_level_parent->get_global_transform().affine_inverse() * p_parent->get_global_transform();
+ const Transform3D xform_to_top_level_parent_space = bounds_orientation.affine_inverse() * p_parent->get_global_transform();
const VisualInstance3D *visual_instance = Object::cast_to<VisualInstance3D>(p_parent);
if (visual_instance) {
@@ -4306,9 +4332,9 @@ AABB Node3DEditorViewport::_calculate_spatial_bounds(const Node3D *p_parent, con
bounds = xform_to_top_level_parent_space.xform(bounds);
for (int i = 0; i < p_parent->get_child_count(); i++) {
- Node3D *child = Object::cast_to<Node3D>(p_parent->get_child(i));
- if (child) {
- AABB child_bounds = _calculate_spatial_bounds(child, p_top_level_parent);
+ const Node3D *child = Object::cast_to<Node3D>(p_parent->get_child(i));
+ if (child && !(p_omit_top_level && child->is_set_as_top_level())) {
+ const AABB child_bounds = _calculate_spatial_bounds(child, p_omit_top_level, &bounds_orientation);
bounds.merge_with(child_bounds);
}
}
@@ -4359,6 +4385,10 @@ void Node3DEditorViewport::_create_preview_node(const Vector<String> &files) con
if (instance) {
instance = _sanitize_preview_node(instance);
preview_node->add_child(instance);
+ Node3D *node_3d = Object::cast_to<Node3D>(instance);
+ if (node_3d) {
+ node_3d->set_as_top_level(false);
+ }
}
add_preview = true;
}
@@ -4579,8 +4609,12 @@ bool Node3DEditorViewport::_create_instance(Node *p_parent, const String &p_path
}
Transform3D new_tf = node3d->get_transform();
- new_tf.origin = parent_tf.affine_inverse().xform(preview_node_pos + node3d->get_position());
- new_tf.basis = parent_tf.affine_inverse().basis * new_tf.basis;
+ if (node3d->is_set_as_top_level()) {
+ new_tf.origin += preview_node_pos;
+ } else {
+ new_tf.origin = parent_tf.affine_inverse().xform(preview_node_pos + node3d->get_position());
+ new_tf.basis = parent_tf.affine_inverse().basis * new_tf.basis;
+ }
undo_redo->add_do_method(instantiated_scene, "set_transform", new_tf);
}
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index c7e6420875..a8cade36fd 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -472,7 +472,7 @@ private:
Point2 _get_warped_mouse_motion(const Ref<InputEventMouseMotion> &p_ev_mouse_motion) const;
Vector3 _get_instance_position(const Point2 &p_pos) const;
- static AABB _calculate_spatial_bounds(const Node3D *p_parent, const Node3D *p_top_level_parent = nullptr);
+ static AABB _calculate_spatial_bounds(const Node3D *p_parent, bool p_omit_top_level = false, const Transform3D *p_bounds_orientation = nullptr);
Node *_sanitize_preview_node(Node *p_node) const;
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
index 8f646a7621..cc488ff340 100644
--- a/editor/plugins/theme_editor_plugin.cpp
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -3596,6 +3596,13 @@ void ThemeEditor::_theme_close_button_cbk() {
}
}
+void ThemeEditor::_scene_closed(const String &p_path) {
+ if (theme.is_valid() && theme->is_built_in() && theme->get_path().get_slice("::", 0) == p_path) {
+ theme = Ref<Theme>();
+ EditorNode::get_singleton()->hide_unused_editors(plugin);
+ }
+}
+
void ThemeEditor::_add_preview_button_cbk() {
preview_scene_dialog->popup_file_dialog();
}
@@ -3679,7 +3686,10 @@ void ThemeEditor::_preview_control_picked(String p_class_name) {
void ThemeEditor::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_READY: {
+ EditorNode::get_singleton()->connect("scene_closed", callable_mp(this, &ThemeEditor::_scene_closed));
+ } break;
+
case NOTIFICATION_THEME_CHANGED: {
preview_tabs->add_theme_style_override("tab_selected", get_theme_stylebox(SNAME("ThemeEditorPreviewFG"), EditorStringName(EditorStyles)));
preview_tabs->add_theme_style_override("tab_unselected", get_theme_stylebox(SNAME("ThemeEditorPreviewBG"), EditorStringName(EditorStyles)));
@@ -3807,71 +3817,7 @@ void ThemeEditorPlugin::make_visible(bool p_visible) {
}
bool ThemeEditorPlugin::can_auto_hide() const {
- Ref<Theme> edited_theme = theme_editor->theme;
- if (edited_theme.is_null()) {
- return true;
- }
-
- Ref<Resource> edited_resource = Ref<Resource>(InspectorDock::get_inspector_singleton()->get_next_edited_object());
- if (edited_resource.is_null()) {
- return true;
- }
-
- // Don't hide if edited resource used by this theme.
- Ref<StyleBox> sbox = edited_resource;
- if (sbox.is_valid()) {
- List<StringName> type_list;
- edited_theme->get_stylebox_type_list(&type_list);
-
- for (const StringName &E : type_list) {
- List<StringName> list;
- edited_theme->get_stylebox_list(E, &list);
-
- for (const StringName &F : list) {
- if (edited_theme->get_stylebox(F, E) == sbox) {
- return false;
- }
- }
- }
- return true;
- }
-
- Ref<Texture2D> tex = edited_resource;
- if (tex.is_valid()) {
- List<StringName> type_list;
- edited_theme->get_icon_type_list(&type_list);
-
- for (const StringName &E : type_list) {
- List<StringName> list;
- edited_theme->get_icon_list(E, &list);
-
- for (const StringName &F : list) {
- if (edited_theme->get_icon(F, E) == tex) {
- return false;
- }
- }
- }
- return true;
- }
-
- Ref<Font> fnt = edited_resource;
- if (fnt.is_valid()) {
- List<StringName> type_list;
- edited_theme->get_font_type_list(&type_list);
-
- for (const StringName &E : type_list) {
- List<StringName> list;
- edited_theme->get_font_list(E, &list);
-
- for (const StringName &F : list) {
- if (edited_theme->get_font(F, E) == fnt) {
- return false;
- }
- }
- }
- return true;
- }
- return true;
+ return theme_editor->theme.is_null();
}
ThemeEditorPlugin::ThemeEditorPlugin() {
diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h
index 1d009637b7..39dc8d154b 100644
--- a/editor/plugins/theme_editor_plugin.h
+++ b/editor/plugins/theme_editor_plugin.h
@@ -445,6 +445,7 @@ class ThemeEditor : public VBoxContainer {
void _theme_save_button_cbk(bool p_save_as);
void _theme_edit_button_cbk();
void _theme_close_button_cbk();
+ void _scene_closed(const String &p_path);
void _add_preview_button_cbk();
void _preview_scene_dialog_cbk(const String &p_path);
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
index 12b9761fd2..c6921699a4 100644
--- a/editor/plugins/tiles/tile_data_editors.cpp
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -874,6 +874,11 @@ void GenericTilePolygonEditor::_notification(int p_what) {
button_expand->set_pressed_no_signal(false);
}
} break;
+
+ case NOTIFICATION_READY: {
+ get_parent()->connect(SceneStringName(tree_exited), callable_mp(TileSetEditor::get_singleton(), &TileSetEditor::remove_expanded_editor));
+ } break;
+
case NOTIFICATION_THEME_CHANGED: {
button_expand->set_icon(get_editor_theme_icon(SNAME("DistractionFree")));
button_create->set_icon(get_editor_theme_icon(SNAME("CurveCreate")));
diff --git a/editor/project_manager/project_dialog.cpp b/editor/project_manager/project_dialog.cpp
index 857c4461c4..7aadb9ac3c 100644
--- a/editor/project_manager/project_dialog.cpp
+++ b/editor/project_manager/project_dialog.cpp
@@ -552,6 +552,21 @@ void ProjectDialog::ok_pressed() {
fa_icon->store_string(get_default_project_icon());
EditorVCSInterface::create_vcs_metadata_files(EditorVCSInterface::VCSMetadata(vcs_metadata_selection->get_selected()), path);
+
+ // Ensures external editors and IDEs use UTF-8 encoding.
+ const String editor_config_path = path.path_join(".editorconfig");
+ Ref<FileAccess> f = FileAccess::open(editor_config_path, FileAccess::WRITE);
+ if (f.is_null()) {
+ // .editorconfig isn't so critical.
+ ERR_PRINT("Couldn't create .editorconfig in project path.");
+ } else {
+ f->store_line("root = true");
+ f->store_line("");
+ f->store_line("[*]");
+ f->store_line("charset = utf-8");
+ f->close();
+ FileAccess::set_hidden_attribute(editor_config_path, true);
+ }
}
// Two cases for importing a ZIP.
diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp
index 418f4e4932..f973367bed 100644
--- a/editor/project_settings_editor.cpp
+++ b/editor/project_settings_editor.cpp
@@ -104,7 +104,8 @@ void ProjectSettingsEditor::_update_advanced(bool p_is_advanced) {
}
void ProjectSettingsEditor::_advanced_toggled(bool p_button_pressed) {
- EditorSettings::get_singleton()->set_project_metadata("project_settings", "advanced_mode", p_button_pressed);
+ EditorSettings::get_singleton()->set("_project_settings_advanced_mode", p_button_pressed);
+ EditorSettings::get_singleton()->save();
_update_advanced(p_button_pressed);
}
@@ -768,8 +769,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
set_ok_button_text(TTR("Close"));
set_hide_on_ok(true);
- bool use_advanced = EditorSettings::get_singleton()->get_project_metadata("project_settings", "advanced_mode", false);
-
+ bool use_advanced = EDITOR_DEF("_project_settings_advanced_mode", false);
if (use_advanced) {
advanced->set_pressed(true);
}
diff --git a/misc/scripts/install_vulkan_sdk_macos.sh b/misc/scripts/install_vulkan_sdk_macos.sh
index 17d567f208..6e9fa69eea 100755
--- a/misc/scripts/install_vulkan_sdk_macos.sh
+++ b/misc/scripts/install_vulkan_sdk_macos.sh
@@ -4,19 +4,13 @@ set -euo pipefail
IFS=$'\n\t'
# Download and install the Vulkan SDK.
-curl -L "https://sdk.lunarg.com/sdk/download/latest/mac/vulkan-sdk.dmg" -o /tmp/vulkan-sdk.dmg
-hdiutil attach /tmp/vulkan-sdk.dmg -mountpoint /Volumes/vulkan-sdk
-/Volumes/vulkan-sdk/InstallVulkan.app/Contents/MacOS/InstallVulkan \
+curl -L "https://sdk.lunarg.com/sdk/download/latest/mac/vulkan-sdk.zip" -o /tmp/vulkan-sdk.zip
+unzip /tmp/vulkan-sdk.zip -d /tmp
+/tmp/InstallVulkan.app/Contents/MacOS/InstallVulkan \
--accept-licenses --default-answer --confirm-command install
-cnt=5
-until hdiutil detach -force /Volumes/vulkan-sdk
-do
- [[ cnt -eq "0" ]] && break
- sleep 1
- ((cnt--))
-done
-rm -f /tmp/vulkan-sdk.dmg
+rm -rf /tmp/InstallVulkan.app
+rm -f /tmp/vulkan-sdk.zip
echo 'Vulkan SDK installed successfully! You can now build Godot by running "scons".'
diff --git a/modules/bcdec/image_decompress_bcdec.cpp b/modules/bcdec/image_decompress_bcdec.cpp
index 30ca1fccb3..c76470e3cc 100644
--- a/modules/bcdec/image_decompress_bcdec.cpp
+++ b/modules/bcdec/image_decompress_bcdec.cpp
@@ -92,8 +92,20 @@ static void decompress_image(BCdecFormat format, const void *src, void *dst, con
void image_decompress_bcdec(Image *p_image) {
uint64_t start_time = OS::get_singleton()->get_ticks_msec();
- int w = p_image->get_width();
- int h = p_image->get_height();
+ int width = p_image->get_width();
+ int height = p_image->get_height();
+
+ // Compressed images' dimensions should be padded to the upper multiple of 4.
+ // If they aren't, they need to be realigned (the actual data is correctly padded though).
+ if (width % 4 != 0 || height % 4 != 0) {
+ int new_width = width + (4 - (width % 4));
+ int new_height = height + (4 - (height % 4));
+
+ print_verbose(vformat("Compressed image's dimensions are not multiples of 4 (%dx%d), aligning to (%dx%d)", width, height, new_width, new_height));
+
+ width = new_width;
+ height = new_height;
+ }
Image::Format source_format = p_image->get_format();
Image::Format target_format = Image::FORMAT_MAX;
@@ -148,30 +160,27 @@ void image_decompress_bcdec(Image *p_image) {
}
int mm_count = p_image->get_mipmap_count();
- int64_t target_size = Image::get_image_data_size(w, h, target_format, p_image->has_mipmaps());
+ int64_t target_size = Image::get_image_data_size(width, height, target_format, p_image->has_mipmaps());
+ // Decompressed data.
Vector<uint8_t> data;
data.resize(target_size);
+ uint8_t *wb = data.ptrw();
+ // Source data.
const uint8_t *rb = p_image->get_data().ptr();
- uint8_t *wb = data.ptrw();
// Decompress mipmaps.
for (int i = 0; i <= mm_count; i++) {
- int64_t src_ofs = 0, mipmap_size = 0;
int mipmap_w = 0, mipmap_h = 0;
- p_image->get_mipmap_offset_size_and_dimensions(i, src_ofs, mipmap_size, mipmap_w, mipmap_h);
-
- int64_t dst_ofs = Image::get_image_mipmap_offset(p_image->get_width(), p_image->get_height(), target_format, i);
+ int64_t src_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, source_format, i, mipmap_w, mipmap_h);
+ int64_t dst_ofs = Image::get_image_mipmap_offset(width, height, target_format, i);
decompress_image(bcdec_format, rb + src_ofs, wb + dst_ofs, mipmap_w, mipmap_h);
-
- w >>= 1;
- h >>= 1;
}
- p_image->set_data(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
+ p_image->set_data(width, height, p_image->has_mipmaps(), target_format, data);
- // Swap channels if necessary.
+ // Swap channels if the format is using a channel swizzle.
if (source_format == Image::FORMAT_DXT5_RA_AS_RG) {
p_image->convert_ra_rgba8_to_rg();
}
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 7f0d5005cb..4a3a3a4b61 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -148,6 +148,15 @@ static GDScriptParser::DataType make_enum_type(const StringName &p_enum_name, co
return type;
}
+static GDScriptParser::DataType make_class_enum_type(const StringName &p_enum_name, GDScriptParser::ClassNode *p_class, const String &p_script_path, bool p_meta = true) {
+ GDScriptParser::DataType type = make_enum_type(p_enum_name, p_class->fqcn, p_meta);
+
+ type.class_type = p_class;
+ type.script_path = p_script_path;
+
+ return type;
+}
+
static GDScriptParser::DataType make_native_enum_type(const StringName &p_enum_name, const StringName &p_native_class, bool p_meta = true) {
// Find out which base class declared the enum, so the name is always the same even when coming from other contexts.
StringName native_base = p_native_class;
@@ -1101,7 +1110,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
check_class_member_name_conflict(p_class, member.m_enum->identifier->name, member.m_enum);
member.m_enum->set_datatype(resolving_datatype);
- GDScriptParser::DataType enum_type = make_enum_type(member.m_enum->identifier->name, p_class->fqcn, true);
+ GDScriptParser::DataType enum_type = make_class_enum_type(member.m_enum->identifier->name, p_class, parser->script_path, true);
const GDScriptParser::EnumNode *prev_enum = current_enum;
current_enum = member.m_enum;
@@ -1194,7 +1203,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
// Also update the original references.
member.enum_value.parent_enum->values.set(member.enum_value.index, member.enum_value);
- member.enum_value.identifier->set_datatype(make_enum_type(UNNAMED_ENUM, p_class->fqcn, false));
+ member.enum_value.identifier->set_datatype(make_class_enum_type(UNNAMED_ENUM, p_class, parser->script_path, false));
} break;
case GDScriptParser::ClassNode::Member::CLASS:
check_class_member_name_conflict(p_class, member.m_class->identifier->name, member.m_class);
@@ -4249,7 +4258,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
const GDScriptParser::EnumNode::Value &element = current_enum->values[i];
if (element.identifier->name == p_identifier->name) {
StringName enum_name = current_enum->identifier ? current_enum->identifier->name : UNNAMED_ENUM;
- GDScriptParser::DataType type = make_enum_type(enum_name, parser->current_class->fqcn, false);
+ GDScriptParser::DataType type = make_class_enum_type(enum_name, parser->current_class, parser->script_path, false);
if (element.parent_enum->identifier) {
type.enum_type = element.parent_enum->identifier->name;
}
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 0fd891aa80..3de1decc18 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -3782,7 +3782,19 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
}
} break;
case GDScriptParser::DataType::ENUM: {
- if (base_type.enum_values.has(p_symbol)) {
+ if (base_type.class_type && base_type.class_type->has_member(base_type.enum_type)) {
+ GDScriptParser::EnumNode *base_enum = base_type.class_type->get_member(base_type.enum_type).m_enum;
+ for (const GDScriptParser::EnumNode::Value &value : base_enum->values) {
+ if (value.identifier && value.identifier->name == p_symbol) {
+ r_result.type = ScriptLanguage::LOOKUP_RESULT_SCRIPT_LOCATION;
+ r_result.class_path = base_type.script_path;
+ r_result.location = value.line;
+ Error err = OK;
+ r_result.script = GDScriptCache::get_shallow_script(r_result.class_path, err);
+ return err;
+ }
+ }
+ } else if (base_type.enum_values.has(p_symbol)) {
r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;
r_result.class_name = String(base_type.native_type).get_slicec('.', 0);
r_result.class_member = p_symbol;
diff --git a/modules/gdscript/tests/scripts/.editorconfig b/modules/gdscript/tests/scripts/.editorconfig
index da1efefe3c..34fff8d1de 100644
--- a/modules/gdscript/tests/scripts/.editorconfig
+++ b/modules/gdscript/tests/scripts/.editorconfig
@@ -1,5 +1,3 @@
-# This file is required to workaround `.editorconfig` autogeneration (see #96845).
-
# Some tests handle invalid syntax deliberately; exclude relevant attributes.
[parser/features/mixed_indentation_on_blank_lines.gd]
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 89655e0b56..946e997c1b 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -2348,9 +2348,17 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
int method_bind_count = 0;
for (const MethodInterface &imethod : itype.methods) {
- Error method_err = _generate_cs_method(itype, imethod, method_bind_count, output);
+ Error method_err = _generate_cs_method(itype, imethod, method_bind_count, output, false);
ERR_FAIL_COND_V_MSG(method_err != OK, method_err,
"Failed to generate method '" + imethod.name + "' for class '" + itype.name + "'.");
+ if (imethod.is_internal) {
+ // No need to generate span overloads for internal methods.
+ continue;
+ }
+
+ method_err = _generate_cs_method(itype, imethod, method_bind_count, output, true);
+ ERR_FAIL_COND_V_MSG(method_err != OK, method_err,
+ "Failed to generate span overload method '" + imethod.name + "' for class '" + itype.name + "'.");
}
// Signals
@@ -2776,7 +2784,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
return OK;
}
-Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output) {
+Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output, bool p_use_span) {
const TypeInterface *return_type = _get_type_or_singleton_or_null(p_imethod.return_type);
ERR_FAIL_NULL_V_MSG(return_type, ERR_BUG, "Return type '" + p_imethod.return_type.cname + "' was not found.");
@@ -2789,6 +2797,35 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
"' from the editor API. Core API cannot have dependencies on the editor API.");
}
+ if (p_imethod.is_virtual && p_use_span) {
+ return OK;
+ }
+
+ bool has_span_argument = false;
+
+ if (p_use_span) {
+ if (p_imethod.is_vararg) {
+ has_span_argument = true;
+ } else {
+ for (const ArgumentInterface &iarg : p_imethod.arguments) {
+ const TypeInterface *arg_type = _get_type_or_singleton_or_null(iarg.type);
+ ERR_FAIL_NULL_V_MSG(arg_type, ERR_BUG, "Argument type '" + iarg.type.cname + "' was not found.");
+
+ if (arg_type->is_span_compatible) {
+ has_span_argument = true;
+ break;
+ }
+ }
+ }
+
+ if (has_span_argument) {
+ // Span overloads use the same method bind as the array overloads.
+ // Since both overloads are generated one after the other, we can decrease the count here
+ // to ensure the span overload uses the same method bind.
+ p_method_bind_count--;
+ }
+ }
+
String method_bind_field = CS_STATIC_FIELD_METHOD_BIND_PREFIX + itos(p_method_bind_count);
String arguments_sig;
@@ -2835,6 +2872,8 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
String arg_cs_type = arg_type->cs_type + _get_generic_type_parameters(*arg_type, iarg.type.generic_type_parameters);
+ bool use_span_for_arg = p_use_span && arg_type->is_span_compatible;
+
// Add the current arguments to the signature
// If the argument has a default value which is not a constant, we will make it Nullable
{
@@ -2846,7 +2885,11 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
arguments_sig += "Nullable<";
}
- arguments_sig += arg_cs_type;
+ if (use_span_for_arg) {
+ arguments_sig += arg_type->c_type_in;
+ } else {
+ arguments_sig += arg_cs_type;
+ }
if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) {
arguments_sig += "> ";
@@ -2856,7 +2899,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
arguments_sig += iarg.name;
- if (!p_imethod.is_compat && iarg.default_argument.size()) {
+ if (!p_use_span && !p_imethod.is_compat && iarg.default_argument.size()) {
if (iarg.def_param_mode != ArgumentInterface::CONSTANT) {
arguments_sig += " = null";
} else {
@@ -2867,7 +2910,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
icall_params += ", ";
- if (iarg.default_argument.size() && iarg.def_param_mode != ArgumentInterface::CONSTANT) {
+ if (iarg.default_argument.size() && iarg.def_param_mode != ArgumentInterface::CONSTANT && !use_span_for_arg) {
// The default value of an argument must be constant. Otherwise we make it Nullable and do the following:
// Type arg_in = arg.HasValue ? arg.Value : <non-const default value>;
String arg_or_defval_local = iarg.name;
@@ -2927,6 +2970,10 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
cs_in_expr_is_unsafe |= arg_type->cs_in_expr_is_unsafe;
}
+ if (p_use_span && !has_span_argument) {
+ return OK;
+ }
+
// Collect caller name for MethodBind
if (p_imethod.is_vararg) {
icall_params += ", (godot_string_name)MethodName." + p_imethod.proxy_name + ".NativeValue";
@@ -2934,7 +2981,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
// Generate method
{
- if (!p_imethod.is_virtual && !p_imethod.requires_object_call) {
+ if (!p_imethod.is_virtual && !p_imethod.requires_object_call && !p_use_span) {
p_output << MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]\n"
<< INDENT1 "private static readonly IntPtr " << method_bind_field << " = ";
@@ -4734,13 +4781,14 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype = TypeInterface();
itype.name = "VarArg";
itype.cname = itype.name;
- itype.proxy_name = "Variant[]";
+ itype.proxy_name = "ReadOnlySpan<Variant>";
itype.cs_type = "params Variant[]";
- itype.cs_in_expr = "%0 ?? Array.Empty<Variant>()";
+ itype.cs_in_expr = "%0";
// c_type, c_in and c_arg_in are hard-coded in the generator.
// c_out and c_type_out are not applicable to VarArg.
itype.c_arg_in = "&%s_in";
- itype.c_type_in = "Variant[]";
+ itype.c_type_in = "ReadOnlySpan<Variant>";
+ itype.is_span_compatible = true;
builtin_types.insert(itype.cname, itype);
#define INSERT_ARRAY_FULL(m_name, m_type, m_managed_type, m_proxy_t) \
@@ -4754,9 +4802,10 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_out = "%5return " C_METHOD_MONOARRAY_FROM(m_type) "(%1);\n"; \
itype.c_arg_in = "&%s_in"; \
itype.c_type = #m_managed_type; \
- itype.c_type_in = itype.proxy_name; \
+ itype.c_type_in = "ReadOnlySpan<" #m_proxy_t ">"; \
itype.c_type_out = itype.proxy_name; \
itype.c_type_is_disposable_struct = true; \
+ itype.is_span_compatible = true; \
builtin_types.insert(itype.name, itype); \
}
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index 556d287af4..1670aca4b3 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -265,6 +265,7 @@ class BindingsGenerator {
bool is_singleton = false;
bool is_singleton_instance = false;
bool is_ref_counted = false;
+ bool is_span_compatible = false;
/**
* Class is a singleton, but can't be declared as a static class as that would
@@ -840,7 +841,7 @@ class BindingsGenerator {
Error _generate_cs_type(const TypeInterface &itype, const String &p_output_file);
Error _generate_cs_property(const TypeInterface &p_itype, const PropertyInterface &p_iprop, StringBuilder &p_output);
- Error _generate_cs_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output);
+ Error _generate_cs_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output, bool p_use_span);
Error _generate_cs_signal(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::SignalInterface &p_isignal, StringBuilder &p_output);
Error _generate_cs_native_calls(const InternalCall &p_icall, StringBuilder &r_output);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
index 864815866a..db6961fd12 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
@@ -174,7 +174,7 @@ namespace Godot.Collections
var keys = Array.CreateTakingOwnershipOfDisposableValue(keysArray);
godot_array valuesArray;
- NativeFuncs.godotsharp_dictionary_keys(ref self, out valuesArray);
+ NativeFuncs.godotsharp_dictionary_values(ref self, out valuesArray);
var values = Array.CreateTakingOwnershipOfDisposableValue(valuesArray);
int count = NativeFuncs.godotsharp_dictionary_count(ref self);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
index 15b7ce7c73..fc68b11932 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
@@ -394,7 +394,12 @@ namespace Godot.NativeInterop
return array;
}
- public static unsafe godot_packed_byte_array ConvertSystemArrayToNativePackedByteArray(Span<byte> p_array)
+ public static godot_packed_byte_array ConvertSystemArrayToNativePackedByteArray(Span<byte> p_array)
+ {
+ return ConvertSystemArrayToNativePackedByteArray((ReadOnlySpan<byte>)p_array);
+ }
+
+ public static unsafe godot_packed_byte_array ConvertSystemArrayToNativePackedByteArray(ReadOnlySpan<byte> p_array)
{
if (p_array.IsEmpty)
return new godot_packed_byte_array();
@@ -417,7 +422,12 @@ namespace Godot.NativeInterop
return array;
}
- public static unsafe godot_packed_int32_array ConvertSystemArrayToNativePackedInt32Array(Span<int> p_array)
+ public static godot_packed_int32_array ConvertSystemArrayToNativePackedInt32Array(Span<int> p_array)
+ {
+ return ConvertSystemArrayToNativePackedInt32Array((ReadOnlySpan<int>)p_array);
+ }
+
+ public static unsafe godot_packed_int32_array ConvertSystemArrayToNativePackedInt32Array(ReadOnlySpan<int> p_array)
{
if (p_array.IsEmpty)
return new godot_packed_int32_array();
@@ -440,7 +450,12 @@ namespace Godot.NativeInterop
return array;
}
- public static unsafe godot_packed_int64_array ConvertSystemArrayToNativePackedInt64Array(Span<long> p_array)
+ public static godot_packed_int64_array ConvertSystemArrayToNativePackedInt64Array(Span<long> p_array)
+ {
+ return ConvertSystemArrayToNativePackedInt64Array((ReadOnlySpan<long>)p_array);
+ }
+
+ public static unsafe godot_packed_int64_array ConvertSystemArrayToNativePackedInt64Array(ReadOnlySpan<long> p_array)
{
if (p_array.IsEmpty)
return new godot_packed_int64_array();
@@ -463,8 +478,13 @@ namespace Godot.NativeInterop
return array;
}
+ public static godot_packed_float32_array ConvertSystemArrayToNativePackedFloat32Array(Span<float> p_array)
+ {
+ return ConvertSystemArrayToNativePackedFloat32Array((ReadOnlySpan<float>)p_array);
+ }
+
public static unsafe godot_packed_float32_array ConvertSystemArrayToNativePackedFloat32Array(
- Span<float> p_array)
+ ReadOnlySpan<float> p_array)
{
if (p_array.IsEmpty)
return new godot_packed_float32_array();
@@ -487,8 +507,13 @@ namespace Godot.NativeInterop
return array;
}
+ public static godot_packed_float64_array ConvertSystemArrayToNativePackedFloat64Array(Span<double> p_array)
+ {
+ return ConvertSystemArrayToNativePackedFloat64Array((ReadOnlySpan<double>)p_array);
+ }
+
public static unsafe godot_packed_float64_array ConvertSystemArrayToNativePackedFloat64Array(
- Span<double> p_array)
+ ReadOnlySpan<double> p_array)
{
if (p_array.IsEmpty)
return new godot_packed_float64_array();
@@ -512,6 +537,11 @@ namespace Godot.NativeInterop
public static godot_packed_string_array ConvertSystemArrayToNativePackedStringArray(Span<string> p_array)
{
+ return ConvertSystemArrayToNativePackedStringArray((ReadOnlySpan<string>)p_array);
+ }
+
+ public static godot_packed_string_array ConvertSystemArrayToNativePackedStringArray(ReadOnlySpan<string> p_array)
+ {
godot_packed_string_array dest = new godot_packed_string_array();
if (p_array.IsEmpty)
@@ -544,8 +574,13 @@ namespace Godot.NativeInterop
return array;
}
+ public static godot_packed_vector2_array ConvertSystemArrayToNativePackedVector2Array(Span<Vector2> p_array)
+ {
+ return ConvertSystemArrayToNativePackedVector2Array((ReadOnlySpan<Vector2>)p_array);
+ }
+
public static unsafe godot_packed_vector2_array ConvertSystemArrayToNativePackedVector2Array(
- Span<Vector2> p_array)
+ ReadOnlySpan<Vector2> p_array)
{
if (p_array.IsEmpty)
return new godot_packed_vector2_array();
@@ -568,8 +603,13 @@ namespace Godot.NativeInterop
return array;
}
+ public static godot_packed_vector3_array ConvertSystemArrayToNativePackedVector3Array(Span<Vector3> p_array)
+ {
+ return ConvertSystemArrayToNativePackedVector3Array((ReadOnlySpan<Vector3>)p_array);
+ }
+
public static unsafe godot_packed_vector3_array ConvertSystemArrayToNativePackedVector3Array(
- Span<Vector3> p_array)
+ ReadOnlySpan<Vector3> p_array)
{
if (p_array.IsEmpty)
return new godot_packed_vector3_array();
@@ -592,8 +632,13 @@ namespace Godot.NativeInterop
return array;
}
+ public static godot_packed_vector4_array ConvertSystemArrayToNativePackedVector4Array(Span<Vector4> p_array)
+ {
+ return ConvertSystemArrayToNativePackedVector4Array((ReadOnlySpan<Vector4>)p_array);
+ }
+
public static unsafe godot_packed_vector4_array ConvertSystemArrayToNativePackedVector4Array(
- Span<Vector4> p_array)
+ ReadOnlySpan<Vector4> p_array)
{
if (p_array.IsEmpty)
return new godot_packed_vector4_array();
@@ -616,7 +661,12 @@ namespace Godot.NativeInterop
return array;
}
- public static unsafe godot_packed_color_array ConvertSystemArrayToNativePackedColorArray(Span<Color> p_array)
+ public static godot_packed_color_array ConvertSystemArrayToNativePackedColorArray(Span<Color> p_array)
+ {
+ return ConvertSystemArrayToNativePackedColorArray((ReadOnlySpan<Color>)p_array);
+ }
+
+ public static unsafe godot_packed_color_array ConvertSystemArrayToNativePackedColorArray(ReadOnlySpan<Color> p_array)
{
if (p_array.IsEmpty)
return new godot_packed_color_array();
diff --git a/modules/noise/doc_classes/FastNoiseLite.xml b/modules/noise/doc_classes/FastNoiseLite.xml
index f2a6c60376..6f6a637893 100644
--- a/modules/noise/doc_classes/FastNoiseLite.xml
+++ b/modules/noise/doc_classes/FastNoiseLite.xml
@@ -91,10 +91,10 @@
Cellular includes both Worley noise and Voronoi diagrams which creates various regions of the same value.
</constant>
<constant name="TYPE_SIMPLEX" value="0" enum="NoiseType">
- As opposed to [constant TYPE_PERLIN], gradients exist in a simplex lattice rather than a grid lattice, avoiding directional artifacts.
+ As opposed to [constant TYPE_PERLIN], gradients exist in a simplex lattice rather than a grid lattice, avoiding directional artifacts. Internally uses FastNoiseLite's OpenSimplex2 noise type.
</constant>
<constant name="TYPE_SIMPLEX_SMOOTH" value="1" enum="NoiseType">
- Modified, higher quality version of [constant TYPE_SIMPLEX], but slower.
+ Modified, higher quality version of [constant TYPE_SIMPLEX], but slower. Internally uses FastNoiseLite's OpenSimplex2S noise type.
</constant>
<constant name="FRACTAL_NONE" value="0" enum="FractalType">
No fractal noise.
diff --git a/platform/ios/detect.py b/platform/ios/detect.py
index 989a7f21f3..20a3a996bc 100644
--- a/platform/ios/detect.py
+++ b/platform/ios/detect.py
@@ -2,7 +2,7 @@ import os
import sys
from typing import TYPE_CHECKING
-from methods import detect_darwin_sdk_path, print_error
+from methods import detect_darwin_sdk_path, print_error, print_warning
if TYPE_CHECKING:
from SCons.Script.SConscript import SConsEnvironment
@@ -156,7 +156,7 @@ def configure(env: "SConsEnvironment"):
env.Append(CPPDEFINES=["IOS_ENABLED", "UNIX_ENABLED", "COREAUDIO_ENABLED"])
if env["metal"] and env["arch"] != "arm64":
- # Only supported on arm64, so skip it for x86_64 builds.
+ print_warning("Target architecture '{}' does not support the Metal rendering driver".format(env["arch"]))
env["metal"] = False
if env["metal"]:
diff --git a/platform/macos/detect.py b/platform/macos/detect.py
index 595a83ead3..a8968b592e 100644
--- a/platform/macos/detect.py
+++ b/platform/macos/detect.py
@@ -2,7 +2,7 @@ import os
import sys
from typing import TYPE_CHECKING
-from methods import detect_darwin_sdk_path, get_compiler_version, is_vanilla_clang, print_error
+from methods import detect_darwin_sdk_path, get_compiler_version, is_vanilla_clang, print_error, print_warning
from platform_methods import detect_arch, detect_mvk
if TYPE_CHECKING:
@@ -249,7 +249,7 @@ def configure(env: "SConsEnvironment"):
env.Append(LINKFLAGS=["-rpath", "@executable_path/../Frameworks", "-rpath", "@executable_path"])
if env["metal"] and env["arch"] != "arm64":
- # Only supported on arm64, so skip it for x86_64 builds.
+ print_warning("Target architecture '{}' does not support the Metal rendering driver".format(env["arch"]))
env["metal"] = False
extra_frameworks = set()
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index 4043f3a8c2..0ee52a09a7 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -662,7 +662,7 @@ def get_ar_version(env):
print_warning("Couldn't check version of `ar`.")
return ret
- match = re.search(r"GNU ar \(GNU Binutils\) (\d+)\.(\d+)(?:\.(\d+))?", output)
+ match = re.search(r"GNU ar(?: \(GNU Binutils\)| version) (\d+)\.(\d+)(?:\.(\d+))?", output)
if match:
ret["major"] = int(match[1])
ret["minor"] = int(match[2])
@@ -788,8 +788,9 @@ def configure_mingw(env: "SConsEnvironment"):
env["CXX"] = mingw_bin_prefix + "g++"
if try_cmd("as --version", env["mingw_prefix"], env["arch"]):
env["AS"] = mingw_bin_prefix + "as"
- if try_cmd("gcc-ar --version", env["mingw_prefix"], env["arch"]):
- env["AR"] = mingw_bin_prefix + "gcc-ar"
+ ar = "ar" if os.name == "nt" else "gcc-ar"
+ if try_cmd(f"{ar} --version", env["mingw_prefix"], env["arch"]):
+ env["AR"] = mingw_bin_prefix + ar
if try_cmd("gcc-ranlib --version", env["mingw_prefix"], env["arch"]):
env["RANLIB"] = mingw_bin_prefix + "gcc-ranlib"
diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp
index 90bfb4c84c..39cfccf983 100644
--- a/scene/2d/skeleton_2d.cpp
+++ b/scene/2d/skeleton_2d.cpp
@@ -159,7 +159,7 @@ void Bone2D::_notification(int p_what) {
case NOTIFICATION_EXIT_TREE: {
if (skeleton) {
- for (int i = 0; i < skeleton->bones.size(); i++) {
+ for (uint32_t i = 0; i < skeleton->bones.size(); i++) {
if (skeleton->bones[i].bone == this) {
skeleton->bones.remove_at(i);
break;
@@ -555,17 +555,17 @@ void Skeleton2D::_update_bone_setup() {
bones.sort(); //sorting so that they are always in the same order/index
- for (int i = 0; i < bones.size(); i++) {
- bones.write[i].rest_inverse = bones[i].bone->get_skeleton_rest().affine_inverse(); //bind pose
- bones.write[i].bone->skeleton_index = i;
+ for (uint32_t i = 0; i < bones.size(); i++) {
+ bones[i].rest_inverse = bones[i].bone->get_skeleton_rest().affine_inverse(); //bind pose
+ bones[i].bone->skeleton_index = i;
Bone2D *parent_bone = Object::cast_to<Bone2D>(bones[i].bone->get_parent());
if (parent_bone) {
- bones.write[i].parent_index = parent_bone->skeleton_index;
+ bones[i].parent_index = parent_bone->skeleton_index;
} else {
- bones.write[i].parent_index = -1;
+ bones[i].parent_index = -1;
}
- bones.write[i].local_pose_override = bones[i].bone->get_skeleton_rest();
+ bones[i].local_pose_override = bones[i].bone->get_skeleton_rest();
}
transform_dirty = true;
@@ -594,16 +594,16 @@ void Skeleton2D::_update_transform() {
transform_dirty = false;
- for (int i = 0; i < bones.size(); i++) {
- ERR_CONTINUE(bones[i].parent_index >= i);
+ for (uint32_t i = 0; i < bones.size(); i++) {
+ ERR_CONTINUE(bones[i].parent_index >= (int)i);
if (bones[i].parent_index >= 0) {
- bones.write[i].accum_transform = bones[bones[i].parent_index].accum_transform * bones[i].bone->get_transform();
+ bones[i].accum_transform = bones[bones[i].parent_index].accum_transform * bones[i].bone->get_transform();
} else {
- bones.write[i].accum_transform = bones[i].bone->get_transform();
+ bones[i].accum_transform = bones[i].bone->get_transform();
}
}
- for (int i = 0; i < bones.size(); i++) {
+ for (uint32_t i = 0; i < bones.size(); i++) {
Transform2D final_xform = bones[i].accum_transform * bones[i].rest_inverse;
RS::get_singleton()->skeleton_bone_set_transform_2d(skeleton, i, final_xform);
}
@@ -621,7 +621,7 @@ int Skeleton2D::get_bone_count() const {
Bone2D *Skeleton2D::get_bone(int p_idx) {
ERR_FAIL_COND_V(!is_inside_tree(), nullptr);
- ERR_FAIL_INDEX_V(p_idx, bones.size(), nullptr);
+ ERR_FAIL_INDEX_V(p_idx, (int)bones.size(), nullptr);
return bones[p_idx].bone;
}
@@ -733,14 +733,14 @@ RID Skeleton2D::get_skeleton() const {
}
void Skeleton2D::set_bone_local_pose_override(int p_bone_idx, Transform2D p_override, real_t p_amount, bool p_persistent) {
- ERR_FAIL_INDEX_MSG(p_bone_idx, bones.size(), "Bone index is out of range!");
- bones.write[p_bone_idx].local_pose_override = p_override;
- bones.write[p_bone_idx].local_pose_override_amount = p_amount;
- bones.write[p_bone_idx].local_pose_override_persistent = p_persistent;
+ ERR_FAIL_INDEX_MSG(p_bone_idx, (int)bones.size(), "Bone index is out of range!");
+ bones[p_bone_idx].local_pose_override = p_override;
+ bones[p_bone_idx].local_pose_override_amount = p_amount;
+ bones[p_bone_idx].local_pose_override_persistent = p_persistent;
}
Transform2D Skeleton2D::get_bone_local_pose_override(int p_bone_idx) {
- ERR_FAIL_INDEX_V_MSG(p_bone_idx, bones.size(), Transform2D(), "Bone index is out of range!");
+ ERR_FAIL_INDEX_V_MSG(p_bone_idx, (int)bones.size(), Transform2D(), "Bone index is out of range!");
return bones[p_bone_idx].local_pose_override;
}
@@ -771,7 +771,7 @@ void Skeleton2D::execute_modifications(real_t p_delta, int p_execution_mode) {
}
// Do not cache the transform changes caused by the modifications!
- for (int i = 0; i < bones.size(); i++) {
+ for (uint32_t i = 0; i < bones.size(); i++) {
bones[i].bone->copy_transform_to_cache = false;
}
@@ -783,7 +783,7 @@ void Skeleton2D::execute_modifications(real_t p_delta, int p_execution_mode) {
// Only apply the local pose override on _process. Otherwise, just calculate the local_pose_override and reset the transform.
if (p_execution_mode == SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_process) {
- for (int i = 0; i < bones.size(); i++) {
+ for (uint32_t i = 0; i < bones.size(); i++) {
if (bones[i].local_pose_override_amount > 0) {
bones[i].bone->set_meta("_local_pose_override_enabled_", true);
@@ -793,7 +793,7 @@ void Skeleton2D::execute_modifications(real_t p_delta, int p_execution_mode) {
bones[i].bone->propagate_call("force_update_transform");
if (bones[i].local_pose_override_persistent) {
- bones.write[i].local_pose_override_amount = 0.0;
+ bones[i].local_pose_override_amount = 0.0;
}
} else {
// TODO: see if there is a way to undo the override without having to resort to setting every bone's transform.
@@ -804,7 +804,7 @@ void Skeleton2D::execute_modifications(real_t p_delta, int p_execution_mode) {
}
// Cache any future transform changes
- for (int i = 0; i < bones.size(); i++) {
+ for (uint32_t i = 0; i < bones.size(); i++) {
bones[i].bone->copy_transform_to_cache = true;
}
diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h
index 033bdff41d..16bd6fd34b 100644
--- a/scene/2d/skeleton_2d.h
+++ b/scene/2d/skeleton_2d.h
@@ -123,7 +123,7 @@ class Skeleton2D : public Node2D {
bool local_pose_override_persistent = false;
};
- Vector<Bone> bones;
+ LocalVector<Bone> bones;
bool bone_setup_dirty = true;
void _make_bone_setup_dirty();
diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp
index 86ce8a881a..85de85a9a6 100644
--- a/scene/3d/node_3d.cpp
+++ b/scene/3d/node_3d.cpp
@@ -1181,15 +1181,16 @@ void Node3D::_validate_property(PropertyInfo &p_property) const {
}
bool Node3D::_property_can_revert(const StringName &p_name) const {
- if (p_name == "basis") {
+ const String sname = p_name;
+ if (sname == "basis") {
return true;
- } else if (p_name == "scale") {
+ } else if (sname == "scale") {
return true;
- } else if (p_name == "quaternion") {
+ } else if (sname == "quaternion") {
return true;
- } else if (p_name == "rotation") {
+ } else if (sname == "rotation") {
return true;
- } else if (p_name == "position") {
+ } else if (sname == "position") {
return true;
}
return false;
@@ -1198,35 +1199,36 @@ bool Node3D::_property_can_revert(const StringName &p_name) const {
bool Node3D::_property_get_revert(const StringName &p_name, Variant &r_property) const {
bool valid = false;
- if (p_name == "basis") {
+ const String sname = p_name;
+ if (sname == "basis") {
Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid);
if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) {
r_property = Transform3D(variant).get_basis();
} else {
r_property = Basis();
}
- } else if (p_name == "scale") {
+ } else if (sname == "scale") {
Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid);
if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) {
r_property = Transform3D(variant).get_basis().get_scale();
} else {
r_property = Vector3(1.0, 1.0, 1.0);
}
- } else if (p_name == "quaternion") {
+ } else if (sname == "quaternion") {
Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid);
if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) {
r_property = Quaternion(Transform3D(variant).get_basis().get_rotation_quaternion());
} else {
r_property = Quaternion();
}
- } else if (p_name == "rotation") {
+ } else if (sname == "rotation") {
Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid);
if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) {
r_property = Transform3D(variant).get_basis().get_euler_normalized(data.euler_rotation_order);
} else {
r_property = Vector3();
}
- } else if (p_name == "position") {
+ } else if (sname == "position") {
Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid);
if (valid) {
r_property = Transform3D(variant).get_origin();
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index db9c4db30d..9e4c9b1832 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -81,7 +81,7 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) {
return false;
}
- int which = path.get_slicec('/', 1).to_int();
+ uint32_t which = path.get_slicec('/', 1).to_int();
String what = path.get_slicec('/', 2);
if (which == bones.size() && what == "name") {
@@ -89,7 +89,7 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) {
return true;
}
- ERR_FAIL_INDEX_V(which, bones.size(), false);
+ ERR_FAIL_UNSIGNED_INDEX_V(which, bones.size(), false);
if (what == "parent") {
set_bone_parent(which, p_value);
@@ -153,10 +153,10 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const {
return false;
}
- int which = path.get_slicec('/', 1).to_int();
+ uint32_t which = path.get_slicec('/', 1).to_int();
String what = path.get_slicec('/', 2);
- ERR_FAIL_INDEX_V(which, bones.size(), false);
+ ERR_FAIL_UNSIGNED_INDEX_V(which, bones.size(), false);
if (what == "name") {
r_ret = get_bone_name(which);
@@ -182,7 +182,7 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const {
}
void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const {
- for (int i = 0; i < bones.size(); i++) {
+ for (uint32_t i = 0; i < bones.size(); i++) {
const String prep = vformat("%s/%d/", PNAME("bones"), i);
p_list->push_back(PropertyInfo(Variant::STRING, prep + PNAME("name"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
p_list->push_back(PropertyInfo(Variant::INT, prep + PNAME("parent"), PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NO_EDITOR));
@@ -241,7 +241,7 @@ void Skeleton3D::_update_process_order() {
return;
}
- Bone *bonesptr = bones.ptrw();
+ Bone *bonesptr = bones.ptr();
int len = bones.size();
parentless_bones.clear();
@@ -276,6 +276,8 @@ void Skeleton3D::_update_process_order() {
concatenated_bone_names = StringName();
+ _update_bones_nested_set();
+
process_order_dirty = false;
emit_signal("bone_list_changed");
@@ -283,7 +285,7 @@ void Skeleton3D::_update_process_order() {
void Skeleton3D::_update_bone_names() const {
String names;
- for (int i = 0; i < bones.size(); i++) {
+ for (uint32_t i = 0; i < bones.size(); i++) {
if (i > 0) {
names += ",";
}
@@ -331,16 +333,21 @@ void Skeleton3D::_notification(int p_what) {
updating = true;
- Bone *bonesptr = bones.ptrw();
+ Bone *bonesptr = bones.ptr();
int len = bones.size();
+ thread_local LocalVector<bool> bone_global_pose_dirty_backup;
+
// Process modifiers.
_find_modifiers();
if (!modifiers.is_empty()) {
// Store unmodified bone poses.
- for (int i = 0; i < bones.size(); i++) {
+ for (uint32_t i = 0; i < bones.size(); i++) {
bones_backup[i].save(bones[i]);
}
+ // Store dirty flags for global bone poses.
+ bone_global_pose_dirty_backup = bone_global_pose_dirty;
+
_process_modifiers();
}
@@ -412,9 +419,11 @@ void Skeleton3D::_notification(int p_what) {
if (!modifiers.is_empty()) {
// Restore unmodified bone poses.
- for (int i = 0; i < bones.size(); i++) {
- bones_backup[i].restore(bones.write[i]);
+ for (uint32_t i = 0; i < bones.size(); i++) {
+ bones_backup[i].restore(bones[i]);
}
+ // Restore dirty flags for global bone poses.
+ bone_global_pose_dirty = bone_global_pose_dirty_backup;
}
updating = false;
@@ -457,10 +466,111 @@ void Skeleton3D::_make_modifiers_dirty() {
_update_deferred(UPDATE_FLAG_MODIFIER);
}
+void Skeleton3D::_update_bones_nested_set() {
+ nested_set_offset_to_bone_index.resize(bones.size());
+ bone_global_pose_dirty.resize(bones.size());
+ _make_bone_global_poses_dirty();
+
+ int offset = 0;
+ for (int bone : parentless_bones) {
+ offset += _update_bone_nested_set(bone, offset);
+ }
+}
+
+int Skeleton3D::_update_bone_nested_set(int p_bone, int p_offset) {
+ Bone &bone = bones[p_bone];
+ int offset = p_offset + 1;
+ int span = 1;
+
+ for (int child_bone : bone.child_bones) {
+ int subspan = _update_bone_nested_set(child_bone, offset);
+ offset += subspan;
+ span += subspan;
+ }
+
+ nested_set_offset_to_bone_index[p_offset] = p_bone;
+ bone.nested_set_offset = p_offset;
+ bone.nested_set_span = span;
+
+ return span;
+}
+
+void Skeleton3D::_make_bone_global_poses_dirty() {
+ for (uint32_t i = 0; i < bone_global_pose_dirty.size(); i++) {
+ bone_global_pose_dirty[i] = true;
+ }
+}
+
+void Skeleton3D::_make_bone_global_pose_subtree_dirty(int p_bone) {
+ if (process_order_dirty) {
+ return;
+ }
+
+ const Bone &bone = bones[p_bone];
+ int span_offset = bone.nested_set_offset;
+ // No need to make subtree dirty when bone is already dirty.
+ if (bone_global_pose_dirty[span_offset]) {
+ return;
+ }
+
+ // Make global poses of subtree dirty.
+ int span_end = span_offset + bone.nested_set_span;
+ for (int i = span_offset; i < span_end; i++) {
+ bone_global_pose_dirty[i] = true;
+ }
+}
+
+void Skeleton3D::_update_bone_global_pose(int p_bone) {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX(p_bone, bone_size);
+
+ _update_process_order();
+
+ // Global pose is already calculated.
+ int nested_set_offset = bones[p_bone].nested_set_offset;
+ if (!bone_global_pose_dirty[nested_set_offset]) {
+ return;
+ }
+
+ thread_local LocalVector<int> bone_list;
+ bone_list.clear();
+ Transform3D global_pose;
+
+ // Create list of parent bones for which the global pose needs to be recalculated.
+ for (int bone = p_bone; bone >= 0; bone = bones[bone].parent) {
+ int offset = bones[bone].nested_set_offset;
+ // Stop searching when global pose is not dirty.
+ if (!bone_global_pose_dirty[offset]) {
+ global_pose = bones[bone].global_pose;
+ break;
+ }
+
+ bone_list.push_back(bone);
+ }
+
+ // Calculate global poses for all parent bones and the current bone.
+ for (int i = bone_list.size() - 1; i >= 0; i--) {
+ int bone_idx = bone_list[i];
+ Bone &bone = bones[bone_idx];
+ bool bone_enabled = bone.enabled && !show_rest_only;
+ Transform3D bone_pose = bone_enabled ? get_bone_pose(bone_idx) : get_bone_rest(bone_idx);
+
+ global_pose *= bone_pose;
+#ifndef DISABLE_DEPRECATED
+ if (bone.global_pose_override_amount >= CMP_EPSILON) {
+ global_pose = global_pose.interpolate_with(bone.global_pose_override, bone.global_pose_override_amount);
+ }
+#endif // _DISABLE_DEPRECATED
+
+ bone.global_pose = global_pose;
+ bone_global_pose_dirty[bone.nested_set_offset] = false;
+ }
+}
+
Transform3D Skeleton3D::get_bone_global_pose(int p_bone) const {
const int bone_size = bones.size();
ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
- const_cast<Skeleton3D *>(this)->force_update_all_dirty_bones();
+ const_cast<Skeleton3D *>(this)->_update_bone_global_pose(p_bone);
return bones[p_bone].global_pose;
}
@@ -534,7 +644,7 @@ void Skeleton3D::set_bone_name(int p_bone, const String &p_name) {
}
name_to_bone_index.erase(bones[p_bone].name);
- bones.write[p_bone].name = p_name;
+ bones[p_bone].name = p_name;
name_to_bone_index.insert(p_name, p_bone);
version++;
@@ -582,13 +692,13 @@ void Skeleton3D::set_bone_meta(int p_bone, const StringName &p_key, const Varian
ERR_FAIL_INDEX(p_bone, bone_size);
if (p_value.get_type() == Variant::NIL) {
- if (bones.write[p_bone].metadata.has(p_key)) {
- bones.write[p_bone].metadata.erase(p_key);
+ if (bones[p_bone].metadata.has(p_key)) {
+ bones[p_bone].metadata.erase(p_key);
}
return;
}
- bones.write[p_bone].metadata.insert(p_key, p_value, false);
+ bones[p_bone].metadata.insert(p_key, p_value, false);
}
bool Skeleton3D::is_bone_parent_of(int p_bone, int p_parent_bone_id) const {
@@ -615,7 +725,7 @@ void Skeleton3D::set_bone_parent(int p_bone, int p_parent) {
ERR_FAIL_COND(p_parent != -1 && (p_parent < 0));
ERR_FAIL_COND(p_bone == p_parent);
- bones.write[p_bone].parent = p_parent;
+ bones[p_bone].parent = p_parent;
process_order_dirty = true;
rest_dirty = true;
_make_dirty();
@@ -629,11 +739,11 @@ void Skeleton3D::unparent_bone_and_rest(int p_bone) {
int parent = bones[p_bone].parent;
while (parent >= 0) {
- bones.write[p_bone].rest = bones[parent].rest * bones[p_bone].rest;
+ bones[p_bone].rest = bones[parent].rest * bones[p_bone].rest;
parent = bones[parent].parent;
}
- bones.write[p_bone].parent = -1;
+ bones[p_bone].parent = -1;
process_order_dirty = true;
rest_dirty = true;
@@ -669,9 +779,10 @@ void Skeleton3D::set_bone_rest(int p_bone, const Transform3D &p_rest) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].rest = p_rest;
+ bones[p_bone].rest = p_rest;
rest_dirty = true;
_make_dirty();
+ _make_bone_global_pose_subtree_dirty(p_bone);
}
Transform3D Skeleton3D::get_bone_rest(int p_bone) const {
const int bone_size = bones.size();
@@ -692,9 +803,10 @@ void Skeleton3D::set_bone_enabled(int p_bone, bool p_enabled) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].enabled = p_enabled;
+ bones[p_bone].enabled = p_enabled;
emit_signal(SceneStringName(bone_enabled_changed), p_bone);
_make_dirty();
+ _make_bone_global_pose_subtree_dirty(p_bone);
}
bool Skeleton3D::is_bone_enabled(int p_bone) const {
@@ -707,6 +819,7 @@ void Skeleton3D::set_show_rest_only(bool p_enabled) {
show_rest_only = p_enabled;
emit_signal(SceneStringName(show_rest_only_changed));
_make_dirty();
+ _make_bone_global_poses_dirty();
}
bool Skeleton3D::is_show_rest_only() const {
@@ -727,12 +840,13 @@ void Skeleton3D::set_bone_pose(int p_bone, const Transform3D &p_pose) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].pose_position = p_pose.origin;
- bones.write[p_bone].pose_rotation = p_pose.basis.get_rotation_quaternion();
- bones.write[p_bone].pose_scale = p_pose.basis.get_scale();
- bones.write[p_bone].pose_cache_dirty = true;
+ bones[p_bone].pose_position = p_pose.origin;
+ bones[p_bone].pose_rotation = p_pose.basis.get_rotation_quaternion();
+ bones[p_bone].pose_scale = p_pose.basis.get_scale();
+ bones[p_bone].pose_cache_dirty = true;
if (is_inside_tree()) {
_make_dirty();
+ _make_bone_global_pose_subtree_dirty(p_bone);
}
}
@@ -740,30 +854,33 @@ void Skeleton3D::set_bone_pose_position(int p_bone, const Vector3 &p_position) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].pose_position = p_position;
- bones.write[p_bone].pose_cache_dirty = true;
+ bones[p_bone].pose_position = p_position;
+ bones[p_bone].pose_cache_dirty = true;
if (is_inside_tree()) {
_make_dirty();
+ _make_bone_global_pose_subtree_dirty(p_bone);
}
}
void Skeleton3D::set_bone_pose_rotation(int p_bone, const Quaternion &p_rotation) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].pose_rotation = p_rotation;
- bones.write[p_bone].pose_cache_dirty = true;
+ bones[p_bone].pose_rotation = p_rotation;
+ bones[p_bone].pose_cache_dirty = true;
if (is_inside_tree()) {
_make_dirty();
+ _make_bone_global_pose_subtree_dirty(p_bone);
}
}
void Skeleton3D::set_bone_pose_scale(int p_bone, const Vector3 &p_scale) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].pose_scale = p_scale;
- bones.write[p_bone].pose_cache_dirty = true;
+ bones[p_bone].pose_scale = p_scale;
+ bones[p_bone].pose_cache_dirty = true;
if (is_inside_tree()) {
_make_dirty();
+ _make_bone_global_pose_subtree_dirty(p_bone);
}
}
@@ -794,7 +911,7 @@ void Skeleton3D::reset_bone_pose(int p_bone) {
}
void Skeleton3D::reset_bone_poses() {
- for (int i = 0; i < bones.size(); i++) {
+ for (uint32_t i = 0; i < bones.size(); i++) {
reset_bone_pose(i);
}
}
@@ -802,7 +919,7 @@ void Skeleton3D::reset_bone_poses() {
Transform3D Skeleton3D::get_bone_pose(int p_bone) const {
const int bone_size = bones.size();
ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
- const_cast<Skeleton3D *>(this)->bones.write[p_bone].update_pose_cache();
+ const_cast<Skeleton3D *>(this)->bones[p_bone].update_pose_cache();
return bones[p_bone].pose_cache;
}
@@ -853,7 +970,7 @@ Ref<Skin> Skeleton3D::create_skin_from_rest_transforms() {
// Pose changed, rebuild cache of inverses.
const Bone *bonesptr = bones.ptr();
- int len = bones.size();
+ uint32_t len = bones.size();
// Calculate global rests and invert them.
LocalVector<int> bones_to_process;
@@ -877,7 +994,7 @@ Ref<Skin> Skeleton3D::create_skin_from_rest_transforms() {
}
}
- for (int i = 0; i < len; i++) {
+ for (uint32_t i = 0; i < len; i++) {
// The inverse is what is actually required.
skin->set_bind_bone(i, i);
skin->set_bind_pose(i, skin->get_bind_pose(i).affine_inverse());
@@ -937,15 +1054,15 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone_idx, bone_size);
- Bone *bonesptr = bones.ptrw();
- thread_local LocalVector<int> bones_to_process;
- bones_to_process.clear();
- bones_to_process.push_back(p_bone_idx);
+ Bone *bonesptr = bones.ptr();
- uint32_t index = 0;
- while (index < bones_to_process.size()) {
- int current_bone_idx = bones_to_process[index];
+ // Loop through nested set.
+ for (int offset = 0; offset < bone_size; offset++) {
+ if (!bone_global_pose_dirty[offset]) {
+ continue;
+ }
+ int current_bone_idx = nested_set_offset_to_bone_index[offset];
Bone &b = bonesptr[current_bone_idx];
bool bone_enabled = b.enabled && !show_rest_only;
@@ -992,13 +1109,7 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
}
#endif // _DISABLE_DEPRECATED
- // Add the bone's children to the list of bones to be processed.
- int child_bone_size = b.child_bones.size();
- for (int i = 0; i < child_bone_size; i++) {
- bones_to_process.push_back(b.child_bones[i]);
- }
-
- index++;
+ bone_global_pose_dirty[offset] = false;
}
}
@@ -1171,20 +1282,22 @@ void Skeleton3D::_bind_methods() {
#ifndef DISABLE_DEPRECATED
void Skeleton3D::clear_bones_global_pose_override() {
- for (int i = 0; i < bones.size(); i += 1) {
- bones.write[i].global_pose_override_amount = 0;
- bones.write[i].global_pose_override_reset = true;
+ for (uint32_t i = 0; i < bones.size(); i += 1) {
+ bones[i].global_pose_override_amount = 0;
+ bones[i].global_pose_override_reset = true;
}
_make_dirty();
+ _make_bone_global_poses_dirty();
}
void Skeleton3D::set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].global_pose_override_amount = p_amount;
- bones.write[p_bone].global_pose_override = p_pose;
- bones.write[p_bone].global_pose_override_reset = !p_persistent;
+ bones[p_bone].global_pose_override_amount = p_amount;
+ bones[p_bone].global_pose_override = p_pose;
+ bones[p_bone].global_pose_override_reset = !p_persistent;
_make_dirty();
+ _make_bone_global_pose_subtree_dirty(p_bone);
}
Transform3D Skeleton3D::get_bone_global_pose_override(int p_bone) const {
diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h
index 07bdeccf2f..0db12600c3 100644
--- a/scene/3d/skeleton_3d.h
+++ b/scene/3d/skeleton_3d.h
@@ -107,6 +107,8 @@ private:
Quaternion pose_rotation;
Vector3 pose_scale = Vector3(1, 1, 1);
Transform3D global_pose;
+ int nested_set_offset = 0; // Offset in nested set of bone hierarchy.
+ int nested_set_span = 0; // Subtree span in nested set of bone hierarchy.
void update_pose_cache() {
if (pose_cache_dirty) {
@@ -153,7 +155,7 @@ private:
HashSet<SkinReference *> skin_bindings;
void _skin_changed();
- Vector<Bone> bones;
+ LocalVector<Bone> bones;
bool process_order_dirty = false;
Vector<int> parentless_bones;
@@ -183,6 +185,15 @@ private:
void _make_modifiers_dirty();
LocalVector<BonePoseBackup> bones_backup;
+ // Global bone pose calculation.
+ LocalVector<int> nested_set_offset_to_bone_index; // Map from Bone::nested_set_offset to bone index.
+ LocalVector<bool> bone_global_pose_dirty; // Indexable with Bone::nested_set_offset.
+ void _update_bones_nested_set();
+ int _update_bone_nested_set(int p_bone, int p_offset);
+ void _make_bone_global_poses_dirty();
+ void _make_bone_global_pose_subtree_dirty(int p_bone);
+ void _update_bone_global_pose(int p_bone);
+
#ifndef DISABLE_DEPRECATED
void _add_bone_bind_compat_88791(const String &p_name);
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index 9040693a6d..646757008a 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -653,7 +653,9 @@ void GraphEdit::remove_child_notify(Node *p_child) {
minimap = nullptr;
} else if (p_child == connections_layer) {
connections_layer = nullptr;
- WARN_PRINT("GraphEdit's connection_layer removed. This should not be done. If you like to remove all GraphElements from a GraphEdit node, do not simply remove all non-internal children but check their type since the connection layer has to be kept non-internal due to technical reasons.");
+ if (is_inside_tree()) {
+ WARN_PRINT("GraphEdit's connection_layer removed. This should not be done. If you like to remove all GraphElements from a GraphEdit node, do not simply remove all non-internal children but check their type since the connection layer has to be kept non-internal due to technical reasons.");
+ }
}
if (top_layer != nullptr && is_inside_tree()) {
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index 2211bd76fc..1ac0e8b59f 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -547,7 +547,7 @@ PackedStringArray ScrollContainer::get_configuration_warnings() const {
int found = 0;
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/tree.cpp b/scene/gui/tree.cpp
index 8238d54381..646cd9c70e 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -4034,25 +4034,25 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
} break;
case MouseButton::WHEEL_UP: {
- if (_scroll(false, -mb->get_factor() / 8)) {
+ if (_scroll(mb->is_shift_pressed(), -mb->get_factor() / 8)) {
accept_event();
}
} break;
case MouseButton::WHEEL_DOWN: {
- if (_scroll(false, mb->get_factor() / 8)) {
+ if (_scroll(mb->is_shift_pressed(), mb->get_factor() / 8)) {
accept_event();
}
} break;
case MouseButton::WHEEL_LEFT: {
- if (_scroll(true, -mb->get_factor() / 8)) {
+ if (_scroll(!mb->is_shift_pressed(), -mb->get_factor() / 8)) {
accept_event();
}
} break;
case MouseButton::WHEEL_RIGHT: {
- if (_scroll(true, mb->get_factor() / 8)) {
+ if (_scroll(!mb->is_shift_pressed(), mb->get_factor() / 8)) {
accept_event();
}
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 106130872d..71d91b970e 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -302,11 +302,15 @@ void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_gro
continue;
}
+ Node *node = gr_nodes[i];
if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
Callable::CallError ce;
- gr_nodes[i]->callp(p_function, p_args, p_argcount, ce);
+ node->callp(p_function, p_args, p_argcount, ce);
+ if (unlikely(ce.error != Callable::CallError::CALL_OK && ce.error != Callable::CallError::CALL_ERROR_INVALID_METHOD)) {
+ ERR_PRINT(vformat("Error calling group method on node \"%s\": %s.", node->get_name(), Variant::get_callable_error_text(Callable(node, p_function), p_args, p_argcount, ce)));
+ }
} else {
- MessageQueue::get_singleton()->push_callp(gr_nodes[i], p_function, p_args, p_argcount);
+ MessageQueue::get_singleton()->push_callp(node, p_function, p_args, p_argcount);
}
}
@@ -316,11 +320,15 @@ void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_gro
continue;
}
+ Node *node = gr_nodes[i];
if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
Callable::CallError ce;
- gr_nodes[i]->callp(p_function, p_args, p_argcount, ce);
+ node->callp(p_function, p_args, p_argcount, ce);
+ if (unlikely(ce.error != Callable::CallError::CALL_OK && ce.error != Callable::CallError::CALL_ERROR_INVALID_METHOD)) {
+ ERR_PRINT(vformat("Error calling group method on node \"%s\": %s.", node->get_name(), Variant::get_callable_error_text(Callable(node, p_function), p_args, p_argcount, ce)));
+ }
} else {
- MessageQueue::get_singleton()->push_callp(gr_nodes[i], p_function, p_args, p_argcount);
+ MessageQueue::get_singleton()->push_callp(node, p_function, p_args, p_argcount);
}
}
}
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 9df009ec28..ecc1982aa5 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -382,14 +382,11 @@ void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const {
bool ShaderMaterial::_property_can_revert(const StringName &p_name) const {
if (shader.is_valid()) {
- const StringName *pr = remap_cache.getptr(p_name);
- if (pr) {
- Variant default_value = RenderingServer::get_singleton()->shader_get_parameter_default(shader->get_rid(), *pr);
- Variant current_value = get_shader_parameter(*pr);
- return default_value.get_type() != Variant::NIL && default_value != current_value;
- } else if (p_name == "render_priority" || p_name == "next_pass") {
+ if (remap_cache.has(p_name)) {
return true;
}
+ const String sname = p_name;
+ return sname == "render_priority" || sname == "next_pass";
}
return false;
}
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index 5aa6bf718c..e234a81c88 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -612,29 +612,27 @@ Error ResourceLoaderText::load() {
}
}
- if (ClassDB::has_property(res->get_class_name(), assign)) {
- if (value.get_type() == Variant::ARRAY) {
- Array set_array = value;
- bool is_get_valid = false;
- Variant get_value = res->get(assign, &is_get_valid);
- if (is_get_valid && get_value.get_type() == Variant::ARRAY) {
- Array get_array = get_value;
- if (!set_array.is_same_typed(get_array)) {
- value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script());
- }
+ if (value.get_type() == Variant::ARRAY) {
+ Array set_array = value;
+ bool is_get_valid = false;
+ Variant get_value = res->get(assign, &is_get_valid);
+ if (is_get_valid && get_value.get_type() == Variant::ARRAY) {
+ Array get_array = get_value;
+ if (!set_array.is_same_typed(get_array)) {
+ value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script());
}
}
+ }
- if (value.get_type() == Variant::DICTIONARY) {
- Dictionary set_dict = value;
- bool is_get_valid = false;
- Variant get_value = res->get(assign, &is_get_valid);
- if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) {
- Dictionary get_dict = get_value;
- if (!set_dict.is_same_typed(get_dict)) {
- value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(),
- get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script());
- }
+ if (value.get_type() == Variant::DICTIONARY) {
+ Dictionary set_dict = value;
+ bool is_get_valid = false;
+ Variant get_value = res->get(assign, &is_get_valid);
+ if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) {
+ Dictionary get_dict = get_value;
+ if (!set_dict.is_same_typed(get_dict)) {
+ value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(),
+ get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script());
}
}
}
@@ -754,29 +752,27 @@ Error ResourceLoaderText::load() {
}
}
- if (ClassDB::has_property(resource->get_class_name(), assign)) {
- if (value.get_type() == Variant::ARRAY) {
- Array set_array = value;
- bool is_get_valid = false;
- Variant get_value = resource->get(assign, &is_get_valid);
- if (is_get_valid && get_value.get_type() == Variant::ARRAY) {
- Array get_array = get_value;
- if (!set_array.is_same_typed(get_array)) {
- value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script());
- }
+ if (value.get_type() == Variant::ARRAY) {
+ Array set_array = value;
+ bool is_get_valid = false;
+ Variant get_value = resource->get(assign, &is_get_valid);
+ if (is_get_valid && get_value.get_type() == Variant::ARRAY) {
+ Array get_array = get_value;
+ if (!set_array.is_same_typed(get_array)) {
+ value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script());
}
}
+ }
- if (value.get_type() == Variant::DICTIONARY) {
- Dictionary set_dict = value;
- bool is_get_valid = false;
- Variant get_value = resource->get(assign, &is_get_valid);
- if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) {
- Dictionary get_dict = get_value;
- if (!set_dict.is_same_typed(get_dict)) {
- value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(),
- get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script());
- }
+ if (value.get_type() == Variant::DICTIONARY) {
+ Dictionary set_dict = value;
+ bool is_get_valid = false;
+ Variant get_value = resource->get(assign, &is_get_valid);
+ if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) {
+ Dictionary get_dict = get_value;
+ if (!set_dict.is_same_typed(get_dict)) {
+ value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(),
+ get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script());
}
}
}
diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp
index 0ec161d8cf..b5873528f7 100644
--- a/servers/rendering/renderer_canvas_cull.cpp
+++ b/servers/rendering/renderer_canvas_cull.cpp
@@ -51,7 +51,7 @@ void RendererCanvasCull::_render_canvas_item_tree(RID p_to_render_target, Canvas
memset(z_last_list, 0, z_range * sizeof(RendererCanvasRender::Item *));
for (int i = 0; i < p_child_item_count; i++) {
- _cull_canvas_item(p_child_items[i].item, p_transform, p_clip_rect, Color(1, 1, 1, 1), 0, z_list, z_last_list, nullptr, nullptr, true, p_canvas_cull_mask, Point2(), 1, nullptr);
+ _cull_canvas_item(p_child_items[i].item, p_transform, p_clip_rect, Color(1, 1, 1, 1), 0, z_list, z_last_list, nullptr, nullptr, false, p_canvas_cull_mask, Point2(), 1, nullptr);
}
RendererCanvasRender::Item *list = nullptr;
@@ -79,45 +79,71 @@ void RendererCanvasCull::_render_canvas_item_tree(RID p_to_render_target, Canvas
}
}
-void _collect_ysort_children(RendererCanvasCull::Item *p_canvas_item, const Transform2D &p_transform, RendererCanvasCull::Item *p_material_owner, const Color &p_modulate, RendererCanvasCull::Item **r_items, int &r_index, int p_z) {
+void RendererCanvasCull::_collect_ysort_children(RendererCanvasCull::Item *p_canvas_item, RendererCanvasCull::Item *p_material_owner, const Color &p_modulate, RendererCanvasCull::Item **r_items, int &r_index, int p_z) {
int child_item_count = p_canvas_item->child_items.size();
RendererCanvasCull::Item **child_items = p_canvas_item->child_items.ptrw();
for (int i = 0; i < child_item_count; i++) {
- int abs_z = 0;
if (child_items[i]->visible) {
- if (r_items) {
- r_items[r_index] = child_items[i];
- child_items[i]->ysort_xform = p_transform;
- child_items[i]->ysort_pos = p_transform.xform(child_items[i]->xform_curr.columns[2]);
- child_items[i]->material_owner = child_items[i]->use_parent_material ? p_material_owner : nullptr;
- child_items[i]->ysort_modulate = p_modulate;
- child_items[i]->ysort_index = r_index;
- child_items[i]->ysort_parent_abs_z_index = p_z;
-
- if (!child_items[i]->repeat_source) {
- child_items[i]->repeat_size = p_canvas_item->repeat_size;
- child_items[i]->repeat_times = p_canvas_item->repeat_times;
- child_items[i]->repeat_source_item = p_canvas_item->repeat_source_item;
- }
+ // To y-sort according to the item's final position, physics interpolation
+ // and transform snapping need to be applied before y-sorting.
+ Transform2D child_xform;
+ if (!_interpolation_data.interpolation_enabled || !child_items[i]->interpolated) {
+ child_xform = child_items[i]->xform_curr;
+ } else {
+ real_t f = Engine::get_singleton()->get_physics_interpolation_fraction();
+ TransformInterpolator::interpolate_transform_2d(child_items[i]->xform_prev, child_items[i]->xform_curr, child_xform, f);
+ }
- // Y sorted canvas items are flattened into r_items. Calculate their absolute z index to use when rendering r_items.
- if (child_items[i]->z_relative) {
- abs_z = CLAMP(p_z + child_items[i]->z_index, RS::CANVAS_ITEM_Z_MIN, RS::CANVAS_ITEM_Z_MAX);
- } else {
- abs_z = child_items[i]->z_index;
- }
+ if (snapping_2d_transforms_to_pixel) {
+ child_xform.columns[2] = child_xform.columns[2].round();
+ }
+
+ r_items[r_index] = child_items[i];
+ child_items[i]->ysort_xform = p_canvas_item->ysort_xform * child_xform;
+ child_items[i]->material_owner = child_items[i]->use_parent_material ? p_material_owner : nullptr;
+ child_items[i]->ysort_modulate = p_modulate;
+ child_items[i]->ysort_index = r_index;
+ child_items[i]->ysort_parent_abs_z_index = p_z;
+
+ if (!child_items[i]->repeat_source) {
+ child_items[i]->repeat_size = p_canvas_item->repeat_size;
+ child_items[i]->repeat_times = p_canvas_item->repeat_times;
+ child_items[i]->repeat_source_item = p_canvas_item->repeat_source_item;
+ }
+
+ // Y sorted canvas items are flattened into r_items. Calculate their absolute z index to use when rendering r_items.
+ int abs_z = 0;
+ if (child_items[i]->z_relative) {
+ abs_z = CLAMP(p_z + child_items[i]->z_index, RS::CANVAS_ITEM_Z_MIN, RS::CANVAS_ITEM_Z_MAX);
+ } else {
+ abs_z = child_items[i]->z_index;
}
r_index++;
if (child_items[i]->sort_y) {
- _collect_ysort_children(child_items[i], p_transform * child_items[i]->xform_curr, child_items[i]->use_parent_material ? p_material_owner : child_items[i], p_modulate * child_items[i]->modulate, r_items, r_index, abs_z);
+ _collect_ysort_children(child_items[i], child_items[i]->use_parent_material ? p_material_owner : child_items[i], p_modulate * child_items[i]->modulate, r_items, r_index, abs_z);
}
}
}
}
-void _mark_ysort_dirty(RendererCanvasCull::Item *ysort_owner, RID_Owner<RendererCanvasCull::Item, true> &canvas_item_owner) {
+int RendererCanvasCull::_count_ysort_children(RendererCanvasCull::Item *p_canvas_item) {
+ int ysort_children_count = 0;
+ int child_item_count = p_canvas_item->child_items.size();
+ RendererCanvasCull::Item *const *child_items = p_canvas_item->child_items.ptr();
+ for (int i = 0; i < child_item_count; i++) {
+ if (child_items[i]->visible) {
+ ysort_children_count++;
+ if (child_items[i]->sort_y) {
+ ysort_children_count += _count_ysort_children(child_items[i]);
+ }
+ }
+ }
+ return ysort_children_count;
+}
+
+void RendererCanvasCull::_mark_ysort_dirty(RendererCanvasCull::Item *ysort_owner) {
do {
ysort_owner->ysort_children_count = -1;
ysort_owner = canvas_item_owner.owns(ysort_owner->parent) ? canvas_item_owner.get_or_null(ysort_owner->parent) : nullptr;
@@ -236,7 +262,7 @@ void RendererCanvasCull::_attach_canvas_item_for_draw(RendererCanvasCull::Item *
}
}
-void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2D &p_parent_xform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool p_allow_y_sort, uint32_t p_canvas_cull_mask, const Point2 &p_repeat_size, int p_repeat_times, RendererCanvasRender::Item *p_repeat_source_item) {
+void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2D &p_parent_xform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool p_is_already_y_sorted, uint32_t p_canvas_cull_mask, const Point2 &p_repeat_size, int p_repeat_times, RendererCanvasRender::Item *p_repeat_source_item) {
Item *ci = p_canvas_item;
if (!ci->visible) {
@@ -260,15 +286,29 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
}
}
+ Transform2D self_xform;
Transform2D final_xform;
- if (!_interpolation_data.interpolation_enabled || !ci->interpolated) {
- final_xform = ci->xform_curr;
+ if (p_is_already_y_sorted) {
+ // Y-sorted item's final transform is calculated before y-sorting,
+ // and is passed as `p_parent_xform` afterwards. No need to recalculate.
+ final_xform = p_parent_xform;
} else {
- real_t f = Engine::get_singleton()->get_physics_interpolation_fraction();
- TransformInterpolator::interpolate_transform_2d(ci->xform_prev, ci->xform_curr, final_xform, f);
- }
+ if (!_interpolation_data.interpolation_enabled || !ci->interpolated) {
+ self_xform = ci->xform_curr;
+ } else {
+ real_t f = Engine::get_singleton()->get_physics_interpolation_fraction();
+ TransformInterpolator::interpolate_transform_2d(ci->xform_prev, ci->xform_curr, self_xform, f);
+ }
- Transform2D parent_xform = p_parent_xform;
+ Transform2D parent_xform = p_parent_xform;
+
+ if (snapping_2d_transforms_to_pixel) {
+ self_xform.columns[2] = self_xform.columns[2].round();
+ parent_xform.columns[2] = parent_xform.columns[2].round();
+ }
+
+ final_xform = parent_xform * self_xform;
+ }
Point2 repeat_size = p_repeat_size;
int repeat_times = p_repeat_times;
@@ -284,13 +324,6 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
ci->repeat_source_item = repeat_source_item;
}
- if (snapping_2d_transforms_to_pixel) {
- final_xform.columns[2] = (final_xform.columns[2] + Point2(0.5, 0.5)).floor();
- parent_xform.columns[2] = (parent_xform.columns[2] + Point2(0.5, 0.5)).floor();
- }
-
- final_xform = parent_xform * final_xform;
-
Rect2 global_rect = final_xform.xform(rect);
if (repeat_source_item && (repeat_size.x || repeat_size.y)) {
// Top-left repeated rect.
@@ -355,29 +388,27 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
}
if (ci->sort_y) {
- if (p_allow_y_sort) {
+ if (!p_is_already_y_sorted) {
if (ci->ysort_children_count == -1) {
- ci->ysort_children_count = 0;
- _collect_ysort_children(ci, Transform2D(), p_material_owner, Color(1, 1, 1, 1), nullptr, ci->ysort_children_count, p_z);
+ ci->ysort_children_count = _count_ysort_children(ci);
}
child_item_count = ci->ysort_children_count + 1;
child_items = (Item **)alloca(child_item_count * sizeof(Item *));
- ci->ysort_xform = ci->xform_curr.affine_inverse();
- ci->ysort_pos = Vector2();
+ ci->ysort_xform = Transform2D();
ci->ysort_modulate = Color(1, 1, 1, 1);
ci->ysort_index = 0;
ci->ysort_parent_abs_z_index = parent_z;
child_items[0] = ci;
int i = 1;
- _collect_ysort_children(ci, Transform2D(), p_material_owner, Color(1, 1, 1, 1), child_items, i, p_z);
+ _collect_ysort_children(ci, p_material_owner, Color(1, 1, 1, 1), child_items, i, p_z);
- SortArray<Item *, ItemPtrSort> sorter;
+ SortArray<Item *, ItemYSort> sorter;
sorter.sort(child_items, child_item_count);
for (i = 0; i < child_item_count; i++) {
- _cull_canvas_item(child_items[i], final_xform * child_items[i]->ysort_xform, p_clip_rect, modulate * child_items[i]->ysort_modulate, child_items[i]->ysort_parent_abs_z_index, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, (Item *)child_items[i]->material_owner, false, p_canvas_cull_mask, child_items[i]->repeat_size, child_items[i]->repeat_times, child_items[i]->repeat_source_item);
+ _cull_canvas_item(child_items[i], final_xform * child_items[i]->ysort_xform, p_clip_rect, modulate * child_items[i]->ysort_modulate, child_items[i]->ysort_parent_abs_z_index, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, (Item *)child_items[i]->material_owner, true, p_canvas_cull_mask, child_items[i]->repeat_size, child_items[i]->repeat_times, child_items[i]->repeat_source_item);
}
} else {
RendererCanvasRender::Item *canvas_group_from = nullptr;
@@ -401,14 +432,14 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
if (!child_items[i]->behind && !use_canvas_group) {
continue;
}
- _cull_canvas_item(child_items[i], final_xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, p_canvas_cull_mask, repeat_size, repeat_times, repeat_source_item);
+ _cull_canvas_item(child_items[i], final_xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, false, p_canvas_cull_mask, repeat_size, repeat_times, repeat_source_item);
}
_attach_canvas_item_for_draw(ci, p_canvas_clip, r_z_list, r_z_last_list, final_xform, p_clip_rect, global_rect, modulate, p_z, p_material_owner, use_canvas_group, canvas_group_from);
for (int i = 0; i < child_item_count; i++) {
if (child_items[i]->behind || use_canvas_group) {
continue;
}
- _cull_canvas_item(child_items[i], final_xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, p_canvas_cull_mask, repeat_size, repeat_times, repeat_source_item);
+ _cull_canvas_item(child_items[i], final_xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, false, p_canvas_cull_mask, repeat_size, repeat_times, repeat_source_item);
}
}
}
@@ -509,7 +540,7 @@ void RendererCanvasCull::canvas_item_set_parent(RID p_item, RID p_parent) {
item_owner->child_items.erase(canvas_item);
if (item_owner->sort_y) {
- _mark_ysort_dirty(item_owner, canvas_item_owner);
+ _mark_ysort_dirty(item_owner);
}
}
@@ -529,7 +560,7 @@ void RendererCanvasCull::canvas_item_set_parent(RID p_item, RID p_parent) {
item_owner->children_order_dirty = true;
if (item_owner->sort_y) {
- _mark_ysort_dirty(item_owner, canvas_item_owner);
+ _mark_ysort_dirty(item_owner);
}
} else {
@@ -546,7 +577,7 @@ void RendererCanvasCull::canvas_item_set_visible(RID p_item, bool p_visible) {
canvas_item->visible = p_visible;
- _mark_ysort_dirty(canvas_item, canvas_item_owner);
+ _mark_ysort_dirty(canvas_item);
}
void RendererCanvasCull::canvas_item_set_light_mask(RID p_item, int p_mask) {
@@ -1742,7 +1773,7 @@ void RendererCanvasCull::canvas_item_set_sort_children_by_y(RID p_item, bool p_e
canvas_item->sort_y = p_enable;
- _mark_ysort_dirty(canvas_item, canvas_item_owner);
+ _mark_ysort_dirty(canvas_item);
}
void RendererCanvasCull::canvas_item_set_z_index(RID p_item, int p_z) {
@@ -2423,7 +2454,7 @@ bool RendererCanvasCull::free(RID p_rid) {
item_owner->child_items.erase(canvas_item);
if (item_owner->sort_y) {
- _mark_ysort_dirty(item_owner, canvas_item_owner);
+ _mark_ysort_dirty(item_owner);
}
}
}
diff --git a/servers/rendering/renderer_canvas_cull.h b/servers/rendering/renderer_canvas_cull.h
index 91c03054f7..9a088d94ed 100644
--- a/servers/rendering/renderer_canvas_cull.h
+++ b/servers/rendering/renderer_canvas_cull.h
@@ -50,8 +50,7 @@ public:
bool children_order_dirty;
int ysort_children_count;
Color ysort_modulate;
- Transform2D ysort_xform;
- Vector2 ysort_pos;
+ Transform2D ysort_xform; // Relative to y-sorted subtree's root item (identity for such root). Its `origin.y` is used for sorting.
int ysort_index;
int ysort_parent_abs_z_index; // Absolute Z index of parent. Only populated and used when y-sorting.
uint32_t visibility_layer = 0xffffffff;
@@ -84,7 +83,6 @@ public:
index = 0;
ysort_children_count = -1;
ysort_xform = Transform2D();
- ysort_pos = Vector2();
ysort_index = 0;
ysort_parent_abs_z_index = 0;
}
@@ -96,13 +94,15 @@ public:
}
};
- struct ItemPtrSort {
+ struct ItemYSort {
_FORCE_INLINE_ bool operator()(const Item *p_left, const Item *p_right) const {
- if (Math::is_equal_approx(p_left->ysort_pos.y, p_right->ysort_pos.y)) {
+ const real_t left_y = p_left->ysort_xform.columns[2].y;
+ const real_t right_y = p_right->ysort_xform.columns[2].y;
+ if (Math::is_equal_approx(left_y, right_y)) {
return p_left->ysort_index < p_right->ysort_index;
}
- return p_left->ysort_pos.y < p_right->ysort_pos.y;
+ return left_y < right_y;
}
};
@@ -187,7 +187,11 @@ public:
private:
void _render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RendererCanvasRender::Light *p_lights, RendererCanvasRender::Light *p_directional_lights, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, uint32_t p_canvas_cull_mask, RenderingMethod::RenderInfo *r_render_info = nullptr);
- void _cull_canvas_item(Item *p_canvas_item, const Transform2D &p_parent_xform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool p_allow_y_sort, uint32_t p_canvas_cull_mask, const Point2 &p_repeat_size, int p_repeat_times, RendererCanvasRender::Item *p_repeat_source_item);
+ void _cull_canvas_item(Item *p_canvas_item, const Transform2D &p_parent_xform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool p_is_already_y_sorted, uint32_t p_canvas_cull_mask, const Point2 &p_repeat_size, int p_repeat_times, RendererCanvasRender::Item *p_repeat_source_item);
+
+ void _collect_ysort_children(RendererCanvasCull::Item *p_canvas_item, RendererCanvasCull::Item *p_material_owner, const Color &p_modulate, RendererCanvasCull::Item **r_items, int &r_index, int p_z);
+ int _count_ysort_children(RendererCanvasCull::Item *p_canvas_item);
+ void _mark_ysort_dirty(RendererCanvasCull::Item *ysort_owner);
static constexpr int z_range = RS::CANVAS_ITEM_Z_MAX - RS::CANVAS_ITEM_Z_MIN + 1;
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index 28958f0393..979f590c4c 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -1446,6 +1446,9 @@ void RendererCanvasRenderRD::CanvasShaderData::_create_pipeline(PipelineKey p_pi
blend_state.attachments.push_back(attachment);
+ RD::PipelineMultisampleState multisample_state;
+ multisample_state.sample_count = RD::get_singleton()->framebuffer_format_get_texture_samples(p_pipeline_key.framebuffer_format_id, 0);
+
// Convert the specialization from the key to pipeline specialization constants.
Vector<RD::PipelineSpecializationConstant> specialization_constants;
RD::PipelineSpecializationConstant sc;
@@ -1457,7 +1460,7 @@ void RendererCanvasRenderRD::CanvasShaderData::_create_pipeline(PipelineKey p_pi
RID shader_rid = get_shader(p_pipeline_key.variant, p_pipeline_key.ubershader);
ERR_FAIL_COND(shader_rid.is_null());
- RID pipeline = RD::get_singleton()->render_pipeline_create(shader_rid, p_pipeline_key.framebuffer_format_id, p_pipeline_key.vertex_format_id, p_pipeline_key.render_primitive, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), blend_state, dynamic_state_flags, 0, specialization_constants);
+ RID pipeline = RD::get_singleton()->render_pipeline_create(shader_rid, p_pipeline_key.framebuffer_format_id, p_pipeline_key.vertex_format_id, p_pipeline_key.render_primitive, RD::PipelineRasterizationState(), multisample_state, RD::PipelineDepthStencilState(), blend_state, dynamic_state_flags, 0, specialization_constants);
ERR_FAIL_COND(pipeline.is_null());
pipeline_hash_map.add_compiled_pipeline(p_pipeline_key.hash(), pipeline);
diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
index 18975320f7..0d468ad1e3 100644
--- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
@@ -2185,7 +2185,7 @@ void MeshStorage::skeleton_allocate_data(RID p_skeleton, int p_bones, bool p_2d_
if (skeleton->size) {
skeleton->data.resize(skeleton->size * (skeleton->use_2d ? 8 : 12));
skeleton->buffer = RD::get_singleton()->storage_buffer_create(skeleton->data.size() * sizeof(float));
- memset(skeleton->data.ptrw(), 0, skeleton->data.size() * sizeof(float));
+ memset(skeleton->data.ptr(), 0, skeleton->data.size() * sizeof(float));
_skeleton_make_dirty(skeleton);
@@ -2219,7 +2219,7 @@ void MeshStorage::skeleton_bone_set_transform(RID p_skeleton, int p_bone, const
ERR_FAIL_INDEX(p_bone, skeleton->size);
ERR_FAIL_COND(skeleton->use_2d);
- float *dataptr = skeleton->data.ptrw() + p_bone * 12;
+ float *dataptr = skeleton->data.ptr() + p_bone * 12;
dataptr[0] = p_transform.basis.rows[0][0];
dataptr[1] = p_transform.basis.rows[0][1];
@@ -2270,8 +2270,7 @@ void MeshStorage::skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, con
ERR_FAIL_NULL(skeleton);
ERR_FAIL_INDEX(p_bone, skeleton->size);
ERR_FAIL_COND(!skeleton->use_2d);
-
- float *dataptr = skeleton->data.ptrw() + p_bone * 8;
+ float *dataptr = skeleton->data.ptr() + p_bone * 8;
dataptr[0] = p_transform.columns[0][0];
dataptr[1] = p_transform.columns[1][0];
diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
index 6784520d17..322f3cc6f4 100644
--- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
@@ -312,7 +312,7 @@ private:
struct Skeleton {
bool use_2d = false;
int size = 0;
- Vector<float> data;
+ LocalVector<float> data;
RID buffer;
bool dirty = false;
diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp
index 527a5e5725..e7339b2cdd 100644
--- a/servers/rendering/shader_compiler.cpp
+++ b/servers/rendering/shader_compiler.cpp
@@ -951,7 +951,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
code = _get_global_shader_uniform_from_type_and_index(p_default_actions.global_buffer_array_variable, code, u.type);
} else if (u.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) {
//instance variable, index it as such
- code = "(" + p_default_actions.instance_uniform_index_variable + "+" + itos(u.instance_index) + ")";
+ code = "(" + p_default_actions.instance_uniform_index_variable + "+" + itos(u.instance_index) + "u)";
code = _get_global_shader_uniform_from_type_and_index(p_default_actions.global_buffer_array_variable, code, u.type);
} else {
//regular uniform, index from UBO
@@ -1051,7 +1051,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
code = _get_global_shader_uniform_from_type_and_index(p_default_actions.global_buffer_array_variable, code, u.type);
} else if (u.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) {
//instance variable, index it as such
- code = "(" + p_default_actions.instance_uniform_index_variable + "+" + itos(u.instance_index) + ")";
+ code = "(" + p_default_actions.instance_uniform_index_variable + "+" + itos(u.instance_index) + "u)";
code = _get_global_shader_uniform_from_type_and_index(p_default_actions.global_buffer_array_variable, code, u.type);
} else {
//regular uniform, index from UBO
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 879a83f519..77035a64f7 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -9089,10 +9089,6 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
_set_error(vformat(RTR("Uniform instances are not yet implemented for '%s' shaders."), shader_type_identifier));
return ERR_PARSE_ERROR;
}
- if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
- _set_error(RTR("Uniform instances are not supported in gl_compatibility shaders."));
- return ERR_PARSE_ERROR;
- }
if (uniform_scope == ShaderNode::Uniform::SCOPE_LOCAL) {
tk = _get_token();
if (tk.type != TK_UNIFORM) {