summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--SConstruct2
-rw-r--r--core/math/color.cpp25
-rw-r--r--core/math/color_names.inc1
-rw-r--r--doc/classes/DisplayServer.xml8
-rw-r--r--doc/classes/EditorInspectorPlugin.xml3
-rw-r--r--doc/classes/NavigationAgent2D.xml2
-rw-r--r--doc/classes/NavigationAgent3D.xml2
-rwxr-xr-xdoc/tools/make_rst.py68
-rw-r--r--drivers/d3d12/rendering_device_driver_d3d12.cpp19
-rw-r--r--drivers/d3d12/rendering_device_driver_d3d12.h3
-rw-r--r--drivers/vulkan/rendering_device_driver_vulkan.cpp8
-rw-r--r--drivers/vulkan/rendering_device_driver_vulkan.h3
-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/editor_inspector.compat.inc41
-rw-r--r--editor/editor_inspector.cpp12
-rw-r--r--editor/editor_inspector.h8
-rw-r--r--editor/editor_log.cpp4
-rw-r--r--editor/editor_properties.cpp3
-rw-r--r--editor/editor_properties.h1
-rw-r--r--editor/filesystem_dock.cpp2
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp119
-rw-r--r--editor/plugins/node_3d_editor_plugin.h6
-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.cpp16
-rw-r--r--methods.py2
-rw-r--r--misc/extension_api_validation/4.2-stable.expected7
-rw-r--r--modules/fbx/editor/editor_scene_importer_ufbx.cpp3
-rw-r--r--modules/fbx/fbx_document.cpp11
-rw-r--r--modules/fbx/fbx_document.h2
-rw-r--r--modules/gltf/doc_classes/GLTFAccessor.xml18
-rw-r--r--modules/gltf/doc_classes/GLTFDocument.xml1
-rw-r--r--modules/gltf/doc_classes/GLTFState.xml3
-rw-r--r--modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp1
-rw-r--r--modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp12
-rw-r--r--modules/gltf/editor/editor_scene_exporter_gltf_settings.h4
-rw-r--r--modules/gltf/editor/editor_scene_importer_gltf.cpp5
-rw-r--r--modules/gltf/gltf_defines.h10
-rw-r--r--modules/gltf/gltf_document.cpp204
-rw-r--r--modules/gltf/gltf_document.h12
-rw-r--r--modules/gltf/gltf_state.cpp3
-rw-r--r--modules/gltf/gltf_state.h9
-rw-r--r--modules/gltf/structures/gltf_accessor.cpp17
-rw-r--r--modules/gltf/structures/gltf_accessor.h16
-rw-r--r--platform/linuxbsd/wayland/wayland_thread.h3
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp17
-rw-r--r--platform/linuxbsd/x11/display_server_x11.h2
-rw-r--r--platform/macos/display_server_macos.h2
-rw-r--r--platform/macos/display_server_macos.mm9
-rw-r--r--platform/web/detect.py2
-rw-r--r--platform/web/display_server_web.cpp39
-rw-r--r--platform/web/display_server_web.h1
-rw-r--r--platform/web/emscripten_helpers.py1
-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.cpp15
-rw-r--r--platform/windows/display_server_windows.h2
-rw-r--r--scene/2d/camera_2d.cpp4
-rw-r--r--scene/3d/navigation_region_3d.cpp4
-rw-r--r--scene/gui/code_edit.cpp2
-rw-r--r--scene/gui/control.cpp12
-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/packed_scene.cpp10
-rw-r--r--servers/display_server.cpp2
-rw-r--r--servers/display_server.h2
-rw-r--r--servers/rendering/renderer_scene_cull.cpp2
-rw-r--r--servers/rendering/rendering_device.cpp13
-rw-r--r--servers/rendering/rendering_device.h2
-rw-r--r--servers/rendering/rendering_device_driver.h2
-rw-r--r--tests/scene/test_instance_placeholder.h532
-rw-r--r--tests/test_main.cpp1
78 files changed, 1349 insertions, 302 deletions
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/math/color.cpp b/core/math/color.cpp
index d36306d968..1638acd74d 100644
--- a/core/math/color.cpp
+++ b/core/math/color.cpp
@@ -33,7 +33,7 @@
#include "color_names.inc"
#include "core/math/math_funcs.h"
#include "core/string/ustring.h"
-#include "core/templates/rb_map.h"
+#include "core/templates/hash_map.h"
#include "thirdparty/misc/ok_color.h"
@@ -414,7 +414,7 @@ Color Color::named(const String &p_name, const Color &p_default) {
int Color::find_named_color(const String &p_name) {
String name = p_name;
- // Normalize name
+ // Normalize name.
name = name.replace(" ", "");
name = name.replace("-", "");
name = name.replace("_", "");
@@ -422,23 +422,24 @@ int Color::find_named_color(const String &p_name) {
name = name.replace(".", "");
name = name.to_upper();
- int idx = 0;
- while (named_colors[idx].name != nullptr) {
- if (name == String(named_colors[idx].name).replace("_", "")) {
- return idx;
+ static HashMap<String, int> named_colors_hashmap;
+ if (unlikely(named_colors_hashmap.is_empty())) {
+ const int named_color_count = get_named_color_count();
+ for (int i = 0; i < named_color_count; i++) {
+ named_colors_hashmap[String(named_colors[i].name).replace("_", "")] = i;
}
- idx++;
+ }
+
+ const HashMap<String, int>::ConstIterator E = named_colors_hashmap.find(name);
+ if (E) {
+ return E->value;
}
return -1;
}
int Color::get_named_color_count() {
- int idx = 0;
- while (named_colors[idx].name != nullptr) {
- idx++;
- }
- return idx;
+ return sizeof(named_colors) / sizeof(NamedColor);
}
String Color::get_named_color_name(int p_idx) {
diff --git a/core/math/color_names.inc b/core/math/color_names.inc
index eaa1b1087f..6c0d2a4bfd 100644
--- a/core/math/color_names.inc
+++ b/core/math/color_names.inc
@@ -189,5 +189,4 @@ static NamedColor named_colors[] = {
{ "WHITE_SMOKE", Color::hex(0xF5F5F5FF) },
{ "YELLOW", Color::hex(0xFFFF00FF) },
{ "YELLOW_GREEN", Color::hex(0x9ACD32FF) },
- { nullptr, Color() },
};
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index 688e1b70ca..e157cbb96c 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -930,6 +930,12 @@
Returns [code]true[/code] if touch events are available (Android or iOS), the capability is detected on the Web platform or if [member ProjectSettings.input_devices/pointing/emulate_touch_from_mouse] is [code]true[/code].
</description>
</method>
+ <method name="is_window_transparency_available" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if the window background can be made transparent. This method returns [code]false[/code] if [member ProjectSettings.display/window/per_pixel_transparency/allowed] is set to [code]false[/code], or if transparency is not supported by the renderer or OS compositor.
+ </description>
+ </method>
<method name="keyboard_get_current_layout" qualifiers="const">
<return type="int" />
<description>
@@ -2045,7 +2051,7 @@
</constant>
<constant name="WINDOW_FLAG_TRANSPARENT" value="3" enum="WindowFlags">
The window background can be transparent.
- [b]Note:[/b] This flag has no effect if [member ProjectSettings.display/window/per_pixel_transparency/allowed] is set to [code]false[/code].
+ [b]Note:[/b] This flag has no effect if [method is_window_transparency_available] returns [code]false[/code].
[b]Note:[/b] Transparency support is implemented on Linux (X11/Wayland), macOS, and Windows, but availability might vary depending on GPU driver, display manager, and compositor capabilities.
</constant>
<constant name="WINDOW_FLAG_NO_FOCUS" value="4" enum="WindowFlags">
diff --git a/doc/classes/EditorInspectorPlugin.xml b/doc/classes/EditorInspectorPlugin.xml
index efa881591c..4f6ef76c4c 100644
--- a/doc/classes/EditorInspectorPlugin.xml
+++ b/doc/classes/EditorInspectorPlugin.xml
@@ -78,8 +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/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/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/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp
index 9407826ebf..b042d41524 100644
--- a/drivers/d3d12/rendering_device_driver_d3d12.cpp
+++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp
@@ -2199,9 +2199,21 @@ Error RenderingDeviceDriverD3D12::swap_chain_resize(CommandQueueID p_cmd_queue,
swap_chain_desc.SampleDesc.Count = 1;
swap_chain_desc.Flags = creation_flags;
swap_chain_desc.Scaling = DXGI_SCALING_NONE;
+ if (OS::get_singleton()->is_layered_allowed()) {
+ swap_chain_desc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
+ has_comp_alpha[(uint64_t)p_cmd_queue.id] = true;
+ } else {
+ swap_chain_desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
+ has_comp_alpha[(uint64_t)p_cmd_queue.id] = false;
+ }
ComPtr<IDXGISwapChain1> swap_chain_1;
res = context_driver->dxgi_factory_get()->CreateSwapChainForHwnd(command_queue->d3d_queue.Get(), surface->hwnd, &swap_chain_desc, nullptr, nullptr, swap_chain_1.GetAddressOf());
+ if (!SUCCEEDED(res) && swap_chain_desc.AlphaMode != DXGI_ALPHA_MODE_IGNORE) {
+ swap_chain_desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
+ has_comp_alpha[(uint64_t)p_cmd_queue.id] = false;
+ res = context_driver->dxgi_factory_get()->CreateSwapChainForHwnd(command_queue->d3d_queue.Get(), surface->hwnd, &swap_chain_desc, nullptr, nullptr, swap_chain_1.GetAddressOf());
+ }
ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE);
swap_chain_1.As(&swap_chain->d3d_swap_chain);
@@ -5980,6 +5992,13 @@ const RDD::Capabilities &RenderingDeviceDriverD3D12::get_capabilities() const {
return device_capabilities;
}
+bool RenderingDeviceDriverD3D12::is_composite_alpha_supported(CommandQueueID p_queue) const {
+ if (has_comp_alpha.has((uint64_t)p_queue.id)) {
+ return has_comp_alpha[(uint64_t)p_queue.id];
+ }
+ return false;
+}
+
/******************/
RenderingDeviceDriverD3D12::RenderingDeviceDriverD3D12(RenderingContextDriverD3D12 *p_context_driver) {
diff --git a/drivers/d3d12/rendering_device_driver_d3d12.h b/drivers/d3d12/rendering_device_driver_d3d12.h
index 8e1223bdaa..3a9677485e 100644
--- a/drivers/d3d12/rendering_device_driver_d3d12.h
+++ b/drivers/d3d12/rendering_device_driver_d3d12.h
@@ -973,6 +973,7 @@ private:
uint32_t frames_drawn = 0;
uint32_t segment_serial = 0;
bool segment_begun = false;
+ HashMap<uint64_t, bool> has_comp_alpha;
public:
virtual void begin_segment(uint32_t p_frame_index, uint32_t p_frames_drawn) override final;
@@ -994,6 +995,8 @@ public:
virtual String get_pipeline_cache_uuid() const override final;
virtual const Capabilities &get_capabilities() const override final;
+ virtual bool is_composite_alpha_supported(CommandQueueID p_queue) const override final;
+
static bool is_in_developer_mode();
private:
diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp
index 896fc6ff91..b03a8418ed 100644
--- a/drivers/vulkan/rendering_device_driver_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp
@@ -2646,6 +2646,7 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue,
break;
}
}
+ has_comp_alpha[(uint64_t)p_cmd_queue.id] = (composite_alpha != VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR);
}
VkSwapchainCreateInfoKHR swap_create_info = {};
@@ -4945,6 +4946,13 @@ const RDD::Capabilities &RenderingDeviceDriverVulkan::get_capabilities() const {
return device_capabilities;
}
+bool RenderingDeviceDriverVulkan::is_composite_alpha_supported(CommandQueueID p_queue) const {
+ if (has_comp_alpha.has((uint64_t)p_queue.id)) {
+ return has_comp_alpha[(uint64_t)p_queue.id];
+ }
+ return false;
+}
+
/******************/
RenderingDeviceDriverVulkan::RenderingDeviceDriverVulkan(RenderingContextDriverVulkan *p_context_driver) {
diff --git a/drivers/vulkan/rendering_device_driver_vulkan.h b/drivers/vulkan/rendering_device_driver_vulkan.h
index e70019962a..b9e7563069 100644
--- a/drivers/vulkan/rendering_device_driver_vulkan.h
+++ b/drivers/vulkan/rendering_device_driver_vulkan.h
@@ -494,6 +494,7 @@ private:
static int caching_instance_count;
PipelineCache pipelines_cache;
String pipeline_cache_id;
+ HashMap<uint64_t, bool> has_comp_alpha;
public:
virtual void pipeline_free(PipelineID p_pipeline) override final;
@@ -627,6 +628,8 @@ public:
virtual String get_pipeline_cache_uuid() const override final;
virtual const Capabilities &get_capabilities() const override final;
+ virtual bool is_composite_alpha_supported(CommandQueueID p_queue) const override final;
+
private:
/*********************/
/**** BOOKKEEPING ****/
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/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 7c5e3877f2..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")
@@ -3782,7 +3784,6 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo
}
emit_signal(_prop_edited, p_name);
-
} else if (Object::cast_to<MultiNodeEdit>(object)) {
Object::cast_to<MultiNodeEdit>(object)->set_property_field(p_name, p_value, p_changed_field);
_edit_request_change(object, p_name);
@@ -3959,7 +3960,7 @@ void EditorInspector::_property_checked(const String &p_path, bool p_checked) {
//property checked
if (autoclear) {
if (!p_checked) {
- object->set(p_path, Variant());
+ _edit_set(p_path, Variant(), false, "");
} else {
Variant to_create;
List<PropertyInfo> pinfo;
@@ -3971,7 +3972,7 @@ void EditorInspector::_property_checked(const String &p_path, bool p_checked) {
break;
}
}
- object->set(p_path, to_create);
+ _edit_set(p_path, to_create, false, "");
}
if (editor_property_map.has(p_path)) {
@@ -3982,7 +3983,6 @@ void EditorInspector::_property_checked(const String &p_path, bool p_checked) {
E->update_cache();
}
}
-
} else {
emit_signal(SNAME("property_toggled"), p_path, p_checked);
}
diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h
index 3cbee5c502..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);
@@ -344,7 +348,7 @@ class EditorInspectorArray : public EditorInspectorSection {
MODE_NONE,
MODE_USE_COUNT_PROPERTY,
MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION,
- } mode;
+ } mode = MODE_NONE;
StringName count_property;
StringName array_element_prefix;
String swap_method;
diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp
index 6615133dea..2a6fe808dd 100644
--- a/editor/editor_log.cpp
+++ b/editor/editor_log.cpp
@@ -273,6 +273,10 @@ void EditorLog::_undo_redo_cbk(void *p_self, const String &p_name) {
}
void EditorLog::_rebuild_log() {
+ if (messages.is_empty()) {
+ return;
+ }
+
log->clear();
int line_count = 0;
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index 49b30bd06e..fe50961b54 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -2645,7 +2645,7 @@ void EditorPropertyColor::_color_changed(const Color &p_color) {
}
void EditorPropertyColor::_popup_closed() {
- get_edited_object()->set(get_edited_property(), last_color);
+ get_edited_object()->set(get_edited_property(), was_checked ? Variant(last_color) : Variant());
if (!picker->get_pick_color().is_equal_approx(last_color)) {
emit_changed(get_edited_property(), picker->get_pick_color(), "", false);
}
@@ -2653,6 +2653,7 @@ void EditorPropertyColor::_popup_closed() {
void EditorPropertyColor::_picker_opening() {
last_color = picker->get_pick_color();
+ was_checked = !is_checkable() || is_checked();
}
void EditorPropertyColor::_notification(int p_what) {
diff --git a/editor/editor_properties.h b/editor/editor_properties.h
index d16c80bfc4..f2c5497e4f 100644
--- a/editor/editor_properties.h
+++ b/editor/editor_properties.h
@@ -625,6 +625,7 @@ class EditorPropertyColor : public EditorProperty {
Color last_color;
bool live_changes_enabled = true;
+ bool was_checked = false;
protected:
virtual void _set_read_only(bool p_read_only) override;
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/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index e9e5b2294f..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");
@@ -2901,10 +2934,8 @@ void Node3DEditorViewport::_notification(int p_what) {
// FPS Counter.
bool show_fps = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_FRAME_TIME));
- if (show_fps != fps_label->is_visible()) {
- cpu_time_label->set_visible(show_fps);
- gpu_time_label->set_visible(show_fps);
- fps_label->set_visible(show_fps);
+ if (show_fps != frame_time_panel->is_visible()) {
+ frame_time_panel->set_visible(show_fps);
RS::get_singleton()->viewport_set_measure_render_time(viewport->get_viewport_rid(), show_fps);
for (int i = 0; i < FRAME_TIME_HISTORY; i++) {
// Initialize to 120 FPS, so that the initial estimation until we get enough data is always reasonable.
@@ -3033,9 +3064,15 @@ void Node3DEditorViewport::_notification(int p_what) {
frame_time_gradient->set_color(2, get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
info_label->add_theme_style_override("normal", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles)));
- cpu_time_label->add_theme_style_override("normal", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles)));
- gpu_time_label->add_theme_style_override("normal", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles)));
- fps_label->add_theme_style_override("normal", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles)));
+
+ frame_time_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles)));
+ // Set a minimum width to prevent the width from changing all the time
+ // when numbers vary rapidly. This minimum width is set based on a
+ // GPU time of 999.99 ms in the current editor language.
+ const float min_width = get_theme_font(SNAME("main"), EditorStringName(EditorFonts))->get_string_size(vformat(TTR("GPU Time: %s ms"), 999.99)).x;
+ frame_time_panel->set_custom_minimum_size(Size2(min_width, 0) * EDSCALE);
+ frame_time_vbox->add_theme_constant_override("separation", Math::round(-1 * EDSCALE));
+
cinema_label->add_theme_style_override("normal", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles)));
locked_label->add_theme_style_override("normal", gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles)));
} break;
@@ -3750,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);
@@ -5379,10 +5416,6 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p
top_right_vbox = memnew(VBoxContainer);
top_right_vbox->set_anchors_and_offsets_preset(PRESET_TOP_RIGHT, PRESET_MODE_MINSIZE, 10.0 * EDSCALE);
top_right_vbox->set_h_grow_direction(GROW_DIRECTION_BEGIN);
- // Make sure frame time labels don't touch the viewport's edge.
- top_right_vbox->set_custom_minimum_size(Size2(100, 0) * EDSCALE);
- // Prevent visible spacing between frame time labels.
- top_right_vbox->add_theme_constant_override("separation", 0);
const int navigation_control_size = 150;
@@ -5414,18 +5447,22 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p
rotation_control->set_viewport(this);
top_right_vbox->add_child(rotation_control);
+ frame_time_panel = memnew(PanelContainer);
+ top_right_vbox->add_child(frame_time_panel);
+ frame_time_panel->hide();
+
+ frame_time_vbox = memnew(VBoxContainer);
+ frame_time_panel->add_child(frame_time_vbox);
+
// Individual Labels are used to allow coloring each label with its own color.
cpu_time_label = memnew(Label);
- top_right_vbox->add_child(cpu_time_label);
- cpu_time_label->hide();
+ frame_time_vbox->add_child(cpu_time_label);
gpu_time_label = memnew(Label);
- top_right_vbox->add_child(gpu_time_label);
- gpu_time_label->hide();
+ frame_time_vbox->add_child(gpu_time_label);
fps_label = memnew(Label);
- top_right_vbox->add_child(fps_label);
- fps_label->hide();
+ frame_time_vbox->add_child(fps_label);
surface->add_child(top_right_vbox);
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index 4990b11a47..859d075732 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -255,6 +255,8 @@ private:
ViewportNavigationControl *look_control = nullptr;
ViewportRotationControl *rotation_control = nullptr;
Gradient *frame_time_gradient = nullptr;
+ PanelContainer *frame_time_panel = nullptr;
+ VBoxContainer *frame_time_vbox = nullptr;
Label *cpu_time_label = nullptr;
Label *gpu_time_label = nullptr;
Label *fps_label = nullptr;
@@ -296,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/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 ec0380f964..f90fa7603f 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -3104,7 +3104,23 @@ void SceneTreeDock::set_edited_scene(Node *p_scene) {
edited_scene = p_scene;
}
+static bool _is_same_selection(const Vector<Node *> &p_first, const List<Node *> &p_second) {
+ if (p_first.size() != p_second.size()) {
+ return false;
+ }
+ for (Node *node : p_second) {
+ if (!p_first.has(node)) {
+ return false;
+ }
+ }
+ return true;
+}
+
void SceneTreeDock::set_selection(const Vector<Node *> &p_nodes) {
+ // If the nodes selected are the same independently of order then return early.
+ if (_is_same_selection(p_nodes, editor_selection->get_full_selected_node_list())) {
+ return;
+ }
editor_selection->clear();
for (Node *node : p_nodes) {
editor_selection->add_node(node);
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/fbx/editor/editor_scene_importer_ufbx.cpp b/modules/fbx/editor/editor_scene_importer_ufbx.cpp
index e0f60fe998..fb5e324390 100644
--- a/modules/fbx/editor/editor_scene_importer_ufbx.cpp
+++ b/modules/fbx/editor/editor_scene_importer_ufbx.cpp
@@ -80,6 +80,7 @@ Node *EditorSceneFormatImporterUFBX::import_scene(const String &p_path, uint32_t
state->set_import_as_skeleton_bones(true);
}
p_flags |= EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS;
+ state->set_bake_fps(p_options["animation/fps"]);
Error err = fbx->append_from_file(path, state, p_flags, p_path.get_base_dir());
if (err != OK) {
if (r_err) {
@@ -87,7 +88,7 @@ Node *EditorSceneFormatImporterUFBX::import_scene(const String &p_path, uint32_t
}
return nullptr;
}
- return fbx->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"], false);
+ return fbx->generate_scene(state, state->get_bake_fps(), (bool)p_options["animation/trimming"], false);
}
Variant EditorSceneFormatImporterUFBX::get_option_visibility(const String &p_path, bool p_for_animation,
diff --git a/modules/fbx/fbx_document.cpp b/modules/fbx/fbx_document.cpp
index 1361e871de..b9d9ec7b6c 100644
--- a/modules/fbx/fbx_document.cpp
+++ b/modules/fbx/fbx_document.cpp
@@ -1381,6 +1381,10 @@ Error FBXDocument::_parse_animations(Ref<FBXState> p_state) {
additional_data["time_end"] = fbx_anim_stack->time_end;
animation->set_additional_data("GODOT_animation_time_begin_time_end", additional_data);
ufbx_bake_opts opts = {};
+ opts.resample_rate = p_state->get_bake_fps();
+ opts.minimum_sample_rate = p_state->get_bake_fps();
+ opts.max_keyframe_segments = 1024;
+
ufbx_error error;
ufbx_unique_ptr<ufbx_baked_anim> fbx_baked_anim{ ufbx_bake_anim(fbx_scene, fbx_anim_stack->anim, &opts, &error) };
if (!fbx_baked_anim) {
@@ -1759,7 +1763,7 @@ void FBXDocument::_generate_skeleton_bone_node(Ref<FBXState> p_state, const GLTF
}
}
-void FBXDocument::_import_animation(Ref<FBXState> p_state, AnimationPlayer *p_animation_player, const GLTFAnimationIndex p_index, const float p_bake_fps, const bool p_trimming, const bool p_remove_immutable_tracks) {
+void FBXDocument::_import_animation(Ref<FBXState> p_state, AnimationPlayer *p_animation_player, const GLTFAnimationIndex p_index, const bool p_trimming, const bool p_remove_immutable_tracks) {
Ref<GLTFAnimation> anim = p_state->animations[p_index];
String anim_name = anim->get_name();
@@ -1771,7 +1775,7 @@ void FBXDocument::_import_animation(Ref<FBXState> p_state, AnimationPlayer *p_an
Ref<Animation> animation;
animation.instantiate();
animation->set_name(anim_name);
- animation->set_step(1.0 / p_bake_fps);
+ animation->set_step(1.0 / p_state->get_bake_fps());
if (anim->get_loop()) {
animation->set_loop_mode(Animation::LOOP_LINEAR);
@@ -2118,6 +2122,7 @@ Node *FBXDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, bool
ERR_FAIL_COND_V(state.is_null(), nullptr);
ERR_FAIL_NULL_V(state, nullptr);
ERR_FAIL_INDEX_V(0, state->root_nodes.size(), nullptr);
+ p_state->set_bake_fps(p_bake_fps);
GLTFNodeIndex fbx_root = state->root_nodes.write[0];
Node *fbx_root_node = state->get_scene_node(fbx_root);
Node *root = fbx_root_node;
@@ -2131,7 +2136,7 @@ Node *FBXDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, bool
root->add_child(ap, true);
ap->set_owner(root);
for (int i = 0; i < state->animations.size(); i++) {
- _import_animation(state, ap, i, p_bake_fps, p_trimming, p_remove_immutable_tracks);
+ _import_animation(state, ap, i, p_trimming, p_remove_immutable_tracks);
}
}
ERR_FAIL_NULL_V(root, nullptr);
diff --git a/modules/fbx/fbx_document.h b/modules/fbx/fbx_document.h
index c9256df444..4a3bb176c2 100644
--- a/modules/fbx/fbx_document.h
+++ b/modules/fbx/fbx_document.h
@@ -99,7 +99,7 @@ public:
void _generate_scene_node(Ref<FBXState> p_state, const GLTFNodeIndex p_node_index, Node *p_scene_parent, Node *p_scene_root);
void _generate_skeleton_bone_node(Ref<FBXState> p_state, const GLTFNodeIndex p_node_index, Node *p_scene_parent, Node *p_scene_root);
void _import_animation(Ref<FBXState> p_state, AnimationPlayer *p_animation_player,
- const GLTFAnimationIndex p_index, const float p_bake_fps, const bool p_trimming, const bool p_remove_immutable_tracks);
+ const GLTFAnimationIndex p_index, const bool p_trimming, const bool p_remove_immutable_tracks);
Error _parse(Ref<FBXState> p_state, String p_path, Ref<FileAccess> p_file);
};
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/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml
index 1f172633da..1b51def28e 100644
--- a/modules/gltf/doc_classes/GLTFDocument.xml
+++ b/modules/gltf/doc_classes/GLTFDocument.xml
@@ -60,6 +60,7 @@
<param index="3" name="remove_immutable_tracks" type="bool" default="true" />
<description>
Takes a [GLTFState] object through the [param state] parameter and returns a Godot Engine scene node.
+ The [param bake_fps] parameter overrides the bake_fps in [param state].
</description>
</method>
<method name="register_gltf_document_extension" qualifiers="static">
diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml
index 6c7c5ee0e6..21a0527813 100644
--- a/modules/gltf/doc_classes/GLTFState.xml
+++ b/modules/gltf/doc_classes/GLTFState.xml
@@ -275,6 +275,9 @@
</method>
</methods>
<members>
+ <member name="bake_fps" type="float" setter="set_bake_fps" getter="get_bake_fps" default="30.0">
+ The baking fps of the animation for either import or export.
+ </member>
<member name="base_path" type="String" setter="set_base_path" getter="get_base_path" default="&quot;&quot;">
The folder path associated with this GLTF data. This is used to find other files the GLTF file references, like images or binary buffers. This will be set during import when appending from a file, and will be set during export when writing to a file.
</member>
diff --git a/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp
index fee8156375..022d2e4477 100644
--- a/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp
+++ b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp
@@ -107,6 +107,7 @@ void SceneExporterGLTFPlugin::_export_scene_as_gltf(const String &p_file_path) {
state->set_copyright(_export_settings->get_copyright());
int32_t flags = 0;
flags |= EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS;
+ state->set_bake_fps(_export_settings->get_bake_fps());
Error err = _gltf_document->append_from_scene(root, state, flags);
if (err != OK) {
ERR_PRINT(vformat("glTF2 save scene error %s.", itos(err)));
diff --git a/modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp b/modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp
index 16f32af903..511da078d8 100644
--- a/modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp
+++ b/modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp
@@ -182,4 +182,16 @@ void EditorSceneExporterGLTFSettings::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_copyright"), &EditorSceneExporterGLTFSettings::get_copyright);
ClassDB::bind_method(D_METHOD("set_copyright", "copyright"), &EditorSceneExporterGLTFSettings::set_copyright);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "copyright", PROPERTY_HINT_PLACEHOLDER_TEXT, "Example: 2014 Godette"), "set_copyright", "get_copyright");
+
+ ClassDB::bind_method(D_METHOD("get_bake_fps"), &EditorSceneExporterGLTFSettings::get_bake_fps);
+ ClassDB::bind_method(D_METHOD("set_bake_fps", "bake_fps"), &EditorSceneExporterGLTFSettings::set_bake_fps);
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_fps"), "set_bake_fps", "get_bake_fps");
+}
+
+double EditorSceneExporterGLTFSettings::get_bake_fps() const {
+ return _bake_fps;
+}
+
+void EditorSceneExporterGLTFSettings::set_bake_fps(const double p_bake_fps) {
+ _bake_fps = p_bake_fps;
}
diff --git a/modules/gltf/editor/editor_scene_exporter_gltf_settings.h b/modules/gltf/editor/editor_scene_exporter_gltf_settings.h
index e1ce674274..898cddfd68 100644
--- a/modules/gltf/editor/editor_scene_exporter_gltf_settings.h
+++ b/modules/gltf/editor/editor_scene_exporter_gltf_settings.h
@@ -42,6 +42,7 @@ class EditorSceneExporterGLTFSettings : public RefCounted {
HashMap<String, Ref<GLTFDocumentExtension>> _config_name_to_extension_map;
String _copyright;
+ double _bake_fps = 30.0;
protected:
static void _bind_methods();
@@ -58,6 +59,9 @@ public:
String get_copyright() const;
void set_copyright(const String &p_copyright);
+
+ double get_bake_fps() const;
+ void set_bake_fps(const double p_bake_fps);
};
#endif // TOOLS_ENABLED
diff --git a/modules/gltf/editor/editor_scene_importer_gltf.cpp b/modules/gltf/editor/editor_scene_importer_gltf.cpp
index af9b2f2363..b38c64de01 100644
--- a/modules/gltf/editor/editor_scene_importer_gltf.cpp
+++ b/modules/gltf/editor/editor_scene_importer_gltf.cpp
@@ -62,6 +62,7 @@ Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t
if (p_options.has(SNAME("nodes/import_as_skeleton_bones")) ? (bool)p_options[SNAME("nodes/import_as_skeleton_bones")] : false) {
state->set_import_as_skeleton_bones(true);
}
+ state->set_bake_fps(p_options["animation/fps"]);
Error err = gltf->append_from_file(p_path, state, p_flags);
if (err != OK) {
if (r_err) {
@@ -75,9 +76,9 @@ Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t
#ifndef DISABLE_DEPRECATED
bool trimming = p_options.has("animation/trimming") ? (bool)p_options["animation/trimming"] : false;
- return gltf->generate_scene(state, (float)p_options["animation/fps"], trimming, false);
+ return gltf->generate_scene(state, state->get_bake_fps(), trimming, false);
#else
- return gltf->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"], false);
+ return gltf->generate_scene(state, state->get_bake_fps(), (bool)p_options["animation/trimming"], false);
#endif
}
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 b92176a63a..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;
}
@@ -4860,7 +4846,7 @@ Error GLTFDocument::_serialize_animations(Ref<GLTFState> p_state) {
t["sampler"] = samplers.size();
Dictionary s;
Vector<real_t> times;
- const double increment = 1.0 / BAKE_FPS;
+ const double increment = 1.0 / p_state->get_bake_fps();
{
double time = 0.0;
bool last = false;
@@ -5902,7 +5888,8 @@ T GLTFDocument::_interpolate_track(const Vector<real_t> &p_times, const Vector<T
ERR_FAIL_V(p_values[0]);
}
-void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_animation_player, const GLTFAnimationIndex p_index, const float p_bake_fps, const bool p_trimming, const bool p_remove_immutable_tracks) {
+void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_animation_player, const GLTFAnimationIndex p_index, const bool p_trimming, const bool p_remove_immutable_tracks) {
+ ERR_FAIL_COND(p_state.is_null());
Ref<GLTFAnimation> anim = p_state->animations[p_index];
String anim_name = anim->get_name();
@@ -5914,7 +5901,7 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_
Ref<Animation> animation;
animation.instantiate();
animation->set_name(anim_name);
- animation->set_step(1.0 / p_bake_fps);
+ animation->set_step(1.0 / p_state->get_bake_fps());
if (anim->get_loop()) {
animation->set_loop_mode(Animation::LOOP_LINEAR);
@@ -6081,7 +6068,7 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_
}
}
- const double increment = 1.0 / p_bake_fps;
+ const double increment = 1.0 / p_state->get_bake_fps();
double time = anim_start;
Vector3 base_pos;
@@ -6158,7 +6145,7 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_
}
} else {
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
- const double increment = 1.0 / p_bake_fps;
+ const double increment = 1.0 / p_state->get_bake_fps();
double time = 0.0;
bool last = false;
while (true) {
@@ -6372,7 +6359,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
p_track.scale_track.times.clear();
p_track.scale_track.values.clear();
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
- const double increment = 1.0 / BAKE_FPS;
+ const double increment = 1.0 / p_state->get_bake_fps();
double time = 0.0;
bool last = false;
while (true) {
@@ -6407,7 +6394,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
p_track.position_track.times.clear();
p_track.position_track.values.clear();
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
- const double increment = 1.0 / BAKE_FPS;
+ const double increment = 1.0 / p_state->get_bake_fps();
double time = 0.0;
bool last = false;
while (true) {
@@ -6442,7 +6429,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
p_track.rotation_track.times.clear();
p_track.rotation_track.values.clear();
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
- const double increment = 1.0 / BAKE_FPS;
+ const double increment = 1.0 / p_state->get_bake_fps();
double time = 0.0;
bool last = false;
while (true) {
@@ -6482,7 +6469,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
p_track.position_track.times.clear();
p_track.position_track.values.clear();
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
- const double increment = 1.0 / BAKE_FPS;
+ const double increment = 1.0 / p_state->get_bake_fps();
double time = 0.0;
bool last = false;
while (true) {
@@ -6515,7 +6502,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
p_track.rotation_track.times.clear();
p_track.rotation_track.values.clear();
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
- const double increment = 1.0 / BAKE_FPS;
+ const double increment = 1.0 / p_state->get_bake_fps();
double time = 0.0;
bool last = false;
while (true) {
@@ -6551,7 +6538,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
p_track.scale_track.times.clear();
p_track.scale_track.values.clear();
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
- const double increment = 1.0 / BAKE_FPS;
+ const double increment = 1.0 / p_state->get_bake_fps();
double time = 0.0;
bool last = false;
while (true) {
@@ -6577,14 +6564,14 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
}
}
} else if (track_type == Animation::TYPE_BEZIER) {
- const int32_t keys = anim_end * BAKE_FPS;
+ const int32_t keys = anim_end * p_state->get_bake_fps();
if (path.contains(":scale")) {
if (!p_track.scale_track.times.size()) {
p_track.scale_track.interpolation = gltf_interpolation;
Vector<real_t> new_times;
new_times.resize(keys);
for (int32_t key_i = 0; key_i < keys; key_i++) {
- new_times.write[key_i] = key_i / BAKE_FPS;
+ new_times.write[key_i] = key_i / p_state->get_bake_fps();
}
p_track.scale_track.times = new_times;
@@ -6597,11 +6584,11 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
for (int32_t key_i = 0; key_i < keys; key_i++) {
Vector3 bezier_track = p_track.scale_track.values[key_i];
if (path.contains(":scale:x")) {
- bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
+ bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
} else if (path.contains(":scale:y")) {
- bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
+ bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
} else if (path.contains(":scale:z")) {
- bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
+ bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
}
p_track.scale_track.values.write[key_i] = bezier_track;
}
@@ -6612,7 +6599,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
Vector<real_t> new_times;
new_times.resize(keys);
for (int32_t key_i = 0; key_i < keys; key_i++) {
- new_times.write[key_i] = key_i / BAKE_FPS;
+ new_times.write[key_i] = key_i / p_state->get_bake_fps();
}
p_track.position_track.times = new_times;
@@ -6622,11 +6609,11 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
for (int32_t key_i = 0; key_i < keys; key_i++) {
Vector3 bezier_track = p_track.position_track.values[key_i];
if (path.contains(":position:x")) {
- bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
+ bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
} else if (path.contains(":position:y")) {
- bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
+ bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
} else if (path.contains(":position:z")) {
- bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
+ bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
}
p_track.position_track.values.write[key_i] = bezier_track;
}
@@ -6636,7 +6623,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
Vector<real_t> new_times;
new_times.resize(keys);
for (int32_t key_i = 0; key_i < keys; key_i++) {
- new_times.write[key_i] = key_i / BAKE_FPS;
+ new_times.write[key_i] = key_i / p_state->get_bake_fps();
}
p_track.rotation_track.times = new_times;
@@ -6645,13 +6632,13 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
for (int32_t key_i = 0; key_i < keys; key_i++) {
Quaternion bezier_track = p_track.rotation_track.values[key_i];
if (path.contains(":rotation:x")) {
- bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
+ bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
} else if (path.contains(":rotation:y")) {
- bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
+ bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
} else if (path.contains(":rotation:z")) {
- bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
+ bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
} else if (path.contains(":rotation:w")) {
- bezier_track.w = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
+ bezier_track.w = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
}
p_track.rotation_track.values.write[key_i] = bezier_track;
}
@@ -7305,6 +7292,7 @@ Node *GLTFDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, boo
ERR_FAIL_NULL_V(state, nullptr);
ERR_FAIL_INDEX_V(0, state->root_nodes.size(), nullptr);
Error err = OK;
+ p_state->set_bake_fps(p_bake_fps);
Node *root = _generate_scene_node_tree(state);
ERR_FAIL_NULL_V(root, nullptr);
_process_mesh_instances(state, root);
@@ -7313,7 +7301,7 @@ Node *GLTFDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, boo
root->add_child(ap, true);
ap->set_owner(root);
for (int i = 0; i < state->animations.size(); i++) {
- _import_animation(state, ap, i, p_bake_fps, p_trimming, p_remove_immutable_tracks);
+ _import_animation(state, ap, i, p_trimming, p_remove_immutable_tracks);
}
}
for (KeyValue<GLTFNodeIndex, Node *> E : state->scene_nodes) {
diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h
index 1901e89d51..4f92ceccca 100644
--- a/modules/gltf/gltf_document.h
+++ b/modules/gltf/gltf_document.h
@@ -75,7 +75,6 @@ public:
};
private:
- const float BAKE_FPS = 30.0f;
int _naming_version = 1;
String _image_format = "PNG";
float _lossy_quality = 0.75f;
@@ -112,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);
@@ -133,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);
@@ -268,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);
@@ -328,7 +326,7 @@ public:
void _generate_scene_node(Ref<GLTFState> p_state, const GLTFNodeIndex p_node_index, Node *p_scene_parent, Node *p_scene_root);
void _generate_skeleton_bone_node(Ref<GLTFState> p_state, const GLTFNodeIndex p_node_index, Node *p_scene_parent, Node *p_scene_root);
void _import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_animation_player,
- const GLTFAnimationIndex p_index, const float p_bake_fps, const bool p_trimming, const bool p_remove_immutable_tracks);
+ const GLTFAnimationIndex p_index, const bool p_trimming, const bool p_remove_immutable_tracks);
void _convert_mesh_instances(Ref<GLTFState> p_state);
GLTFCameraIndex _convert_camera(Ref<GLTFState> p_state, Camera3D *p_camera);
void _convert_light_to_gltf(Light3D *p_light, Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node);
diff --git a/modules/gltf/gltf_state.cpp b/modules/gltf/gltf_state.cpp
index ed31aadc01..73a61ff77f 100644
--- a/modules/gltf/gltf_state.cpp
+++ b/modules/gltf/gltf_state.cpp
@@ -100,6 +100,8 @@ void GLTFState::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_additional_data", "extension_name", "additional_data"), &GLTFState::set_additional_data);
ClassDB::bind_method(D_METHOD("get_handle_binary_image"), &GLTFState::get_handle_binary_image);
ClassDB::bind_method(D_METHOD("set_handle_binary_image", "method"), &GLTFState::set_handle_binary_image);
+ ClassDB::bind_method(D_METHOD("set_bake_fps", "value"), &GLTFState::set_bake_fps);
+ ClassDB::bind_method(D_METHOD("get_bake_fps"), &GLTFState::get_bake_fps);
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "json"), "set_json", "get_json"); // Dictionary
ADD_PROPERTY(PropertyInfo(Variant::INT, "major_version"), "set_major_version", "get_major_version"); // int
@@ -130,6 +132,7 @@ void GLTFState::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "import_as_skeleton_bones"), "set_import_as_skeleton_bones", "get_import_as_skeleton_bones"); // bool
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_animations", "get_animations"); // Vector<Ref<GLTFAnimation>>
ADD_PROPERTY(PropertyInfo(Variant::INT, "handle_binary_image", PROPERTY_HINT_ENUM, "Discard All Textures,Extract Textures,Embed as Basis Universal,Embed as Uncompressed", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_handle_binary_image", "get_handle_binary_image"); // enum
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_fps"), "set_bake_fps", "get_bake_fps");
BIND_CONSTANT(HANDLE_BINARY_DISCARD_TEXTURES);
BIND_CONSTANT(HANDLE_BINARY_EXTRACT_TEXTURES);
diff --git a/modules/gltf/gltf_state.h b/modules/gltf/gltf_state.h
index c9efffa3ae..07efafe13b 100644
--- a/modules/gltf/gltf_state.h
+++ b/modules/gltf/gltf_state.h
@@ -57,6 +57,7 @@ protected:
int minor_version = 0;
String copyright;
Vector<uint8_t> glb_data;
+ double bake_fps = 30.0;
bool use_named_skin_binds = false;
bool use_khr_texture_transform = false;
@@ -108,6 +109,14 @@ protected:
static void _bind_methods();
public:
+ double get_bake_fps() const {
+ return bake_fps;
+ }
+
+ void set_bake_fps(double value) {
+ bake_fps = value;
+ }
+
void add_used_extension(const String &p_extension, bool p_required = false);
GLTFBufferViewIndex append_data_to_buffers(const Vector<uint8_t> &p_data, const bool p_deduplication);
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/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 fb22cf5983..2c07acf94f 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -5192,6 +5192,23 @@ void DisplayServerX11::set_context(Context p_context) {
}
}
+bool DisplayServerX11::is_window_transparency_available() const {
+ CharString net_wm_cm_name = vformat("_NET_WM_CM_S%d", XDefaultScreen(x11_display)).ascii();
+ Atom net_wm_cm = XInternAtom(x11_display, net_wm_cm_name.get_data(), False);
+ if (net_wm_cm == None) {
+ return false;
+ }
+ if (XGetSelectionOwner(x11_display, net_wm_cm) == None) {
+ return false;
+ }
+#if defined(RD_ENABLED)
+ if (rendering_device && !rendering_device->is_composite_alpha_supported()) {
+ return false;
+ }
+#endif
+ return OS::get_singleton()->is_layered_allowed();
+}
+
void DisplayServerX11::set_native_icon(const String &p_filename) {
WARN_PRINT("Native icon not supported by this display server.");
}
diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h
index 0861789b4a..7c69df3df0 100644
--- a/platform/linuxbsd/x11/display_server_x11.h
+++ b/platform/linuxbsd/x11/display_server_x11.h
@@ -530,6 +530,8 @@ public:
virtual void set_context(Context p_context) override;
+ virtual bool is_window_transparency_available() const override;
+
virtual void set_native_icon(const String &p_filename) override;
virtual void set_icon(const Ref<Image> &p_icon) override;
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h
index 3068ed071b..608c34edc6 100644
--- a/platform/macos/display_server_macos.h
+++ b/platform/macos/display_server_macos.h
@@ -439,6 +439,8 @@ public:
virtual Rect2 status_indicator_get_rect(IndicatorID p_id) const override;
virtual void delete_status_indicator(IndicatorID p_id) 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 Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error);
static Vector<String> get_rendering_drivers_func();
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index 747de17439..db71c48eff 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -3314,6 +3314,15 @@ void DisplayServerMacOS::delete_status_indicator(IndicatorID p_id) {
indicators.erase(p_id);
}
+bool DisplayServerMacOS::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();
+}
+
DisplayServer *DisplayServerMacOS::create_func(const String &p_rendering_driver, WindowMode p_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) {
DisplayServer *ds = memnew(DisplayServerMacOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, r_error));
if (r_error != OK) {
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..0be44f05e5 100644
--- a/platform/web/display_server_web.cpp
+++ b/platform/web/display_server_web.cpp
@@ -866,6 +866,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) {
@@ -1353,29 +1356,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/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 6ee35d4fbd..03584977fe 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -3401,6 +3401,21 @@ DisplayServer::VSyncMode DisplayServerWindows::window_get_vsync_mode(WindowID p_
void DisplayServerWindows::set_context(Context p_context) {
}
+bool DisplayServerWindows::is_window_transparency_available() const {
+ BOOL dwm_enabled = true;
+ if (DwmIsCompositionEnabled(&dwm_enabled) == S_OK) { // Note: Always enabled on Windows 8+, this check can be removed after Windows 7 support is dropped.
+ if (!dwm_enabled) {
+ return false;
+ }
+ }
+#if defined(RD_ENABLED)
+ if (rendering_device && !rendering_device->is_composite_alpha_supported()) {
+ return false;
+ }
+#endif
+ return OS::get_singleton()->is_layered_allowed();
+}
+
#define MI_WP_SIGNATURE 0xFF515700
#define SIGNATURE_MASK 0xFFFFFF00
// Keeping the name suggested by Microsoft, but this macro really answers:
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index ef9c470157..9a4eeba486 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -697,6 +697,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 Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error);
static Vector<String> get_rendering_drivers_func();
static void register_windows_driver();
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/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp
index 856c52b5d5..40e04f0fb4 100644
--- a/scene/3d/navigation_region_3d.cpp
+++ b/scene/3d/navigation_region_3d.cpp
@@ -474,7 +474,7 @@ void NavigationRegion3D::_update_debug_mesh() {
return;
}
- if (!NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) {
+ if (!NavigationServer3D::get_singleton()->get_debug_enabled() || !NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) {
if (debug_instance.is_valid()) {
RS::get_singleton()->instance_set_visible(debug_instance, false);
}
@@ -640,7 +640,7 @@ void NavigationRegion3D::_update_debug_mesh() {
#ifdef DEBUG_ENABLED
void NavigationRegion3D::_update_debug_edge_connections_mesh() {
- if (!NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) {
+ if (!NavigationServer3D::get_singleton()->get_debug_enabled() || !NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) {
if (debug_edge_connections_instance.is_valid()) {
RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false);
}
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/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/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/display_server.cpp b/servers/display_server.cpp
index 17f1548017..2150a3038e 100644
--- a/servers/display_server.cpp
+++ b/servers/display_server.cpp
@@ -996,6 +996,8 @@ void DisplayServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("tablet_get_current_driver"), &DisplayServer::tablet_get_current_driver);
ClassDB::bind_method(D_METHOD("tablet_set_current_driver", "name"), &DisplayServer::tablet_set_current_driver);
+ ClassDB::bind_method(D_METHOD("is_window_transparency_available"), &DisplayServer::is_window_transparency_available);
+
#ifndef DISABLE_DEPRECATED
BIND_ENUM_CONSTANT(FEATURE_GLOBAL_MENU);
#endif
diff --git a/servers/display_server.h b/servers/display_server.h
index 9a9bb28a06..5224d59c04 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -580,6 +580,8 @@ public:
virtual void set_context(Context p_context);
+ virtual bool is_window_transparency_available() const { return false; }
+
static void register_create_function(const char *p_name, CreateFunction p_function, GetRenderingDriversFunction p_get_drivers);
static int get_create_function_count();
static const char *get_create_function_name(int p_index);
diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp
index 96c0479ac3..b02d3def88 100644
--- a/servers/rendering/renderer_scene_cull.cpp
+++ b/servers/rendering/renderer_scene_cull.cpp
@@ -958,6 +958,8 @@ void RendererSceneCull::instance_set_blend_shape_weight(RID p_instance, int p_sh
if (instance->mesh_instance.is_valid()) {
RSG::mesh_storage->mesh_instance_set_blend_shape_weight(instance->mesh_instance, p_shape, p_weight);
}
+
+ _instance_queue_update(instance, false, false);
}
void RendererSceneCull::instance_set_surface_override_material(RID p_instance, int p_surface, RID p_material) {
diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp
index 15e1731823..0227472d0e 100644
--- a/servers/rendering/rendering_device.cpp
+++ b/servers/rendering/rendering_device.cpp
@@ -3332,12 +3332,15 @@ Error RenderingDevice::_draw_list_setup_framebuffer(Framebuffer *p_framebuffer,
}
Error RenderingDevice::_draw_list_render_pass_begin(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i p_viewport_offset, Point2i p_viewport_size, RDD::FramebufferID p_framebuffer_driver_id, RDD::RenderPassID p_render_pass) {
- LocalVector<RDD::RenderPassClearValue> clear_values;
- LocalVector<RDG::ResourceTracker *> resource_trackers;
- LocalVector<RDG::ResourceUsage> resource_usages;
+ thread_local LocalVector<RDD::RenderPassClearValue> clear_values;
+ thread_local LocalVector<RDG::ResourceTracker *> resource_trackers;
+ thread_local LocalVector<RDG::ResourceUsage> resource_usages;
bool uses_color = false;
bool uses_depth = false;
+ clear_values.clear();
clear_values.resize(p_framebuffer->texture_ids.size());
+ resource_trackers.clear();
+ resource_usages.clear();
int clear_values_count = 0;
{
int color_index = 0;
@@ -4734,6 +4737,10 @@ String RenderingDevice::get_device_api_name() const {
return driver->get_api_name();
}
+bool RenderingDevice::is_composite_alpha_supported() const {
+ return driver->is_composite_alpha_supported(main_queue);
+}
+
String RenderingDevice::get_device_api_version() const {
return driver->get_api_version();
}
diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h
index 42773fc347..d0fa4ab1fa 100644
--- a/servers/rendering/rendering_device.h
+++ b/servers/rendering/rendering_device.h
@@ -1360,6 +1360,8 @@ public:
String get_device_api_version() const;
String get_device_pipeline_cache_uuid() const;
+ bool is_composite_alpha_supported() const;
+
uint64_t get_driver_resource(DriverResource p_resource, RID p_rid = RID(), uint64_t p_index = 0);
static RenderingDevice *get_singleton();
diff --git a/servers/rendering/rendering_device_driver.h b/servers/rendering/rendering_device_driver.h
index e9464ba321..f9a861426a 100644
--- a/servers/rendering/rendering_device_driver.h
+++ b/servers/rendering/rendering_device_driver.h
@@ -769,6 +769,8 @@ public:
virtual String get_pipeline_cache_uuid() const = 0;
virtual const Capabilities &get_capabilities() const = 0;
+ virtual bool is_composite_alpha_supported(CommandQueueID p_queue) const { return false; }
+
/******************/
virtual ~RenderingDeviceDriver();
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"