summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/windows_builds.yml13
-rw-r--r--SConstruct2
-rw-r--r--core/io/resource_loader.h4
-rw-r--r--core/object/class_db.cpp6
-rw-r--r--core/object/undo_redo.cpp14
-rw-r--r--doc/classes/AnimationMixer.xml8
-rw-r--r--doc/classes/DisplayServer.xml7
-rw-r--r--doc/classes/EditorInspectorPlugin.xml2
-rw-r--r--doc/classes/Light3D.xml2
-rw-r--r--doc/classes/Mesh.xml2
-rw-r--r--doc/classes/MeshLibrary.xml2
-rw-r--r--doc/classes/NavigationAgent2D.xml2
-rw-r--r--doc/classes/NavigationAgent3D.xml2
-rw-r--r--doc/classes/PhysicsMaterial.xml2
-rw-r--r--doc/classes/ProjectSettings.xml5
-rw-r--r--doc/classes/RenderingServer.xml3
-rwxr-xr-xdoc/tools/make_rst.py68
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp42
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.h2
-rw-r--r--editor/debugger/editor_debugger_node.cpp8
-rw-r--r--editor/debugger/editor_debugger_tree.cpp9
-rw-r--r--editor/debugger/editor_debugger_tree.h1
-rw-r--r--editor/debugger/editor_profiler.cpp14
-rw-r--r--editor/debugger/editor_profiler.h5
-rw-r--r--editor/editor_help.cpp2
-rw-r--r--editor/editor_inspector.compat.inc41
-rw-r--r--editor/editor_inspector.cpp6
-rw-r--r--editor/editor_inspector.h6
-rw-r--r--editor/editor_log.cpp2
-rw-r--r--editor/editor_node.cpp316
-rw-r--r--editor/editor_node.h15
-rw-r--r--editor/editor_properties.cpp2
-rw-r--r--editor/editor_properties_array_dict.cpp1
-rw-r--r--editor/filesystem_dock.cpp2
-rw-r--r--editor/gui/editor_toaster.cpp2
-rw-r--r--editor/import/resource_importer_shader_file.cpp2
-rw-r--r--editor/input_event_configuration_dialog.cpp6
-rw-r--r--editor/input_event_configuration_dialog.h1
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp2
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp81
-rw-r--r--editor/plugins/node_3d_editor_plugin.h4
-rw-r--r--editor/plugins/script_editor_plugin.cpp16
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp8
-rw-r--r--editor/plugins/text_shader_editor.cpp15
-rw-r--r--editor/plugins/text_shader_editor.h2
-rw-r--r--editor/plugins/tiles/tile_atlas_view.cpp38
-rw-r--r--editor/plugins/tiles/tile_atlas_view.h4
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp12
-rw-r--r--editor/scene_tree_dock.cpp4
-rw-r--r--main/main.cpp2
-rw-r--r--methods.py2
-rw-r--r--misc/extension_api_validation/4.2-stable.expected7
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp21
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/lambda_captures.gd26
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/lambda_captures.out5
-rw-r--r--modules/gltf/doc_classes/GLTFAccessor.xml18
-rw-r--r--modules/gltf/gltf_defines.h10
-rw-r--r--modules/gltf/gltf_document.cpp150
-rw-r--r--modules/gltf/gltf_document.h9
-rw-r--r--modules/gltf/structures/gltf_accessor.cpp17
-rw-r--r--modules/gltf/structures/gltf_accessor.h16
-rw-r--r--modules/multiplayer/scene_cache_interface.cpp2
-rw-r--r--modules/navigation/2d/nav_mesh_generator_2d.cpp2
-rw-r--r--platform/ios/display_server_ios.mm7
-rw-r--r--platform/linuxbsd/freedesktop_portal_desktop.cpp7
-rw-r--r--platform/linuxbsd/wayland/display_server_wayland.cpp18
-rw-r--r--platform/linuxbsd/wayland/display_server_wayland.h2
-rw-r--r--platform/linuxbsd/wayland/wayland_thread.h3
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp14
-rw-r--r--platform/macos/display_server_macos.mm12
-rw-r--r--platform/macos/godot_content_view.mm9
-rw-r--r--platform/macos/godot_status_item.mm5
-rw-r--r--platform/web/detect.py2
-rw-r--r--platform/web/display_server_web.cpp76
-rw-r--r--platform/web/display_server_web.h1
-rw-r--r--platform/web/emscripten_helpers.py1
-rw-r--r--platform/web/javascript_bridge_singleton.cpp2
-rw-r--r--platform/web/js/libs/library_godot_input.js17
-rw-r--r--platform/windows/SCsub2
-rw-r--r--platform/windows/display_server_windows.cpp21
-rw-r--r--scene/2d/camera_2d.cpp4
-rw-r--r--scene/2d/navigation_region_2d.cpp178
-rw-r--r--scene/2d/navigation_region_2d.h6
-rw-r--r--scene/animation/animation_mixer.cpp28
-rw-r--r--scene/animation/animation_mixer.h6
-rw-r--r--scene/animation/animation_player.cpp4
-rw-r--r--scene/gui/code_edit.cpp2
-rw-r--r--scene/gui/control.cpp12
-rw-r--r--scene/gui/rich_text_label.cpp28
-rw-r--r--scene/gui/text_edit.cpp23
-rw-r--r--scene/gui/texture_button.cpp31
-rw-r--r--scene/main/instance_placeholder.cpp131
-rw-r--r--scene/main/instance_placeholder.h4
-rw-r--r--scene/resources/2d/tile_set.cpp2
-rw-r--r--scene/resources/packed_scene.cpp10
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp26
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h5
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp26
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h5
-rw-r--r--servers/rendering/shader_compiler.cpp4
-rw-r--r--servers/rendering/shader_language.cpp28
-rw-r--r--servers/rendering/shader_language.h4
-rw-r--r--servers/rendering_server.cpp1
-rw-r--r--tests/scene/test_instance_placeholder.h532
-rw-r--r--tests/test_main.cpp1
106 files changed, 1868 insertions, 536 deletions
diff --git a/.github/workflows/windows_builds.yml b/.github/workflows/windows_builds.yml
index f9513af5e3..7a3347d49c 100644
--- a/.github/workflows/windows_builds.yml
+++ b/.github/workflows/windows_builds.yml
@@ -7,7 +7,7 @@ on:
env:
# Used for the cache key. Add version suffix to force clean build.
GODOT_BASE_BRANCH: master
- SCONSFLAGS: verbose=yes warnings=extra werror=yes module_text_server_fb_enabled=yes d3d12=yes
+ SCONSFLAGS: verbose=yes warnings=extra werror=yes module_text_server_fb_enabled=yes d3d12=yes "angle_libs=${{github.workspace}}/"
SCONS_CACHE_MSVC_CONFIG: true
concurrency:
@@ -54,6 +54,17 @@ jobs:
- name: Download Direct3D 12 SDK components
run: python ./misc/scripts/install_d3d12_sdk_windows.py
+ - name: Download pre-built ANGLE static libraries
+ 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
+ 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
uses: ammaraskar/msvc-problem-matcher@master
diff --git a/SConstruct b/SConstruct
index 6fa3e00325..117528de37 100644
--- a/SConstruct
+++ b/SConstruct
@@ -218,7 +218,7 @@ opts.Add(BoolVariable("brotli", "Enable Brotli for decompresson and WOFF2 fonts
opts.Add(BoolVariable("xaudio2", "Enable the XAudio2 audio driver", False))
opts.Add(BoolVariable("vulkan", "Enable the vulkan rendering driver", True))
opts.Add(BoolVariable("opengl3", "Enable the OpenGL/GLES3 rendering driver", True))
-opts.Add(BoolVariable("d3d12", "Enable the Direct3D 12 rendering driver (Windows only)", False))
+opts.Add(BoolVariable("d3d12", "Enable the Direct3D 12 rendering driver", False))
opts.Add(BoolVariable("openxr", "Enable the OpenXR driver", True))
opts.Add(BoolVariable("use_volk", "Use the volk library to load the Vulkan loader dynamically", True))
opts.Add(BoolVariable("disable_exceptions", "Force disabling exception handling code", True))
diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h
index 5caf699d32..486f7bbf37 100644
--- a/core/io/resource_loader.h
+++ b/core/io/resource_loader.h
@@ -226,7 +226,7 @@ public:
// Loaders can safely use this regardless which thread they are running on.
static void notify_load_error(const String &p_err) {
if (err_notify) {
- callable_mp_static(err_notify).bind(p_err).call_deferred();
+ callable_mp_static(err_notify).call_deferred(p_err);
}
}
static void set_error_notify_func(ResourceLoadErrorNotify p_err_notify) {
@@ -239,7 +239,7 @@ public:
if (Thread::get_caller_id() == Thread::get_main_id()) {
dep_err_notify(p_path, p_dependency, p_type);
} else {
- callable_mp_static(dep_err_notify).bind(p_path, p_dependency, p_type).call_deferred();
+ callable_mp_static(dep_err_notify).call_deferred(p_path, p_dependency, p_type);
}
}
}
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp
index 3d93ce8e73..fe4345aa0d 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -505,7 +505,7 @@ Object *ClassDB::_instantiate_internal(const StringName &p_class, bool p_require
ERR_FAIL_NULL_V_MSG(ti->creation_func, nullptr, "Class '" + String(p_class) + "' or its base class cannot be instantiated.");
}
#ifdef TOOLS_ENABLED
- if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) {
+ if ((ti->api == API_EDITOR || ti->api == API_EDITOR_EXTENSION) && !Engine::get_singleton()->is_editor_hint()) {
ERR_PRINT("Class '" + String(p_class) + "' can only be instantiated by editor.");
return nullptr;
}
@@ -664,7 +664,7 @@ bool ClassDB::can_instantiate(const StringName &p_class) {
return scr.is_valid() && scr->is_valid() && !scr->is_abstract();
}
#ifdef TOOLS_ENABLED
- if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) {
+ if ((ti->api == API_EDITOR || ti->api == API_EDITOR_EXTENSION) && !Engine::get_singleton()->is_editor_hint()) {
return false;
}
#endif
@@ -684,7 +684,7 @@ bool ClassDB::is_virtual(const StringName &p_class) {
return scr.is_valid() && scr->is_valid() && scr->is_abstract();
}
#ifdef TOOLS_ENABLED
- if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) {
+ if ((ti->api == API_EDITOR || ti->api == API_EDITOR_EXTENSION) && !Engine::get_singleton()->is_editor_hint()) {
return false;
}
#endif
diff --git a/core/object/undo_redo.cpp b/core/object/undo_redo.cpp
index 4d67cd930e..0f7884305a 100644
--- a/core/object/undo_redo.cpp
+++ b/core/object/undo_redo.cpp
@@ -159,10 +159,11 @@ void UndoRedo::add_do_method(const Callable &p_callable) {
do_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(object));
}
do_op.type = Operation::TYPE_METHOD;
- do_op.name = p_callable.get_method();
- if (do_op.name == StringName()) {
- // There's no `get_method()` for custom callables, so use `operator String()` instead.
+ // There's no `get_method()` for custom callables, so use `operator String()` instead.
+ if (p_callable.is_custom()) {
do_op.name = static_cast<String>(p_callable);
+ } else {
+ do_op.name = p_callable.get_method();
}
actions.write[current_action + 1].do_ops.push_back(do_op);
@@ -190,10 +191,11 @@ void UndoRedo::add_undo_method(const Callable &p_callable) {
}
undo_op.type = Operation::TYPE_METHOD;
undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends;
- undo_op.name = p_callable.get_method();
- if (undo_op.name == StringName()) {
- // There's no `get_method()` for custom callables, so use `operator String()` instead.
+ // There's no `get_method()` for custom callables, so use `operator String()` instead.
+ if (p_callable.is_custom()) {
undo_op.name = static_cast<String>(p_callable);
+ } else {
+ undo_op.name = p_callable.get_method();
}
actions.write[current_action + 1].undo_ops.push_back(undo_op);
diff --git a/doc/classes/AnimationMixer.xml b/doc/classes/AnimationMixer.xml
index a77e9e28c6..c635eba2ab 100644
--- a/doc/classes/AnimationMixer.xml
+++ b/doc/classes/AnimationMixer.xml
@@ -273,7 +273,7 @@
The number of possible simultaneous sounds for each of the assigned AudioStreamPlayers.
For example, if this value is [code]32[/code] and the animation has two audio tracks, the two [AudioStreamPlayer]s assigned can play simultaneously up to [code]32[/code] voices each.
</member>
- <member name="callback_mode_discrete" type="int" setter="set_callback_mode_discrete" getter="get_callback_mode_discrete" enum="AnimationMixer.AnimationCallbackModeDiscrete" default="0">
+ <member name="callback_mode_discrete" type="int" setter="set_callback_mode_discrete" getter="get_callback_mode_discrete" enum="AnimationMixer.AnimationCallbackModeDiscrete" default="1">
Ordinarily, tracks can be set to [constant Animation.UPDATE_DISCRETE] to update infrequently, usually when using nearest interpolation.
However, when blending with [constant Animation.UPDATE_CONTINUOUS] several results are considered. The [member callback_mode_discrete] specify it explicitly. See also [enum AnimationCallbackModeDiscrete].
To make the blended results look good, it is recommended to set this to [constant ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS] to update every frame during blending. Other values exist for compatibility and they are fine if there is no blending, but not so, may produce artifacts.
@@ -361,14 +361,14 @@
Make method calls immediately when reached in the animation.
</constant>
<constant name="ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT" value="0" enum="AnimationCallbackModeDiscrete">
- An [constant Animation.UPDATE_DISCRETE] track value takes precedence when blending [constant Animation.UPDATE_CONTINUOUS] or [constant Animation.UPDATE_CAPTURE] track values and [constant Animation.UPDATE_DISCRETE] track values. This is the default behavior for [AnimationPlayer].
- [b]Note:[/b] If a value track has non-numeric type key values, it is internally converted to use [constant ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT] with [constant Animation.UPDATE_DISCRETE].
+ An [constant Animation.UPDATE_DISCRETE] track value takes precedence when blending [constant Animation.UPDATE_CONTINUOUS] or [constant Animation.UPDATE_CAPTURE] track values and [constant Animation.UPDATE_DISCRETE] track values.
</constant>
<constant name="ANIMATION_CALLBACK_MODE_DISCRETE_RECESSIVE" value="1" enum="AnimationCallbackModeDiscrete">
- An [constant Animation.UPDATE_CONTINUOUS] or [constant Animation.UPDATE_CAPTURE] track value takes precedence when blending the [constant Animation.UPDATE_CONTINUOUS] or [constant Animation.UPDATE_CAPTURE] track values and the [constant Animation.UPDATE_DISCRETE] track values.
+ An [constant Animation.UPDATE_CONTINUOUS] or [constant Animation.UPDATE_CAPTURE] track value takes precedence when blending the [constant Animation.UPDATE_CONTINUOUS] or [constant Animation.UPDATE_CAPTURE] track values and the [constant Animation.UPDATE_DISCRETE] track values. This is the default behavior for [AnimationPlayer].
</constant>
<constant name="ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS" value="2" enum="AnimationCallbackModeDiscrete">
Always treat the [constant Animation.UPDATE_DISCRETE] track value as [constant Animation.UPDATE_CONTINUOUS] with [constant Animation.INTERPOLATION_NEAREST]. This is the default behavior for [AnimationTree].
+ If a value track has non-numeric type key values, it is internally converted to use [constant ANIMATION_CALLBACK_MODE_DISCRETE_RECESSIVE] with [constant Animation.UPDATE_DISCRETE].
</constant>
</constants>
</class>
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index e157cbb96c..ff1c390b3c 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -63,6 +63,7 @@
<param index="2" name="callback" type="Callable" />
<description>
Creates a new application status indicator with the specified icon, tooltip, and activation callback.
+ [param callback] should take two arguments: the pressed mouse button (one of the [enum MouseButton] constants) and the click position in screen coordinates (a [Vector2i]).
</description>
</method>
<method name="cursor_get_shape" qualifiers="const">
@@ -875,7 +876,7 @@
<param index="1" name="open_callback" type="Callable" />
<param index="2" name="close_callback" type="Callable" />
<description>
- Registers callables to emit when the menu is respectively about to show or closed.
+ Registers callables to emit when the menu is respectively about to show or closed. Callback methods should have zero arguments.
</description>
</method>
<method name="has_feature" qualifiers="const">
@@ -1187,7 +1188,7 @@
<param index="0" name="id" type="int" />
<param index="1" name="callback" type="Callable" />
<description>
- Sets the application status indicator activation callback.
+ Sets the application status indicator activation callback. [param callback] should take two arguments: [int] mouse button index (one of [enum MouseButton] values) and [Vector2i] click position in screen coordinates.
[b]Note:[/b] This method is implemented on macOS and Windows.
</description>
</method>
@@ -1568,7 +1569,7 @@
<param index="0" name="callback" type="Callable" />
<param index="1" name="window_id" type="int" default="0" />
<description>
- Sets the [param callback] that should be called when files are dropped from the operating system's file manager to the window specified by [param window_id].
+ Sets the [param callback] that should be called when files are dropped from the operating system's file manager to the window specified by [param window_id]. [param callback] should take one [PackedStringArray] argument, which is the list of dropped files.
[b]Warning:[/b] Advanced users only! Adding such a callback to a [Window] node will override its default implementation, which can introduce bugs.
[b]Note:[/b] This method is implemented on Windows, macOS, Linux (X11/Wayland), and Web.
</description>
diff --git a/doc/classes/EditorInspectorPlugin.xml b/doc/classes/EditorInspectorPlugin.xml
index e5e6905cd2..4f6ef76c4c 100644
--- a/doc/classes/EditorInspectorPlugin.xml
+++ b/doc/classes/EditorInspectorPlugin.xml
@@ -78,9 +78,11 @@
<param index="0" name="property" type="String" />
<param index="1" name="editor" type="Control" />
<param index="2" name="add_to_end" type="bool" default="false" />
+ <param index="3" name="label" type="String" default="&quot;&quot;" />
<description>
Adds a property editor for an individual property. The [param editor] control must extend [EditorProperty].
There can be multiple property editors for a property. If [param add_to_end] is [code]true[/code], this newly added editor will be displayed after all the other editors of the property whose [param add_to_end] is [code]false[/code]. For example, the editor uses this parameter to add an "Edit Region" button for [member Sprite2D.region_rect] below the regular [Rect2] editor.
+ [param label] can be used to choose a custom label for the property editor in the inspector. If left empty, the label is computed from the name of the property instead.
</description>
</method>
<method name="add_property_editor_for_multiple_properties">
diff --git a/doc/classes/Light3D.xml b/doc/classes/Light3D.xml
index c1fc49cf9f..bda5fb69de 100644
--- a/doc/classes/Light3D.xml
+++ b/doc/classes/Light3D.xml
@@ -199,7 +199,7 @@
</constant>
<constant name="BAKE_DISABLED" value="0" enum="BakeMode">
Light is ignored when baking. This is the fastest mode, but the light will be taken into account when baking global illumination. This mode should generally be used for dynamic lights that change quickly, as the effect of global illumination is less noticeable on those lights.
- [b]Note:[/b] Hiding a light does [i]not[/i] affect baking [LightmapGI]. Hiding a light will still affect baking [VoxelGI] and SDFGI (see [member Environment.sdfgi_enabled).
+ [b]Note:[/b] Hiding a light does [i]not[/i] affect baking [LightmapGI]. Hiding a light will still affect baking [VoxelGI] and SDFGI (see [member Environment.sdfgi_enabled]).
</constant>
<constant name="BAKE_STATIC" value="1" enum="BakeMode">
Light is taken into account in static baking ([VoxelGI], [LightmapGI], SDFGI ([member Environment.sdfgi_enabled])). The light can be moved around or modified, but its global illumination will not update in real-time. This is suitable for subtle changes (such as flickering torches), but generally not large changes such as toggling a light on and off.
diff --git a/doc/classes/Mesh.xml b/doc/classes/Mesh.xml
index 966e870940..6b5a50d97b 100644
--- a/doc/classes/Mesh.xml
+++ b/doc/classes/Mesh.xml
@@ -4,7 +4,7 @@
A [Resource] that contains vertex array-based geometry.
</brief_description>
<description>
- Mesh is a type of [Resource] that contains vertex array-based geometry, divided in [i]surfaces[/i]. Each surface contains a completely separate array and a material used to draw it. Design wise, a mesh with multiple surfaces is preferred to a single surface, because objects created in 3D editing software commonly contain multiple materials.
+ Mesh is a type of [Resource] that contains vertex array-based geometry, divided in [i]surfaces[/i]. Each surface contains a completely separate array and a material used to draw it. Design wise, a mesh with multiple surfaces is preferred to a single surface, because objects created in 3D editing software commonly contain multiple materials. The maximum number of surfaces per mesh is [constant RenderingServer.MAX_MESH_SURFACES].
</description>
<tutorials>
<link title="3D Material Testers Demo">https://godotengine.org/asset-library/asset/2742</link>
diff --git a/doc/classes/MeshLibrary.xml b/doc/classes/MeshLibrary.xml
index c5e8d8cbe7..f65e29af8e 100644
--- a/doc/classes/MeshLibrary.xml
+++ b/doc/classes/MeshLibrary.xml
@@ -29,7 +29,7 @@
<return type="int" />
<param index="0" name="name" type="String" />
<description>
- Returns the first item with the given name.
+ Returns the first item with the given name, or [code]-1[/code] if no item is found.
</description>
</method>
<method name="get_item_list" qualifiers="const">
diff --git a/doc/classes/NavigationAgent2D.xml b/doc/classes/NavigationAgent2D.xml
index 6f0561e66e..94c372106b 100644
--- a/doc/classes/NavigationAgent2D.xml
+++ b/doc/classes/NavigationAgent2D.xml
@@ -261,7 +261,7 @@
<signal name="velocity_computed">
<param index="0" name="safe_velocity" type="Vector2" />
<description>
- Notifies when the collision avoidance velocity is calculated. Emitted when [member velocity] is set. Only emitted when [member avoidance_enabled] is true.
+ Notifies when the collision avoidance velocity is calculated. Emitted every update as long as [member avoidance_enabled] is [code]true[/code] and the agent has a navigation map.
</description>
</signal>
<signal name="waypoint_reached">
diff --git a/doc/classes/NavigationAgent3D.xml b/doc/classes/NavigationAgent3D.xml
index 64ee35a84b..5f9f4991d1 100644
--- a/doc/classes/NavigationAgent3D.xml
+++ b/doc/classes/NavigationAgent3D.xml
@@ -271,7 +271,7 @@
<signal name="velocity_computed">
<param index="0" name="safe_velocity" type="Vector3" />
<description>
- Notifies when the collision avoidance velocity is calculated. Emitted when [member velocity] is set. Only emitted when [member avoidance_enabled] is true.
+ Notifies when the collision avoidance velocity is calculated. Emitted every update as long as [member avoidance_enabled] is [code]true[/code] and the agent has a navigation map.
</description>
</signal>
<signal name="waypoint_reached">
diff --git a/doc/classes/PhysicsMaterial.xml b/doc/classes/PhysicsMaterial.xml
index 1601a1040e..03cbfb4ce7 100644
--- a/doc/classes/PhysicsMaterial.xml
+++ b/doc/classes/PhysicsMaterial.xml
@@ -14,7 +14,7 @@
</member>
<member name="bounce" type="float" setter="set_bounce" getter="get_bounce" default="0.0">
The body's bounciness. Values range from [code]0[/code] (no bounce) to [code]1[/code] (full bounciness).
- [b]Note:[/b] Even with [member bounce] set to [code]1.0[/code], some energy will be lost over time due to linear and angular damping. To have a [PhysicsBody3D] that preserves all its energy over time, set [member bounce] to [code]1.0[/code], the body's linear damp mode to [b]Replace[/b] (if applicable), its linear damp to [code]0.0[/code], its angular damp mode to [b]Replace[/b] (if applicable), and its angular damp to [code]0.0[/code].
+ [b]Note:[/b] Even with [member bounce] set to [code]1.0[/code], some energy will be lost over time due to linear and angular damping. To have a physics body that preserves all its energy over time, set [member bounce] to [code]1.0[/code], the body's linear damp mode to [b]Replace[/b] (if applicable), its linear damp to [code]0.0[/code], its angular damp mode to [b]Replace[/b] (if applicable), and its angular damp to [code]0.0[/code].
</member>
<member name="friction" type="float" setter="set_friction" getter="get_friction" default="1.0">
The body's friction. Values range from [code]0[/code] (frictionless) to [code]1[/code] (maximum friction).
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 5ac4c96d93..32e71ce030 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -902,8 +902,9 @@
<member name="display/window/vsync/vsync_mode" type="int" setter="" getter="" default="1">
Sets the V-Sync mode for the main game window. The editor's own V-Sync mode can be set using [member EditorSettings.interface/editor/vsync_mode].
See [enum DisplayServer.VSyncMode] for possible values and how they affect the behavior of your application.
- Depending on the platform and used renderer, the engine will fall back to [b]Enabled[/b] if the desired mode is not supported.
- [b]Note:[/b] V-Sync modes other than [b]Enabled[/b] are only supported in the Forward+ and Mobile rendering methods, not Compatibility.
+ Depending on the platform and rendering method, the engine will fall back to [b]Enabled[/b] if the desired mode is not supported.
+ V-Sync can be disabled on the command line using the [code]--disable-vsync[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url].
+ [b]Note:[/b] The [b]Adaptive[/b] and [b]Mailbox[/b] V-Sync modes are only supported in the Forward+ and Mobile rendering methods, not Compatibility.
[b]Note:[/b] This property is only read when the project starts. To change the V-Sync mode at runtime, call [method DisplayServer.window_set_vsync_mode] instead.
</member>
<member name="dotnet/project/assembly_name" type="String" setter="" getter="" default="&quot;&quot;">
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 3ddc0d8f7b..3c9f0fc7af 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -4239,6 +4239,9 @@
<constant name="MAX_2D_DIRECTIONAL_LIGHTS" value="8">
The maximum number of directional lights that can be rendered at a given time in 2D.
</constant>
+ <constant name="MAX_MESH_SURFACES" value="256">
+ The maximum number of surfaces a mesh can have.
+ </constant>
<constant name="TEXTURE_LAYERED_2D_ARRAY" value="0" enum="TextureLayeredType">
Array of 2-dimensional textures (see [Texture2DArray]).
</constant>
diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py
index bc379f8553..761a7f8f4a 100755
--- a/doc/tools/make_rst.py
+++ b/doc/tools/make_rst.py
@@ -1110,11 +1110,13 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:
# Create signal signature and anchor point.
- f.write(f".. _class_{class_name}_signal_{signal.name}:\n\n")
+ signal_anchor = f"class_{class_name}_signal_{signal.name}"
+ f.write(f".. _{signal_anchor}:\n\n")
+ self_link = f":ref:`🔗<{signal_anchor}>`"
f.write(".. rst-class:: classref-signal\n\n")
_, signature = make_method_signature(class_def, signal, "", state)
- f.write(f"{signature}\n\n")
+ f.write(f"{signature} {self_link}\n\n")
# Add signal description, or a call to action if it's missing.
@@ -1147,13 +1149,15 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:
# Create enumeration signature and anchor point.
- f.write(f".. _enum_{class_name}_{e.name}:\n\n")
+ enum_anchor = f"enum_{class_name}_{e.name}"
+ f.write(f".. _{enum_anchor}:\n\n")
+ self_link = f":ref:`🔗<{enum_anchor}>`"
f.write(".. rst-class:: classref-enumeration\n\n")
if e.is_bitfield:
- f.write(f"flags **{e.name}**:\n\n")
+ f.write(f"flags **{e.name}**: {self_link}\n\n")
else:
- f.write(f"enum **{e.name}**:\n\n")
+ f.write(f"enum **{e.name}**: {self_link}\n\n")
for value in e.values.values():
# Also create signature and anchor point for each enum constant.
@@ -1191,10 +1195,12 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:
for constant in class_def.constants.values():
# Create constant signature and anchor point.
- f.write(f".. _class_{class_name}_constant_{constant.name}:\n\n")
+ constant_anchor = f"class_{class_name}_constant_{constant.name}"
+ f.write(f".. _{constant_anchor}:\n\n")
+ self_link = f":ref:`🔗<{constant_anchor}>`"
f.write(".. rst-class:: classref-constant\n\n")
- f.write(f"**{constant.name}** = ``{constant.value}``\n\n")
+ f.write(f"**{constant.name}** = ``{constant.value}`` {self_link}\n\n")
# Add constant description.
@@ -1227,13 +1233,16 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:
# Create annotation signature and anchor point.
+ self_link = ""
if i == 0:
- f.write(f".. _class_{class_name}_annotation_{m.name}:\n\n")
+ annotation_anchor = f"class_{class_name}_annotation_{m.name}"
+ f.write(f".. _{annotation_anchor}:\n\n")
+ self_link = f" :ref:`🔗<{annotation_anchor}>`"
f.write(".. rst-class:: classref-annotation\n\n")
_, signature = make_method_signature(class_def, m, "", state)
- f.write(f"{signature}\n\n")
+ f.write(f"{signature}{self_link}\n\n")
# Add annotation description, or a call to action if it's missing.
@@ -1267,13 +1276,17 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:
# Create property signature and anchor point.
- f.write(f".. _class_{class_name}_property_{property_def.name}:\n\n")
+ property_anchor = f"class_{class_name}_property_{property_def.name}"
+ f.write(f".. _{property_anchor}:\n\n")
+ self_link = f":ref:`🔗<{property_anchor}>`"
f.write(".. rst-class:: classref-property\n\n")
property_default = ""
if property_def.default_value is not None:
property_default = f" = {property_def.default_value}"
- f.write(f"{property_def.type_name.to_rst(state)} **{property_def.name}**{property_default}\n\n")
+ f.write(
+ f"{property_def.type_name.to_rst(state)} **{property_def.name}**{property_default} {self_link}\n\n"
+ )
# Create property setter and getter records.
@@ -1327,13 +1340,16 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:
# Create constructor signature and anchor point.
+ self_link = ""
if i == 0:
- f.write(f".. _class_{class_name}_constructor_{m.name}:\n\n")
+ constructor_anchor = f"class_{class_name}_constructor_{m.name}"
+ f.write(f".. _{constructor_anchor}:\n\n")
+ self_link = f" :ref:`🔗<{constructor_anchor}>`"
f.write(".. rst-class:: classref-constructor\n\n")
ret_type, signature = make_method_signature(class_def, m, "", state)
- f.write(f"{ret_type} {signature}\n\n")
+ f.write(f"{ret_type} {signature}{self_link}\n\n")
# Add constructor description, or a call to action if it's missing.
@@ -1366,17 +1382,21 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:
# Create method signature and anchor point.
+ self_link = ""
+
if i == 0:
method_qualifier = ""
if m.name.startswith("_"):
method_qualifier = "private_"
-
- f.write(f".. _class_{class_name}_{method_qualifier}method_{m.name}:\n\n")
+ method_anchor = f"class_{class_name}_{method_qualifier}method_{m.name}"
+ f.write(f".. _{method_anchor}:\n\n")
+ self_link = f" :ref:`🔗<{method_anchor}>`"
f.write(".. rst-class:: classref-method\n\n")
ret_type, signature = make_method_signature(class_def, m, "", state)
- f.write(f"{ret_type} {signature}\n\n")
+
+ f.write(f"{ret_type} {signature}{self_link}\n\n")
# Add method description, or a call to action if it's missing.
@@ -1409,16 +1429,16 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:
# Create operator signature and anchor point.
- operator_anchor = f".. _class_{class_name}_operator_{sanitize_operator_name(m.name, state)}"
+ operator_anchor = f"class_{class_name}_operator_{sanitize_operator_name(m.name, state)}"
for parameter in m.parameters:
operator_anchor += f"_{parameter.type_name.type_name}"
- operator_anchor += ":\n\n"
- f.write(operator_anchor)
+ f.write(f".. _{operator_anchor}:\n\n")
+ self_link = f":ref:`🔗<{operator_anchor}>`"
f.write(".. rst-class:: classref-operator\n\n")
ret_type, signature = make_method_signature(class_def, m, "", state)
- f.write(f"{ret_type} {signature}\n\n")
+ f.write(f"{ret_type} {signature} {self_link}\n\n")
# Add operator description, or a call to action if it's missing.
@@ -1451,13 +1471,17 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:
# Create theme property signature and anchor point.
- f.write(f".. _class_{class_name}_theme_{theme_item_def.data_name}_{theme_item_def.name}:\n\n")
+ theme_item_anchor = f"class_{class_name}_theme_{theme_item_def.data_name}_{theme_item_def.name}"
+ f.write(f".. _{theme_item_anchor}:\n\n")
+ self_link = f":ref:`🔗<{theme_item_anchor}>`"
f.write(".. rst-class:: classref-themeproperty\n\n")
theme_item_default = ""
if theme_item_def.default_value is not None:
theme_item_default = f" = {theme_item_def.default_value}"
- f.write(f"{theme_item_def.type_name.to_rst(state)} **{theme_item_def.name}**{theme_item_default}\n\n")
+ f.write(
+ f"{theme_item_def.type_name.to_rst(state)} **{theme_item_def.name}**{theme_item_default} {self_link}\n\n"
+ )
# Add theme property description, or a call to action if it's missing.
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 03f947cd05..6cbdf5e935 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -1359,23 +1359,26 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const
// LOD
if (p_render_data->screen_mesh_lod_threshold > 0.0 && mesh_storage->mesh_surface_has_lod(surf->surface)) {
- // Get the LOD support points on the mesh AABB.
- Vector3 lod_support_min = inst->transformed_aabb.get_support(p_render_data->cam_transform.basis.get_column(Vector3::AXIS_Z));
- Vector3 lod_support_max = inst->transformed_aabb.get_support(-p_render_data->cam_transform.basis.get_column(Vector3::AXIS_Z));
-
- // Get the distances to those points on the AABB from the camera origin.
- float distance_min = (float)p_render_data->cam_transform.origin.distance_to(lod_support_min);
- float distance_max = (float)p_render_data->cam_transform.origin.distance_to(lod_support_max);
-
float distance = 0.0;
- if (distance_min * distance_max < 0.0) {
- //crossing plane
- distance = 0.0;
- } else if (distance_min >= 0.0) {
- distance = distance_min;
- } else if (distance_max <= 0.0) {
- distance = -distance_max;
+ // Check if camera is NOT inside the mesh AABB.
+ if (!inst->transformed_aabb.has_point(p_render_data->main_cam_transform.origin)) {
+ // Get the LOD support points on the mesh AABB.
+ Vector3 lod_support_min = inst->transformed_aabb.get_support(p_render_data->main_cam_transform.basis.get_column(Vector3::AXIS_Z));
+ Vector3 lod_support_max = inst->transformed_aabb.get_support(-p_render_data->main_cam_transform.basis.get_column(Vector3::AXIS_Z));
+
+ // Get the distances to those points on the AABB from the camera origin.
+ float distance_min = (float)p_render_data->main_cam_transform.origin.distance_to(lod_support_min);
+ float distance_max = (float)p_render_data->main_cam_transform.origin.distance_to(lod_support_max);
+
+ if (distance_min * distance_max < 0.0) {
+ //crossing plane
+ distance = 0.0;
+ } else if (distance_min >= 0.0) {
+ distance = distance_min;
+ } else if (distance_max <= 0.0) {
+ distance = -distance_max;
+ }
}
if (p_render_data->cam_orthogonal) {
@@ -1985,7 +1988,6 @@ void RasterizerSceneGLES3::_render_shadows(const RenderDataGLES3 *p_render_data,
LocalVector<int> shadows;
LocalVector<int> directional_shadows;
- Plane camera_plane(-p_render_data->cam_transform.basis.get_column(Vector3::AXIS_Z), p_render_data->cam_transform.origin);
float lod_distance_multiplier = p_render_data->cam_projection.get_lod_multiplier();
// Put lights into buckets for omni (cube shadows), directional, and spot.
@@ -2014,20 +2016,20 @@ void RasterizerSceneGLES3::_render_shadows(const RenderDataGLES3 *p_render_data,
// Render cubemap shadows.
for (const int &index : cube_shadows) {
- _render_shadow_pass(p_render_data->render_shadows[index].light, p_render_data->shadow_atlas, p_render_data->render_shadows[index].pass, p_render_data->render_shadows[index].instances, camera_plane, lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, p_render_data->render_info, p_viewport_size, p_render_data->cam_transform);
+ _render_shadow_pass(p_render_data->render_shadows[index].light, p_render_data->shadow_atlas, p_render_data->render_shadows[index].pass, p_render_data->render_shadows[index].instances, lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, p_render_data->render_info, p_viewport_size, p_render_data->cam_transform);
}
// Render directional shadows.
for (uint32_t i = 0; i < directional_shadows.size(); i++) {
- _render_shadow_pass(p_render_data->render_shadows[directional_shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[directional_shadows[i]].pass, p_render_data->render_shadows[directional_shadows[i]].instances, camera_plane, lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, p_render_data->render_info, p_viewport_size, p_render_data->cam_transform);
+ _render_shadow_pass(p_render_data->render_shadows[directional_shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[directional_shadows[i]].pass, p_render_data->render_shadows[directional_shadows[i]].instances, lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, p_render_data->render_info, p_viewport_size, p_render_data->cam_transform);
}
// Render positional shadows (Spotlight and Omnilight with dual-paraboloid).
for (uint32_t i = 0; i < shadows.size(); i++) {
- _render_shadow_pass(p_render_data->render_shadows[shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[shadows[i]].pass, p_render_data->render_shadows[shadows[i]].instances, camera_plane, lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, p_render_data->render_info, p_viewport_size, p_render_data->cam_transform);
+ _render_shadow_pass(p_render_data->render_shadows[shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[shadows[i]].pass, p_render_data->render_shadows[shadows[i]].instances, lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, p_render_data->render_info, p_viewport_size, p_render_data->cam_transform);
}
}
}
-void RasterizerSceneGLES3::_render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, const Plane &p_camera_plane, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, RenderingMethod::RenderInfo *p_render_info, const Size2i &p_viewport_size, const Transform3D &p_main_cam_transform) {
+void RasterizerSceneGLES3::_render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, RenderingMethod::RenderInfo *p_render_info, const Size2i &p_viewport_size, const Transform3D &p_main_cam_transform) {
GLES3::LightStorage *light_storage = GLES3::LightStorage::get_singleton();
ERR_FAIL_COND(!light_storage->owns_light_instance(p_light));
diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h
index 5cde4ee9de..b6c7a0c5a5 100644
--- a/drivers/gles3/rasterizer_scene_gles3.h
+++ b/drivers/gles3/rasterizer_scene_gles3.h
@@ -640,7 +640,7 @@ private:
void _setup_environment(const RenderDataGLES3 *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_pancake_shadows, float p_shadow_bias = 0.0);
void _fill_render_list(RenderListType p_render_list, const RenderDataGLES3 *p_render_data, PassMode p_pass_mode, bool p_append = false);
void _render_shadows(const RenderDataGLES3 *p_render_data, const Size2i &p_viewport_size = Size2i(1, 1));
- void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, RenderingMethod::RenderInfo *p_render_info = nullptr, const Size2i &p_viewport_size = Size2i(1, 1), const Transform3D &p_main_cam_transform = Transform3D());
+ void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, RenderingMethod::RenderInfo *p_render_info = nullptr, const Size2i &p_viewport_size = Size2i(1, 1), const Transform3D &p_main_cam_transform = Transform3D());
void _render_post_processing(const RenderDataGLES3 *p_render_data);
template <PassMode p_pass_mode>
diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp
index 1d3c7aec3f..2f7183b883 100644
--- a/editor/debugger/editor_debugger_node.cpp
+++ b/editor/debugger/editor_debugger_node.cpp
@@ -314,12 +314,18 @@ void EditorDebuggerNode::stop(bool p_force) {
void EditorDebuggerNode::_notification(int p_what) {
switch (p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- if (tabs->get_tab_count() > 1 && EditorThemeManager::is_generated_theme_outdated()) {
+ if (!EditorThemeManager::is_generated_theme_outdated()) {
+ return;
+ }
+
+ if (tabs->get_tab_count() > 1) {
add_theme_constant_override("margin_left", -EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("BottomPanelDebuggerOverride"), EditorStringName(EditorStyles))->get_margin(SIDE_LEFT));
add_theme_constant_override("margin_right", -EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("BottomPanelDebuggerOverride"), EditorStringName(EditorStyles))->get_margin(SIDE_RIGHT));
tabs->add_theme_style_override("panel", EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("DebuggerPanel"), EditorStringName(EditorStyles)));
}
+
+ remote_scene_tree->update_icon_max_width();
} break;
case NOTIFICATION_READY: {
diff --git a/editor/debugger/editor_debugger_tree.cpp b/editor/debugger/editor_debugger_tree.cpp
index 63053d2574..12b590da3c 100644
--- a/editor/debugger/editor_debugger_tree.cpp
+++ b/editor/debugger/editor_debugger_tree.cpp
@@ -31,6 +31,7 @@
#include "editor_debugger_tree.h"
#include "editor/editor_node.h"
+#include "editor/editor_string_names.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/scene_tree_dock.h"
#include "scene/debugger/scene_debugger.h"
@@ -62,6 +63,10 @@ void EditorDebuggerTree::_notification(int p_what) {
connect("item_collapsed", callable_mp(this, &EditorDebuggerTree::_scene_tree_folded));
connect("item_mouse_selected", callable_mp(this, &EditorDebuggerTree::_scene_tree_rmb_selected));
} break;
+
+ case NOTIFICATION_ENTER_TREE: {
+ update_icon_max_width();
+ } break;
}
}
@@ -293,6 +298,10 @@ Variant EditorDebuggerTree::get_drag_data(const Point2 &p_point) {
return vformat("\"%s\"", path);
}
+void EditorDebuggerTree::update_icon_max_width() {
+ add_theme_constant_override("icon_max_width", get_theme_constant("class_icon_size", EditorStringName(Editor)));
+}
+
String EditorDebuggerTree::get_selected_path() {
if (!get_selected()) {
return "";
diff --git a/editor/debugger/editor_debugger_tree.h b/editor/debugger/editor_debugger_tree.h
index 895f33f1a2..dbffb0f219 100644
--- a/editor/debugger/editor_debugger_tree.h
+++ b/editor/debugger/editor_debugger_tree.h
@@ -72,6 +72,7 @@ public:
virtual Variant get_drag_data(const Point2 &p_point) override;
+ void update_icon_max_width();
String get_selected_path();
ObjectID get_selected_object();
int get_current_debugger(); // Would love to have one tree for every debugger.
diff --git a/editor/debugger/editor_profiler.cpp b/editor/debugger/editor_profiler.cpp
index 69cf13ea0b..0e2a7ee599 100644
--- a/editor/debugger/editor_profiler.cpp
+++ b/editor/debugger/editor_profiler.cpp
@@ -34,6 +34,7 @@
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/themes/editor_scale.h"
+#include "editor/themes/editor_theme_manager.h"
#include "scene/resources/image_texture.h"
void EditorProfiler::_make_metric_ptrs(Metric &m) {
@@ -423,6 +424,15 @@ void EditorProfiler::_notification(int p_what) {
case NOTIFICATION_TRANSLATION_CHANGED: {
activate->set_icon(get_editor_theme_icon(SNAME("Play")));
clear_button->set_icon(get_editor_theme_icon(SNAME("Clear")));
+
+ theme_cache.seek_line_color = get_theme_color(SNAME("font_color"), EditorStringName(Editor));
+ theme_cache.seek_line_color.a = 0.8;
+ theme_cache.seek_line_hover_color = theme_cache.seek_line_color;
+ theme_cache.seek_line_hover_color.a = 0.4;
+
+ if (total_metrics > 0) {
+ _update_plot();
+ }
} break;
}
}
@@ -434,11 +444,11 @@ void EditorProfiler::_graph_tex_draw() {
if (seeking) {
int frame = cursor_metric_edit->get_value() - _get_frame_metric(0).frame_number;
int cur_x = (2 * frame + 1) * graph->get_size().x / (2 * frame_metrics.size()) + 1;
- graph->draw_line(Vector2(cur_x, 0), Vector2(cur_x, graph->get_size().y), Color(1, 1, 1, 0.8));
+ graph->draw_line(Vector2(cur_x, 0), Vector2(cur_x, graph->get_size().y), theme_cache.seek_line_color);
}
if (hover_metric > -1 && hover_metric < total_metrics) {
int cur_x = (2 * hover_metric + 1) * graph->get_size().x / (2 * frame_metrics.size()) + 1;
- graph->draw_line(Vector2(cur_x, 0), Vector2(cur_x, graph->get_size().y), Color(1, 1, 1, 0.4));
+ graph->draw_line(Vector2(cur_x, 0), Vector2(cur_x, graph->get_size().y), theme_cache.seek_line_hover_color);
}
}
diff --git a/editor/debugger/editor_profiler.h b/editor/debugger/editor_profiler.h
index 620d21fe98..64253070b1 100644
--- a/editor/debugger/editor_profiler.h
+++ b/editor/debugger/editor_profiler.h
@@ -94,6 +94,11 @@ public:
};
private:
+ struct ThemeCache {
+ Color seek_line_color;
+ Color seek_line_hover_color;
+ } theme_cache;
+
Button *activate = nullptr;
Button *clear_button = nullptr;
TextureRect *graph = nullptr;
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index 8ecf31d875..b3c3196aad 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -2893,7 +2893,7 @@ void EditorHelp::_load_doc_thread(void *p_udata) {
callable_mp_static(&EditorHelp::_gen_extensions_docs).call_deferred();
} else {
// We have to go back to the main thread to start from scratch, bypassing any possibly existing cache.
- callable_mp_static(&EditorHelp::generate_doc).bind(false).call_deferred();
+ callable_mp_static(&EditorHelp::generate_doc).call_deferred(false);
}
OS::get_singleton()->benchmark_end_measure("EditorHelp", vformat("Generate Documentation (Run %d)", doc_generation_count));
diff --git a/editor/editor_inspector.compat.inc b/editor/editor_inspector.compat.inc
new file mode 100644
index 0000000000..53c410ba26
--- /dev/null
+++ b/editor/editor_inspector.compat.inc
@@ -0,0 +1,41 @@
+/**************************************************************************/
+/* editor_inspector.compat.inc */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef DISABLE_DEPRECATED
+
+void EditorInspectorPlugin::_add_property_editor_bind_compat_92322(const String &p_for_property, Control *p_prop, bool p_add_to_end) {
+ add_property_editor(p_for_property, p_prop, p_add_to_end, "");
+}
+
+void EditorInspectorPlugin::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("add_property_editor", "property", "editor", "add_to_end"), &EditorInspectorPlugin::_add_property_editor_bind_compat_92322, DEFVAL(false));
+}
+
+#endif // DISABLE_DEPRECATED
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index a9f32927a8..f4dcc8bd4a 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "editor_inspector.h"
+#include "editor_inspector.compat.inc"
#include "core/os/keyboard.h"
#include "editor/doc_tools.h"
@@ -1128,11 +1129,12 @@ void EditorInspectorPlugin::add_custom_control(Control *control) {
added_editors.push_back(ae);
}
-void EditorInspectorPlugin::add_property_editor(const String &p_for_property, Control *p_prop, bool p_add_to_end) {
+void EditorInspectorPlugin::add_property_editor(const String &p_for_property, Control *p_prop, bool p_add_to_end, const String &p_label) {
AddedEditor ae;
ae.properties.push_back(p_for_property);
ae.property_editor = p_prop;
ae.add_to_end = p_add_to_end;
+ ae.label = p_label;
added_editors.push_back(ae);
}
@@ -1174,7 +1176,7 @@ void EditorInspectorPlugin::parse_end(Object *p_object) {
void EditorInspectorPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_custom_control", "control"), &EditorInspectorPlugin::add_custom_control);
- ClassDB::bind_method(D_METHOD("add_property_editor", "property", "editor", "add_to_end"), &EditorInspectorPlugin::add_property_editor, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("add_property_editor", "property", "editor", "add_to_end", "label"), &EditorInspectorPlugin::add_property_editor, DEFVAL(false), DEFVAL(String()));
ClassDB::bind_method(D_METHOD("add_property_editor_for_multiple_properties", "label", "properties", "editor"), &EditorInspectorPlugin::add_property_editor_for_multiple_properties);
GDVIRTUAL_BIND(_can_handle, "object")
diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h
index e52903101d..a0ced55bd8 100644
--- a/editor/editor_inspector.h
+++ b/editor/editor_inspector.h
@@ -252,9 +252,13 @@ protected:
GDVIRTUAL7R(bool, _parse_property, Object *, Variant::Type, String, PropertyHint, String, BitField<PropertyUsageFlags>, bool)
GDVIRTUAL1(_parse_end, Object *)
+#ifndef DISABLE_DEPRECATED
+ void _add_property_editor_bind_compat_92322(const String &p_for_property, Control *p_prop, bool p_add_to_end);
+ static void _bind_compatibility_methods();
+#endif // DISABLE_DEPRECATED
public:
void add_custom_control(Control *control);
- void add_property_editor(const String &p_for_property, Control *p_prop, bool p_add_to_end = false);
+ void add_property_editor(const String &p_for_property, Control *p_prop, bool p_add_to_end = false, const String &p_label = String());
void add_property_editor_for_multiple_properties(const String &p_label, const Vector<String> &p_properties, Control *p_prop);
virtual bool can_handle(Object *p_object);
diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp
index 2a6fe808dd..166d09af30 100644
--- a/editor/editor_log.cpp
+++ b/editor/editor_log.cpp
@@ -59,7 +59,7 @@ void EditorLog::_error_handler(void *p_self, const char *p_func, const char *p_f
MessageType message_type = p_type == ERR_HANDLER_WARNING ? MSG_TYPE_WARNING : MSG_TYPE_ERROR;
if (self->current != Thread::get_caller_id()) {
- callable_mp(self, &EditorLog::add_message).bind(err_str, message_type).call_deferred();
+ callable_mp(self, &EditorLog::add_message).call_deferred(err_str, message_type);
} else {
self->add_message(err_str, message_type);
}
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 949916c2a3..964061505f 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -671,7 +671,7 @@ void EditorNode::_notification(int p_what) {
callable_mp(this, &EditorNode::_begin_first_scan).call_deferred();
- DisplayServer::get_singleton()->set_system_theme_change_callback(callable_mp(this, &EditorNode::_update_theme));
+ DisplayServer::get_singleton()->set_system_theme_change_callback(callable_mp(this, &EditorNode::_update_theme).bind(false));
/* DO NOT LOAD SCENES HERE, WAIT FOR FILE SCANNING AND REIMPORT TO COMPLETE */
} break;
@@ -4107,7 +4107,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
return OK;
}
-HashMap<StringName, Variant> EditorNode::get_modified_properties_for_node(Node *p_node) {
+HashMap<StringName, Variant> EditorNode::get_modified_properties_for_node(Node *p_node, bool p_node_references_only) {
HashMap<StringName, Variant> modified_property_map;
List<PropertyInfo> pinfo;
@@ -4119,7 +4119,17 @@ HashMap<StringName, Variant> EditorNode::get_modified_properties_for_node(Node *
Variant current_value = p_node->get(E.name);
if (is_valid_revert) {
if (PropertyUtils::is_property_value_different(current_value, revert_value)) {
- modified_property_map[E.name] = current_value;
+ // If this property is a direct node reference, save a NodePath instead to prevent corrupted references.
+ if (E.type == Variant::OBJECT && E.hint == PROPERTY_HINT_NODE_TYPE) {
+ Node *target_node = Object::cast_to<Node>(current_value);
+ if (target_node) {
+ modified_property_map[E.name] = p_node->get_path_to(target_node);
+ }
+ } else {
+ if (!p_node_references_only) {
+ modified_property_map[E.name] = current_value;
+ }
+ }
}
}
}
@@ -4137,10 +4147,118 @@ void EditorNode::update_ownership_table_for_addition_node_ancestors(Node *p_curr
}
}
-void EditorNode::update_diff_data_for_node(
- Node *p_edited_scene,
+void EditorNode::update_node_from_node_modification_entry(Node *p_node, ModificationNodeEntry &p_node_modification) {
+ if (p_node) {
+ // First, attempt to restore the script property since it may affect the get_property_list method.
+ Variant *script_property_table_entry = p_node_modification.property_table.getptr(CoreStringName(script));
+ if (script_property_table_entry) {
+ p_node->set_script(*script_property_table_entry);
+ }
+
+ // Get properties for this node.
+ List<PropertyInfo> pinfo;
+ p_node->get_property_list(&pinfo);
+
+ // Get names of all valid property names.
+ HashMap<StringName, bool> property_node_reference_table;
+ for (const PropertyInfo &E : pinfo) {
+ if (E.usage & PROPERTY_USAGE_STORAGE) {
+ if (E.type == Variant::OBJECT && E.hint == PROPERTY_HINT_NODE_TYPE) {
+ property_node_reference_table[E.name] = true;
+ } else {
+ property_node_reference_table[E.name] = false;
+ }
+ }
+ }
+
+ // Restore the modified properties for this node.
+ for (const KeyValue<StringName, Variant> &E : p_node_modification.property_table) {
+ bool *property_node_reference_table_entry = property_node_reference_table.getptr(E.key);
+ if (property_node_reference_table_entry) {
+ // If the property is a node reference, attempt to restore from the node path instead.
+ bool is_node_reference = *property_node_reference_table_entry;
+ if (is_node_reference) {
+ if (E.value.get_type() == Variant::NODE_PATH) {
+ p_node->set(E.key, p_node->get_node_or_null(E.value));
+ }
+ } else {
+ p_node->set(E.key, E.value);
+ }
+ }
+ }
+
+ // Restore the connections to other nodes.
+ for (const ConnectionWithNodePath &E : p_node_modification.connections_to) {
+ Connection conn = E.connection;
+
+ // Get the node the callable is targeting.
+ Node *target_node = Object::cast_to<Node>(conn.callable.get_object());
+
+ // If the callable object no longer exists or is marked for deletion,
+ // attempt to reaccquire the closest match by using the node path
+ // we saved earlier.
+ if (!target_node || !target_node->is_queued_for_deletion()) {
+ target_node = p_node->get_node_or_null(E.node_path);
+ }
+
+ if (target_node) {
+ // Reconstruct the callable.
+ Callable new_callable = Callable(target_node, conn.callable.get_method());
+
+ if (!p_node->is_connected(conn.signal.get_name(), new_callable)) {
+ ERR_FAIL_COND(p_node->connect(conn.signal.get_name(), new_callable, conn.flags) != OK);
+ }
+ }
+ }
+
+ // Restore the connections from other nodes.
+ for (const Connection &E : p_node_modification.connections_from) {
+ Connection conn = E;
+
+ bool valid = p_node->has_method(conn.callable.get_method()) || Ref<Script>(p_node->get_script()).is_null() || Ref<Script>(p_node->get_script())->has_method(conn.callable.get_method());
+ ERR_CONTINUE_MSG(!valid, vformat("Attempt to connect signal '%s.%s' to nonexistent method '%s.%s'.", conn.signal.get_object()->get_class(), conn.signal.get_name(), conn.callable.get_object()->get_class(), conn.callable.get_method()));
+
+ // Get the object which the signal is connected from.
+ Object *source_object = conn.signal.get_object();
+
+ if (source_object) {
+ ERR_FAIL_COND(source_object->connect(conn.signal.get_name(), Callable(p_node, conn.callable.get_method()), conn.flags) != OK);
+ }
+ }
+
+ // Re-add the groups.
+ for (const Node::GroupInfo &E : p_node_modification.groups) {
+ p_node->add_to_group(E.name, E.persistent);
+ }
+ }
+}
+
+void EditorNode::update_node_reference_modification_table_for_node(
Node *p_root,
Node *p_node,
+ List<Node *> p_excluded_nodes,
+ HashMap<NodePath, ModificationNodeEntry> &p_modification_table) {
+ if (!p_excluded_nodes.find(p_node)) {
+ HashMap<StringName, Variant> modified_properties = get_modified_properties_for_node(p_node, false);
+
+ if (!modified_properties.is_empty()) {
+ ModificationNodeEntry modification_node_entry;
+ modification_node_entry.property_table = modified_properties;
+
+ p_modification_table[p_root->get_path_to(p_node)] = modification_node_entry;
+ }
+
+ for (int i = 0; i < p_node->get_child_count(); i++) {
+ Node *child = p_node->get_child(i);
+ update_node_reference_modification_table_for_node(p_root, child, p_excluded_nodes, p_modification_table);
+ }
+ }
+}
+
+void EditorNode::update_reimported_diff_data_for_node(
+ Node *p_edited_scene,
+ Node *p_reimported_root,
+ Node *p_node,
HashMap<NodePath, ModificationNodeEntry> &p_modification_table,
List<AdditiveNodeEntry> &p_addition_list) {
bool node_part_of_subscene = p_node != p_edited_scene &&
@@ -4150,14 +4268,14 @@ void EditorNode::update_diff_data_for_node(
// Loop through the owners until either we reach the root node or nullptr
Node *valid_node_owner = p_node->get_owner();
while (valid_node_owner) {
- if (valid_node_owner == p_root) {
+ if (valid_node_owner == p_reimported_root) {
break;
}
valid_node_owner = valid_node_owner->get_owner();
}
- if ((valid_node_owner == p_root && (p_root != p_edited_scene || !p_edited_scene->get_scene_file_path().is_empty())) || node_part_of_subscene || p_node == p_root) {
- HashMap<StringName, Variant> modified_properties = get_modified_properties_for_node(p_node);
+ if ((valid_node_owner == p_reimported_root && (p_reimported_root != p_edited_scene || !p_edited_scene->get_scene_file_path().is_empty())) || node_part_of_subscene || p_node == p_reimported_root) {
+ HashMap<StringName, Variant> modified_properties = get_modified_properties_for_node(p_node, false);
// Find all valid connections to other nodes.
List<Connection> connections_to;
@@ -4189,7 +4307,7 @@ void EditorNode::update_diff_data_for_node(
if (source_node) {
valid_source_owner = source_node->get_owner();
while (valid_source_owner) {
- if (valid_source_owner == p_root) {
+ if (valid_source_owner == p_reimported_root) {
break;
}
valid_source_owner = valid_source_owner->get_owner();
@@ -4215,41 +4333,55 @@ void EditorNode::update_diff_data_for_node(
modification_node_entry.connections_from = valid_connections_from;
modification_node_entry.groups = groups;
- p_modification_table[p_root->get_path_to(p_node)] = modification_node_entry;
+ p_modification_table[p_reimported_root->get_path_to(p_node)] = modification_node_entry;
}
} else {
- AdditiveNodeEntry new_additive_node_entry;
- new_additive_node_entry.node = p_node;
- new_additive_node_entry.parent = p_root->get_path_to(p_node->get_parent());
- new_additive_node_entry.owner = p_node->get_owner();
- new_additive_node_entry.index = p_node->get_index();
+ // Only save additional nodes which have an owner since this was causing issues transient ownerless nodes
+ // which get recreated upon scene tree entry.
+ // For now instead, assume all ownerless nodes are transient and will have to be recreated.
+ if (p_node->get_owner()) {
+ HashMap<StringName, Variant> modified_properties = get_modified_properties_for_node(p_node, true);
+
+ if (p_node->get_parent()->get_owner() != nullptr && p_node->get_parent()->get_owner() != p_edited_scene) {
+ AdditiveNodeEntry new_additive_node_entry;
+ new_additive_node_entry.node = p_node;
+ new_additive_node_entry.parent = p_reimported_root->get_path_to(p_node->get_parent());
+ new_additive_node_entry.owner = p_node->get_owner();
+ new_additive_node_entry.index = p_node->get_index();
+
+ Node2D *node_2d = Object::cast_to<Node2D>(p_node);
+ if (node_2d) {
+ new_additive_node_entry.transform_2d = node_2d->get_relative_transform_to_parent(node_2d->get_parent());
+ }
+ Node3D *node_3d = Object::cast_to<Node3D>(p_node);
+ if (node_3d) {
+ new_additive_node_entry.transform_3d = node_3d->get_relative_transform(node_3d->get_parent());
+ }
- Node2D *node_2d = Object::cast_to<Node2D>(p_node);
- if (node_2d) {
- new_additive_node_entry.transform_2d = node_2d->get_relative_transform_to_parent(node_2d->get_parent());
- }
- Node3D *node_3d = Object::cast_to<Node3D>(p_node);
- if (node_3d) {
- new_additive_node_entry.transform_3d = node_3d->get_relative_transform(node_3d->get_parent());
- }
+ // Gathers the ownership of all ancestor nodes for later use.
+ HashMap<Node *, Node *> ownership_table;
+ for (int i = 0; i < p_node->get_child_count(); i++) {
+ Node *child = p_node->get_child(i);
+ update_ownership_table_for_addition_node_ancestors(child, ownership_table);
+ }
- // Gathers the ownership of all ancestor nodes for later use.
- HashMap<Node *, Node *> ownership_table;
- for (int i = 0; i < p_node->get_child_count(); i++) {
- Node *child = p_node->get_child(i);
- update_ownership_table_for_addition_node_ancestors(child, ownership_table);
- }
+ new_additive_node_entry.ownership_table = ownership_table;
- new_additive_node_entry.ownership_table = ownership_table;
+ p_addition_list.push_back(new_additive_node_entry);
+ }
- p_addition_list.push_back(new_additive_node_entry);
+ if (!modified_properties.is_empty()) {
+ ModificationNodeEntry modification_node_entry;
+ modification_node_entry.property_table = modified_properties;
- return;
+ p_modification_table[p_reimported_root->get_path_to(p_node)] = modification_node_entry;
+ }
+ }
}
for (int i = 0; i < p_node->get_child_count(); i++) {
Node *child = p_node->get_child(i);
- update_diff_data_for_node(p_edited_scene, p_root, child, p_modification_table, p_addition_list);
+ update_reimported_diff_data_for_node(p_edited_scene, p_reimported_root, child, p_modification_table, p_addition_list);
}
}
//
@@ -5541,19 +5673,18 @@ void EditorNode::_add_dropped_files_recursive(const Vector<String> &p_files, Str
}
void EditorNode::_file_access_close_error_notify(const String &p_str) {
- callable_mp_static(&EditorNode::_file_access_close_error_notify_impl).bind(p_str).call_deferred();
+ callable_mp_static(&EditorNode::_file_access_close_error_notify_impl).call_deferred(p_str);
}
void EditorNode::_file_access_close_error_notify_impl(const String &p_str) {
add_io_error(vformat(TTR("Unable to write to file '%s', file in use, locked or lacking permissions."), p_str));
}
-// Since we felt that a bespoke NOTIFICATION might not be desirable, this function
-// provides the hardcoded callbacks to address known bugs which occur on certain
-// nodes during reimport.
-// Ideally, we should probably agree on a standardized method name which could be
-// called from here instead.
-void EditorNode::_notify_scene_updated(Node *p_node) {
+// Recursive function to inform nodes that an array of nodes have had their scene reimported.
+// It will attempt to call a method named '_nodes_scene_reimported' on every node in the
+// tree so that editor scripts which create transient nodes will have the opportunity
+// to recreate them.
+void EditorNode::_notify_nodes_scene_reimported(Node *p_node, Array p_reimported_nodes) {
Skeleton3D *skel_3d = Object::cast_to<Skeleton3D>(p_node);
if (skel_3d) {
skel_3d->reset_bone_poses();
@@ -5564,8 +5695,12 @@ void EditorNode::_notify_scene_updated(Node *p_node) {
}
}
+ if (p_node->has_method("_nodes_scene_reimported")) {
+ p_node->call("_nodes_scene_reimported", p_reimported_nodes);
+ }
+
for (int i = 0; i < p_node->get_child_count(); i++) {
- _notify_scene_updated(p_node->get_child(i));
+ _notify_nodes_scene_reimported(p_node->get_child(i), p_reimported_nodes);
}
}
@@ -5635,6 +5770,7 @@ void EditorNode::find_all_instances_inheriting_path_in_node(Node *p_root, Node *
void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_instance_path) {
int original_edited_scene_idx = editor_data.get_edited_scene();
HashMap<int, List<Node *>> edited_scene_map;
+ Array replaced_nodes;
// Walk through each opened scene to get a global list of all instances which match
// the current reimported scenes.
@@ -5675,12 +5811,16 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins
// Update the version
editor_data.is_scene_changed(current_scene_idx);
+ // Contains modifications in the edited scene which reference nodes inside of any nodes we will be reimporting.
+ HashMap<NodePath, ModificationNodeEntry> edited_scene_global_modification_table;
+ update_node_reference_modification_table_for_node(current_edited_scene, current_edited_scene, edited_scene_map_elem.value, edited_scene_global_modification_table);
+
for (Node *original_node : edited_scene_map_elem.value) {
// Walk the tree for the current node and extract relevant diff data, storing it in the modification table.
// For additional nodes which are part of the current scene, they get added to the addition table.
HashMap<NodePath, ModificationNodeEntry> modification_table;
List<AdditiveNodeEntry> addition_list;
- update_diff_data_for_node(current_edited_scene, original_node, original_node, modification_table, addition_list);
+ update_reimported_diff_data_for_node(current_edited_scene, original_node, original_node, modification_table, addition_list);
// Disconnect all relevant connections, all connections from and persistent connections to.
for (const KeyValue<NodePath, ModificationNodeEntry> &modification_table_entry : modification_table) {
@@ -5762,7 +5902,7 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins
// be properly updated.
for (String path : required_load_paths) {
if (!local_scene_cache.find(path)) {
- current_packed_scene = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REPLACE, &err);
+ current_packed_scene = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REPLACE_DEEP, &err);
local_scene_cache[path] = current_packed_scene;
} else {
current_packed_scene = local_scene_cache[path];
@@ -5780,6 +5920,9 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins
ERR_FAIL_NULL(instantiated_node);
+ // For clear instance state for path recaching.
+ instantiated_node->set_scene_instance_state(Ref<SceneState>());
+
bool original_node_is_displayed_folded = original_node->is_displayed_folded();
bool original_node_scene_instance_load_placeholder = original_node->get_scene_instance_load_placeholder();
@@ -5885,77 +6028,30 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins
NodePath new_current_path = E.key;
Node *modifiable_node = instantiated_node->get_node_or_null(new_current_path);
- if (modifiable_node) {
- // Get properties for this node.
- List<PropertyInfo> pinfo;
- modifiable_node->get_property_list(&pinfo);
-
- // Get names of all valid property names (TODO: make this more efficient).
- List<String> property_names;
- for (const PropertyInfo &E2 : pinfo) {
- if (E2.usage & PROPERTY_USAGE_STORAGE) {
- property_names.push_back(E2.name);
- }
- }
-
- // Restore the modified properties for this node.
- for (const KeyValue<StringName, Variant> &E2 : E.value.property_table) {
- if (property_names.find(E2.key)) {
- modifiable_node->set(E2.key, E2.value);
- }
- }
- // Restore the connections to other nodes.
- for (const ConnectionWithNodePath &E2 : E.value.connections_to) {
- Connection conn = E2.connection;
-
- // Get the node the callable is targeting.
- Node *target_node = cast_to<Node>(conn.callable.get_object());
-
- // If the callable object no longer exists or is marked for deletion,
- // attempt to reaccquire the closest match by using the node path
- // we saved earlier.
- if (!target_node || !target_node->is_queued_for_deletion()) {
- target_node = modifiable_node->get_node_or_null(E2.node_path);
- }
-
- if (target_node) {
- // Reconstruct the callable.
- Callable new_callable = Callable(target_node, conn.callable.get_method());
-
- if (!modifiable_node->is_connected(conn.signal.get_name(), new_callable)) {
- ERR_FAIL_COND(modifiable_node->connect(conn.signal.get_name(), new_callable, conn.flags) != OK);
- }
- }
- }
-
- // Restore the connections from other nodes.
- for (const Connection &E2 : E.value.connections_from) {
- Connection conn = E2;
-
- bool valid = modifiable_node->has_method(conn.callable.get_method()) || Ref<Script>(modifiable_node->get_script()).is_null() || Ref<Script>(modifiable_node->get_script())->has_method(conn.callable.get_method());
- ERR_CONTINUE_MSG(!valid, vformat("Attempt to connect signal '%s.%s' to nonexistent method '%s.%s'.", conn.signal.get_object()->get_class(), conn.signal.get_name(), conn.callable.get_object()->get_class(), conn.callable.get_method()));
-
- // Get the object which the signal is connected from.
- Object *source_object = conn.signal.get_object();
+ update_node_from_node_modification_entry(modifiable_node, E.value);
+ }
+ // Add the newly instantiated node to the edited scene's replaced node list.
+ replaced_nodes.push_back(instantiated_node);
+ }
- if (source_object) {
- ERR_FAIL_COND(source_object->connect(conn.signal.get_name(), Callable(modifiable_node, conn.callable.get_method()), conn.flags) != OK);
- }
- }
+ // Attempt to restore the modified properties and signals for the instantitated node and all its owned children.
+ for (KeyValue<NodePath, ModificationNodeEntry> &E : edited_scene_global_modification_table) {
+ NodePath new_current_path = E.key;
+ Node *modifiable_node = current_edited_scene->get_node_or_null(new_current_path);
- // Re-add the groups.
- for (const Node::GroupInfo &E2 : E.value.groups) {
- modifiable_node->add_to_group(E2.name, E2.persistent);
- }
- }
+ if (modifiable_node) {
+ update_node_from_node_modification_entry(modifiable_node, E.value);
}
}
// Cleanup the history of the changes.
editor_history.cleanup_history();
-
- _notify_scene_updated(current_edited_scene);
}
+
+ // For the whole editor, call the _notify_nodes_scene_reimported with a list of replaced nodes.
+ // To inform anything that depends on them that they should update as appropriate.
+ _notify_nodes_scene_reimported(this, replaced_nodes);
+
edited_scene_map.clear();
}
editor_data.set_edited_scene(original_edited_scene_idx);
@@ -6163,7 +6259,7 @@ static Node *_resource_get_edited_scene() {
}
void EditorNode::_print_handler(void *p_this, const String &p_string, bool p_error, bool p_rich) {
- callable_mp_static(&EditorNode::_print_handler_impl).bind(p_string, p_error, p_rich).call_deferred();
+ callable_mp_static(&EditorNode::_print_handler_impl).call_deferred(p_string, p_error, p_rich);
}
void EditorNode::_print_handler_impl(const String &p_string, bool p_error, bool p_rich) {
@@ -7228,6 +7324,8 @@ EditorNode::EditorNode() {
disk_changed = memnew(ConfirmationDialog);
{
+ disk_changed->set_title(TTR("Files have been modified on disk"));
+
VBoxContainer *vbc = memnew(VBoxContainer);
disk_changed->add_child(vbc);
@@ -7241,9 +7339,9 @@ EditorNode::EditorNode() {
disk_changed->connect("confirmed", callable_mp(this, &EditorNode::_reload_modified_scenes));
disk_changed->connect("confirmed", callable_mp(this, &EditorNode::_reload_project_settings));
- disk_changed->set_ok_button_text(TTR("Reload"));
+ disk_changed->set_ok_button_text(TTR("Discard local changes and reload"));
- disk_changed->add_button(TTR("Resave"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave");
+ disk_changed->add_button(TTR("Keep local changes and overwrite"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave");
disk_changed->connect("custom_action", callable_mp(this, &EditorNode::_resave_scenes));
}
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 6b3359eaee..9643bc2229 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -662,7 +662,7 @@ private:
void _begin_first_scan();
- void _notify_scene_updated(Node *p_node);
+ void _notify_nodes_scene_reimported(Node *p_node, Array p_reimported_nodes);
protected:
friend class FileSystemDock;
@@ -779,7 +779,7 @@ public:
Error load_scene(const String &p_scene, bool p_ignore_broken_deps = false, bool p_set_inherited = false, bool p_clear_errors = true, bool p_force_open_imported = false, bool p_silent_change_tab = false);
Error load_resource(const String &p_resource, bool p_ignore_broken_deps = false);
- HashMap<StringName, Variant> get_modified_properties_for_node(Node *p_node);
+ HashMap<StringName, Variant> get_modified_properties_for_node(Node *p_node, bool p_node_references_only);
struct AdditiveNodeEntry {
Node *node = nullptr;
@@ -806,11 +806,18 @@ public:
};
void update_ownership_table_for_addition_node_ancestors(Node *p_current_node, HashMap<Node *, Node *> &p_ownership_table);
+ void update_node_from_node_modification_entry(Node *p_node, ModificationNodeEntry &p_node_modification);
- void update_diff_data_for_node(
- Node *p_edited_scene,
+ void update_node_reference_modification_table_for_node(
Node *p_root,
Node *p_node,
+ List<Node *> p_excluded_nodes,
+ HashMap<NodePath, ModificationNodeEntry> &p_modification_table);
+
+ void update_reimported_diff_data_for_node(
+ Node *p_edited_scene,
+ Node *p_reimported_root,
+ Node *p_node,
HashMap<NodePath, ModificationNodeEntry> &p_modification_table,
List<AdditiveNodeEntry> &p_addition_list);
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index fe50961b54..103ea3ffc3 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -3218,7 +3218,7 @@ void EditorPropertyResource::_open_editor_pressed() {
Ref<Resource> res = get_edited_property_value();
if (res.is_valid()) {
// May clear the editor so do it deferred.
- callable_mp(EditorNode::get_singleton(), &EditorNode::edit_item).bind(res.ptr(), this).call_deferred();
+ callable_mp(EditorNode::get_singleton(), &EditorNode::edit_item).call_deferred(res.ptr(), this);
}
}
diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp
index ca1070d7f3..b4fc47323a 100644
--- a/editor/editor_properties_array_dict.cpp
+++ b/editor/editor_properties_array_dict.cpp
@@ -960,6 +960,7 @@ void EditorPropertyDictionary::update_property() {
memdelete(container);
button_add_item = nullptr;
container = nullptr;
+ add_panel = nullptr;
slots.clear();
}
return;
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 5f311ae445..2e88540fc4 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -3279,7 +3279,7 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, const Vect
if (p_paths.size() == 1) {
const String &fpath = p_paths[0];
- bool added_separator = false;
+ [[maybe_unused]] bool added_separator = false;
if (favorites_list.has(fpath)) {
TreeItem *favorites_item = tree->get_root()->get_first_child();
diff --git a/editor/gui/editor_toaster.cpp b/editor/gui/editor_toaster.cpp
index 7aa5335b77..df6c494392 100644
--- a/editor/gui/editor_toaster.cpp
+++ b/editor/gui/editor_toaster.cpp
@@ -149,7 +149,7 @@ void EditorToaster::_notification(int p_what) {
void EditorToaster::_error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, bool p_editor_notify, ErrorHandlerType p_type) {
// This may be called from a thread. Since we will deal with non-thread-safe elements,
// we have to put it in the queue for safety.
- callable_mp_static(&EditorToaster::_error_handler_impl).bind(String::utf8(p_file), p_line, String::utf8(p_error), String::utf8(p_errorexp), p_editor_notify, p_type).call_deferred();
+ callable_mp_static(&EditorToaster::_error_handler_impl).call_deferred(String::utf8(p_file), p_line, String::utf8(p_error), String::utf8(p_errorexp), p_editor_notify, p_type);
}
void EditorToaster::_error_handler_impl(const String &p_file, int p_line, const String &p_error, const String &p_errorexp, bool p_editor_notify, int p_type) {
diff --git a/editor/import/resource_importer_shader_file.cpp b/editor/import/resource_importer_shader_file.cpp
index bde2e3d0bf..6b20a8c9d5 100644
--- a/editor/import/resource_importer_shader_file.cpp
+++ b/editor/import/resource_importer_shader_file.cpp
@@ -106,7 +106,7 @@ Error ResourceImporterShaderFile::import(const String &p_source_file, const Stri
if (err != OK) {
if (!ShaderFileEditor::singleton->is_visible_in_tree()) {
- callable_mp_static(&EditorNode::add_io_error).bind(vformat(TTR("Error importing GLSL shader file: '%s'. Open the file in the filesystem dock in order to see the reason."), p_source_file)).call_deferred();
+ callable_mp_static(&EditorNode::add_io_error).call_deferred(vformat(TTR("Error importing GLSL shader file: '%s'. Open the file in the filesystem dock in order to see the reason."), p_source_file));
}
}
diff --git a/editor/input_event_configuration_dialog.cpp b/editor/input_event_configuration_dialog.cpp
index 865729c7c3..2ecce2f739 100644
--- a/editor/input_event_configuration_dialog.cpp
+++ b/editor/input_event_configuration_dialog.cpp
@@ -446,6 +446,11 @@ void InputEventConfigurationDialog::_key_location_selected(int p_location) {
_set_event(k, original_event);
}
+void InputEventConfigurationDialog::_input_list_item_activated() {
+ TreeItem *selected = input_list_tree->get_selected();
+ selected->set_collapsed(!selected->is_collapsed());
+}
+
void InputEventConfigurationDialog::_input_list_item_selected() {
TreeItem *selected = input_list_tree->get_selected();
@@ -670,6 +675,7 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() {
input_list_tree = memnew(Tree);
input_list_tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
input_list_tree->set_custom_minimum_size(Size2(0, 100 * EDSCALE)); // Min height for tree
+ input_list_tree->connect("item_activated", callable_mp(this, &InputEventConfigurationDialog::_input_list_item_activated));
input_list_tree->connect("item_selected", callable_mp(this, &InputEventConfigurationDialog::_input_list_item_selected));
input_list_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
manual_vbox->add_child(input_list_tree);
diff --git a/editor/input_event_configuration_dialog.h b/editor/input_event_configuration_dialog.h
index 1d2cc8ba36..b27f25a5b7 100644
--- a/editor/input_event_configuration_dialog.h
+++ b/editor/input_event_configuration_dialog.h
@@ -107,6 +107,7 @@ private:
void _search_term_updated(const String &p_term);
void _update_input_list();
+ void _input_list_item_activated();
void _input_list_item_selected();
void _mod_toggled(bool p_checked, int p_index);
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index 1366a38bec..62369cc2c1 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -1711,7 +1711,7 @@ void AnimationPlayerEditor::_prepare_onion_layers_2_step_prepare(int p_step_offs
OS::get_singleton()->get_main_loop()->process(0);
// This is the key: process the frame and let all callbacks/updates/notifications happen
// so everything (transforms, skeletons, etc.) is up-to-date visually.
- callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_2_step_capture).bind(p_step_offset, p_capture_idx).call_deferred();
+ callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_2_step_capture).call_deferred(p_step_offset, p_capture_idx);
return;
} else {
next_capture_idx++;
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index ee96de8f23..8b44d6b486 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -5638,7 +5638,7 @@ CanvasItemEditor::CanvasItemEditor() {
clear(); // Make sure values are initialized.
// Update the menus' checkboxes.
- callable_mp(this, &CanvasItemEditor::set_state).bind(get_state()).call_deferred();
+ callable_mp(this, &CanvasItemEditor::set_state).call_deferred(get_state());
}
CanvasItemEditor *CanvasItemEditor::singleton = nullptr;
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index d211bd8588..69b66cd7b2 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -739,9 +739,21 @@ void Node3DEditorViewport::_select_clicked(bool p_allow_locked) {
return;
}
+ Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
+
+ // Prevent selection of nodes not owned by the edited scene.
+ while (node && node != edited_scene->get_parent()) {
+ Node *node_owner = node->get_owner();
+ if (node_owner == edited_scene || node == edited_scene || (node_owner != nullptr && edited_scene->is_editable_instance(node_owner))) {
+ break;
+ }
+ node = node->get_parent();
+ selected = Object::cast_to<Node3D>(node);
+ }
+
if (!p_allow_locked) {
// Replace the node by the group if grouped
- while (node && node != EditorNode::get_singleton()->get_edited_scene()->get_parent()) {
+ while (node && node != edited_scene->get_parent()) {
Node3D *selected_tmp = Object::cast_to<Node3D>(node);
if (selected_tmp && node->has_meta("_edit_group_")) {
selected = selected_tmp;
@@ -1044,25 +1056,34 @@ void Node3DEditorViewport::_select_region() {
found_nodes.insert(sp);
- Node *item = Object::cast_to<Node>(sp);
- if (item != edited_scene) {
- item = edited_scene->get_deepest_editable_node(item);
+ Node *node = Object::cast_to<Node>(sp);
+ if (node != edited_scene) {
+ node = edited_scene->get_deepest_editable_node(node);
+ }
+
+ // Prevent selection of nodes not owned by the edited scene.
+ while (node && node != edited_scene->get_parent()) {
+ Node *node_owner = node->get_owner();
+ if (node_owner == edited_scene || node == edited_scene || (node_owner != nullptr && edited_scene->is_editable_instance(node_owner))) {
+ break;
+ }
+ node = node->get_parent();
}
// Replace the node by the group if grouped
- if (item->is_class("Node3D")) {
- Node3D *sel = Object::cast_to<Node3D>(item);
- while (item && item != EditorNode::get_singleton()->get_edited_scene()->get_parent()) {
- Node3D *selected_tmp = Object::cast_to<Node3D>(item);
- if (selected_tmp && item->has_meta("_edit_group_")) {
+ if (node->is_class("Node3D")) {
+ Node3D *sel = Object::cast_to<Node3D>(node);
+ while (node && node != EditorNode::get_singleton()->get_edited_scene()->get_parent()) {
+ Node3D *selected_tmp = Object::cast_to<Node3D>(node);
+ if (selected_tmp && node->has_meta("_edit_group_")) {
sel = selected_tmp;
}
- item = item->get_parent();
+ node = node->get_parent();
}
- item = sel;
+ node = sel;
}
- if (_is_node_locked(item)) {
+ if (_is_node_locked(node)) {
continue;
}
@@ -1074,7 +1095,7 @@ void Node3DEditorViewport::_select_region() {
}
if (seg->intersect_frustum(camera, frustum)) {
- selected.push_back(item);
+ selected.push_back(node);
}
}
}
@@ -1531,23 +1552,35 @@ bool Node3DEditorViewport ::_is_node_locked(const Node *p_node) {
}
void Node3DEditorViewport::_list_select(Ref<InputEventMouseButton> b) {
- _find_items_at_pos(b->get_position(), selection_results, spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT);
+ Vector<_RayResult> potential_selection_results;
+ _find_items_at_pos(b->get_position(), potential_selection_results, spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT);
- Node *scene = EditorNode::get_singleton()->get_edited_scene();
+ Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
- for (int i = 0; i < selection_results.size(); i++) {
- Node3D *item = selection_results[i].item;
- if (item != scene && item->get_owner() != scene && item != scene->get_deepest_editable_node(item)) {
- //invalid result
- selection_results.remove_at(i);
- i--;
+ // Filter to a list of nodes which include either the edited scene or nodes directly owned by the edited scene.
+ // If a node has an invalid owner, recursively check their parents until a valid node is found.
+ for (int i = 0; i < potential_selection_results.size(); i++) {
+ Node3D *node = potential_selection_results[i].item;
+ while (true) {
+ if (node == nullptr || node == edited_scene->get_parent()) {
+ break;
+ } else {
+ Node *node_owner = node->get_owner();
+ if (node == edited_scene || node_owner == edited_scene || (node_owner != nullptr && edited_scene->is_editable_instance(node_owner))) {
+ if (selection_results.has(node)) {
+ selection_results.append(node);
+ }
+ break;
+ }
+ }
+ node = Object::cast_to<Node3D>(node->get_parent());
}
}
clicked_wants_append = b->is_shift_pressed();
if (selection_results.size() == 1) {
- clicked = selection_results[0].item->get_instance_id();
+ clicked = selection_results[0]->get_instance_id();
selection_results.clear();
if (clicked.is_valid()) {
@@ -1558,7 +1591,7 @@ void Node3DEditorViewport::_list_select(Ref<InputEventMouseButton> b) {
StringName root_name = root_path.get_name(root_path.get_name_count() - 1);
for (int i = 0; i < selection_results.size(); i++) {
- Node3D *spat = selection_results[i].item;
+ Node3D *spat = selection_results[i];
Ref<Texture2D> icon = EditorNode::get_singleton()->get_object_icon(spat, "Node");
@@ -3754,7 +3787,7 @@ void Node3DEditorViewport::_selection_result_pressed(int p_result) {
return;
}
- clicked = selection_results_menu[p_result].item->get_instance_id();
+ clicked = selection_results_menu[p_result]->get_instance_id();
if (clicked.is_valid()) {
_select_clicked(spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT);
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index ebdf951773..859d075732 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -298,8 +298,8 @@ private:
ObjectID clicked;
ObjectID material_target;
- Vector<_RayResult> selection_results;
- Vector<_RayResult> selection_results_menu;
+ Vector<Node3D *> selection_results;
+ Vector<Node3D *> selection_results_menu;
bool clicked_wants_append = false;
bool selection_in_progress = false;
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 22dbb6e9f2..0a0909ec9f 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -4249,12 +4249,18 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) {
disk_changed = memnew(ConfirmationDialog);
{
+ disk_changed->set_title(TTR("Files have been modified on disk"));
+
VBoxContainer *vbc = memnew(VBoxContainer);
disk_changed->add_child(vbc);
- Label *dl = memnew(Label);
- dl->set_text(TTR("The following files are newer on disk.\nWhat action should be taken?:"));
- vbc->add_child(dl);
+ Label *files_are_newer_label = memnew(Label);
+ files_are_newer_label->set_text(TTR("The following files are newer on disk."));
+ vbc->add_child(files_are_newer_label);
+
+ Label *what_action_label = memnew(Label);
+ what_action_label->set_text(TTR("What action should be taken?:"));
+ vbc->add_child(what_action_label);
disk_changed_list = memnew(Tree);
vbc->add_child(disk_changed_list);
@@ -4262,9 +4268,9 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) {
disk_changed_list->set_v_size_flags(SIZE_EXPAND_FILL);
disk_changed->connect("confirmed", callable_mp(this, &ScriptEditor::reload_scripts).bind(false));
- disk_changed->set_ok_button_text(TTR("Reload"));
+ disk_changed->set_ok_button_text(TTR("Discard local changes and reload"));
- disk_changed->add_button(TTR("Resave"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave");
+ disk_changed->add_button(TTR("Keep local changes and overwrite"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave");
disk_changed->connect("custom_action", callable_mp(this, &ScriptEditor::_resave_scripts));
}
diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp
index 56f8e1173f..c14336418c 100644
--- a/editor/plugins/sprite_frames_editor_plugin.cpp
+++ b/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -2181,6 +2181,7 @@ SpriteFramesEditor::SpriteFramesEditor() {
split_sheet_h->set_min(1);
split_sheet_h->set_max(128);
split_sheet_h->set_step(1);
+ split_sheet_h->set_select_all_on_focus(true);
split_sheet_h_hb->add_child(split_sheet_h);
split_sheet_h->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_FRAME_COUNT));
split_sheet_settings_vb->add_child(split_sheet_h_hb);
@@ -2197,6 +2198,7 @@ SpriteFramesEditor::SpriteFramesEditor() {
split_sheet_v->set_min(1);
split_sheet_v->set_max(128);
split_sheet_v->set_step(1);
+ split_sheet_v->set_select_all_on_focus(true);
split_sheet_v_hb->add_child(split_sheet_v);
split_sheet_v->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_FRAME_COUNT));
split_sheet_settings_vb->add_child(split_sheet_v_hb);
@@ -2216,6 +2218,7 @@ SpriteFramesEditor::SpriteFramesEditor() {
split_sheet_size_x->set_min(1);
split_sheet_size_x->set_step(1);
split_sheet_size_x->set_suffix("px");
+ split_sheet_size_x->set_select_all_on_focus(true);
split_sheet_size_x->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_SIZE));
split_sheet_size_vb->add_child(split_sheet_size_x);
split_sheet_size_y = memnew(SpinBox);
@@ -2223,6 +2226,7 @@ SpriteFramesEditor::SpriteFramesEditor() {
split_sheet_size_y->set_min(1);
split_sheet_size_y->set_step(1);
split_sheet_size_y->set_suffix("px");
+ split_sheet_size_y->set_select_all_on_focus(true);
split_sheet_size_y->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_SIZE));
split_sheet_size_vb->add_child(split_sheet_size_y);
split_sheet_size_hb->add_child(split_sheet_size_vb);
@@ -2242,12 +2246,14 @@ SpriteFramesEditor::SpriteFramesEditor() {
split_sheet_sep_x->set_min(0);
split_sheet_sep_x->set_step(1);
split_sheet_sep_x->set_suffix("px");
+ split_sheet_sep_x->set_select_all_on_focus(true);
split_sheet_sep_x->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_USE_CURRENT));
split_sheet_sep_vb->add_child(split_sheet_sep_x);
split_sheet_sep_y = memnew(SpinBox);
split_sheet_sep_y->set_min(0);
split_sheet_sep_y->set_step(1);
split_sheet_sep_y->set_suffix("px");
+ split_sheet_sep_y->set_select_all_on_focus(true);
split_sheet_sep_y->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_USE_CURRENT));
split_sheet_sep_vb->add_child(split_sheet_sep_y);
split_sheet_sep_hb->add_child(split_sheet_sep_vb);
@@ -2267,12 +2273,14 @@ SpriteFramesEditor::SpriteFramesEditor() {
split_sheet_offset_x->set_min(0);
split_sheet_offset_x->set_step(1);
split_sheet_offset_x->set_suffix("px");
+ split_sheet_offset_x->set_select_all_on_focus(true);
split_sheet_offset_x->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_USE_CURRENT));
split_sheet_offset_vb->add_child(split_sheet_offset_x);
split_sheet_offset_y = memnew(SpinBox);
split_sheet_offset_y->set_min(0);
split_sheet_offset_y->set_step(1);
split_sheet_offset_y->set_suffix("px");
+ split_sheet_offset_y->set_select_all_on_focus(true);
split_sheet_offset_y->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_sheet_spin_changed).bind(PARAM_USE_CURRENT));
split_sheet_offset_vb->add_child(split_sheet_offset_y);
split_sheet_offset_hb->add_child(split_sheet_offset_vb);
diff --git a/editor/plugins/text_shader_editor.cpp b/editor/plugins/text_shader_editor.cpp
index bb74bf8d1f..d6cef3ccb9 100644
--- a/editor/plugins/text_shader_editor.cpp
+++ b/editor/plugins/text_shader_editor.cpp
@@ -30,12 +30,12 @@
#include "text_shader_editor.h"
+#include "core/config/project_settings.h"
#include "core/version_generated.gen.h"
+#include "editor/editor_file_system.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
-#include "editor/filesystem_dock.h"
-#include "editor/project_settings_editor.h"
#include "editor/themes/editor_scale.h"
#include "editor/themes/editor_theme_manager.h"
#include "scene/gui/split_container.h"
@@ -735,6 +735,13 @@ void TextShaderEditor::_menu_option(int p_option) {
}
}
+void TextShaderEditor::_prepare_edit_menu() {
+ const CodeEdit *tx = code_editor->get_text_editor();
+ PopupMenu *popup = edit_menu->get_popup();
+ popup->set_item_disabled(popup->get_item_index(EDIT_UNDO), !tx->has_undo());
+ popup->set_item_disabled(popup->get_item_index(EDIT_REDO), !tx->has_redo());
+}
+
void TextShaderEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
@@ -1088,6 +1095,9 @@ void TextShaderEditor::_make_context_menu(bool p_selection, Vector2 p_position)
context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT);
context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE);
+ context_menu->set_item_disabled(context_menu->get_item_index(EDIT_UNDO), !code_editor->get_text_editor()->has_undo());
+ context_menu->set_item_disabled(context_menu->get_item_index(EDIT_REDO), !code_editor->get_text_editor()->has_redo());
+
context_menu->set_position(get_screen_position() + p_position);
context_menu->reset_size();
context_menu->popup();
@@ -1128,6 +1138,7 @@ TextShaderEditor::TextShaderEditor() {
edit_menu->set_shortcut_context(this);
edit_menu->set_text(TTR("Edit"));
edit_menu->set_switch_on_hover(true);
+ edit_menu->connect("about_to_popup", callable_mp(this, &TextShaderEditor::_prepare_edit_menu));
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO);
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO);
diff --git a/editor/plugins/text_shader_editor.h b/editor/plugins/text_shader_editor.h
index be16148744..6d2ac743b8 100644
--- a/editor/plugins/text_shader_editor.h
+++ b/editor/plugins/text_shader_editor.h
@@ -34,7 +34,6 @@
#include "editor/code_editor.h"
#include "scene/gui/margin_container.h"
#include "scene/gui/menu_button.h"
-#include "scene/gui/panel_container.h"
#include "scene/gui/rich_text_label.h"
#include "servers/rendering/shader_warnings.h"
@@ -153,6 +152,7 @@ class TextShaderEditor : public MarginContainer {
bool compilation_success = true;
void _menu_option(int p_option);
+ void _prepare_edit_menu();
mutable Ref<Shader> shader;
mutable Ref<ShaderInclude> shader_inc;
diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp
index 2b86268414..52b58b74a3 100644
--- a/editor/plugins/tiles/tile_atlas_view.cpp
+++ b/editor/plugins/tiles/tile_atlas_view.cpp
@@ -59,6 +59,9 @@ void TileAtlasView::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<In
}
Size2i TileAtlasView::_compute_base_tiles_control_size() {
+ if (tile_set_atlas_source.is_null()) {
+ return Size2i();
+ }
// Update the texture.
Vector2i size;
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
@@ -69,6 +72,9 @@ Size2i TileAtlasView::_compute_base_tiles_control_size() {
}
Size2i TileAtlasView::_compute_alternative_tiles_control_size() {
+ if (tile_set_atlas_source.is_null()) {
+ return Size2i();
+ }
Vector2i size;
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
@@ -89,6 +95,9 @@ Size2i TileAtlasView::_compute_alternative_tiles_control_size() {
}
void TileAtlasView::_update_zoom_and_panning(bool p_zoom_on_mouse_pos) {
+ if (tile_set_atlas_source.is_null()) {
+ return;
+ }
float zoom = zoom_widget->get_zoom();
// Compute the minimum sizes.
@@ -153,6 +162,9 @@ void TileAtlasView::_center_view() {
}
void TileAtlasView::_base_tiles_root_control_gui_input(const Ref<InputEvent> &p_event) {
+ if (tile_set_atlas_source.is_null()) {
+ return;
+ }
base_tiles_root_control->set_tooltip_text("");
Ref<InputEventMouseMotion> mm = p_event;
@@ -169,6 +181,9 @@ void TileAtlasView::_base_tiles_root_control_gui_input(const Ref<InputEvent> &p_
}
void TileAtlasView::_draw_base_tiles() {
+ if (tile_set.is_null() || tile_set_atlas_source.is_null()) {
+ return;
+ }
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (texture.is_valid()) {
Vector2i margins = tile_set_atlas_source->get_margins();
@@ -314,6 +329,9 @@ void TileAtlasView::_clear_material_canvas_items() {
}
void TileAtlasView::_draw_base_tiles_texture_grid() {
+ if (tile_set_atlas_source.is_null()) {
+ return;
+ }
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (texture.is_valid()) {
Vector2i margins = tile_set_atlas_source->get_margins();
@@ -344,6 +362,9 @@ void TileAtlasView::_draw_base_tiles_texture_grid() {
}
void TileAtlasView::_draw_base_tiles_shape_grid() {
+ if (tile_set.is_null() || tile_set_atlas_source.is_null()) {
+ return;
+ }
// Draw the shapes.
Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color");
Vector2i tile_shape_size = tile_set->get_tile_size();
@@ -382,6 +403,9 @@ void TileAtlasView::_alternative_tiles_root_control_gui_input(const Ref<InputEve
}
void TileAtlasView::_draw_alternatives() {
+ if (tile_set.is_null() || tile_set_atlas_source.is_null()) {
+ return;
+ }
// Draw the alternative tiles.
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (texture.is_valid()) {
@@ -432,12 +456,12 @@ void TileAtlasView::_draw_background_right() {
}
void TileAtlasView::set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id) {
- tile_set = p_tile_set;
- tile_set_atlas_source = p_tile_set_atlas_source;
+ tile_set = Ref<TileSet>(p_tile_set);
+ tile_set_atlas_source = Ref<TileSetAtlasSource>(p_tile_set_atlas_source);
_clear_material_canvas_items();
- if (!tile_set) {
+ if (tile_set.is_null()) {
return;
}
@@ -485,6 +509,10 @@ void TileAtlasView::set_padding(Side p_side, int p_padding) {
}
Vector2i TileAtlasView::get_atlas_tile_coords_at_pos(const Vector2 p_pos, bool p_clamp) const {
+ if (tile_set_atlas_source.is_null()) {
+ return Vector2i();
+ }
+
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (!texture.is_valid()) {
return TileSetSource::INVALID_ATLAS_COORDS;
@@ -508,6 +536,10 @@ Vector2i TileAtlasView::get_atlas_tile_coords_at_pos(const Vector2 p_pos, bool p
}
void TileAtlasView::_update_alternative_tiles_rect_cache() {
+ if (tile_set_atlas_source.is_null()) {
+ return;
+ }
+
alternative_tiles_rect_cache.clear();
Rect2i current;
diff --git a/editor/plugins/tiles/tile_atlas_view.h b/editor/plugins/tiles/tile_atlas_view.h
index e5b4863b05..8fcf942056 100644
--- a/editor/plugins/tiles/tile_atlas_view.h
+++ b/editor/plugins/tiles/tile_atlas_view.h
@@ -45,8 +45,8 @@ class TileAtlasView : public Control {
GDCLASS(TileAtlasView, Control);
private:
- TileSet *tile_set = nullptr;
- TileSetAtlasSource *tile_set_atlas_source = nullptr;
+ Ref<TileSet> tile_set;
+ Ref<TileSetAtlasSource> tile_set_atlas_source;
int source_id = TileSet::INVALID_SOURCE;
enum DragType {
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index 1daba1f665..03070bc6b5 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -2938,6 +2938,18 @@ bool EditorInspectorPluginTileData::parse_property(Object *p_object, const Varia
add_property_editor(p_path, ep);
return true;
}
+ } else if (p_path.begins_with("custom_data_") && p_path.trim_prefix("custom_data_").is_valid_int()) {
+ // Custom data layers.
+ int layer_index = components[0].trim_prefix("custom_data_").to_int();
+ ERR_FAIL_COND_V(layer_index < 0, false);
+ EditorProperty *ep = EditorInspectorDefaultPlugin::get_editor_for_property(p_object, p_type, p_path, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT);
+ const TileSetAtlasSourceEditor::AtlasTileProxyObject *proxy_obj = Object::cast_to<TileSetAtlasSourceEditor::AtlasTileProxyObject>(p_object);
+ const TileSetAtlasSource *atlas_source = *proxy_obj->get_edited_tile_set_atlas_source();
+ ERR_FAIL_NULL_V(atlas_source, false);
+ const TileSet *tile_set = atlas_source->get_tile_set();
+ ERR_FAIL_NULL_V(tile_set, false);
+ add_property_editor(p_path, ep, false, tile_set->get_custom_data_layer_name(layer_index));
+ return true;
}
return false;
}
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index f90fa7603f..9209c26876 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -3333,9 +3333,9 @@ void SceneTreeDock::_files_dropped(const Vector<String> &p_files, NodePath p_to,
// Either instantiate scenes or create AudioStreamPlayers.
int to_pos = -1;
_normalize_drop(node, to_pos, p_type);
- if (res_type == "PackedScene") {
+ if (ClassDB::is_parent_class(res_type, "PackedScene")) {
_perform_instantiate_scenes(p_files, node, to_pos);
- } else {
+ } else if (ClassDB::is_parent_class(res_type, "AudioStream")) {
_perform_create_audio_stream_players(p_files, node, to_pos);
}
}
diff --git a/main/main.cpp b/main/main.cpp
index 20ffdf5ae1..2bd421e5af 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -2841,8 +2841,10 @@ Error Main::setup2() {
OS::get_singleton()->benchmark_end_measure("Startup", "Servers");
+#ifndef WEB_ENABLED
// Add a blank line for readability.
Engine::get_singleton()->print_header("");
+#endif // WEB_ENABLED
register_core_singletons();
diff --git a/methods.py b/methods.py
index 7600e6d445..f3798d121a 100644
--- a/methods.py
+++ b/methods.py
@@ -531,6 +531,7 @@ def no_verbose(env):
link_shared_library_message = "{}Linking Shared Library {}$TARGET{} ...{}".format(*colors)
java_library_message = "{}Creating Java Archive {}$TARGET{} ...{}".format(*colors)
compiled_resource_message = "{}Creating Compiled Resource {}$TARGET{} ...{}".format(*colors)
+ zip_archive_message = "{}Archiving {}$TARGET{} ...{}".format(*colors)
generated_file_message = "{}Generating {}$TARGET{} ...{}".format(*colors)
env["CXXCOMSTR"] = compile_source_message
@@ -544,6 +545,7 @@ def no_verbose(env):
env["JARCOMSTR"] = java_library_message
env["JAVACCOMSTR"] = java_compile_source_message
env["RCCOMSTR"] = compiled_resource_message
+ env["ZIPCOMSTR"] = zip_archive_message
env["GENCOMSTR"] = generated_file_message
diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected
index 6761512c15..b27f80ee29 100644
--- a/misc/extension_api_validation/4.2-stable.expected
+++ b/misc/extension_api_validation/4.2-stable.expected
@@ -357,3 +357,10 @@ Validate extension JSON: Error: Field 'classes/RenderingServer/methods/canvas_it
Validate extension JSON: Error: Field 'classes/RenderingServer/methods/canvas_item_add_rect/arguments': size changed value in new API, from 3 to 4.
Optional arguments added. Compatibility methods registered.
+
+
+GH-92322
+--------
+Validate extension JSON: Error: Field 'classes/EditorInspectorPlugin/methods/add_property_editor/arguments': size changed value in new API, from 3 to 4.
+
+Optional arguments added. Compatibility methods registered.
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index a2680c932f..7fe96146da 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -4061,10 +4061,23 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
mark_lambda_use_self();
return; // No need to capture.
}
- // If the identifier is local, check if it's any kind of capture by comparing their source function.
- // Only capture locals and enum values. Constants are still accessible from the lambda using the script reference. If not, this method is done.
- if (p_identifier->source == GDScriptParser::IdentifierNode::UNDEFINED_SOURCE || p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_CONSTANT) {
- return;
+
+ switch (p_identifier->source) {
+ case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER:
+ case GDScriptParser::IdentifierNode::LOCAL_VARIABLE:
+ case GDScriptParser::IdentifierNode::LOCAL_ITERATOR:
+ case GDScriptParser::IdentifierNode::LOCAL_BIND:
+ break; // Need to capture.
+ case GDScriptParser::IdentifierNode::UNDEFINED_SOURCE: // A global.
+ case GDScriptParser::IdentifierNode::LOCAL_CONSTANT:
+ case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:
+ case GDScriptParser::IdentifierNode::MEMBER_CONSTANT:
+ case GDScriptParser::IdentifierNode::MEMBER_FUNCTION:
+ case GDScriptParser::IdentifierNode::MEMBER_SIGNAL:
+ case GDScriptParser::IdentifierNode::MEMBER_CLASS:
+ case GDScriptParser::IdentifierNode::INHERITED_VARIABLE:
+ case GDScriptParser::IdentifierNode::STATIC_VARIABLE:
+ return; // No need to capture.
}
GDScriptParser::FunctionNode *function_test = current_lambda->function;
diff --git a/modules/gdscript/tests/scripts/runtime/features/lambda_captures.gd b/modules/gdscript/tests/scripts/runtime/features/lambda_captures.gd
new file mode 100644
index 0000000000..bbdf745540
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/lambda_captures.gd
@@ -0,0 +1,26 @@
+# GH-92217
+# TODO: Add more tests.
+
+static var static_var: int:
+ set(value):
+ prints("set static_var", value)
+ get:
+ print("get static_var")
+ return 0
+
+var member_var: int:
+ set(value):
+ prints("set member_var", value)
+ get:
+ print("get member_var")
+ return 0
+
+func test():
+ var lambda := func ():
+ var _tmp := static_var
+ _tmp = member_var
+
+ static_var = 1
+ member_var = 1
+
+ lambda.call()
diff --git a/modules/gdscript/tests/scripts/runtime/features/lambda_captures.out b/modules/gdscript/tests/scripts/runtime/features/lambda_captures.out
new file mode 100644
index 0000000000..0bdf74a43f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/lambda_captures.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+get static_var
+get member_var
+set static_var 1
+set member_var 1
diff --git a/modules/gltf/doc_classes/GLTFAccessor.xml b/modules/gltf/doc_classes/GLTFAccessor.xml
index ba7323b7cd..54762faed7 100644
--- a/modules/gltf/doc_classes/GLTFAccessor.xml
+++ b/modules/gltf/doc_classes/GLTFAccessor.xml
@@ -12,34 +12,50 @@
<link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link>
</tutorials>
<members>
+ <member name="accessor_type" type="int" setter="set_accessor_type" getter="get_accessor_type" default="0">
+ The GLTF accessor type as an enum. Possible values are 0 for "SCALAR", 1 for "VEC2", 2 for "VEC3", 3 for "VEC4", 4 for "MAT2", 5 for "MAT3", and 6 for "MAT4".
+ </member>
<member name="buffer_view" type="int" setter="set_buffer_view" getter="get_buffer_view" default="-1">
The index of the buffer view this accessor is referencing. If [code]-1[/code], this accessor is not referencing any buffer view.
</member>
<member name="byte_offset" type="int" setter="set_byte_offset" getter="get_byte_offset" default="0">
+ The offset relative to the start of the buffer view in bytes.
</member>
<member name="component_type" type="int" setter="set_component_type" getter="get_component_type" default="0">
+ The GLTF component type as an enum. Possible values are 5120 for "BYTE", 5121 for "UNSIGNED_BYTE", 5122 for "SHORT", 5123 for "UNSIGNED_SHORT", 5125 for "UNSIGNED_INT", and 5126 for "FLOAT". A value of 5125 or "UNSIGNED_INT" must not be used for any accessor that is not referenced by mesh.primitive.indices.
</member>
<member name="count" type="int" setter="set_count" getter="get_count" default="0">
+ The number of elements referenced by this accessor.
</member>
<member name="max" type="PackedFloat64Array" setter="set_max" getter="get_max" default="PackedFloat64Array()">
+ Maximum value of each component in this accessor.
</member>
<member name="min" type="PackedFloat64Array" setter="set_min" getter="get_min" default="PackedFloat64Array()">
+ Minimum value of each component in this accessor.
</member>
<member name="normalized" type="bool" setter="set_normalized" getter="get_normalized" default="false">
+ Specifies whether integer data values are normalized before usage.
</member>
<member name="sparse_count" type="int" setter="set_sparse_count" getter="get_sparse_count" default="0">
+ Number of deviating accessor values stored in the sparse array.
</member>
<member name="sparse_indices_buffer_view" type="int" setter="set_sparse_indices_buffer_view" getter="get_sparse_indices_buffer_view" default="0">
+ The index of the buffer view with sparse indices. The referenced buffer view MUST NOT have its target or byteStride properties defined. The buffer view and the optional byteOffset MUST be aligned to the componentType byte length.
</member>
<member name="sparse_indices_byte_offset" type="int" setter="set_sparse_indices_byte_offset" getter="get_sparse_indices_byte_offset" default="0">
+ The offset relative to the start of the buffer view in bytes.
</member>
<member name="sparse_indices_component_type" type="int" setter="set_sparse_indices_component_type" getter="get_sparse_indices_component_type" default="0">
+ The indices component data type as an enum. Possible values are 5121 for "UNSIGNED_BYTE", 5123 for "UNSIGNED_SHORT", and 5125 for "UNSIGNED_INT".
</member>
<member name="sparse_values_buffer_view" type="int" setter="set_sparse_values_buffer_view" getter="get_sparse_values_buffer_view" default="0">
+ The index of the bufferView with sparse values. The referenced buffer view MUST NOT have its target or byteStride properties defined.
</member>
<member name="sparse_values_byte_offset" type="int" setter="set_sparse_values_byte_offset" getter="get_sparse_values_byte_offset" default="0">
+ The offset relative to the start of the bufferView in bytes.
</member>
- <member name="type" type="int" setter="set_type" getter="get_type" default="0">
+ <member name="type" type="int" setter="set_type" getter="get_type" default="0" deprecated="Use [member accessor_type] instead.">
+ The GLTF accessor type as an enum. Use [member accessor_type] instead.
</member>
</members>
</class>
diff --git a/modules/gltf/gltf_defines.h b/modules/gltf/gltf_defines.h
index d044105b42..c1918e5908 100644
--- a/modules/gltf/gltf_defines.h
+++ b/modules/gltf/gltf_defines.h
@@ -66,14 +66,4 @@ using GLTFSkinIndex = int;
using GLTFTextureIndex = int;
using GLTFTextureSamplerIndex = int;
-enum GLTFType {
- TYPE_SCALAR,
- TYPE_VEC2,
- TYPE_VEC3,
- TYPE_VEC4,
- TYPE_MAT2,
- TYPE_MAT3,
- TYPE_MAT4,
-};
-
#endif // GLTF_DEFINES_H
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 5dcf67b768..3a1a2f4e60 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -886,7 +886,7 @@ Error GLTFDocument::_encode_accessors(Ref<GLTFState> p_state) {
Ref<GLTFAccessor> accessor = p_state->accessors[i];
d["componentType"] = accessor->component_type;
d["count"] = accessor->count;
- d["type"] = _get_accessor_type_name(accessor->type);
+ d["type"] = _get_accessor_type_name(accessor->accessor_type);
d["normalized"] = accessor->normalized;
d["max"] = accessor->max;
d["min"] = accessor->min;
@@ -934,58 +934,58 @@ Error GLTFDocument::_encode_accessors(Ref<GLTFState> p_state) {
return OK;
}
-String GLTFDocument::_get_accessor_type_name(const GLTFType p_type) {
- if (p_type == GLTFType::TYPE_SCALAR) {
+String GLTFDocument::_get_accessor_type_name(const GLTFAccessorType p_accessor_type) {
+ if (p_accessor_type == GLTFAccessorType::TYPE_SCALAR) {
return "SCALAR";
}
- if (p_type == GLTFType::TYPE_VEC2) {
+ if (p_accessor_type == GLTFAccessorType::TYPE_VEC2) {
return "VEC2";
}
- if (p_type == GLTFType::TYPE_VEC3) {
+ if (p_accessor_type == GLTFAccessorType::TYPE_VEC3) {
return "VEC3";
}
- if (p_type == GLTFType::TYPE_VEC4) {
+ if (p_accessor_type == GLTFAccessorType::TYPE_VEC4) {
return "VEC4";
}
- if (p_type == GLTFType::TYPE_MAT2) {
+ if (p_accessor_type == GLTFAccessorType::TYPE_MAT2) {
return "MAT2";
}
- if (p_type == GLTFType::TYPE_MAT3) {
+ if (p_accessor_type == GLTFAccessorType::TYPE_MAT3) {
return "MAT3";
}
- if (p_type == GLTFType::TYPE_MAT4) {
+ if (p_accessor_type == GLTFAccessorType::TYPE_MAT4) {
return "MAT4";
}
ERR_FAIL_V("SCALAR");
}
-GLTFType GLTFDocument::_get_type_from_str(const String &p_string) {
+GLTFAccessorType GLTFDocument::_get_accessor_type_from_str(const String &p_string) {
if (p_string == "SCALAR") {
- return GLTFType::TYPE_SCALAR;
+ return GLTFAccessorType::TYPE_SCALAR;
}
if (p_string == "VEC2") {
- return GLTFType::TYPE_VEC2;
+ return GLTFAccessorType::TYPE_VEC2;
}
if (p_string == "VEC3") {
- return GLTFType::TYPE_VEC3;
+ return GLTFAccessorType::TYPE_VEC3;
}
if (p_string == "VEC4") {
- return GLTFType::TYPE_VEC4;
+ return GLTFAccessorType::TYPE_VEC4;
}
if (p_string == "MAT2") {
- return GLTFType::TYPE_MAT2;
+ return GLTFAccessorType::TYPE_MAT2;
}
if (p_string == "MAT3") {
- return GLTFType::TYPE_MAT3;
+ return GLTFAccessorType::TYPE_MAT3;
}
if (p_string == "MAT4") {
- return GLTFType::TYPE_MAT4;
+ return GLTFAccessorType::TYPE_MAT4;
}
- ERR_FAIL_V(GLTFType::TYPE_SCALAR);
+ ERR_FAIL_V(GLTFAccessorType::TYPE_SCALAR);
}
Error GLTFDocument::_parse_accessors(Ref<GLTFState> p_state) {
@@ -1004,7 +1004,7 @@ Error GLTFDocument::_parse_accessors(Ref<GLTFState> p_state) {
ERR_FAIL_COND_V(!d.has("count"), ERR_PARSE_ERROR);
accessor->count = d["count"];
ERR_FAIL_COND_V(!d.has("type"), ERR_PARSE_ERROR);
- accessor->type = _get_type_from_str(d["type"]);
+ accessor->accessor_type = _get_accessor_type_from_str(d["type"]);
if (d.has("bufferView")) {
accessor->buffer_view = d["bufferView"]; //optional because it may be sparse...
@@ -1088,26 +1088,12 @@ String GLTFDocument::_get_component_type_name(const uint32_t p_component) {
return "<Error>";
}
-String GLTFDocument::_get_type_name(const GLTFType p_component) {
- static const char *names[] = {
- "float",
- "vec2",
- "vec3",
- "vec4",
- "mat2",
- "mat3",
- "mat4"
- };
-
- return names[p_component];
-}
-
-Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> p_state, const double *p_src, const int p_count, const GLTFType p_type, const int p_component_type, const bool p_normalized, const int p_byte_offset, const bool p_for_vertex, GLTFBufferViewIndex &r_accessor, const bool p_for_vertex_indices) {
+Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> p_state, const double *p_src, const int p_count, const GLTFAccessorType p_accessor_type, const int p_component_type, const bool p_normalized, const int p_byte_offset, const bool p_for_vertex, GLTFBufferViewIndex &r_accessor, const bool p_for_vertex_indices) {
const int component_count_for_type[7] = {
1, 2, 3, 4, 4, 9, 16
};
- const int component_count = component_count_for_type[p_type];
+ const int component_count = component_count_for_type[p_accessor_type];
const int component_size = _get_component_type_size(p_component_type);
ERR_FAIL_COND_V(component_size == 0, FAILED);
@@ -1117,18 +1103,18 @@ Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> p_state, const double *p_
switch (p_component_type) {
case COMPONENT_TYPE_BYTE:
case COMPONENT_TYPE_UNSIGNED_BYTE: {
- if (p_type == TYPE_MAT2) {
+ if (p_accessor_type == TYPE_MAT2) {
skip_every = 2;
skip_bytes = 2;
}
- if (p_type == TYPE_MAT3) {
+ if (p_accessor_type == TYPE_MAT3) {
skip_every = 3;
skip_bytes = 1;
}
} break;
case COMPONENT_TYPE_SHORT:
case COMPONENT_TYPE_UNSIGNED_SHORT: {
- if (p_type == TYPE_MAT3) {
+ if (p_accessor_type == TYPE_MAT3) {
skip_every = 6;
skip_bytes = 4;
}
@@ -1147,7 +1133,7 @@ Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> p_state, const double *p_
stride += 4 - (stride % 4); //according to spec must be multiple of 4
}
//use to debug
- print_verbose("glTF: encoding type " + _get_type_name(p_type) + " component type: " + _get_component_type_name(p_component_type) + " stride: " + itos(stride) + " amount " + itos(p_count));
+ print_verbose("glTF: encoding accessor type " + _get_accessor_type_name(p_accessor_type) + " component type: " + _get_component_type_name(p_component_type) + " stride: " + itos(stride) + " amount " + itos(p_count));
print_verbose("glTF: encoding accessor offset " + itos(p_byte_offset) + " view offset: " + itos(bv->byte_offset) + " total buffer len: " + itos(gltf_buffer.size()) + " view len " + itos(bv->byte_length));
@@ -1310,7 +1296,7 @@ Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> p_state, const double *p_
return OK;
}
-Error GLTFDocument::_decode_buffer_view(Ref<GLTFState> p_state, double *p_dst, const GLTFBufferViewIndex p_buffer_view, const int p_skip_every, const int p_skip_bytes, const int p_element_size, const int p_count, const GLTFType p_type, const int p_component_count, const int p_component_type, const int p_component_size, const bool p_normalized, const int p_byte_offset, const bool p_for_vertex) {
+Error GLTFDocument::_decode_buffer_view(Ref<GLTFState> p_state, double *p_dst, const GLTFBufferViewIndex p_buffer_view, const int p_skip_every, const int p_skip_bytes, const int p_element_size, const int p_count, const GLTFAccessorType p_accessor_type, const int p_component_count, const int p_component_type, const int p_component_size, const bool p_normalized, const int p_byte_offset, const bool p_for_vertex) {
const Ref<GLTFBufferView> bv = p_state->buffer_views[p_buffer_view];
int stride = p_element_size;
@@ -1328,7 +1314,7 @@ Error GLTFDocument::_decode_buffer_view(Ref<GLTFState> p_state, double *p_dst, c
const uint8_t *bufptr = buffer.ptr();
//use to debug
- print_verbose("glTF: type " + _get_type_name(p_type) + " component type: " + _get_component_type_name(p_component_type) + " stride: " + itos(stride) + " amount " + itos(p_count));
+ print_verbose("glTF: accessor type " + _get_accessor_type_name(p_accessor_type) + " component type: " + _get_component_type_name(p_component_type) + " stride: " + itos(stride) + " amount " + itos(p_count));
print_verbose("glTF: accessor offset " + itos(p_byte_offset) + " view offset: " + itos(bv->byte_offset) + " total buffer len: " + itos(buffer.size()) + " view len " + itos(bv->byte_length));
const int buffer_end = (stride * (p_count - 1)) + p_element_size;
@@ -1430,7 +1416,7 @@ Vector<double> GLTFDocument::_decode_accessor(Ref<GLTFState> p_state, const GLTF
1, 2, 3, 4, 4, 9, 16
};
- const int component_count = component_count_for_type[a->type];
+ const int component_count = component_count_for_type[a->accessor_type];
const int component_size = _get_component_type_size(a->component_type);
ERR_FAIL_COND_V(component_size == 0, Vector<double>());
int element_size = component_count * component_size;
@@ -1441,12 +1427,12 @@ Vector<double> GLTFDocument::_decode_accessor(Ref<GLTFState> p_state, const GLTF
switch (a->component_type) {
case COMPONENT_TYPE_BYTE:
case COMPONENT_TYPE_UNSIGNED_BYTE: {
- if (a->type == TYPE_MAT2) {
+ if (a->accessor_type == TYPE_MAT2) {
skip_every = 2;
skip_bytes = 2;
element_size = 8; //override for this case
}
- if (a->type == TYPE_MAT3) {
+ if (a->accessor_type == TYPE_MAT3) {
skip_every = 3;
skip_bytes = 1;
element_size = 12; //override for this case
@@ -1454,7 +1440,7 @@ Vector<double> GLTFDocument::_decode_accessor(Ref<GLTFState> p_state, const GLTF
} break;
case COMPONENT_TYPE_SHORT:
case COMPONENT_TYPE_UNSIGNED_SHORT: {
- if (a->type == TYPE_MAT3) {
+ if (a->accessor_type == TYPE_MAT3) {
skip_every = 6;
skip_bytes = 4;
element_size = 16; //override for this case
@@ -1471,7 +1457,7 @@ Vector<double> GLTFDocument::_decode_accessor(Ref<GLTFState> p_state, const GLTF
if (a->buffer_view >= 0) {
ERR_FAIL_INDEX_V(a->buffer_view, p_state->buffer_views.size(), Vector<double>());
- const Error err = _decode_buffer_view(p_state, dst, a->buffer_view, skip_every, skip_bytes, element_size, a->count, a->type, component_count, a->component_type, component_size, a->normalized, a->byte_offset, p_for_vertex);
+ const Error err = _decode_buffer_view(p_state, dst, a->buffer_view, skip_every, skip_bytes, element_size, a->count, a->accessor_type, component_count, a->component_type, component_size, a->normalized, a->byte_offset, p_for_vertex);
if (err != OK) {
return Vector<double>();
}
@@ -1495,7 +1481,7 @@ Vector<double> GLTFDocument::_decode_accessor(Ref<GLTFState> p_state, const GLTF
Vector<double> data;
data.resize(component_count * a->sparse_count);
- err = _decode_buffer_view(p_state, data.ptrw(), a->sparse_values_buffer_view, skip_every, skip_bytes, element_size, a->sparse_count, a->type, component_count, a->component_type, component_size, a->normalized, a->sparse_values_byte_offset, p_for_vertex);
+ err = _decode_buffer_view(p_state, data.ptrw(), a->sparse_values_buffer_view, skip_every, skip_bytes, element_size, a->sparse_count, a->accessor_type, component_count, a->component_type, component_size, a->normalized, a->sparse_values_byte_offset, p_for_vertex);
if (err != OK) {
return Vector<double>();
}
@@ -1550,7 +1536,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_ints(Ref<GLTFState> p_state,
p_state->buffers.push_back(Vector<uint8_t>());
}
int64_t size = p_state->buffers[0].size();
- const GLTFType type = GLTFType::TYPE_SCALAR;
+ const GLTFAccessorType accessor_type = GLTFAccessorType::TYPE_SCALAR;
int component_type;
if (max_index > 65535 || p_for_vertex) {
component_type = GLTFDocument::COMPONENT_TYPE_INT;
@@ -1562,10 +1548,10 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_ints(Ref<GLTFState> p_state,
accessor->min = type_min;
accessor->normalized = false;
accessor->count = ret_size;
- accessor->type = type;
+ accessor->accessor_type = accessor_type;
accessor->component_type = component_type;
accessor->byte_offset = 0;
- Error err = _encode_buffer_view(p_state, attribs.ptr(), attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i, p_for_vertex_indices);
+ Error err = _encode_buffer_view(p_state, attribs.ptr(), attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i, p_for_vertex_indices);
if (err != OK) {
return -1;
}
@@ -1664,17 +1650,17 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec2(Ref<GLTFState> p_state,
p_state->buffers.push_back(Vector<uint8_t>());
}
int64_t size = p_state->buffers[0].size();
- const GLTFType type = GLTFType::TYPE_VEC2;
+ const GLTFAccessorType accessor_type = GLTFAccessorType::TYPE_VEC2;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
accessor->max = type_max;
accessor->min = type_min;
accessor->normalized = false;
accessor->count = p_attribs.size();
- accessor->type = type;
+ accessor->accessor_type = accessor_type;
accessor->component_type = component_type;
accessor->byte_offset = 0;
- Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
+ Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
if (err != OK) {
return -1;
}
@@ -1717,17 +1703,17 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_color(Ref<GLTFState> p_state
p_state->buffers.push_back(Vector<uint8_t>());
}
int64_t size = p_state->buffers[0].size();
- const GLTFType type = GLTFType::TYPE_VEC4;
+ const GLTFAccessorType accessor_type = GLTFAccessorType::TYPE_VEC4;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
accessor->max = type_max;
accessor->min = type_min;
accessor->normalized = false;
accessor->count = p_attribs.size();
- accessor->type = type;
+ accessor->accessor_type = accessor_type;
accessor->component_type = component_type;
accessor->byte_offset = 0;
- Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
+ Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
if (err != OK) {
return -1;
}
@@ -1784,17 +1770,17 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_weights(Ref<GLTFState> p_sta
p_state->buffers.push_back(Vector<uint8_t>());
}
int64_t size = p_state->buffers[0].size();
- const GLTFType type = GLTFType::TYPE_VEC4;
+ const GLTFAccessorType accessor_type = GLTFAccessorType::TYPE_VEC4;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
accessor->max = type_max;
accessor->min = type_min;
accessor->normalized = false;
accessor->count = p_attribs.size();
- accessor->type = type;
+ accessor->accessor_type = accessor_type;
accessor->component_type = component_type;
accessor->byte_offset = 0;
- Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
+ Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
if (err != OK) {
return -1;
}
@@ -1835,17 +1821,17 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_joints(Ref<GLTFState> p_stat
p_state->buffers.push_back(Vector<uint8_t>());
}
int64_t size = p_state->buffers[0].size();
- const GLTFType type = GLTFType::TYPE_VEC4;
+ const GLTFAccessorType accessor_type = GLTFAccessorType::TYPE_VEC4;
const int component_type = GLTFDocument::COMPONENT_TYPE_UNSIGNED_SHORT;
accessor->max = type_max;
accessor->min = type_min;
accessor->normalized = false;
accessor->count = p_attribs.size();
- accessor->type = type;
+ accessor->accessor_type = accessor_type;
accessor->component_type = component_type;
accessor->byte_offset = 0;
- Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
+ Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
if (err != OK) {
return -1;
}
@@ -1888,17 +1874,17 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_quaternions(Ref<GLTFState> p
p_state->buffers.push_back(Vector<uint8_t>());
}
int64_t size = p_state->buffers[0].size();
- const GLTFType type = GLTFType::TYPE_VEC4;
+ const GLTFAccessorType accessor_type = GLTFAccessorType::TYPE_VEC4;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
accessor->max = type_max;
accessor->min = type_min;
accessor->normalized = false;
accessor->count = p_attribs.size();
- accessor->type = type;
+ accessor->accessor_type = accessor_type;
accessor->component_type = component_type;
accessor->byte_offset = 0;
- Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
+ Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
if (err != OK) {
return -1;
}
@@ -1963,17 +1949,17 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_floats(Ref<GLTFState> p_stat
p_state->buffers.push_back(Vector<uint8_t>());
}
int64_t size = p_state->buffers[0].size();
- const GLTFType type = GLTFType::TYPE_SCALAR;
+ const GLTFAccessorType accessor_type = GLTFAccessorType::TYPE_SCALAR;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
accessor->max = type_max;
accessor->min = type_min;
accessor->normalized = false;
accessor->count = ret_size;
- accessor->type = type;
+ accessor->accessor_type = accessor_type;
accessor->component_type = component_type;
accessor->byte_offset = 0;
- Error err = _encode_buffer_view(p_state, attribs.ptr(), attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
+ Error err = _encode_buffer_view(p_state, attribs.ptr(), attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
if (err != OK) {
return -1;
}
@@ -2013,17 +1999,17 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec3(Ref<GLTFState> p_state,
p_state->buffers.push_back(Vector<uint8_t>());
}
int64_t size = p_state->buffers[0].size();
- const GLTFType type = GLTFType::TYPE_VEC3;
+ const GLTFAccessorType accessor_type = GLTFAccessorType::TYPE_VEC3;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
accessor->max = type_max;
accessor->min = type_min;
accessor->normalized = false;
accessor->count = p_attribs.size();
- accessor->type = type;
+ accessor->accessor_type = accessor_type;
accessor->component_type = component_type;
accessor->byte_offset = 0;
- Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
+ Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
if (err != OK) {
return -1;
}
@@ -2089,12 +2075,12 @@ GLTFAccessorIndex GLTFDocument::_encode_sparse_accessor_as_vec3(Ref<GLTFState> p
p_state->buffers.push_back(Vector<uint8_t>());
}
int64_t size = p_state->buffers[0].size();
- const GLTFType type = GLTFType::TYPE_VEC3;
+ const GLTFAccessorType accessor_type = GLTFAccessorType::TYPE_VEC3;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
sparse_accessor->normalized = false;
sparse_accessor->count = p_attribs.size();
- sparse_accessor->type = type;
+ sparse_accessor->accessor_type = accessor_type;
sparse_accessor->component_type = component_type;
if (p_reference_accessor < p_state->accessors.size() && p_reference_accessor >= 0 && p_state->accessors[p_reference_accessor].is_valid()) {
sparse_accessor->byte_offset = p_state->accessors[p_reference_accessor]->byte_offset;
@@ -2117,11 +2103,11 @@ GLTFAccessorIndex GLTFDocument::_encode_sparse_accessor_as_vec3(Ref<GLTFState> p
} else {
sparse_accessor->sparse_indices_component_type = GLTFDocument::COMPONENT_TYPE_UNSIGNED_SHORT;
}
- if (_encode_buffer_view(p_state, changed_indices.ptr(), changed_indices.size(), GLTFType::TYPE_SCALAR, sparse_accessor->sparse_indices_component_type, sparse_accessor->normalized, sparse_accessor->sparse_indices_byte_offset, false, buffer_view_i_indices) != OK) {
+ if (_encode_buffer_view(p_state, changed_indices.ptr(), changed_indices.size(), GLTFAccessorType::TYPE_SCALAR, sparse_accessor->sparse_indices_component_type, sparse_accessor->normalized, sparse_accessor->sparse_indices_byte_offset, false, buffer_view_i_indices) != OK) {
return -1;
}
// We use changed_indices.size() here, because we must pass the number of vec3 values rather than the number of components.
- if (_encode_buffer_view(p_state, changed_values.ptr(), changed_indices.size(), sparse_accessor->type, sparse_accessor->component_type, sparse_accessor->normalized, sparse_accessor->sparse_values_byte_offset, false, buffer_view_i_values) != OK) {
+ if (_encode_buffer_view(p_state, changed_values.ptr(), changed_indices.size(), sparse_accessor->accessor_type, sparse_accessor->component_type, sparse_accessor->normalized, sparse_accessor->sparse_values_byte_offset, false, buffer_view_i_values) != OK) {
return -1;
}
sparse_accessor->sparse_indices_buffer_view = buffer_view_i_indices;
@@ -2130,7 +2116,7 @@ GLTFAccessorIndex GLTFDocument::_encode_sparse_accessor_as_vec3(Ref<GLTFState> p
} else if (changed_indices.size() > 0) {
GLTFBufferIndex buffer_view_i;
sparse_accessor->byte_offset = 0;
- Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), type, component_type, sparse_accessor->normalized, size, p_for_vertex, buffer_view_i);
+ Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), accessor_type, component_type, sparse_accessor->normalized, size, p_for_vertex, buffer_view_i);
if (err != OK) {
return -1;
}
@@ -2194,17 +2180,17 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_xform(Ref<GLTFState> p_state
p_state->buffers.push_back(Vector<uint8_t>());
}
int64_t size = p_state->buffers[0].size();
- const GLTFType type = GLTFType::TYPE_MAT4;
+ const GLTFAccessorType accessor_type = GLTFAccessorType::TYPE_MAT4;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
accessor->max = type_max;
accessor->min = type_min;
accessor->normalized = false;
accessor->count = p_attribs.size();
- accessor->type = type;
+ accessor->accessor_type = accessor_type;
accessor->component_type = component_type;
accessor->byte_offset = 0;
- Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
+ Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
if (err != OK) {
return -1;
}
@@ -2247,10 +2233,10 @@ Vector<Color> GLTFDocument::_decode_accessor_as_color(Ref<GLTFState> p_state, co
return ret;
}
- const int type = p_state->accessors[p_accessor]->type;
- ERR_FAIL_COND_V(!(type == TYPE_VEC3 || type == TYPE_VEC4), ret);
+ const int accessor_type = p_state->accessors[p_accessor]->accessor_type;
+ ERR_FAIL_COND_V(!(accessor_type == TYPE_VEC3 || accessor_type == TYPE_VEC4), ret);
int vec_len = 3;
- if (type == TYPE_VEC4) {
+ if (accessor_type == TYPE_VEC4) {
vec_len = 4;
}
diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h
index 5eb9d90811..4f92ceccca 100644
--- a/modules/gltf/gltf_document.h
+++ b/modules/gltf/gltf_document.h
@@ -111,8 +111,7 @@ private:
int _get_component_type_size(const int p_component_type);
Error _parse_scenes(Ref<GLTFState> p_state);
Error _parse_nodes(Ref<GLTFState> p_state);
- String _get_type_name(const GLTFType p_component);
- String _get_accessor_type_name(const GLTFType p_type);
+ String _get_accessor_type_name(const GLTFAccessorType p_accessor_type);
String _sanitize_animation_name(const String &p_name);
String _gen_unique_animation_name(Ref<GLTFState> p_state, const String &p_name);
String _sanitize_bone_name(const String &p_name);
@@ -132,13 +131,13 @@ private:
void _compute_node_heights(Ref<GLTFState> p_state);
Error _parse_buffers(Ref<GLTFState> p_state, const String &p_base_path);
Error _parse_buffer_views(Ref<GLTFState> p_state);
- GLTFType _get_type_from_str(const String &p_string);
+ GLTFAccessorType _get_accessor_type_from_str(const String &p_string);
Error _parse_accessors(Ref<GLTFState> p_state);
Error _decode_buffer_view(Ref<GLTFState> p_state, double *p_dst,
const GLTFBufferViewIndex p_buffer_view,
const int p_skip_every, const int p_skip_bytes,
const int p_element_size, const int p_count,
- const GLTFType p_type, const int p_component_count,
+ const GLTFAccessorType p_accessor_type, const int p_component_count,
const int p_component_type, const int p_component_size,
const bool p_normalized, const int p_byte_offset,
const bool p_for_vertex);
@@ -267,7 +266,7 @@ private:
const Vector<Transform3D> p_attribs,
const bool p_for_vertex);
Error _encode_buffer_view(Ref<GLTFState> p_state, const double *p_src,
- const int p_count, const GLTFType p_type,
+ const int p_count, const GLTFAccessorType p_accessor_type,
const int p_component_type, const bool p_normalized,
const int p_byte_offset, const bool p_for_vertex,
GLTFBufferViewIndex &r_accessor, const bool p_for_indices = false);
diff --git a/modules/gltf/structures/gltf_accessor.cpp b/modules/gltf/structures/gltf_accessor.cpp
index 2119a0ee82..602f0d9dc4 100644
--- a/modules/gltf/structures/gltf_accessor.cpp
+++ b/modules/gltf/structures/gltf_accessor.cpp
@@ -41,8 +41,10 @@ void GLTFAccessor::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_normalized", "normalized"), &GLTFAccessor::set_normalized);
ClassDB::bind_method(D_METHOD("get_count"), &GLTFAccessor::get_count);
ClassDB::bind_method(D_METHOD("set_count", "count"), &GLTFAccessor::set_count);
- ClassDB::bind_method(D_METHOD("get_type"), &GLTFAccessor::get_type);
- ClassDB::bind_method(D_METHOD("set_type", "type"), &GLTFAccessor::set_type);
+ ClassDB::bind_method(D_METHOD("get_accessor_type"), &GLTFAccessor::get_accessor_type);
+ ClassDB::bind_method(D_METHOD("set_accessor_type", "accessor_type"), &GLTFAccessor::set_accessor_type);
+ ClassDB::bind_method(D_METHOD("get_type"), &GLTFAccessor::get_accessor_type);
+ ClassDB::bind_method(D_METHOD("set_type", "type"), &GLTFAccessor::set_accessor_type);
ClassDB::bind_method(D_METHOD("get_min"), &GLTFAccessor::get_min);
ClassDB::bind_method(D_METHOD("set_min", "min"), &GLTFAccessor::set_min);
ClassDB::bind_method(D_METHOD("get_max"), &GLTFAccessor::get_max);
@@ -65,7 +67,8 @@ void GLTFAccessor::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "component_type"), "set_component_type", "get_component_type"); // int
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "normalized"), "set_normalized", "get_normalized"); // bool
ADD_PROPERTY(PropertyInfo(Variant::INT, "count"), "set_count", "get_count"); // int
- ADD_PROPERTY(PropertyInfo(Variant::INT, "type"), "set_type", "get_type"); // GLTFType
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "accessor_type"), "set_accessor_type", "get_accessor_type"); // GLTFAccessorType
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "type"), "set_type", "get_type"); // Deprecated, GLTFAccessorType
ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT64_ARRAY, "min"), "set_min", "get_min"); // Vector<real_t>
ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT64_ARRAY, "max"), "set_max", "get_max"); // Vector<real_t>
ADD_PROPERTY(PropertyInfo(Variant::INT, "sparse_count"), "set_sparse_count", "get_sparse_count"); // int
@@ -116,12 +119,12 @@ void GLTFAccessor::set_count(int p_count) {
count = p_count;
}
-int GLTFAccessor::get_type() {
- return (int)type;
+int GLTFAccessor::get_accessor_type() {
+ return (int)accessor_type;
}
-void GLTFAccessor::set_type(int p_type) {
- type = (GLTFType)p_type; // TODO: Register enum
+void GLTFAccessor::set_accessor_type(int p_accessor_type) {
+ accessor_type = (GLTFAccessorType)p_accessor_type; // TODO: Register enum
}
Vector<double> GLTFAccessor::get_min() {
diff --git a/modules/gltf/structures/gltf_accessor.h b/modules/gltf/structures/gltf_accessor.h
index 6b1734601a..51ca282630 100644
--- a/modules/gltf/structures/gltf_accessor.h
+++ b/modules/gltf/structures/gltf_accessor.h
@@ -35,6 +35,16 @@
#include "core/io/resource.h"
+enum GLTFAccessorType {
+ TYPE_SCALAR,
+ TYPE_VEC2,
+ TYPE_VEC3,
+ TYPE_VEC4,
+ TYPE_MAT2,
+ TYPE_MAT3,
+ TYPE_MAT4,
+};
+
struct GLTFAccessor : public Resource {
GDCLASS(GLTFAccessor, Resource);
friend class GLTFDocument;
@@ -45,7 +55,7 @@ private:
int component_type = 0;
bool normalized = false;
int count = 0;
- GLTFType type = GLTFType::TYPE_SCALAR;
+ GLTFAccessorType accessor_type = GLTFAccessorType::TYPE_SCALAR;
Vector<double> min;
Vector<double> max;
int sparse_count = 0;
@@ -74,8 +84,8 @@ public:
int get_count();
void set_count(int p_count);
- int get_type();
- void set_type(int p_type);
+ int get_accessor_type();
+ void set_accessor_type(int p_accessor_type);
Vector<double> get_min();
void set_min(Vector<double> p_min);
diff --git a/modules/multiplayer/scene_cache_interface.cpp b/modules/multiplayer/scene_cache_interface.cpp
index 2ea9ce8819..af43123b29 100644
--- a/modules/multiplayer/scene_cache_interface.cpp
+++ b/modules/multiplayer/scene_cache_interface.cpp
@@ -76,7 +76,7 @@ void SceneCacheInterface::on_peer_change(int p_id, bool p_connected) {
for (KeyValue<int, ObjectID> E : pinfo->recv_nodes) {
NodeCache *nc = nodes_cache.getptr(E.value);
ERR_CONTINUE(!nc);
- nc->recv_ids.erase(E.key);
+ nc->recv_ids.erase(p_id);
}
for (const ObjectID &oid : pinfo->sent_nodes) {
NodeCache *nc = nodes_cache.getptr(oid);
diff --git a/modules/navigation/2d/nav_mesh_generator_2d.cpp b/modules/navigation/2d/nav_mesh_generator_2d.cpp
index 2198158f9c..ace361a08a 100644
--- a/modules/navigation/2d/nav_mesh_generator_2d.cpp
+++ b/modules/navigation/2d/nav_mesh_generator_2d.cpp
@@ -263,7 +263,7 @@ void NavMeshGenerator2D::generator_parse_geometry_node(Ref<NavigationPolygon> p_
// Special case for TileMap, so that internal layer get parsed even if p_recurse_children is false.
for (int i = 0; i < p_node->get_child_count(); i++) {
TileMapLayer *tile_map_layer = Object::cast_to<TileMapLayer>(p_node->get_child(i));
- if (tile_map_layer->get_index_in_tile_map() >= 0) {
+ if (tile_map_layer && tile_map_layer->get_index_in_tile_map() >= 0) {
generator_parse_tile_map_layer_node(p_navigation_mesh, p_source_geometry_data, tile_map_layer);
}
}
diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm
index 5d7af25f28..a454dd5ba0 100644
--- a/platform/ios/display_server_ios.mm
+++ b/platform/ios/display_server_ios.mm
@@ -399,7 +399,12 @@ void DisplayServerIOS::set_system_theme_change_callback(const Callable &p_callab
void DisplayServerIOS::emit_system_theme_changed() {
if (system_theme_changed.is_valid()) {
- system_theme_changed.call();
+ Variant ret;
+ Callable::CallError ce;
+ system_theme_changed.callp(nullptr, 0, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat("Failed to execute system theme changed callback: %s.", Variant::get_callable_error_text(system_theme_changed, nullptr, 0, ce)));
+ }
}
}
diff --git a/platform/linuxbsd/freedesktop_portal_desktop.cpp b/platform/linuxbsd/freedesktop_portal_desktop.cpp
index e65404a531..464c0e4286 100644
--- a/platform/linuxbsd/freedesktop_portal_desktop.cpp
+++ b/platform/linuxbsd/freedesktop_portal_desktop.cpp
@@ -597,7 +597,12 @@ void FreeDesktopPortalDesktop::_thread_monitor(void *p_ud) {
void FreeDesktopPortalDesktop::_system_theme_changed_callback() {
if (system_theme_changed.is_valid()) {
- system_theme_changed.call();
+ Variant ret;
+ Callable::CallError ce;
+ system_theme_changed.callp(nullptr, 0, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat("Failed to execute system theme changed callback: %s.", Variant::get_callable_error_text(system_theme_changed, nullptr, 0, ce)));
+ }
}
}
diff --git a/platform/linuxbsd/wayland/display_server_wayland.cpp b/platform/linuxbsd/wayland/display_server_wayland.cpp
index ce8a53a856..12d3a6fd2f 100644
--- a/platform/linuxbsd/wayland/display_server_wayland.cpp
+++ b/platform/linuxbsd/wayland/display_server_wayland.cpp
@@ -1137,7 +1137,14 @@ void DisplayServerWayland::process_events() {
WindowData wd = main_window;
if (wd.drop_files_callback.is_valid()) {
- wd.drop_files_callback.call(dropfiles_msg->files);
+ Variant v_files = dropfiles_msg->files;
+ const Variant *v_args[1] = { &v_files };
+ Variant ret;
+ Callable::CallError ce;
+ wd.drop_files_callback.callp((const Variant **)&v_args, 1, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat("Failed to execute drop files callback: %s.", Variant::get_callable_error_text(wd.drop_files_callback, v_args, 1, ce)));
+ }
}
}
}
@@ -1209,6 +1216,15 @@ void DisplayServerWayland::set_context(Context p_context) {
wayland_thread.window_set_app_id(MAIN_WINDOW_ID, app_id);
}
+bool DisplayServerWayland::is_window_transparency_available() const {
+#if defined(RD_ENABLED)
+ if (rendering_device && !rendering_device->is_composite_alpha_supported()) {
+ return false;
+ }
+#endif
+ return OS::get_singleton()->is_layered_allowed();
+}
+
Vector<String> DisplayServerWayland::get_rendering_drivers_func() {
Vector<String> drivers;
diff --git a/platform/linuxbsd/wayland/display_server_wayland.h b/platform/linuxbsd/wayland/display_server_wayland.h
index 38ecbd703d..c24eb0ee62 100644
--- a/platform/linuxbsd/wayland/display_server_wayland.h
+++ b/platform/linuxbsd/wayland/display_server_wayland.h
@@ -280,6 +280,8 @@ public:
virtual void set_context(Context p_context) override;
+ virtual bool is_window_transparency_available() const override;
+
static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, int p_screen, Context p_context, Error &r_error);
static Vector<String> get_rendering_drivers_func();
diff --git a/platform/linuxbsd/wayland/wayland_thread.h b/platform/linuxbsd/wayland/wayland_thread.h
index d35a5b7139..0756b6b0ea 100644
--- a/platform/linuxbsd/wayland/wayland_thread.h
+++ b/platform/linuxbsd/wayland/wayland_thread.h
@@ -43,6 +43,9 @@
#else
#include <wayland-client-core.h>
#include <wayland-cursor.h>
+#ifdef GLES3_ENABLED
+#include <wayland-egl.h>
+#endif
#include <xkbcommon/xkbcommon.h>
#endif // SOWRAP_ENABLED
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 2c07acf94f..b5d08a377c 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -4206,7 +4206,10 @@ void DisplayServerX11::popup_close(WindowID p_window) {
WindowID win_id = E->get();
popup_list.erase(E);
- _send_window_event(windows[win_id], DisplayServerX11::WINDOW_EVENT_CLOSE_REQUEST);
+ if (win_id != p_window) {
+ // Only request close on related windows, not this window. We are already processing it.
+ _send_window_event(windows[win_id], DisplayServerX11::WINDOW_EVENT_CLOSE_REQUEST);
+ }
E = F;
}
}
@@ -5008,7 +5011,14 @@ void DisplayServerX11::process_events() {
}
if (windows[window_id].drop_files_callback.is_valid()) {
- windows[window_id].drop_files_callback.call(files);
+ Variant v_files = files;
+ const Variant *v_args[1] = { &v_files };
+ Variant ret;
+ Callable::CallError ce;
+ windows[window_id].drop_files_callback.callp((const Variant **)&v_args, 1, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat("Failed to execute drop files callback: %s.", Variant::get_callable_error_text(windows[window_id].drop_files_callback, v_args, 1, ce)));
+ }
}
//Reply that all is well.
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index db71c48eff..0ccce1ad6a 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -907,7 +907,12 @@ void DisplayServerMacOS::set_system_theme_change_callback(const Callable &p_call
void DisplayServerMacOS::emit_system_theme_changed() {
if (system_theme_changed.is_valid()) {
- system_theme_changed.call();
+ Variant ret;
+ Callable::CallError ce;
+ system_theme_changed.callp(nullptr, 0, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat("Failed to execute system theme changed callback: %s.", Variant::get_callable_error_text(system_theme_changed, nullptr, 0, ce)));
+ }
}
}
@@ -3448,7 +3453,10 @@ void DisplayServerMacOS::popup_close(WindowID p_window) {
WindowID win_id = E->get();
popup_list.erase(E);
- send_window_event(windows[win_id], DisplayServerMacOS::WINDOW_EVENT_CLOSE_REQUEST);
+ if (win_id != p_window) {
+ // Only request close on related windows, not this window. We are already processing it.
+ send_window_event(windows[win_id], DisplayServerMacOS::WINDOW_EVENT_CLOSE_REQUEST);
+ }
E = F;
}
if (!was_empty && popup_list.is_empty()) {
diff --git a/platform/macos/godot_content_view.mm b/platform/macos/godot_content_view.mm
index 68a7288ad4..7dcb1ac9db 100644
--- a/platform/macos/godot_content_view.mm
+++ b/platform/macos/godot_content_view.mm
@@ -323,7 +323,14 @@
NSString *file = [NSURL URLWithString:url].path;
files.push_back(String::utf8([file UTF8String]));
}
- wd.drop_files_callback.call(files);
+ Variant v_files = files;
+ const Variant *v_args[1] = { &v_files };
+ Variant ret;
+ Callable::CallError ce;
+ wd.drop_files_callback.callp((const Variant **)&v_args, 1, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat("Failed to execute drop files callback: %s.", Variant::get_callable_error_text(wd.drop_files_callback, v_args, 1, ce)));
+ }
}
return NO;
diff --git a/platform/macos/godot_status_item.mm b/platform/macos/godot_status_item.mm
index 0990a16b2b..1b16d496a2 100644
--- a/platform/macos/godot_status_item.mm
+++ b/platform/macos/godot_status_item.mm
@@ -66,10 +66,13 @@
if (cb.is_valid()) {
Variant v_button = index;
Variant v_pos = ds->mouse_get_position();
- Variant *v_args[2] = { &v_button, &v_pos };
+ const Variant *v_args[2] = { &v_button, &v_pos };
Variant ret;
Callable::CallError ce;
cb.callp((const Variant **)&v_args, 2, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat("Failed to execute status indicator callback: %s.", Variant::get_callable_error_text(cb, v_args, 2, ce)));
+ }
}
}
diff --git a/platform/web/detect.py b/platform/web/detect.py
index 3df8cbad7c..c6568625c1 100644
--- a/platform/web/detect.py
+++ b/platform/web/detect.py
@@ -181,7 +181,7 @@ def configure(env: "SConsEnvironment"):
# Use TempFileMunge since some AR invocations are too long for cmd.exe.
# Use POSIX-style paths, required with TempFileMunge.
env["ARCOM_POSIX"] = env["ARCOM"].replace("$TARGET", "$TARGET.posix").replace("$SOURCES", "$SOURCES.posix")
- env["ARCOM"] = "${TEMPFILE(ARCOM_POSIX)}"
+ env["ARCOM"] = "${TEMPFILE('$ARCOM_POSIX','$ARCOMSTR')}"
# All intermediate files are just object files.
env["OBJPREFIX"] = ""
diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp
index 0420a01533..fab92b1894 100644
--- a/platform/web/display_server_web.cpp
+++ b/platform/web/display_server_web.cpp
@@ -70,7 +70,7 @@ bool DisplayServerWeb::check_size_force_redraw() {
void DisplayServerWeb::fullscreen_change_callback(int p_fullscreen) {
#ifdef PROXY_TO_PTHREAD_ENABLED
if (!Thread::is_main_thread()) {
- callable_mp_static(DisplayServerWeb::_fullscreen_change_callback).bind(p_fullscreen).call_deferred();
+ callable_mp_static(DisplayServerWeb::_fullscreen_change_callback).call_deferred(p_fullscreen);
return;
}
#endif
@@ -96,7 +96,7 @@ void DisplayServerWeb::drop_files_js_callback(const char **p_filev, int p_filec)
#ifdef PROXY_TO_PTHREAD_ENABLED
if (!Thread::is_main_thread()) {
- callable_mp_static(DisplayServerWeb::_drop_files_js_callback).bind(files).call_deferred();
+ callable_mp_static(DisplayServerWeb::_drop_files_js_callback).call_deferred(files);
return;
}
#endif
@@ -112,7 +112,14 @@ void DisplayServerWeb::_drop_files_js_callback(const Vector<String> &p_files) {
if (!ds->drop_files_callback.is_valid()) {
return;
}
- ds->drop_files_callback.call(p_files);
+ Variant v_files = p_files;
+ const Variant *v_args[1] = { &v_files };
+ Variant ret;
+ Callable::CallError ce;
+ ds->drop_files_callback.callp((const Variant **)&v_args, 1, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat("Failed to execute drop files callback: %s.", Variant::get_callable_error_text(ds->drop_files_callback, v_args, 1, ce)));
+ }
}
// Web quit request callback.
@@ -161,7 +168,7 @@ void DisplayServerWeb::key_callback(int p_pressed, int p_repeat, int p_modifiers
#ifdef PROXY_TO_PTHREAD_ENABLED
if (!Thread::is_main_thread()) {
- callable_mp_static(DisplayServerWeb::_key_callback).bind(code, key, p_pressed, p_repeat, p_modifiers).call_deferred();
+ callable_mp_static(DisplayServerWeb::_key_callback).call_deferred(code, key, p_pressed, p_repeat, p_modifiers);
return;
}
#endif
@@ -214,7 +221,7 @@ void DisplayServerWeb::_key_callback(const String &p_key_event_code, const Strin
int DisplayServerWeb::mouse_button_callback(int p_pressed, int p_button, double p_x, double p_y, int p_modifiers) {
#ifdef PROXY_TO_PTHREAD_ENABLED
if (!Thread::is_main_thread()) {
- callable_mp_static(DisplayServerWeb::_mouse_button_callback).bind(p_pressed, p_button, p_x, p_y, p_modifiers).call_deferred();
+ callable_mp_static(DisplayServerWeb::_mouse_button_callback).call_deferred(p_pressed, p_button, p_x, p_y, p_modifiers);
return true;
}
#endif
@@ -301,7 +308,7 @@ int DisplayServerWeb::_mouse_button_callback(int p_pressed, int p_button, double
void DisplayServerWeb::mouse_move_callback(double p_x, double p_y, double p_rel_x, double p_rel_y, int p_modifiers) {
#ifdef PROXY_TO_PTHREAD_ENABLED
if (!Thread::is_main_thread()) {
- callable_mp_static(DisplayServerWeb::_mouse_move_callback).bind(p_x, p_y, p_rel_x, p_rel_y, p_modifiers).call_deferred();
+ callable_mp_static(DisplayServerWeb::_mouse_move_callback).call_deferred(p_x, p_y, p_rel_x, p_rel_y, p_modifiers);
return;
}
#endif
@@ -394,7 +401,7 @@ void DisplayServerWeb::update_voices_callback(int p_size, const char **p_voice)
#ifdef PROXY_TO_PTHREAD_ENABLED
if (!Thread::is_main_thread()) {
- callable_mp_static(DisplayServerWeb::_update_voices_callback).bind(voices).call_deferred();
+ callable_mp_static(DisplayServerWeb::_update_voices_callback).call_deferred(voices);
return;
}
#endif
@@ -461,7 +468,7 @@ void DisplayServerWeb::tts_stop() {
void DisplayServerWeb::js_utterance_callback(int p_event, int p_id, int p_pos) {
#ifdef PROXY_TO_PTHREAD_ENABLED
if (!Thread::is_main_thread()) {
- callable_mp_static(DisplayServerWeb::_js_utterance_callback).bind(p_event, p_id, p_pos).call_deferred();
+ callable_mp_static(DisplayServerWeb::_js_utterance_callback).call_deferred(p_event, p_id, p_pos);
return;
}
#endif
@@ -591,7 +598,7 @@ Point2i DisplayServerWeb::mouse_get_position() const {
int DisplayServerWeb::mouse_wheel_callback(double p_delta_x, double p_delta_y) {
#ifdef PROXY_TO_PTHREAD_ENABLED
if (!Thread::is_main_thread()) {
- callable_mp_static(DisplayServerWeb::_mouse_wheel_callback).bind(p_delta_x, p_delta_y).call_deferred();
+ callable_mp_static(DisplayServerWeb::_mouse_wheel_callback).call_deferred(p_delta_x, p_delta_y);
return true;
}
#endif
@@ -654,7 +661,7 @@ int DisplayServerWeb::_mouse_wheel_callback(double p_delta_x, double p_delta_y)
void DisplayServerWeb::touch_callback(int p_type, int p_count) {
#ifdef PROXY_TO_PTHREAD_ENABLED
if (!Thread::is_main_thread()) {
- callable_mp_static(DisplayServerWeb::_touch_callback).bind(p_type, p_count).call_deferred();
+ callable_mp_static(DisplayServerWeb::_touch_callback).call_deferred(p_type, p_count);
return;
}
#endif
@@ -712,7 +719,7 @@ void DisplayServerWeb::vk_input_text_callback(const char *p_text, int p_cursor)
#ifdef PROXY_TO_PTHREAD_ENABLED
if (!Thread::is_main_thread()) {
- callable_mp_static(DisplayServerWeb::_vk_input_text_callback).bind(text, p_cursor).call_deferred();
+ callable_mp_static(DisplayServerWeb::_vk_input_text_callback).call_deferred(text, p_cursor);
return;
}
#endif
@@ -774,7 +781,7 @@ void DisplayServerWeb::gamepad_callback(int p_index, int p_connected, const char
#ifdef PROXY_TO_PTHREAD_ENABLED
if (!Thread::is_main_thread()) {
- callable_mp_static(DisplayServerWeb::_gamepad_callback).bind(p_index, p_connected, id, guid).call_deferred();
+ callable_mp_static(DisplayServerWeb::_gamepad_callback).call_deferred(p_index, p_connected, id, guid);
return;
}
#endif
@@ -797,7 +804,7 @@ void DisplayServerWeb::ime_callback(int p_type, const char *p_text) {
#ifdef PROXY_TO_PTHREAD_ENABLED
if (!Thread::is_main_thread()) {
- callable_mp_static(DisplayServerWeb::_ime_callback).bind(p_type, text).call_deferred();
+ callable_mp_static(DisplayServerWeb::_ime_callback).call_deferred(p_type, text);
return;
}
#endif
@@ -866,6 +873,9 @@ void DisplayServerWeb::_ime_callback(int p_type, const String &p_text) {
default:
break;
}
+
+ ds->process_keys();
+ Input::get_singleton()->flush_buffered_events();
}
void DisplayServerWeb::window_set_ime_active(const bool p_active, WindowID p_window) {
@@ -927,7 +937,7 @@ void DisplayServerWeb::update_clipboard_callback(const char *p_text) {
#ifdef PROXY_TO_PTHREAD_ENABLED
if (!Thread::is_main_thread()) {
- callable_mp_static(DisplayServerWeb::_update_clipboard_callback).bind(text).call_deferred();
+ callable_mp_static(DisplayServerWeb::_update_clipboard_callback).call_deferred(text);
return;
}
#endif
@@ -953,7 +963,7 @@ String DisplayServerWeb::clipboard_get() const {
void DisplayServerWeb::send_window_event_callback(int p_notification) {
#ifdef PROXY_TO_PTHREAD_ENABLED
if (!Thread::is_main_thread()) {
- callable_mp_static(DisplayServerWeb::_send_window_event_callback).bind(p_notification).call_deferred();
+ callable_mp_static(DisplayServerWeb::_send_window_event_callback).call_deferred(p_notification);
return;
}
#endif
@@ -1353,29 +1363,33 @@ DisplayServer::VSyncMode DisplayServerWeb::window_get_vsync_mode(WindowID p_vsyn
}
void DisplayServerWeb::process_events() {
+ process_keys();
Input::get_singleton()->flush_buffered_events();
if (godot_js_input_gamepad_sample() == OK) {
process_joypads();
- for (int i = 0; i < key_event_pos; i++) {
- const DisplayServerWeb::KeyEvent &ke = key_event_buffer[i];
+ }
+}
- Ref<InputEventKey> ev;
- ev.instantiate();
- ev->set_pressed(ke.pressed);
- ev->set_echo(ke.echo);
- ev->set_keycode(ke.keycode);
- ev->set_physical_keycode(ke.physical_keycode);
- ev->set_key_label(ke.key_label);
- ev->set_unicode(ke.unicode);
- ev->set_location(ke.location);
- if (ke.raw) {
- dom2godot_mod(ev, ke.mod, ke.keycode);
- }
+void DisplayServerWeb::process_keys() {
+ for (int i = 0; i < key_event_pos; i++) {
+ const DisplayServerWeb::KeyEvent &ke = key_event_buffer[i];
- Input::get_singleton()->parse_input_event(ev);
+ Ref<InputEventKey> ev;
+ ev.instantiate();
+ ev->set_pressed(ke.pressed);
+ ev->set_echo(ke.echo);
+ ev->set_keycode(ke.keycode);
+ ev->set_physical_keycode(ke.physical_keycode);
+ ev->set_key_label(ke.key_label);
+ ev->set_unicode(ke.unicode);
+ ev->set_location(ke.location);
+ if (ke.raw) {
+ dom2godot_mod(ev, ke.mod, ke.keycode);
}
- key_event_pos = 0;
+
+ Input::get_singleton()->parse_input_event(ev);
}
+ key_event_pos = 0;
}
int DisplayServerWeb::get_current_video_driver() const {
diff --git a/platform/web/display_server_web.h b/platform/web/display_server_web.h
index 276ca5210c..352b3fe523 100644
--- a/platform/web/display_server_web.h
+++ b/platform/web/display_server_web.h
@@ -145,6 +145,7 @@ private:
static void _drop_files_js_callback(const Vector<String> &p_files);
void process_joypads();
+ void process_keys();
static Vector<String> get_rendering_drivers_func();
static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error);
diff --git a/platform/web/emscripten_helpers.py b/platform/web/emscripten_helpers.py
index 745b2457fa..2cee3e8110 100644
--- a/platform/web/emscripten_helpers.py
+++ b/platform/web/emscripten_helpers.py
@@ -123,7 +123,6 @@ def create_template_zip(env, js, wasm, worker, side):
zip_files,
ZIPROOT=zip_dir,
ZIPSUFFIX="${PROGSUFFIX}${ZIPSUFFIX}",
- ZIPCOMSTR="Archiving $SOURCES as $TARGET",
)
diff --git a/platform/web/javascript_bridge_singleton.cpp b/platform/web/javascript_bridge_singleton.cpp
index a2c83d2f2b..c4dbb405a3 100644
--- a/platform/web/javascript_bridge_singleton.cpp
+++ b/platform/web/javascript_bridge_singleton.cpp
@@ -262,7 +262,7 @@ void JavaScriptObjectImpl::callback(void *p_ref, int p_args_id, int p_argc) {
#ifdef PROXY_TO_PTHREAD_ENABLED
if (!Thread::is_main_thread()) {
- callable_mp_static(JavaScriptObjectImpl::_callback).bind(obj, arg).call_deferred();
+ callable_mp_static(JavaScriptObjectImpl::_callback).call_deferred(obj, arg);
return;
}
#endif
diff --git a/platform/web/js/libs/library_godot_input.js b/platform/web/js/libs/library_godot_input.js
index 1292c468f5..7ea89d553f 100644
--- a/platform/web/js/libs/library_godot_input.js
+++ b/platform/web/js/libs/library_godot_input.js
@@ -63,8 +63,15 @@ const GodotIME = {
ime_position: function (x, y) {
if (GodotIME.ime) {
- GodotIME.ime.style.left = `${x}px`;
- GodotIME.ime.style.top = `${y}px`;
+ const canvas = GodotConfig.canvas;
+ const rect = canvas.getBoundingClientRect();
+ const rw = canvas.width / rect.width;
+ const rh = canvas.height / rect.height;
+ const clx = (x / rw) + rect.x;
+ const cly = (y / rh) + rect.y;
+
+ GodotIME.ime.style.left = `${clx}px`;
+ GodotIME.ime.style.top = `${cly}px`;
}
},
@@ -99,10 +106,12 @@ const GodotIME = {
ime.style.background = 'none';
ime.style.opacity = 0.0;
ime.style.position = 'fixed';
+ ime.style.textAlign = 'left';
+ ime.style.fontSize = '1px';
ime.style.left = '0px';
ime.style.top = '0px';
- ime.style.width = '2px';
- ime.style.height = '2px';
+ ime.style.width = '100%';
+ ime.style.height = '40px';
ime.style.display = 'none';
ime.contentEditable = 'true';
diff --git a/platform/windows/SCsub b/platform/windows/SCsub
index 1c2bfb9b75..f2fb8616ae 100644
--- a/platform/windows/SCsub
+++ b/platform/windows/SCsub
@@ -50,6 +50,7 @@ def arrange_program_clean(prog):
res_file = "godot_res.rc"
res_target = "godot_res" + env["OBJSUFFIX"]
res_obj = env.RES(res_target, res_file)
+env.Depends(res_obj, "#core/version_generated.gen.h")
env.add_source_files(sources, common_win)
sources += res_obj
@@ -63,6 +64,7 @@ if env["windows_subsystem"] == "gui":
res_wrap_file = "godot_res_wrap.rc"
res_wrap_target = "godot_res_wrap" + env["OBJSUFFIX"]
res_wrap_obj = env_wrap.RES(res_wrap_target, res_wrap_file)
+ env_wrap.Depends(res_wrap_obj, "#core/version_generated.gen.h")
if env.msvc:
env_wrap.Append(LINKFLAGS=["/SUBSYSTEM:CONSOLE"])
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 03584977fe..04a7d7f4b8 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -3842,7 +3842,12 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
}
if (system_theme_changed.is_valid()) {
- system_theme_changed.call();
+ Variant ret;
+ Callable::CallError ce;
+ system_theme_changed.callp(nullptr, 0, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat("Failed to execute system theme changed callback: %s.", Variant::get_callable_error_text(system_theme_changed, nullptr, 0, ce)));
+ }
}
} break;
case WM_THEMECHANGED: {
@@ -3897,10 +3902,13 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
} else if (indicators[iid].callback.is_valid()) {
Variant v_button = mb;
Variant v_pos = mouse_get_position();
- Variant *v_args[2] = { &v_button, &v_pos };
+ const Variant *v_args[2] = { &v_button, &v_pos };
Variant ret;
Callable::CallError ce;
indicators[iid].callback.callp((const Variant **)&v_args, 2, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat("Failed to execute status indicator callback: %s.", Variant::get_callable_error_text(indicators[iid].callback, v_args, 2, ce)));
+ }
}
}
return 0;
@@ -4868,7 +4876,14 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
if (files.size() && windows[window_id].drop_files_callback.is_valid()) {
- windows[window_id].drop_files_callback.call(files);
+ Variant v_files = files;
+ const Variant *v_args[1] = { &v_files };
+ Variant ret;
+ Callable::CallError ce;
+ windows[window_id].drop_files_callback.callp((const Variant **)&v_args, 1, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat("Failed to execute drop files callback: %s.", Variant::get_callable_error_text(windows[window_id].drop_files_callback, v_args, 1, ce)));
+ }
}
} break;
default: {
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
index 822f1b58fd..18ef2d8505 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -54,16 +54,18 @@ void Camera2D::_update_scroll() {
if (is_current()) {
ERR_FAIL_COND(custom_viewport && !ObjectDB::get_instance(custom_viewport_id));
+ Size2 screen_size = _get_camera_screen_size();
+
Transform2D xform;
if (is_physics_interpolated_and_enabled()) {
xform = _interpolation_data.xform_prev.interpolate_with(_interpolation_data.xform_curr, Engine::get_singleton()->get_physics_interpolation_fraction());
+ camera_screen_center = xform.affine_inverse().xform(0.5 * screen_size);
} else {
xform = get_camera_transform();
}
viewport->set_canvas_transform(xform);
- Size2 screen_size = _get_camera_screen_size();
Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5) : Point2());
Point2 adj_screen_pos = camera_screen_center - (screen_size * 0.5);
diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp
index ab44e57d05..bad9de5daa 100644
--- a/scene/2d/navigation_region_2d.cpp
+++ b/scene/2d/navigation_region_2d.cpp
@@ -162,8 +162,19 @@ void NavigationRegion2D::_notification(int p_what) {
set_physics_process_internal(true);
} break;
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+#ifdef DEBUG_ENABLED
+ if (debug_instance_rid.is_valid()) {
+ RS::get_singleton()->canvas_item_set_visible(debug_instance_rid, is_visible_in_tree());
+ }
+#endif // DEBUG_ENABLED
+ } break;
+
case NOTIFICATION_EXIT_TREE: {
_region_exit_navigation_map();
+#ifdef DEBUG_ENABLED
+ _free_debug();
+#endif // DEBUG_ENABLED
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
@@ -189,6 +200,9 @@ void NavigationRegion2D::set_navigation_polygon(const Ref<NavigationPolygon> &p_
}
navigation_polygon = p_navigation_polygon;
+#ifdef DEBUG_ENABLED
+ debug_mesh_dirty = true;
+#endif // DEBUG_ENABLED
NavigationServer2D::get_singleton()->region_set_navigation_polygon(region, p_navigation_polygon);
if (navigation_polygon.is_valid()) {
@@ -420,12 +434,42 @@ void NavigationRegion2D::_region_update_transform() {
#ifdef DEBUG_ENABLED
void NavigationRegion2D::_update_debug_mesh() {
- Vector<Vector2> navigation_polygon_vertices = navigation_polygon->get_vertices();
- if (navigation_polygon_vertices.size() < 3) {
+ if (!is_inside_tree()) {
+ _free_debug();
return;
}
const NavigationServer2D *ns2d = NavigationServer2D::get_singleton();
+ RenderingServer *rs = RenderingServer::get_singleton();
+
+ if (!debug_instance_rid.is_valid()) {
+ debug_instance_rid = rs->canvas_item_create();
+ }
+ if (!debug_mesh_rid.is_valid()) {
+ debug_mesh_rid = rs->mesh_create();
+ }
+
+ const Transform2D region_gt = get_global_transform();
+
+ rs->canvas_item_set_parent(debug_instance_rid, get_world_2d()->get_canvas());
+ rs->canvas_item_set_transform(debug_instance_rid, region_gt);
+
+ if (!debug_mesh_dirty) {
+ return;
+ }
+
+ rs->mesh_clear(debug_mesh_rid);
+ debug_mesh_dirty = false;
+
+ const Vector<Vector2> &vertices = navigation_polygon->get_vertices();
+ if (vertices.size() < 3) {
+ return;
+ }
+
+ int polygon_count = navigation_polygon->get_polygon_count();
+ if (polygon_count == 0) {
+ return;
+ }
bool enabled_geometry_face_random_color = ns2d->get_debug_navigation_enable_geometry_face_random_color();
bool enabled_edge_lines = ns2d->get_debug_navigation_enable_edge_lines();
@@ -438,39 +482,109 @@ void NavigationRegion2D::_update_debug_mesh() {
debug_edge_color = ns2d->get_debug_navigation_geometry_edge_disabled_color();
}
+ int vertex_count = 0;
+ int line_count = 0;
+
+ for (int i = 0; i < polygon_count; i++) {
+ const Vector<int> &polygon = navigation_polygon->get_polygon(i);
+ int polygon_size = polygon.size();
+ if (polygon_size < 3) {
+ continue;
+ }
+ line_count += polygon_size * 2;
+ vertex_count += (polygon_size - 2) * 3;
+ }
+
+ Vector<Vector2> face_vertex_array;
+ face_vertex_array.resize(vertex_count);
+
+ Vector<Color> face_color_array;
+ if (enabled_geometry_face_random_color) {
+ face_color_array.resize(vertex_count);
+ }
+
+ Vector<Vector2> line_vertex_array;
+ if (enabled_edge_lines) {
+ line_vertex_array.resize(line_count);
+ }
+
RandomPCG rand;
+ Color polygon_color = debug_face_color;
- for (int i = 0; i < navigation_polygon->get_polygon_count(); i++) {
- // An array of vertices for this polygon.
- Vector<int> polygon = navigation_polygon->get_polygon(i);
- Vector<Vector2> debug_polygon_vertices;
- debug_polygon_vertices.resize(polygon.size());
- for (int j = 0; j < polygon.size(); j++) {
- ERR_FAIL_INDEX(polygon[j], navigation_polygon_vertices.size());
- debug_polygon_vertices.write[j] = navigation_polygon_vertices[polygon[j]];
+ int face_vertex_index = 0;
+ int line_vertex_index = 0;
+
+ Vector2 *face_vertex_array_ptrw = face_vertex_array.ptrw();
+ Color *face_color_array_ptrw = face_color_array.ptrw();
+ Vector2 *line_vertex_array_ptrw = line_vertex_array.ptrw();
+
+ for (int polygon_index = 0; polygon_index < polygon_count; polygon_index++) {
+ const Vector<int> &polygon_indices = navigation_polygon->get_polygon(polygon_index);
+ int polygon_indices_size = polygon_indices.size();
+ if (polygon_indices_size < 3) {
+ continue;
}
- // Generate the polygon color, slightly randomly modified from the settings one.
- Color random_variation_color = debug_face_color;
if (enabled_geometry_face_random_color) {
- random_variation_color.set_hsv(
- debug_face_color.get_h() + rand.random(-1.0, 1.0) * 0.1,
- debug_face_color.get_s(),
- debug_face_color.get_v() + rand.random(-1.0, 1.0) * 0.2);
+ // Generate the polygon color, slightly randomly modified from the settings one.
+ polygon_color.set_hsv(debug_face_color.get_h() + rand.random(-1.0, 1.0) * 0.1, debug_face_color.get_s(), debug_face_color.get_v() + rand.random(-1.0, 1.0) * 0.2);
+ polygon_color.a = debug_face_color.a;
}
- random_variation_color.a = debug_face_color.a;
- Vector<Color> debug_face_colors;
- debug_face_colors.push_back(random_variation_color);
- RS::get_singleton()->canvas_item_add_polygon(get_canvas_item(), debug_polygon_vertices, debug_face_colors);
+ for (int polygon_indices_index = 0; polygon_indices_index < polygon_indices_size - 2; polygon_indices_index++) {
+ face_vertex_array_ptrw[face_vertex_index] = vertices[polygon_indices[0]];
+ face_vertex_array_ptrw[face_vertex_index + 1] = vertices[polygon_indices[polygon_indices_index + 1]];
+ face_vertex_array_ptrw[face_vertex_index + 2] = vertices[polygon_indices[polygon_indices_index + 2]];
+ if (enabled_geometry_face_random_color) {
+ face_color_array_ptrw[face_vertex_index] = polygon_color;
+ face_color_array_ptrw[face_vertex_index + 1] = polygon_color;
+ face_color_array_ptrw[face_vertex_index + 2] = polygon_color;
+ }
+ face_vertex_index += 3;
+ }
if (enabled_edge_lines) {
- Vector<Color> debug_edge_colors;
- debug_edge_colors.push_back(debug_edge_color);
- debug_polygon_vertices.push_back(debug_polygon_vertices[0]); // Add first again for closing polyline.
- RS::get_singleton()->canvas_item_add_polyline(get_canvas_item(), debug_polygon_vertices, debug_edge_colors);
+ for (int polygon_indices_index = 0; polygon_indices_index < polygon_indices_size; polygon_indices_index++) {
+ line_vertex_array_ptrw[line_vertex_index] = vertices[polygon_indices[polygon_indices_index]];
+ line_vertex_index += 1;
+ if (polygon_indices_index + 1 == polygon_indices_size) {
+ line_vertex_array_ptrw[line_vertex_index] = vertices[polygon_indices[0]];
+ line_vertex_index += 1;
+ } else {
+ line_vertex_array_ptrw[line_vertex_index] = vertices[polygon_indices[polygon_indices_index + 1]];
+ line_vertex_index += 1;
+ }
+ }
}
}
+
+ if (!enabled_geometry_face_random_color) {
+ face_color_array.resize(face_vertex_array.size());
+ face_color_array.fill(debug_face_color);
+ }
+
+ Array face_mesh_array;
+ face_mesh_array.resize(Mesh::ARRAY_MAX);
+ face_mesh_array[Mesh::ARRAY_VERTEX] = face_vertex_array;
+ face_mesh_array[Mesh::ARRAY_COLOR] = face_color_array;
+
+ rs->mesh_add_surface_from_arrays(debug_mesh_rid, RS::PRIMITIVE_TRIANGLES, face_mesh_array, Array(), Dictionary(), RS::ARRAY_FLAG_USE_2D_VERTICES);
+
+ if (enabled_edge_lines) {
+ Vector<Color> line_color_array;
+ line_color_array.resize(line_vertex_array.size());
+ line_color_array.fill(debug_edge_color);
+
+ Array line_mesh_array;
+ line_mesh_array.resize(Mesh::ARRAY_MAX);
+ line_mesh_array[Mesh::ARRAY_VERTEX] = line_vertex_array;
+ line_mesh_array[Mesh::ARRAY_COLOR] = line_color_array;
+
+ rs->mesh_add_surface_from_arrays(debug_mesh_rid, RS::PRIMITIVE_LINES, line_mesh_array, Array(), Dictionary(), RS::ARRAY_FLAG_USE_2D_VERTICES);
+ }
+
+ rs->canvas_item_add_mesh(debug_instance_rid, debug_mesh_rid, Transform2D());
+ rs->canvas_item_set_visible(debug_instance_rid, is_visible_in_tree());
}
#endif // DEBUG_ENABLED
@@ -512,3 +626,19 @@ void NavigationRegion2D::_update_debug_baking_rect() {
}
}
#endif // DEBUG_ENABLED
+
+#ifdef DEBUG_ENABLED
+void NavigationRegion2D::_free_debug() {
+ RenderingServer *rs = RenderingServer::get_singleton();
+ ERR_FAIL_NULL(rs);
+ if (debug_instance_rid.is_valid()) {
+ rs->canvas_item_clear(debug_instance_rid);
+ rs->free(debug_instance_rid);
+ debug_instance_rid = RID();
+ }
+ if (debug_mesh_rid.is_valid()) {
+ rs->free(debug_mesh_rid);
+ debug_mesh_rid = RID();
+ }
+}
+#endif // DEBUG_ENABLED
diff --git a/scene/2d/navigation_region_2d.h b/scene/2d/navigation_region_2d.h
index 5a86dd607d..52101cb93e 100644
--- a/scene/2d/navigation_region_2d.h
+++ b/scene/2d/navigation_region_2d.h
@@ -52,6 +52,12 @@ class NavigationRegion2D : public Node2D {
#ifdef DEBUG_ENABLED
private:
+ RID debug_mesh_rid;
+ RID debug_instance_rid;
+
+ bool debug_mesh_dirty = true;
+
+ void _free_debug();
void _update_debug_mesh();
void _update_debug_edge_connections_mesh();
void _update_debug_baking_rect();
diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp
index d4c6ca3ea0..e600de6b8b 100644
--- a/scene/animation/animation_mixer.cpp
+++ b/scene/animation/animation_mixer.cpp
@@ -501,6 +501,7 @@ AnimationMixer::AnimationCallbackModeMethod AnimationMixer::get_callback_mode_me
void AnimationMixer::set_callback_mode_discrete(AnimationCallbackModeDiscrete p_mode) {
callback_mode_discrete = p_mode;
+ _clear_caches();
emit_signal(SNAME("mixer_updated"));
}
@@ -688,7 +689,7 @@ bool AnimationMixer::_update_caches() {
track_value->init_value = anim->track_get_key_value(i, 0);
track_value->init_value.zero();
- track_value->init_use_continuous = callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS;
+ track_value->is_init = false;
// Can't interpolate them, need to convert.
track_value->is_variant_interpolatable = Animation::is_variant_interpolatable(track_value->init_value);
@@ -698,7 +699,6 @@ bool AnimationMixer::_update_caches() {
int rt = reset_anim->find_track(path, track_src_type);
if (rt >= 0) {
if (track_src_type == Animation::TYPE_VALUE) {
- track_value->init_use_continuous = track_value->init_use_continuous || (reset_anim->value_track_get_update_mode(rt) != Animation::UPDATE_DISCRETE); // Take precedence Force Continuous.
if (reset_anim->track_get_key_count(rt) > 0) {
track_value->init_value = reset_anim->track_get_key_value(rt, 0);
}
@@ -1006,7 +1006,7 @@ void AnimationMixer::_blend_init() {
TrackCacheValue *t = static_cast<TrackCacheValue *>(track);
t->value = Animation::cast_to_blendwise(t->init_value);
t->element_size = t->init_value.is_string() ? (real_t)(t->init_value.operator String()).length() : 0;
- t->use_continuous = t->init_use_continuous;
+ t->use_continuous = false;
t->use_discrete = false;
} break;
case Animation::TYPE_AUDIO: {
@@ -1462,12 +1462,12 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
t->value = Animation::blend_variant(t->value, value, blend);
}
} else {
- t->use_discrete = true;
if (seeked) {
int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT, true);
if (idx < 0) {
continue;
}
+ t->use_discrete = true;
Variant value = a->track_get_key_value(i, idx);
value = post_process_key_value(a, i, value, t->object_id);
Object *t_obj = ObjectDB::get_instance(t->object_id);
@@ -1478,6 +1478,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
List<int> indices;
a->track_get_key_indices_in_range(i, time, delta, &indices, looped_flag);
for (int &F : indices) {
+ t->use_discrete = true;
Variant value = a->track_get_key_value(i, F);
value = post_process_key_value(a, i, value, t->object_id);
Object *t_obj = ObjectDB::get_instance(t->object_id);
@@ -1682,7 +1683,8 @@ void AnimationMixer::_blend_apply() {
// Finally, set the tracks.
for (const KeyValue<Animation::TypeHash, TrackCache *> &K : track_cache) {
TrackCache *track = K.value;
- if (!deterministic && Math::is_zero_approx(track->total_weight)) {
+ bool is_zero_amount = Math::is_zero_approx(track->total_weight);
+ if (!deterministic && is_zero_amount) {
continue;
}
switch (track->type) {
@@ -1742,10 +1744,24 @@ void AnimationMixer::_blend_apply() {
case Animation::TYPE_VALUE: {
TrackCacheValue *t = static_cast<TrackCacheValue *>(track);
- if (!t->is_variant_interpolatable || !t->use_continuous || (callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT && t->use_discrete)) {
+ if (t->use_discrete && !t->use_continuous) {
+ t->is_init = true; // If only disctere value is applied, no more RESET.
+ }
+
+ if ((t->is_init && (is_zero_amount || !t->use_continuous)) ||
+ (callback_mode_discrete != ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS &&
+ !is_zero_amount &&
+ callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT &&
+ t->use_discrete)) {
break; // Don't overwrite the value set by UPDATE_DISCRETE.
}
+ if (callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS) {
+ t->is_init = false; // Always update in Force Continuous.
+ } else {
+ t->is_init = !t->use_continuous; // If there is no Continuous in non-Force Continuous type, it means RESET.
+ }
+
// Trim unused elements if init array/string is not blended.
if (t->value.is_array()) {
int actual_blended_size = (int)Math::round(Math::abs(t->element_size.operator real_t()));
diff --git a/scene/animation/animation_mixer.h b/scene/animation/animation_mixer.h
index b7898cffc9..089a210193 100644
--- a/scene/animation/animation_mixer.h
+++ b/scene/animation/animation_mixer.h
@@ -126,7 +126,7 @@ protected:
/* ---- General settings for animation ---- */
AnimationCallbackModeProcess callback_mode_process = ANIMATION_CALLBACK_MODE_PROCESS_IDLE;
AnimationCallbackModeMethod callback_mode_method = ANIMATION_CALLBACK_MODE_METHOD_DEFERRED;
- AnimationCallbackModeDiscrete callback_mode_discrete = ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT;
+ AnimationCallbackModeDiscrete callback_mode_discrete = ANIMATION_CALLBACK_MODE_DISCRETE_RECESSIVE;
int audio_max_polyphony = 32;
NodePath root_node;
@@ -224,7 +224,7 @@ protected:
Vector<StringName> subpath;
// TODO: There are many boolean, can be packed into one integer.
- bool init_use_continuous = false;
+ bool is_init = false;
bool use_continuous = false;
bool use_discrete = false;
bool is_using_angle = false;
@@ -237,7 +237,7 @@ protected:
init_value(p_other.init_value),
value(p_other.value),
subpath(p_other.subpath),
- init_use_continuous(p_other.init_use_continuous),
+ is_init(p_other.is_init),
use_continuous(p_other.use_continuous),
use_discrete(p_other.use_discrete),
is_using_angle(p_other.is_using_angle),
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 435776843c..0c24d79ad7 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -447,10 +447,10 @@ void AnimationPlayer::_play(const StringName &p_name, double p_custom_blend, flo
} else {
if (p_from_end && c.current.pos == 0) {
// Animation reset but played backwards, set position to the end.
- c.current.pos = c.current.from->animation->get_length();
+ seek(c.current.from->animation->get_length(), true, true);
} else if (!p_from_end && c.current.pos == c.current.from->animation->get_length()) {
// Animation resumed but already ended, set position to the beginning.
- c.current.pos = 0;
+ seek(0, true, true);
} else if (playing) {
return;
}
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index 8131fe7aaa..c843bb8c44 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -3050,7 +3050,7 @@ void CodeEdit::_update_delimiter_cache(int p_from_line, int p_to_line) {
}
int CodeEdit::_is_in_delimiter(int p_line, int p_column, DelimiterType p_type) const {
- if (delimiters.size() == 0) {
+ if (delimiters.size() == 0 || p_line >= delimiter_cache.size()) {
return -1;
}
ERR_FAIL_INDEX_V(p_line, get_line_count(), 0);
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 1c175f9f95..0d5c69b207 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -211,17 +211,17 @@ void Control::get_argument_options(const StringName &p_function, int p_idx, List
const String pf = p_function;
Theme::DataType type = Theme::DATA_TYPE_MAX;
- if (pf == "add_theme_color_override" || pf == "has_theme_color" || pf == "has_theme_color_override" || pf == "get_theme_color") {
+ if (pf == "add_theme_color_override" || pf == "has_theme_color" || pf == "has_theme_color_override" || pf == "get_theme_color" || pf == "remove_theme_color_override") {
type = Theme::DATA_TYPE_COLOR;
- } else if (pf == "add_theme_constant_override" || pf == "has_theme_constant" || pf == "has_theme_constant_override" || pf == "get_theme_constant") {
+ } else if (pf == "add_theme_constant_override" || pf == "has_theme_constant" || pf == "has_theme_constant_override" || pf == "get_theme_constant" || pf == "remove_theme_constant_override") {
type = Theme::DATA_TYPE_CONSTANT;
- } else if (pf == "add_theme_font_override" || pf == "has_theme_font" || pf == "has_theme_font_override" || pf == "get_theme_font") {
+ } else if (pf == "add_theme_font_override" || pf == "has_theme_font" || pf == "has_theme_font_override" || pf == "get_theme_font" || pf == "remove_theme_font_override") {
type = Theme::DATA_TYPE_FONT;
- } else if (pf == "add_theme_font_size_override" || pf == "has_theme_font_size" || pf == "has_theme_font_size_override" || pf == "get_theme_font_size") {
+ } else if (pf == "add_theme_font_size_override" || pf == "has_theme_font_size" || pf == "has_theme_font_size_override" || pf == "get_theme_font_size" || pf == "remove_theme_font_size_override") {
type = Theme::DATA_TYPE_FONT_SIZE;
- } else if (pf == "add_theme_icon_override" || pf == "has_theme_icon" || pf == "has_theme_icon_override" || pf == "get_theme_icon") {
+ } else if (pf == "add_theme_icon_override" || pf == "has_theme_icon" || pf == "has_theme_icon_override" || pf == "get_theme_icon" || pf == "remove_theme_icon_override") {
type = Theme::DATA_TYPE_ICON;
- } else if (pf == "add_theme_style_override" || pf == "has_theme_style" || pf == "has_theme_style_override" || pf == "get_theme_style") {
+ } else if (pf == "add_theme_stylebox_override" || pf == "has_theme_stylebox" || pf == "has_theme_stylebox_override" || pf == "get_theme_stylebox" || pf == "remove_theme_stylebox_override") {
type = Theme::DATA_TYPE_STYLEBOX;
}
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 5122b0a155..f6942ca206 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -1607,8 +1607,34 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
if (p_meta) {
int64_t glyph_idx = TS->shaped_text_hit_test_grapheme(rid, p_click.x - rect.position.x);
if (glyph_idx >= 0) {
+ float baseline_y = rect.position.y + TS->shaped_text_get_ascent(rid);
const Glyph *glyphs = TS->shaped_text_get_glyphs(rid);
- char_pos = glyphs[glyph_idx].start;
+ if (glyphs[glyph_idx].flags & TextServer::GRAPHEME_IS_EMBEDDED_OBJECT) {
+ // Emebedded object.
+ for (int i = 0; i < objects.size(); i++) {
+ if (TS->shaped_text_get_object_glyph(rid, objects[i]) == glyph_idx) {
+ Rect2 obj_rect = TS->shaped_text_get_object_rect(rid, objects[i]);
+ obj_rect.position.y += baseline_y;
+ if (p_click.y >= obj_rect.position.y && p_click.y <= obj_rect.position.y + obj_rect.size.y) {
+ char_pos = glyphs[glyph_idx].start;
+ }
+ break;
+ }
+ }
+ } else if (glyphs[glyph_idx].font_rid != RID()) {
+ // Normal glyph.
+ float fa = TS->font_get_ascent(glyphs[glyph_idx].font_rid, glyphs[glyph_idx].font_size);
+ float fd = TS->font_get_descent(glyphs[glyph_idx].font_rid, glyphs[glyph_idx].font_size);
+ if (p_click.y >= baseline_y - fa && p_click.y <= baseline_y + fd) {
+ char_pos = glyphs[glyph_idx].start;
+ }
+ } else if (!(glyphs[glyph_idx].flags & TextServer::GRAPHEME_IS_VIRTUAL)) {
+ // Hex code box.
+ Vector2 gl_size = TS->get_hex_code_box_size(glyphs[glyph_idx].font_size, glyphs[glyph_idx].index);
+ if (p_click.y >= baseline_y - gl_size.y * 0.9 && p_click.y <= baseline_y + gl_size.y * 0.2) {
+ char_pos = glyphs[glyph_idx].start;
+ }
+ }
}
} else {
char_pos = TS->shaped_text_hit_test_position(rid, p_click.x - rect.position.x);
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 69b84da23d..a36eb0652e 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -1003,23 +1003,12 @@ void TextEdit::_notification(int p_what) {
}
}
- if (str.length() == 0) {
- // Draw line background if empty as we won't loop at all.
- if (caret_line_wrap_index_map.has(line) && caret_line_wrap_index_map[line].has(line_wrap_index) && highlight_current_line) {
- if (rtl) {
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - ofs_x - xmargin_end, ofs_y, xmargin_end, row_height), theme_cache.current_line_color);
- } else {
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(ofs_x, ofs_y, xmargin_end, row_height), theme_cache.current_line_color);
- }
- }
- } else {
- // If it has text, then draw current line marker in the margin, as line number etc will draw over it, draw the rest of line marker later.
- if (caret_line_wrap_index_map.has(line) && caret_line_wrap_index_map[line].has(line_wrap_index) && highlight_current_line) {
- if (rtl) {
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - ofs_x - xmargin_end, ofs_y, xmargin_end, row_height), theme_cache.current_line_color);
- } else {
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(ofs_x, ofs_y, xmargin_end, row_height), theme_cache.current_line_color);
- }
+ // Draw current line highlight.
+ if (highlight_current_line && caret_line_wrap_index_map.has(line) && caret_line_wrap_index_map[line].has(line_wrap_index)) {
+ if (rtl) {
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - ofs_x - xmargin_end, ofs_y, xmargin_end, row_height), theme_cache.current_line_color);
+ } else {
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(ofs_x, ofs_y, xmargin_end, row_height), theme_cache.current_line_color);
}
}
diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp
index df90257e03..c267ff93c6 100644
--- a/scene/gui/texture_button.cpp
+++ b/scene/gui/texture_button.cpp
@@ -178,13 +178,14 @@ void TextureButton::_notification(int p_what) {
texdraw = focused;
}
- if (texdraw.is_valid()) {
- size = texdraw->get_size();
- _texture_region = Rect2(Point2(), texdraw->get_size());
+ if (texdraw.is_valid() || click_mask.is_valid()) {
+ const Size2 texdraw_size = texdraw.is_valid() ? texdraw->get_size() : Size2(click_mask->get_size());
+
+ size = texdraw_size;
+ _texture_region = Rect2(Point2(), texdraw_size);
_tile = false;
switch (stretch_mode) {
case STRETCH_KEEP:
- size = texdraw->get_size();
break;
case STRETCH_SCALE:
size = get_size();
@@ -194,18 +195,17 @@ void TextureButton::_notification(int p_what) {
_tile = true;
break;
case STRETCH_KEEP_CENTERED:
- ofs = (get_size() - texdraw->get_size()) / 2;
- size = texdraw->get_size();
+ ofs = (get_size() - texdraw_size) / 2;
break;
case STRETCH_KEEP_ASPECT_CENTERED:
case STRETCH_KEEP_ASPECT: {
Size2 _size = get_size();
- float tex_width = texdraw->get_width() * _size.height / texdraw->get_height();
+ float tex_width = texdraw_size.width * _size.height / texdraw_size.height;
float tex_height = _size.height;
if (tex_width > _size.width) {
tex_width = _size.width;
- tex_height = texdraw->get_height() * tex_width / texdraw->get_width();
+ tex_height = texdraw_size.height * tex_width / texdraw_size.width;
}
if (stretch_mode == STRETCH_KEEP_ASPECT_CENTERED) {
@@ -217,10 +217,9 @@ void TextureButton::_notification(int p_what) {
} break;
case STRETCH_KEEP_ASPECT_COVERED: {
size = get_size();
- Size2 tex_size = texdraw->get_size();
- Size2 scale_size(size.width / tex_size.width, size.height / tex_size.height);
+ Size2 scale_size = size / texdraw_size;
float scale = scale_size.width > scale_size.height ? scale_size.width : scale_size.height;
- Size2 scaled_tex_size = tex_size * scale;
+ Size2 scaled_tex_size = texdraw_size * scale;
Point2 ofs2 = ((scaled_tex_size - size) / scale).abs() / 2.0f;
_texture_region = Rect2(ofs2, size / scale);
} break;
@@ -233,10 +232,12 @@ void TextureButton::_notification(int p_what) {
if (draw_focus_only) {
// Do nothing, we only needed to calculate the rectangle.
- } else if (_tile) {
- draw_texture_rect(texdraw, Rect2(ofs, size), _tile);
- } else {
- draw_texture_rect_region(texdraw, Rect2(ofs, size), _texture_region);
+ } else if (texdraw.is_valid()) {
+ if (_tile) {
+ draw_texture_rect(texdraw, Rect2(ofs, size), _tile);
+ } else {
+ draw_texture_rect_region(texdraw, Rect2(ofs, size), _texture_region);
+ }
}
} else {
_position_rect = Rect2();
diff --git a/scene/main/instance_placeholder.cpp b/scene/main/instance_placeholder.cpp
index fe23ca1800..f36bbe9395 100644
--- a/scene/main/instance_placeholder.cpp
+++ b/scene/main/instance_placeholder.cpp
@@ -88,16 +88,16 @@ Node *InstancePlaceholder::create_instance(bool p_replace, const Ref<PackedScene
if (!ps.is_valid()) {
return nullptr;
}
- Node *scene = ps->instantiate();
- if (!scene) {
+ Node *instance = ps->instantiate();
+ if (!instance) {
return nullptr;
}
- scene->set_name(get_name());
- scene->set_multiplayer_authority(get_multiplayer_authority());
+ instance->set_name(get_name());
+ instance->set_multiplayer_authority(get_multiplayer_authority());
int pos = get_index();
for (const PropSet &E : stored_values) {
- scene->set(E.name, E.value);
+ set_value_on_instance(this, instance, E);
}
if (p_replace) {
@@ -105,10 +105,125 @@ Node *InstancePlaceholder::create_instance(bool p_replace, const Ref<PackedScene
base->remove_child(this);
}
- base->add_child(scene);
- base->move_child(scene, pos);
+ base->add_child(instance);
+ base->move_child(instance, pos);
- return scene;
+ return instance;
+}
+
+// This method will attempt to set the correct values on the placeholder instance
+// for regular types this is trivial and unnecessary.
+// For nodes however this becomes a bit tricky because they might now have existed until the instantiation,
+// so this method will try to find the correct nodes and resolve them.
+void InstancePlaceholder::set_value_on_instance(InstancePlaceholder *p_placeholder, Node *p_instance, const PropSet &p_set) {
+ bool is_valid;
+
+ // If we don't have any info, we can't do anything,
+ // so try setting the value directly.
+ Variant current = p_instance->get(p_set.name, &is_valid);
+ if (!is_valid) {
+ p_instance->set(p_set.name, p_set.value, &is_valid);
+ return;
+ }
+
+ Variant::Type current_type = current.get_type();
+ Variant::Type placeholder_type = p_set.value.get_type();
+
+ // Arrays are a special case, because their containing type might be different.
+ if (current_type != Variant::Type::ARRAY) {
+ // Check if the variant types match.
+ if (Variant::evaluate(Variant::OP_EQUAL, current_type, placeholder_type)) {
+ p_instance->set(p_set.name, p_set.value, &is_valid);
+ if (is_valid) {
+ return;
+ }
+ // Types match but setting failed? This is strange, so let's print a warning!
+ WARN_PRINT(vformat("Property '%s' with type '%s' could not be set when creating instance of '%s'.", p_set.name, Variant::get_type_name(current_type), p_placeholder->get_name()));
+ return;
+ }
+ } else {
+ // We are dealing with an Array.
+ // Let's check if the subtype of the array matches first.
+ // This is needed because the set method of ScriptInstance checks for type,
+ // but the ClassDB set method doesn't! So we cannot reliably know what actually happens.
+ Array current_array = current;
+ Array placeholder_array = p_set.value;
+ if (current_array.is_same_typed(placeholder_array)) {
+ p_instance->set(p_set.name, p_set.value, &is_valid);
+ if (is_valid) {
+ return;
+ }
+ // Internal array types match but setting failed? This is strange, so let's print a warning!
+ WARN_PRINT(vformat("Array Property '%s' with type '%s' could not be set when creating instance of '%s'.", p_set.name, Variant::get_type_name(Variant::Type(current_array.get_typed_builtin())), p_placeholder->get_name()));
+ }
+ // Arrays are not the same internal type. This should be happening because we have a NodePath Array,
+ // but the instance wants a Node Array.
+ }
+
+ switch (current_type) {
+ case Variant::Type::NIL:
+ if (placeholder_type != Variant::Type::NODE_PATH) {
+ break;
+ }
+ // If it's nil but we have a NodePath, we guess what works.
+
+ p_instance->set(p_set.name, p_set.value, &is_valid);
+ if (is_valid) {
+ break;
+ }
+
+ p_instance->set(p_set.name, try_get_node(p_placeholder, p_instance, p_set.value), &is_valid);
+ break;
+ case Variant::Type::OBJECT:
+ if (placeholder_type != Variant::Type::NODE_PATH) {
+ break;
+ }
+ // Easiest case, we want a node, but we have a deferred NodePath.
+ p_instance->set(p_set.name, try_get_node(p_placeholder, p_instance, p_set.value));
+ break;
+ case Variant::Type::ARRAY: {
+ // If we have reached here it means our array types don't match,
+ // so we will convert the placeholder array into the correct type
+ // and resolve nodes if necessary.
+ Array current_array = current;
+ Array converted_array;
+ Array placeholder_array = p_set.value;
+ converted_array = current_array.duplicate();
+ converted_array.resize(placeholder_array.size());
+
+ if (Variant::evaluate(Variant::OP_EQUAL, current_array.get_typed_builtin(), Variant::Type::NODE_PATH)) {
+ // We want a typed NodePath array.
+ for (int i = 0; i < placeholder_array.size(); i++) {
+ converted_array.set(i, placeholder_array[i]);
+ }
+ } else {
+ // We want Nodes, convert NodePaths.
+ for (int i = 0; i < placeholder_array.size(); i++) {
+ converted_array.set(i, try_get_node(p_placeholder, p_instance, placeholder_array[i]));
+ }
+ }
+
+ p_instance->set(p_set.name, converted_array, &is_valid);
+ if (!is_valid) {
+ WARN_PRINT(vformat("Property '%s' with type '%s' could not be set when creating instance of '%s'.", p_set.name, Variant::get_type_name(current_type), p_placeholder->get_name()));
+ }
+ break;
+ }
+ default:
+ WARN_PRINT(vformat("Property '%s' with type '%s' could not be set when creating instance of '%s'.", p_set.name, Variant::get_type_name(current_type), p_placeholder->get_name()));
+ break;
+ }
+}
+
+Node *InstancePlaceholder::try_get_node(InstancePlaceholder *p_placeholder, Node *p_instance, const NodePath &p_path) {
+ // First try to resolve internally,
+ // if that fails try resolving externally.
+ Node *node = p_instance->get_node_or_null(p_path);
+ if (node == nullptr) {
+ node = p_placeholder->get_node_or_null(p_path);
+ }
+
+ return node;
}
Dictionary InstancePlaceholder::get_stored_values(bool p_with_order) {
diff --git a/scene/main/instance_placeholder.h b/scene/main/instance_placeholder.h
index 480474d0bd..ccf1e63a16 100644
--- a/scene/main/instance_placeholder.h
+++ b/scene/main/instance_placeholder.h
@@ -46,6 +46,10 @@ class InstancePlaceholder : public Node {
List<PropSet> stored_values;
+private:
+ void set_value_on_instance(InstancePlaceholder *p_placeholder, Node *p_instance, const PropSet &p_set);
+ Node *try_get_node(InstancePlaceholder *p_placeholder, Node *p_instance, const NodePath &p_path);
+
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
diff --git a/scene/resources/2d/tile_set.cpp b/scene/resources/2d/tile_set.cpp
index 0f73577768..6c3356a205 100644
--- a/scene/resources/2d/tile_set.cpp
+++ b/scene/resources/2d/tile_set.cpp
@@ -6378,7 +6378,7 @@ Ref<ConvexPolygonShape2D> TileData::get_collision_polygon_shape(int p_layer_id,
for (int i = 0; i < size; i++) {
Ref<ConvexPolygonShape2D> transformed_polygon;
transformed_polygon.instantiate();
- transformed_polygon->set_points(get_transformed_vertices(shapes_data.shapes[shape_index]->get_points(), p_flip_h, p_flip_v, p_transpose));
+ transformed_polygon->set_points(get_transformed_vertices(shapes_data.shapes[i]->get_points(), p_flip_h, p_flip_v, p_transpose));
shapes_data.transformed_shapes[key][i] = transformed_polygon;
}
return shapes_data.transformed_shapes[key][shape_index];
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index 5e50b9a240..0c57c6b7ba 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -314,6 +314,16 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
ERR_FAIL_INDEX_V(nprops[j].value, prop_count, nullptr);
if (nprops[j].name & FLAG_PATH_PROPERTY_IS_NODE) {
+ if (!Engine::get_singleton()->is_editor_hint() && node->get_scene_instance_load_placeholder()) {
+ // We cannot know if the referenced nodes exist yet, so instead of deferring, we write the NodePaths directly.
+
+ uint32_t name_idx = nprops[j].name & (FLAG_PATH_PROPERTY_IS_NODE - 1);
+ ERR_FAIL_UNSIGNED_INDEX_V(name_idx, (uint32_t)sname_count, nullptr);
+
+ node->set(snames[name_idx], props[nprops[j].value], &valid);
+ continue;
+ }
+
uint32_t name_idx = nprops[j].name & (FLAG_PATH_PROPERTY_IS_NODE - 1);
ERR_FAIL_UNSIGNED_INDEX_V(name_idx, (uint32_t)sname_count, nullptr);
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
index 068f9d9ef2..aae32f0b3e 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
@@ -972,14 +972,14 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con
float distance = 0.0;
// Check if camera is NOT inside the mesh AABB.
- if (!inst->transformed_aabb.has_point(p_render_data->scene_data->cam_transform.origin)) {
+ if (!inst->transformed_aabb.has_point(p_render_data->scene_data->main_cam_transform.origin)) {
// Get the LOD support points on the mesh AABB.
- Vector3 lod_support_min = inst->transformed_aabb.get_support(p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z));
- Vector3 lod_support_max = inst->transformed_aabb.get_support(-p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z));
+ Vector3 lod_support_min = inst->transformed_aabb.get_support(p_render_data->scene_data->main_cam_transform.basis.get_column(Vector3::AXIS_Z));
+ Vector3 lod_support_max = inst->transformed_aabb.get_support(-p_render_data->scene_data->main_cam_transform.basis.get_column(Vector3::AXIS_Z));
// Get the distances to those points on the AABB from the camera origin.
- float distance_min = (float)p_render_data->scene_data->cam_transform.origin.distance_to(lod_support_min);
- float distance_max = (float)p_render_data->scene_data->cam_transform.origin.distance_to(lod_support_max);
+ float distance_min = (float)p_render_data->scene_data->main_cam_transform.origin.distance_to(lod_support_min);
+ float distance_max = (float)p_render_data->scene_data->main_cam_transform.origin.distance_to(lod_support_max);
if (distance_min * distance_max < 0.0) {
//crossing plane
@@ -1388,7 +1388,6 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo
p_render_data->shadows.clear();
p_render_data->directional_shadows.clear();
- Plane camera_plane(-p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z), p_render_data->scene_data->cam_transform.origin);
float lod_distance_multiplier = p_render_data->scene_data->cam_projection.get_lod_multiplier();
{
for (int i = 0; i < p_render_data->render_shadow_count; i++) {
@@ -1407,7 +1406,7 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo
RENDER_TIMESTAMP("Render OmniLight Shadows");
// Cube shadows are rendered in their own way.
for (const int &index : p_render_data->cube_shadows) {
- _render_shadow_pass(p_render_data->render_shadows[index].light, p_render_data->shadow_atlas, p_render_data->render_shadows[index].pass, p_render_data->render_shadows[index].instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, true, true, true, p_render_data->render_info, viewport_size, p_render_data->scene_data->cam_transform);
+ _render_shadow_pass(p_render_data->render_shadows[index].light, p_render_data->shadow_atlas, p_render_data->render_shadows[index].pass, p_render_data->render_shadows[index].instances, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, true, true, true, p_render_data->render_info, viewport_size, p_render_data->scene_data->cam_transform);
}
if (p_render_data->directional_shadows.size()) {
@@ -1437,11 +1436,11 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo
//render directional shadows
for (uint32_t i = 0; i < p_render_data->directional_shadows.size(); i++) {
- _render_shadow_pass(p_render_data->render_shadows[p_render_data->directional_shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[p_render_data->directional_shadows[i]].pass, p_render_data->render_shadows[p_render_data->directional_shadows[i]].instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, false, i == p_render_data->directional_shadows.size() - 1, false, p_render_data->render_info, viewport_size, p_render_data->scene_data->cam_transform);
+ _render_shadow_pass(p_render_data->render_shadows[p_render_data->directional_shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[p_render_data->directional_shadows[i]].pass, p_render_data->render_shadows[p_render_data->directional_shadows[i]].instances, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, false, i == p_render_data->directional_shadows.size() - 1, false, p_render_data->render_info, viewport_size, p_render_data->scene_data->cam_transform);
}
//render positional shadows
for (uint32_t i = 0; i < p_render_data->shadows.size(); i++) {
- _render_shadow_pass(p_render_data->render_shadows[p_render_data->shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[p_render_data->shadows[i]].pass, p_render_data->render_shadows[p_render_data->shadows[i]].instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, i == 0, i == p_render_data->shadows.size() - 1, true, p_render_data->render_info, viewport_size, p_render_data->scene_data->cam_transform);
+ _render_shadow_pass(p_render_data->render_shadows[p_render_data->shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[p_render_data->shadows[i]].pass, p_render_data->render_shadows[p_render_data->shadows[i]].instances, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, i == 0, i == p_render_data->shadows.size() - 1, true, p_render_data->render_info, viewport_size, p_render_data->scene_data->cam_transform);
}
_render_shadow_process();
@@ -2371,7 +2370,7 @@ void RenderForwardClustered::_render_buffers_debug_draw(const RenderDataRD *p_re
}
}
-void RenderForwardClustered::_render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, const Plane &p_camera_plane, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, bool p_open_pass, bool p_close_pass, bool p_clear_region, RenderingMethod::RenderInfo *p_render_info, const Size2i &p_viewport_size, const Transform3D &p_main_cam_transform) {
+void RenderForwardClustered::_render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, bool p_open_pass, bool p_close_pass, bool p_clear_region, RenderingMethod::RenderInfo *p_render_info, const Size2i &p_viewport_size, const Transform3D &p_main_cam_transform) {
RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton();
ERR_FAIL_COND(!light_storage->owns_light_instance(p_light));
@@ -2526,7 +2525,7 @@ void RenderForwardClustered::_render_shadow_pass(RID p_light, RID p_shadow_atlas
if (render_cubemap) {
//rendering to cubemap
- _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, reverse_cull_face, false, false, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, Rect2(), false, true, true, true, p_render_info, p_viewport_size, p_main_cam_transform);
+ _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, reverse_cull_face, false, false, use_pancake, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, Rect2(), false, true, true, true, p_render_info, p_viewport_size, p_main_cam_transform);
if (finalize_cubemap) {
_render_shadow_process();
_render_shadow_end();
@@ -2544,7 +2543,7 @@ void RenderForwardClustered::_render_shadow_pass(RID p_light, RID p_shadow_atlas
} else {
//render shadow
- _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, reverse_cull_face, using_dual_paraboloid, using_dual_paraboloid_flip, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, atlas_rect, flip_y, p_clear_region, p_open_pass, p_close_pass, p_render_info, p_viewport_size, p_main_cam_transform);
+ _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, reverse_cull_face, using_dual_paraboloid, using_dual_paraboloid_flip, use_pancake, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, atlas_rect, flip_y, p_clear_region, p_open_pass, p_close_pass, p_render_info, p_viewport_size, p_main_cam_transform);
}
}
@@ -2557,7 +2556,7 @@ void RenderForwardClustered::_render_shadow_begin() {
scene_state.instance_data[RENDER_LIST_SECONDARY].clear();
}
-void RenderForwardClustered::_render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_reverse_cull_face, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, const Rect2i &p_rect, bool p_flip_y, bool p_clear_region, bool p_begin, bool p_end, RenderingMethod::RenderInfo *p_render_info, const Size2i &p_viewport_size, const Transform3D &p_main_cam_transform) {
+void RenderForwardClustered::_render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_reverse_cull_face, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, const Rect2i &p_rect, bool p_flip_y, bool p_clear_region, bool p_begin, bool p_end, RenderingMethod::RenderInfo *p_render_info, const Size2i &p_viewport_size, const Transform3D &p_main_cam_transform) {
uint32_t shadow_pass_index = scene_state.shadow_passes.size();
SceneState::ShadowPass shadow_pass;
@@ -2615,7 +2614,6 @@ void RenderForwardClustered::_render_shadow_append(RID p_framebuffer, const Page
shadow_pass.pass_mode = pass_mode;
shadow_pass.rp_uniform_set = RID(); //will be filled later when instance buffer is complete
- shadow_pass.camera_plane = p_camera_plane;
shadow_pass.screen_mesh_lod_threshold = scene_data.screen_mesh_lod_threshold;
shadow_pass.lod_distance_multiplier = scene_data.lod_distance_multiplier;
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
index 1f12d92754..ae9e5e7c10 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
@@ -347,7 +347,6 @@ class RenderForwardClustered : public RendererSceneRenderRD {
PassMode pass_mode;
RID rp_uniform_set;
- Plane camera_plane;
float lod_distance_multiplier;
float screen_mesh_lod_threshold;
@@ -594,9 +593,9 @@ class RenderForwardClustered : public RendererSceneRenderRD {
/* Render shadows */
- void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, bool p_open_pass = true, bool p_close_pass = true, bool p_clear_region = true, RenderingMethod::RenderInfo *p_render_info = nullptr, const Size2i &p_viewport_size = Size2i(1, 1), const Transform3D &p_main_cam_transform = Transform3D());
+ void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, bool p_open_pass = true, bool p_close_pass = true, bool p_clear_region = true, RenderingMethod::RenderInfo *p_render_info = nullptr, const Size2i &p_viewport_size = Size2i(1, 1), const Transform3D &p_main_cam_transform = Transform3D());
void _render_shadow_begin();
- void _render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_reverse_cull_face, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, const Rect2i &p_rect = Rect2i(), bool p_flip_y = false, bool p_clear_region = true, bool p_begin = true, bool p_end = true, RenderingMethod::RenderInfo *p_render_info = nullptr, const Size2i &p_viewport_size = Size2i(1, 1), const Transform3D &p_main_cam_transform = Transform3D());
+ void _render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_reverse_cull_face, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, const Rect2i &p_rect = Rect2i(), bool p_flip_y = false, bool p_clear_region = true, bool p_begin = true, bool p_end = true, RenderingMethod::RenderInfo *p_render_info = nullptr, const Size2i &p_viewport_size = Size2i(1, 1), const Transform3D &p_main_cam_transform = Transform3D());
void _render_shadow_process();
void _render_shadow_end();
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
index bd45dbfbc9..194a70dc22 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -590,7 +590,6 @@ void RenderForwardMobile::_pre_opaque_render(RenderDataRD *p_render_data) {
p_render_data->shadows.clear();
p_render_data->directional_shadows.clear();
- Plane camera_plane(-p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z), p_render_data->scene_data->cam_transform.origin);
float lod_distance_multiplier = p_render_data->scene_data->cam_projection.get_lod_multiplier();
{
for (int i = 0; i < p_render_data->render_shadow_count; i++) {
@@ -608,7 +607,7 @@ void RenderForwardMobile::_pre_opaque_render(RenderDataRD *p_render_data) {
//cube shadows are rendered in their own way
for (const int &index : p_render_data->cube_shadows) {
- _render_shadow_pass(p_render_data->render_shadows[index].light, p_render_data->shadow_atlas, p_render_data->render_shadows[index].pass, p_render_data->render_shadows[index].instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, true, true, true, p_render_data->render_info, p_render_data->scene_data->cam_transform);
+ _render_shadow_pass(p_render_data->render_shadows[index].light, p_render_data->shadow_atlas, p_render_data->render_shadows[index].pass, p_render_data->render_shadows[index].instances, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, true, true, true, p_render_data->render_info, p_render_data->scene_data->cam_transform);
}
if (p_render_data->directional_shadows.size()) {
@@ -629,11 +628,11 @@ void RenderForwardMobile::_pre_opaque_render(RenderDataRD *p_render_data) {
//render directional shadows
for (uint32_t i = 0; i < p_render_data->directional_shadows.size(); i++) {
- _render_shadow_pass(p_render_data->render_shadows[p_render_data->directional_shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[p_render_data->directional_shadows[i]].pass, p_render_data->render_shadows[p_render_data->directional_shadows[i]].instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, false, i == p_render_data->directional_shadows.size() - 1, false, p_render_data->render_info, p_render_data->scene_data->cam_transform);
+ _render_shadow_pass(p_render_data->render_shadows[p_render_data->directional_shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[p_render_data->directional_shadows[i]].pass, p_render_data->render_shadows[p_render_data->directional_shadows[i]].instances, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, false, i == p_render_data->directional_shadows.size() - 1, false, p_render_data->render_info, p_render_data->scene_data->cam_transform);
}
//render positional shadows
for (uint32_t i = 0; i < p_render_data->shadows.size(); i++) {
- _render_shadow_pass(p_render_data->render_shadows[p_render_data->shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[p_render_data->shadows[i]].pass, p_render_data->render_shadows[p_render_data->shadows[i]].instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, i == 0, i == p_render_data->shadows.size() - 1, true, p_render_data->render_info, p_render_data->scene_data->cam_transform);
+ _render_shadow_pass(p_render_data->render_shadows[p_render_data->shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[p_render_data->shadows[i]].pass, p_render_data->render_shadows[p_render_data->shadows[i]].instances, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, i == 0, i == p_render_data->shadows.size() - 1, true, p_render_data->render_info, p_render_data->scene_data->cam_transform);
}
_render_shadow_process();
@@ -1115,7 +1114,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
/* these are being called from RendererSceneRenderRD::_pre_opaque_render */
-void RenderForwardMobile::_render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, const Plane &p_camera_plane, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, bool p_open_pass, bool p_close_pass, bool p_clear_region, RenderingMethod::RenderInfo *p_render_info, const Transform3D &p_main_cam_transform) {
+void RenderForwardMobile::_render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, bool p_open_pass, bool p_close_pass, bool p_clear_region, RenderingMethod::RenderInfo *p_render_info, const Transform3D &p_main_cam_transform) {
RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton();
ERR_FAIL_COND(!light_storage->owns_light_instance(p_light));
@@ -1269,7 +1268,7 @@ void RenderForwardMobile::_render_shadow_pass(RID p_light, RID p_shadow_atlas, i
if (render_cubemap) {
//rendering to cubemap
- _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, false, false, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, Rect2(), false, true, true, true, p_render_info, p_main_cam_transform);
+ _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, false, false, use_pancake, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, Rect2(), false, true, true, true, p_render_info, p_main_cam_transform);
if (finalize_cubemap) {
_render_shadow_process();
_render_shadow_end();
@@ -1288,7 +1287,7 @@ void RenderForwardMobile::_render_shadow_pass(RID p_light, RID p_shadow_atlas, i
} else {
//render shadow
- _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, using_dual_paraboloid, using_dual_paraboloid_flip, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, atlas_rect, flip_y, p_clear_region, p_open_pass, p_close_pass, p_render_info, p_main_cam_transform);
+ _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, using_dual_paraboloid, using_dual_paraboloid_flip, use_pancake, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, atlas_rect, flip_y, p_clear_region, p_open_pass, p_close_pass, p_render_info, p_main_cam_transform);
}
}
@@ -1300,7 +1299,7 @@ void RenderForwardMobile::_render_shadow_begin() {
render_list[RENDER_LIST_SECONDARY].clear();
}
-void RenderForwardMobile::_render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, const Rect2i &p_rect, bool p_flip_y, bool p_clear_region, bool p_begin, bool p_end, RenderingMethod::RenderInfo *p_render_info, const Transform3D &p_main_cam_transform) {
+void RenderForwardMobile::_render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, const Rect2i &p_rect, bool p_flip_y, bool p_clear_region, bool p_begin, bool p_end, RenderingMethod::RenderInfo *p_render_info, const Transform3D &p_main_cam_transform) {
uint32_t shadow_pass_index = scene_state.shadow_passes.size();
SceneState::ShadowPass shadow_pass;
@@ -1357,7 +1356,6 @@ void RenderForwardMobile::_render_shadow_append(RID p_framebuffer, const PagedAr
shadow_pass.pass_mode = pass_mode;
shadow_pass.rp_uniform_set = RID(); //will be filled later when instance buffer is complete
- shadow_pass.camera_plane = p_camera_plane;
shadow_pass.screen_mesh_lod_threshold = scene_data.screen_mesh_lod_threshold;
shadow_pass.lod_distance_multiplier = scene_data.lod_distance_multiplier;
@@ -1882,14 +1880,14 @@ void RenderForwardMobile::_fill_render_list(RenderListType p_render_list, const
float distance = 0.0;
// Check if camera is NOT inside the mesh AABB.
- if (!inst->transformed_aabb.has_point(p_render_data->scene_data->cam_transform.origin)) {
+ if (!inst->transformed_aabb.has_point(p_render_data->scene_data->main_cam_transform.origin)) {
// Get the LOD support points on the mesh AABB.
- Vector3 lod_support_min = inst->transformed_aabb.get_support(p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z));
- Vector3 lod_support_max = inst->transformed_aabb.get_support(-p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z));
+ Vector3 lod_support_min = inst->transformed_aabb.get_support(p_render_data->scene_data->main_cam_transform.basis.get_column(Vector3::AXIS_Z));
+ Vector3 lod_support_max = inst->transformed_aabb.get_support(-p_render_data->scene_data->main_cam_transform.basis.get_column(Vector3::AXIS_Z));
// Get the distances to those points on the AABB from the camera origin.
- float distance_min = (float)p_render_data->scene_data->cam_transform.origin.distance_to(lod_support_min);
- float distance_max = (float)p_render_data->scene_data->cam_transform.origin.distance_to(lod_support_max);
+ float distance_min = (float)p_render_data->scene_data->main_cam_transform.origin.distance_to(lod_support_min);
+ float distance_max = (float)p_render_data->scene_data->main_cam_transform.origin.distance_to(lod_support_max);
if (distance_min * distance_max < 0.0) {
//crossing plane
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
index f29503e5ec..aa1b8f34b2 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
@@ -178,9 +178,9 @@ private:
/* Render shadows */
- void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, bool p_open_pass = true, bool p_close_pass = true, bool p_clear_region = true, RenderingMethod::RenderInfo *p_render_info = nullptr, const Transform3D &p_main_cam_transform = Transform3D());
+ void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, bool p_open_pass = true, bool p_close_pass = true, bool p_clear_region = true, RenderingMethod::RenderInfo *p_render_info = nullptr, const Transform3D &p_main_cam_transform = Transform3D());
void _render_shadow_begin();
- void _render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, const Rect2i &p_rect = Rect2i(), bool p_flip_y = false, bool p_clear_region = true, bool p_begin = true, bool p_end = true, RenderingMethod::RenderInfo *p_render_info = nullptr, const Transform3D &p_main_cam_transform = Transform3D());
+ void _render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, const Rect2i &p_rect = Rect2i(), bool p_flip_y = false, bool p_clear_region = true, bool p_begin = true, bool p_end = true, RenderingMethod::RenderInfo *p_render_info = nullptr, const Transform3D &p_main_cam_transform = Transform3D());
void _render_shadow_process();
void _render_shadow_end();
@@ -270,7 +270,6 @@ private:
PassMode pass_mode;
RID rp_uniform_set;
- Plane camera_plane;
float lod_distance_multiplier;
float screen_mesh_lod_threshold;
diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp
index 87f608bfe6..a4ee33ecc0 100644
--- a/servers/rendering/shader_compiler.cpp
+++ b/servers/rendering/shader_compiler.cpp
@@ -362,7 +362,7 @@ void ShaderCompiler::_dump_function_deps(const SL::ShaderNode *p_node, const Str
}
header += _constr(fnode->arguments[i].is_const);
if (fnode->arguments[i].type == SL::TYPE_STRUCT) {
- header += _qualstr(fnode->arguments[i].qualifier) + _mkid(fnode->arguments[i].type_str) + " " + _mkid(fnode->arguments[i].name);
+ header += _qualstr(fnode->arguments[i].qualifier) + _mkid(fnode->arguments[i].struct_name) + " " + _mkid(fnode->arguments[i].name);
} else {
header += _qualstr(fnode->arguments[i].qualifier) + _prestr(fnode->arguments[i].precision) + _typestr(fnode->arguments[i].type) + " " + _mkid(fnode->arguments[i].name);
}
@@ -743,7 +743,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
gcode += _constr(true);
gcode += _prestr(cnode.precision, ShaderLanguage::is_float_type(cnode.type));
if (cnode.type == SL::TYPE_STRUCT) {
- gcode += _mkid(cnode.type_str);
+ gcode += _mkid(cnode.struct_name);
} else {
gcode += _typestr(cnode.type);
}
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 5a02980929..1a4c8ea742 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -1389,7 +1389,7 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea
*r_data_type = function->arguments[i].type;
}
if (r_struct_name) {
- *r_struct_name = function->arguments[i].type_str;
+ *r_struct_name = function->arguments[i].struct_name;
}
if (r_array_size) {
*r_array_size = function->arguments[i].array_size;
@@ -1442,7 +1442,7 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea
*r_array_size = shader->constants[p_identifier].array_size;
}
if (r_struct_name) {
- *r_struct_name = shader->constants[p_identifier].type_str;
+ *r_struct_name = shader->constants[p_identifier].struct_name;
}
if (r_constant_value) {
if (shader->constants[p_identifier].initializer && shader->constants[p_identifier].initializer->values.size() == 1) {
@@ -3432,7 +3432,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
}
String func_arg_name;
if (pfunc->arguments[j].type == TYPE_STRUCT) {
- func_arg_name = pfunc->arguments[j].type_str;
+ func_arg_name = pfunc->arguments[j].struct_name;
} else {
func_arg_name = get_datatype_name(pfunc->arguments[j].type);
}
@@ -3455,10 +3455,10 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
for (int j = 0; j < args.size(); j++) {
if (get_scalar_type(args[j]) == args[j] && p_func->arguments[j + 1]->type == Node::NODE_TYPE_CONSTANT && args3[j] == 0 && convert_constant(static_cast<ConstantNode *>(p_func->arguments[j + 1]), pfunc->arguments[j].type)) {
//all good, but it needs implicit conversion later
- } else if (args[j] != pfunc->arguments[j].type || (args[j] == TYPE_STRUCT && args2[j] != pfunc->arguments[j].type_str) || args3[j] != pfunc->arguments[j].array_size) {
+ } else if (args[j] != pfunc->arguments[j].type || (args[j] == TYPE_STRUCT && args2[j] != pfunc->arguments[j].struct_name) || args3[j] != pfunc->arguments[j].array_size) {
String func_arg_name;
if (pfunc->arguments[j].type == TYPE_STRUCT) {
- func_arg_name = pfunc->arguments[j].type_str;
+ func_arg_name = pfunc->arguments[j].struct_name;
} else {
func_arg_name = get_datatype_name(pfunc->arguments[j].type);
}
@@ -9228,7 +9228,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
ShaderNode::Constant constant;
constant.name = name;
constant.type = is_struct ? TYPE_STRUCT : type;
- constant.type_str = struct_name;
+ constant.struct_name = struct_name;
constant.precision = precision;
constant.initializer = nullptr;
constant.array_size = array_size;
@@ -9407,7 +9407,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
expr->datatype = constant.type;
- expr->struct_name = constant.type_str;
+ expr->struct_name = constant.struct_name;
expr->array_size = constant.array_size;
@@ -9748,7 +9748,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
FunctionNode::Argument arg;
arg.type = param_type;
arg.name = param_name;
- arg.type_str = param_struct_name;
+ arg.struct_name = param_struct_name;
arg.precision = param_precision;
arg.qualifier = param_qualifier;
arg.tex_argument_check = false;
@@ -10371,7 +10371,11 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_
if (shader->vfunctions[i].name == completion_function) {
String calltip;
- calltip += get_datatype_name(shader->vfunctions[i].function->return_type);
+ if (shader->vfunctions[i].function->return_type == TYPE_STRUCT) {
+ calltip += String(shader->vfunctions[i].function->return_struct_name);
+ } else {
+ calltip += get_datatype_name(shader->vfunctions[i].function->return_type);
+ }
if (shader->vfunctions[i].function->return_array_size > 0) {
calltip += "[";
@@ -10406,7 +10410,11 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_
}
}
- calltip += get_datatype_name(shader->vfunctions[i].function->arguments[j].type);
+ if (shader->vfunctions[i].function->arguments[j].type == TYPE_STRUCT) {
+ calltip += String(shader->vfunctions[i].function->arguments[j].struct_name);
+ } else {
+ calltip += get_datatype_name(shader->vfunctions[i].function->arguments[j].type);
+ }
calltip += " ";
calltip += shader->vfunctions[i].function->arguments[j].name;
diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h
index 816a202b50..38f304ff31 100644
--- a/servers/rendering/shader_language.h
+++ b/servers/rendering/shader_language.h
@@ -583,7 +583,7 @@ public:
ArgumentQualifier qualifier;
StringName name;
DataType type;
- StringName type_str;
+ StringName struct_name;
DataPrecision precision;
//for passing textures as arguments
bool tex_argument_check;
@@ -618,7 +618,7 @@ public:
struct Constant {
StringName name;
DataType type;
- StringName type_str;
+ StringName struct_name;
DataPrecision precision;
ConstantNode *initializer = nullptr;
int array_size;
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 5cfd8d3cd2..7637d4e7da 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -2229,6 +2229,7 @@ void RenderingServer::_bind_methods() {
BIND_CONSTANT(MAX_GLOW_LEVELS);
BIND_CONSTANT(MAX_CURSORS);
BIND_CONSTANT(MAX_2D_DIRECTIONAL_LIGHTS);
+ BIND_CONSTANT(MAX_MESH_SURFACES);
/* TEXTURE */
diff --git a/tests/scene/test_instance_placeholder.h b/tests/scene/test_instance_placeholder.h
new file mode 100644
index 0000000000..8e8cf7c9df
--- /dev/null
+++ b/tests/scene/test_instance_placeholder.h
@@ -0,0 +1,532 @@
+/**************************************************************************/
+/* test_instance_placeholder.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef TEST_INSTANCE_PLACEHOLDER_H
+#define TEST_INSTANCE_PLACEHOLDER_H
+
+#include "scene/main/instance_placeholder.h"
+#include "scene/resources/packed_scene.h"
+
+#include "tests/test_macros.h"
+
+class _TestInstancePlaceholderNode : public Node {
+ GDCLASS(_TestInstancePlaceholderNode, Node);
+
+protected:
+ static void _bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_int_property", "int_property"), &_TestInstancePlaceholderNode::set_int_property);
+ ClassDB::bind_method(D_METHOD("get_int_property"), &_TestInstancePlaceholderNode::get_int_property);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "int_property"), "set_int_property", "get_int_property");
+
+ ClassDB::bind_method(D_METHOD("set_reference_property", "reference_property"), &_TestInstancePlaceholderNode::set_reference_property);
+ ClassDB::bind_method(D_METHOD("get_reference_property"), &_TestInstancePlaceholderNode::get_reference_property);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "reference_property", PROPERTY_HINT_NODE_TYPE), "set_reference_property", "get_reference_property");
+
+ ClassDB::bind_method(D_METHOD("set_reference_array_property", "reference_array_property"), &_TestInstancePlaceholderNode::set_reference_array_property);
+ ClassDB::bind_method(D_METHOD("get_reference_array_property"), &_TestInstancePlaceholderNode::get_reference_array_property);
+
+ // The hint string value "24/34:Node" is determined from existing PackedScenes with typed Array properties.
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "reference_array_property", PROPERTY_HINT_TYPE_STRING, "24/34:Node"), "set_reference_array_property", "get_reference_array_property");
+ }
+
+public:
+ int int_property = 0;
+
+ void set_int_property(int p_int) {
+ int_property = p_int;
+ }
+
+ int get_int_property() const {
+ return int_property;
+ }
+
+ Variant reference_property;
+
+ void set_reference_property(const Variant &p_node) {
+ reference_property = p_node;
+ }
+
+ Variant get_reference_property() const {
+ return reference_property;
+ }
+
+ Array reference_array_property;
+
+ void set_reference_array_property(const Array &p_array) {
+ reference_array_property = p_array;
+ }
+
+ Array get_reference_array_property() const {
+ return reference_array_property;
+ }
+
+ _TestInstancePlaceholderNode() {
+ reference_array_property.set_typed(Variant::OBJECT, "Node", Variant());
+ }
+};
+
+namespace TestInstancePlaceholder {
+
+TEST_CASE("[SceneTree][InstancePlaceholder] Instantiate from placeholder with no overrides") {
+ GDREGISTER_CLASS(_TestInstancePlaceholderNode);
+
+ SUBCASE("with non-node values") {
+ InstancePlaceholder *ip = memnew(InstancePlaceholder);
+ ip->set_name("TestScene");
+ Node *root = memnew(Node);
+ SceneTree::get_singleton()->get_root()->add_child(root);
+
+ root->add_child(ip);
+ // Create a scene to instance.
+ _TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);
+ scene->set_int_property(12);
+
+ // Pack the scene.
+ PackedScene *packed_scene = memnew(PackedScene);
+ const Error err = packed_scene->pack(scene);
+ REQUIRE(err == OK);
+
+ // Instantiate the scene.
+ _TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));
+ REQUIRE(created != nullptr);
+ CHECK(created->get_name() == "TestScene");
+ CHECK(created->get_int_property() == 12);
+
+ root->queue_free();
+ memdelete(scene);
+ }
+
+ SUBCASE("with node value") {
+ InstancePlaceholder *ip = memnew(InstancePlaceholder);
+ ip->set_name("TestScene");
+ Node *root = memnew(Node);
+ SceneTree::get_singleton()->get_root()->add_child(root);
+
+ root->add_child(ip);
+ // Create a scene to instance.
+ _TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);
+ Node *referenced = memnew(Node);
+ scene->add_child(referenced);
+ referenced->set_owner(scene);
+ scene->set_reference_property(referenced);
+ // Pack the scene.
+ PackedScene *packed_scene = memnew(PackedScene);
+ const Error err = packed_scene->pack(scene);
+ REQUIRE(err == OK);
+
+ // Instantiate the scene.
+ _TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));
+ REQUIRE(created != nullptr);
+ CHECK(created->get_name() == "TestScene");
+ CHECK(created->get_child_count() == 1);
+ CHECK(created->get_reference_property().identity_compare(created->get_child(0, false)));
+ CHECK_FALSE(created->get_reference_property().identity_compare(referenced));
+
+ root->queue_free();
+ memdelete(scene);
+ }
+
+ SUBCASE("with node-array value") {
+ InstancePlaceholder *ip = memnew(InstancePlaceholder);
+ ip->set_name("TestScene");
+ Node *root = memnew(Node);
+ SceneTree::get_singleton()->get_root()->add_child(root);
+
+ root->add_child(ip);
+ // Create a scene to instance.
+ _TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);
+ Node *referenced1 = memnew(Node);
+ Node *referenced2 = memnew(Node);
+ scene->add_child(referenced1);
+ scene->add_child(referenced2);
+ referenced1->set_owner(scene);
+ referenced2->set_owner(scene);
+ Array node_array;
+ node_array.set_typed(Variant::OBJECT, "Node", Variant());
+ node_array.push_back(referenced1);
+ node_array.push_back(referenced2);
+ scene->set_reference_array_property(node_array);
+ // Pack the scene.
+ PackedScene *packed_scene = memnew(PackedScene);
+ const Error err = packed_scene->pack(scene);
+ REQUIRE(err == OK);
+
+ // Instantiate the scene.
+ _TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));
+ REQUIRE(created != nullptr);
+ CHECK(created->get_name() == "TestScene");
+ CHECK(created->get_child_count() == 2);
+ Array created_array = created->get_reference_array_property();
+ REQUIRE(created_array.size() == node_array.size());
+ REQUIRE(created_array.size() == created->get_child_count());
+
+ // Iterate over all nodes, since the ordering is not guaranteed.
+ for (int i = 0; i < node_array.size(); i++) {
+ bool node_found = false;
+ for (int j = 0; j < created->get_child_count(); j++) {
+ if (created_array[i].identity_compare(created->get_child(j, true))) {
+ node_found = true;
+ }
+ }
+ CHECK(node_found);
+ }
+ root->queue_free();
+ memdelete(scene);
+ }
+}
+
+TEST_CASE("[SceneTree][InstancePlaceholder] Instantiate from placeholder with overrides") {
+ GDREGISTER_CLASS(_TestInstancePlaceholderNode);
+
+ SUBCASE("with non-node values") {
+ InstancePlaceholder *ip = memnew(InstancePlaceholder);
+ Node *root = memnew(Node);
+ SceneTree::get_singleton()->get_root()->add_child(root);
+
+ root->add_child(ip);
+ ip->set_name("TestScene");
+ ip->set("int_property", 45);
+ // Create a scene to pack.
+ _TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);
+ scene->set_int_property(12);
+
+ // Pack the scene.
+ PackedScene *packed_scene = memnew(PackedScene);
+ packed_scene->pack(scene);
+
+ // Instantiate the scene.
+ _TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));
+ REQUIRE(created != nullptr);
+ CHECK(created->get_int_property() == 45);
+
+ root->queue_free();
+ memdelete(scene);
+ }
+
+ SUBCASE("with node values") {
+ InstancePlaceholder *ip = memnew(InstancePlaceholder);
+ ip->set_name("TestScene");
+ Node *root = memnew(Node);
+ Node *overriding = memnew(Node);
+ SceneTree::get_singleton()->get_root()->add_child(root);
+
+ root->add_child(ip);
+ root->add_child(overriding);
+ ip->set("reference_property", overriding);
+ // Create a scene to instance.
+ _TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);
+ Node *referenced = memnew(Node);
+ scene->add_child(referenced);
+ referenced->set_owner(scene);
+ scene->set_reference_property(referenced);
+ // Pack the scene.
+ PackedScene *packed_scene = memnew(PackedScene);
+ const Error err = packed_scene->pack(scene);
+ REQUIRE(err == OK);
+
+ // Instantiate the scene.
+ _TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));
+ REQUIRE(created != nullptr);
+ CHECK(created->get_name() == "TestScene");
+ CHECK(created->get_child_count() == 1);
+ CHECK(created->get_reference_property().identity_compare(overriding));
+ CHECK_FALSE(created->get_reference_property().identity_compare(referenced));
+
+ root->queue_free();
+ memdelete(scene);
+ }
+
+ SUBCASE("with node-array value") {
+ InstancePlaceholder *ip = memnew(InstancePlaceholder);
+ ip->set_name("TestScene");
+ Node *root = memnew(Node);
+ SceneTree::get_singleton()->get_root()->add_child(root);
+
+ Node *override1 = memnew(Node);
+ Node *override2 = memnew(Node);
+ Node *override3 = memnew(Node);
+ root->add_child(ip);
+ root->add_child(override1);
+ root->add_child(override2);
+ root->add_child(override3);
+
+ Array override_node_array;
+ override_node_array.set_typed(Variant::OBJECT, "Node", Variant());
+ override_node_array.push_back(override1);
+ override_node_array.push_back(override2);
+ override_node_array.push_back(override3);
+
+ ip->set("reference_array_property", override_node_array);
+
+ // Create a scene to instance.
+ _TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);
+ Node *referenced1 = memnew(Node);
+ Node *referenced2 = memnew(Node);
+
+ scene->add_child(referenced1);
+ scene->add_child(referenced2);
+
+ referenced1->set_owner(scene);
+ referenced2->set_owner(scene);
+ Array referenced_array;
+ referenced_array.set_typed(Variant::OBJECT, "Node", Variant());
+ referenced_array.push_back(referenced1);
+ referenced_array.push_back(referenced2);
+
+ scene->set_reference_array_property(referenced_array);
+ // Pack the scene.
+ PackedScene *packed_scene = memnew(PackedScene);
+ const Error err = packed_scene->pack(scene);
+ REQUIRE(err == OK);
+
+ // Instantiate the scene.
+ _TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));
+ REQUIRE(created != nullptr);
+ CHECK(created->get_name() == "TestScene");
+ CHECK(created->get_child_count() == 2);
+ Array created_array = created->get_reference_array_property();
+ REQUIRE_FALSE(created_array.size() == referenced_array.size());
+ REQUIRE(created_array.size() == override_node_array.size());
+ REQUIRE_FALSE(created_array.size() == created->get_child_count());
+
+ // Iterate over all nodes, since the ordering is not guaranteed.
+ for (int i = 0; i < override_node_array.size(); i++) {
+ bool node_found = false;
+ for (int j = 0; j < created_array.size(); j++) {
+ if (override_node_array[i].identity_compare(created_array[j])) {
+ node_found = true;
+ }
+ }
+ CHECK(node_found);
+ }
+ root->queue_free();
+ memdelete(scene);
+ }
+}
+
+TEST_CASE("[SceneTree][InstancePlaceholder] Instance a PackedScene containing an InstancePlaceholder with no overrides") {
+ GDREGISTER_CLASS(_TestInstancePlaceholderNode);
+
+ // Create the internal scene.
+ _TestInstancePlaceholderNode *internal = memnew(_TestInstancePlaceholderNode);
+ internal->set_name("InternalNode");
+ Node *referenced = memnew(Node);
+ referenced->set_name("OriginalReference");
+ internal->add_child(referenced);
+ referenced->set_owner(internal);
+ internal->set_reference_property(referenced);
+
+ // Pack the internal scene.
+ PackedScene *internal_scene = memnew(PackedScene);
+ Error err = internal_scene->pack(internal);
+ REQUIRE(err == OK);
+
+ const String internal_path = OS::get_singleton()->get_cache_path().path_join("instance_placeholder_test_internal.tscn");
+ err = ResourceSaver::save(internal_scene, internal_path);
+ REQUIRE(err == OK);
+
+ Ref<PackedScene> internal_scene_loaded = ResourceLoader::load(internal_path, "PackedScene", ResourceFormatLoader::CacheMode::CACHE_MODE_IGNORE, &err);
+ REQUIRE(err == OK);
+
+ // Create the main scene.
+ Node *root = memnew(Node);
+ root->set_name("MainNode");
+ Node *overriding = memnew(Node);
+ overriding->set_name("OverridingReference");
+
+ _TestInstancePlaceholderNode *internal_created = Object::cast_to<_TestInstancePlaceholderNode>(internal_scene_loaded->instantiate(PackedScene::GEN_EDIT_STATE_MAIN_INHERITED));
+ internal_created->set_scene_instance_load_placeholder(true);
+ root->add_child(internal_created);
+ internal_created->set_owner(root);
+
+ root->add_child(overriding);
+ overriding->set_owner(root);
+ // Here we introduce an error, we override the property with an internal node to the instance placeholder.
+ // The InstancePlaceholder is now forced to properly resolve the Node.
+ internal_created->set("reference_property", NodePath("OriginalReference"));
+
+ // Pack the main scene.
+ PackedScene *main_scene = memnew(PackedScene);
+ err = main_scene->pack(root);
+ REQUIRE(err == OK);
+
+ const String main_path = OS::get_singleton()->get_cache_path().path_join("instance_placeholder_test_main.tscn");
+ err = ResourceSaver::save(main_scene, main_path);
+ REQUIRE(err == OK);
+
+ // // Instantiate the scene.
+ Ref<PackedScene> main_scene_loaded = ResourceLoader::load(main_path, "PackedScene", ResourceFormatLoader::CacheMode::CACHE_MODE_IGNORE, &err);
+ REQUIRE(err == OK);
+
+ Node *instanced_main_node = main_scene_loaded->instantiate();
+ REQUIRE(instanced_main_node != nullptr);
+ SceneTree::get_singleton()->get_root()->add_child(instanced_main_node);
+ CHECK(instanced_main_node->get_name() == "MainNode");
+ REQUIRE(instanced_main_node->get_child_count() == 2);
+ InstancePlaceholder *instanced_placeholder = Object::cast_to<InstancePlaceholder>(instanced_main_node->get_child(0, true));
+ REQUIRE(instanced_placeholder != nullptr);
+
+ _TestInstancePlaceholderNode *final_node = Object::cast_to<_TestInstancePlaceholderNode>(instanced_placeholder->create_instance(true));
+ REQUIRE(final_node != nullptr);
+ REQUIRE(final_node->get_child_count() == 1);
+ REQUIRE(final_node->get_reference_property().identity_compare(final_node->get_child(0, true)));
+
+ instanced_main_node->queue_free();
+ memdelete(overriding);
+ memdelete(root);
+ memdelete(internal);
+ DirAccess::remove_file_or_error(internal_path);
+ DirAccess::remove_file_or_error(main_path);
+}
+
+TEST_CASE("[SceneTree][InstancePlaceholder] Instance a PackedScene containing an InstancePlaceholder with overrides") {
+ GDREGISTER_CLASS(_TestInstancePlaceholderNode);
+
+ // Create the internal scene.
+ _TestInstancePlaceholderNode *internal = memnew(_TestInstancePlaceholderNode);
+ internal->set_name("InternalNode");
+ Node *referenced = memnew(Node);
+ referenced->set_name("OriginalReference");
+ internal->add_child(referenced);
+ referenced->set_owner(internal);
+ internal->set_reference_property(referenced);
+
+ Node *array_ref1 = memnew(Node);
+ array_ref1->set_name("ArrayRef1");
+ internal->add_child(array_ref1);
+ array_ref1->set_owner(internal);
+ Node *array_ref2 = memnew(Node);
+ array_ref2->set_name("ArrayRef2");
+ internal->add_child(array_ref2);
+ array_ref2->set_owner(internal);
+ Array referenced_array;
+ referenced_array.set_typed(Variant::OBJECT, "Node", Variant());
+ referenced_array.push_back(array_ref1);
+ referenced_array.push_back(array_ref2);
+ internal->set_reference_array_property(referenced_array);
+
+ // Pack the internal scene.
+ PackedScene *internal_scene = memnew(PackedScene);
+ Error err = internal_scene->pack(internal);
+ REQUIRE(err == OK);
+
+ const String internal_path = OS::get_singleton()->get_cache_path().path_join("instance_placeholder_test_internal_override.tscn");
+ err = ResourceSaver::save(internal_scene, internal_path);
+ REQUIRE(err == OK);
+
+ Ref<PackedScene> internal_scene_loaded = ResourceLoader::load(internal_path, "PackedScene", ResourceFormatLoader::CacheMode::CACHE_MODE_IGNORE, &err);
+ REQUIRE(err == OK);
+
+ // Create the main scene.
+ Node *root = memnew(Node);
+ root->set_name("MainNode");
+ Node *overriding = memnew(Node);
+ overriding->set_name("OverridingReference");
+ Node *array_ext = memnew(Node);
+ array_ext->set_name("ExternalArrayMember");
+
+ _TestInstancePlaceholderNode *internal_created = Object::cast_to<_TestInstancePlaceholderNode>(internal_scene_loaded->instantiate(PackedScene::GEN_EDIT_STATE_MAIN_INHERITED));
+ internal_created->set_scene_instance_load_placeholder(true);
+ root->add_child(internal_created);
+ internal_created->set_owner(root);
+
+ root->add_child(overriding);
+ overriding->set_owner(root);
+ root->add_child(array_ext);
+ array_ext->set_owner(root);
+ // Here we introduce an error, we override the property with an internal node to the instance placeholder.
+ // The InstancePlaceholder is now forced to properly resolve the Node.
+ internal_created->set_reference_property(overriding);
+ Array internal_array = internal_created->get_reference_array_property();
+ Array override_array;
+ override_array.set_typed(Variant::OBJECT, "Node", Variant());
+ for (int i = 0; i < internal_array.size(); i++) {
+ override_array.push_back(internal_array[i]);
+ }
+ override_array.push_back(array_ext);
+ internal_created->set_reference_array_property(override_array);
+
+ // Pack the main scene.
+ PackedScene *main_scene = memnew(PackedScene);
+ err = main_scene->pack(root);
+ REQUIRE(err == OK);
+
+ const String main_path = OS::get_singleton()->get_cache_path().path_join("instance_placeholder_test_main_override.tscn");
+ err = ResourceSaver::save(main_scene, main_path);
+ REQUIRE(err == OK);
+
+ // // Instantiate the scene.
+ Ref<PackedScene> main_scene_loaded = ResourceLoader::load(main_path, "PackedScene", ResourceFormatLoader::CacheMode::CACHE_MODE_IGNORE, &err);
+ REQUIRE(err == OK);
+
+ Node *instanced_main_node = main_scene_loaded->instantiate();
+ REQUIRE(instanced_main_node != nullptr);
+ SceneTree::get_singleton()->get_root()->add_child(instanced_main_node);
+ CHECK(instanced_main_node->get_name() == "MainNode");
+ REQUIRE(instanced_main_node->get_child_count() == 3);
+ InstancePlaceholder *instanced_placeholder = Object::cast_to<InstancePlaceholder>(instanced_main_node->get_child(0, true));
+ REQUIRE(instanced_placeholder != nullptr);
+
+ _TestInstancePlaceholderNode *final_node = Object::cast_to<_TestInstancePlaceholderNode>(instanced_placeholder->create_instance(true));
+ REQUIRE(final_node != nullptr);
+ REQUIRE(final_node->get_child_count() == 3);
+ REQUIRE(final_node->get_reference_property().identity_compare(instanced_main_node->get_child(1, true)));
+ Array final_array = final_node->get_reference_array_property();
+ REQUIRE(final_array.size() == 3);
+ Array wanted_node_array;
+ wanted_node_array.push_back(instanced_main_node->get_child(2, true)); // ExternalArrayMember
+ wanted_node_array.push_back(final_node->get_child(1, true)); // ArrayRef1
+ wanted_node_array.push_back(final_node->get_child(2, true)); // ArrayRef2
+
+ // Iterate over all nodes, since the ordering is not guaranteed.
+ for (int i = 0; i < wanted_node_array.size(); i++) {
+ bool node_found = false;
+ for (int j = 0; j < final_array.size(); j++) {
+ if (wanted_node_array[i].identity_compare(final_array[j])) {
+ node_found = true;
+ }
+ }
+ CHECK(node_found);
+ }
+
+ instanced_main_node->queue_free();
+ memdelete(array_ext);
+ memdelete(overriding);
+ memdelete(root);
+ memdelete(internal);
+ DirAccess::remove_file_or_error(internal_path);
+ DirAccess::remove_file_or_error(main_path);
+}
+
+} //namespace TestInstancePlaceholder
+
+#endif // TEST_INSTANCE_PLACEHOLDER_H
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index dd30003c01..041231888b 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -111,6 +111,7 @@
#include "tests/scene/test_curve_3d.h"
#include "tests/scene/test_gradient.h"
#include "tests/scene/test_image_texture.h"
+#include "tests/scene/test_instance_placeholder.h"
#include "tests/scene/test_node.h"
#include "tests/scene/test_node_2d.h"
#include "tests/scene/test_packed_scene.h"