summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--SConstruct6
-rw-r--r--core/SCsub12
-rw-r--r--core/extension/gdextension_interface.cpp102
-rw-r--r--core/extension/gdextension_interface.h67
-rw-r--r--core/input/SCsub2
-rw-r--r--core/math/random_number_generator.h2
-rw-r--r--core/math/random_pcg.cpp2
-rw-r--r--core/math/random_pcg.h2
-rw-r--r--core/object/script_language_extension.h35
-rw-r--r--doc/classes/Environment.xml18
-rw-r--r--doc/classes/Parallax2D.xml46
-rw-r--r--doc/classes/RandomNumberGenerator.xml6
-rw-r--r--doc/classes/RenderingServer.xml9
-rw-r--r--doc/classes/RichTextLabel.xml5
-rw-r--r--doc/classes/TextMesh.xml1
-rw-r--r--doc/classes/Transform3D.xml78
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp25
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.h2
-rw-r--r--drivers/gles3/storage/particles_storage.cpp2
-rw-r--r--drivers/unix/file_access_unix.cpp4
-rw-r--r--editor/SCsub10
-rw-r--r--editor/code_editor.cpp3
-rw-r--r--editor/debugger/debug_adapter/debug_adapter_parser.cpp1
-rw-r--r--editor/editor_node.cpp4
-rw-r--r--editor/export/editor_export.cpp6
-rw-r--r--editor/icons/Parallax2D.svg1
-rw-r--r--editor/icons/SCsub2
-rw-r--r--editor/import/resource_importer_wav.cpp12
-rw-r--r--editor/plugins/parallax_background_editor_plugin.cpp137
-rw-r--r--editor/plugins/parallax_background_editor_plugin.h67
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp303
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.h18
-rw-r--r--editor/register_editor_types.cpp2
-rw-r--r--editor/themes/SCsub2
-rw-r--r--main/SCsub6
-rw-r--r--methods.py15
-rw-r--r--modules/SCsub2
-rw-r--r--modules/gdscript/editor/script_templates/SCsub2
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp8
-rw-r--r--modules/gdscript/tests/scripts/completion/class_a.notest.gd8
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/get_node.tscn19
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/get_node_member_annotated.cfg4
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/literal/dollar.cfg7
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/literal/dollar.gd4
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/literal/percent.cfg7
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/literal/percent.gd4
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_class_scene.cfg14
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_class_scene.gd4
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_native_scene.cfg14
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_native_scene.gd4
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_class_scene.cfg14
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_class_scene.gd4
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_native_scene.cfg14
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_native_scene.gd4
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local/local.cfg7
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local/local.gd5
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_interfered/local_interfered.cfg7
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_interfered/local_interfered.gd5
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/class_local_interfered_scene.cfg14
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/class_local_interfered_scene.gd5
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/native_local_interfered_scene.cfg14
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/native_local_interfered_scene.gd5
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_scene/class_local_scene.cfg14
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_scene/class_local_scene.gd5
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_scene/native_local_scene.cfg14
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_scene/native_local_scene.gd5
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_typehint/class_local_typehint.cfg12
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_typehint/class_local_typehint.gd7
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_typehint/native_local_typehint.cfg12
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_typehint/native_local_typehint.gd5
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/class_local_typehint_scene.cfg14
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/class_local_typehint_scene.gd7
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/native_local_typehint_scene.cfg14
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/native_local_typehint_scene.gd5
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/class_local_typehint_scene_broad.cfg14
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/class_local_typehint_scene_broad.notest.gd6
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/native_local_typehint_scene_broad.cfg14
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/native_local_typehint_scene_broad.notest.gd6
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/class_local_typehint_scene_incompatible.cfg20
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/class_local_typehint_scene_incompatible.gd5
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/native_local_typehint_scene_incompatible.cfg20
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/native_local_typehint_scene_incompatible.gd5
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member/member.cfg7
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member/member.gd6
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_interfered/member_interfered.cfg7
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_interfered/member_interfered.gd6
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/class_member_interfered_scene.cfg14
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/class_member_interfered_scene.gd6
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/native_member_interfered_scene.cfg14
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/native_member_interfered_scene.gd6
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_scene/class_member_scene.cfg14
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_scene/class_member_scene.gd6
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_scene/native_member_scene.cfg14
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_scene/native_member_scene.gd6
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_typehint/class_member_typehint.cfg12
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_typehint/class_member_typehint.gd8
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_typehint/native_member_typehint.cfg12
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_typehint/native_member_typehint.gd (renamed from modules/gdscript/tests/scripts/completion/get_node/get_node_member_annotated.gd)2
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/class_member_typehint_scene.cfg14
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/class_member_typehint_scene.gd8
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/native_member_typehint_scene.cfg14
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/native_member_typehint_scene.gd6
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/class_member_typehint_scene_broad.cfg16
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/class_member_typehint_scene_broad.gd6
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/native_member_typehint_scene_broad.cfg16
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/native_member_typehint_scene_broad.gd6
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/class_member_typehint_scene_incompatible.cfg20
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/class_member_typehint_scene_incompatible.gd6
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/native_member_typehint_scene_incompatible.cfg20
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/native_member_typehint_scene_incompatible.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.gd28
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.out10
-rw-r--r--modules/gdscript/tests/test_completion.h2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedComplexStrings.cs26
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPropertyDefValGeneratorTests.cs9
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedComplexStrings_ScriptPropertyDefVal.generated.cs29
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportedComplexStrings.cs23
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs19
-rw-r--r--modules/mono/editor/bindings_generator.cpp25
-rw-r--r--modules/mono/editor/script_templates/SCsub2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs2
-rw-r--r--modules/multiplayer/scene_replication_config.cpp8
-rw-r--r--modules/multiplayer/scene_replication_config.h2
-rw-r--r--modules/text_server_adv/gdextension_build/methods.py8
-rw-r--r--modules/text_server_fb/gdextension_build/methods.py8
-rw-r--r--platform/ios/app_delegate.mm12
-rw-r--r--platform/linuxbsd/wayland/wayland_thread.cpp3
-rw-r--r--platform/linuxbsd/wayland/wayland_thread.h2
-rw-r--r--platform/windows/detect.py15
-rw-r--r--scene/2d/camera_2d.cpp6
-rw-r--r--scene/2d/parallax_2d.cpp290
-rw-r--r--scene/2d/parallax_2d.h101
-rw-r--r--scene/2d/parallax_background.cpp2
-rw-r--r--scene/2d/parallax_background.h2
-rw-r--r--scene/3d/bone_attachment_3d.cpp4
-rw-r--r--scene/3d/label_3d.cpp4
-rw-r--r--scene/gui/rich_text_label.cpp20
-rw-r--r--scene/gui/slider.cpp10
-rw-r--r--scene/register_scene_types.cpp2
-rw-r--r--scene/resources/2d/world_boundary_shape_2d.cpp39
-rw-r--r--scene/theme/icons/SCsub4
-rw-r--r--servers/rendering/renderer_canvas_cull.cpp73
-rw-r--r--servers/rendering/renderer_canvas_cull.h5
-rw-r--r--servers/rendering/renderer_canvas_render.h4
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp25
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.h2
-rw-r--r--servers/rendering/renderer_rd/storage_rd/particles_storage.cpp4
-rw-r--r--servers/rendering/rendering_server_default.h1
-rw-r--r--servers/rendering_server.cpp1
-rw-r--r--servers/rendering_server.h1
150 files changed, 2179 insertions, 350 deletions
diff --git a/SConstruct b/SConstruct
index b7809dfe42..b6de04a910 100644
--- a/SConstruct
+++ b/SConstruct
@@ -930,17 +930,17 @@ if selected_platform in platform_list:
GLSL_BUILDERS = {
"RD_GLSL": env.Builder(
- action=env.Run(glsl_builders.build_rd_headers, 'Building RD_GLSL header: "$TARGET"'),
+ action=env.Run(glsl_builders.build_rd_headers),
suffix="glsl.gen.h",
src_suffix=".glsl",
),
"GLSL_HEADER": env.Builder(
- action=env.Run(glsl_builders.build_raw_headers, 'Building GLSL header: "$TARGET"'),
+ action=env.Run(glsl_builders.build_raw_headers),
suffix="glsl.gen.h",
src_suffix=".glsl",
),
"GLES3_GLSL": env.Builder(
- action=env.Run(gles3_builders.build_gles3_headers, 'Building GLES3 GLSL header: "$TARGET"'),
+ action=env.Run(gles3_builders.build_gles3_headers),
suffix="glsl.gen.h",
src_suffix=".glsl",
),
diff --git a/core/SCsub b/core/SCsub
index 3b1a7ca79a..1f2166937a 100644
--- a/core/SCsub
+++ b/core/SCsub
@@ -202,27 +202,23 @@ env.Depends(
env.CommandNoCache(
"#core/io/certs_compressed.gen.h",
"#thirdparty/certs/ca-certificates.crt",
- env.Run(core_builders.make_certs_header, "Building ca-certificates header."),
+ env.Run(core_builders.make_certs_header),
)
# Authors
env.Depends("#core/authors.gen.h", "../AUTHORS.md")
-env.CommandNoCache(
- "#core/authors.gen.h", "../AUTHORS.md", env.Run(core_builders.make_authors_header, "Generating authors header.")
-)
+env.CommandNoCache("#core/authors.gen.h", "../AUTHORS.md", env.Run(core_builders.make_authors_header))
# Donors
env.Depends("#core/donors.gen.h", "../DONORS.md")
-env.CommandNoCache(
- "#core/donors.gen.h", "../DONORS.md", env.Run(core_builders.make_donors_header, "Generating donors header.")
-)
+env.CommandNoCache("#core/donors.gen.h", "../DONORS.md", env.Run(core_builders.make_donors_header))
# License
env.Depends("#core/license.gen.h", ["../COPYRIGHT.txt", "../LICENSE.txt"])
env.CommandNoCache(
"#core/license.gen.h",
["../COPYRIGHT.txt", "../LICENSE.txt"],
- env.Run(core_builders.make_license_header, "Generating license header."),
+ env.Run(core_builders.make_license_header),
)
# Chain load SCsubs
diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp
index f96a8873ca..67ec09d764 100644
--- a/core/extension/gdextension_interface.cpp
+++ b/core/extension/gdextension_interface.cpp
@@ -1240,43 +1240,84 @@ static void gdextension_ref_set_object(GDExtensionRefPtr p_ref, GDExtensionObjec
#ifndef DISABLE_DEPRECATED
static GDExtensionScriptInstancePtr gdextension_script_instance_create(const GDExtensionScriptInstanceInfo *p_info, GDExtensionScriptInstanceDataPtr p_instance_data) {
- GDExtensionScriptInstanceInfo2 *info_2 = memnew(GDExtensionScriptInstanceInfo2);
- info_2->set_func = p_info->set_func;
- info_2->get_func = p_info->get_func;
- info_2->get_property_list_func = p_info->get_property_list_func;
- info_2->free_property_list_func = p_info->free_property_list_func;
- info_2->get_class_category_func = nullptr;
- info_2->property_can_revert_func = p_info->property_can_revert_func;
- info_2->property_get_revert_func = p_info->property_get_revert_func;
- info_2->get_owner_func = p_info->get_owner_func;
- info_2->get_property_state_func = p_info->get_property_state_func;
- info_2->get_method_list_func = p_info->get_method_list_func;
- info_2->free_method_list_func = p_info->free_method_list_func;
- info_2->get_property_type_func = p_info->get_property_type_func;
- info_2->validate_property_func = nullptr;
- info_2->has_method_func = p_info->has_method_func;
- info_2->call_func = p_info->call_func;
- info_2->notification_func = nullptr;
- info_2->to_string_func = p_info->to_string_func;
- info_2->refcount_incremented_func = p_info->refcount_incremented_func;
- info_2->refcount_decremented_func = p_info->refcount_decremented_func;
- info_2->get_script_func = p_info->get_script_func;
- info_2->is_placeholder_func = p_info->is_placeholder_func;
- info_2->set_fallback_func = p_info->set_fallback_func;
- info_2->get_fallback_func = p_info->get_fallback_func;
- info_2->get_language_func = p_info->get_language_func;
- info_2->free_func = p_info->free_func;
+ GDExtensionScriptInstanceInfo3 *info_3 = memnew(GDExtensionScriptInstanceInfo3);
+ info_3->set_func = p_info->set_func;
+ info_3->get_func = p_info->get_func;
+ info_3->get_property_list_func = p_info->get_property_list_func;
+ info_3->free_property_list_func = nullptr;
+ info_3->get_class_category_func = nullptr;
+ info_3->property_can_revert_func = p_info->property_can_revert_func;
+ info_3->property_get_revert_func = p_info->property_get_revert_func;
+ info_3->get_owner_func = p_info->get_owner_func;
+ info_3->get_property_state_func = p_info->get_property_state_func;
+ info_3->get_method_list_func = p_info->get_method_list_func;
+ info_3->free_method_list_func = nullptr;
+ info_3->get_property_type_func = p_info->get_property_type_func;
+ info_3->validate_property_func = nullptr;
+ info_3->has_method_func = p_info->has_method_func;
+ info_3->call_func = p_info->call_func;
+ info_3->notification_func = nullptr;
+ info_3->to_string_func = p_info->to_string_func;
+ info_3->refcount_incremented_func = p_info->refcount_incremented_func;
+ info_3->refcount_decremented_func = p_info->refcount_decremented_func;
+ info_3->get_script_func = p_info->get_script_func;
+ info_3->is_placeholder_func = p_info->is_placeholder_func;
+ info_3->set_fallback_func = p_info->set_fallback_func;
+ info_3->get_fallback_func = p_info->get_fallback_func;
+ info_3->get_language_func = p_info->get_language_func;
+ info_3->free_func = p_info->free_func;
ScriptInstanceExtension *script_instance_extension = memnew(ScriptInstanceExtension);
script_instance_extension->instance = p_instance_data;
- script_instance_extension->native_info = info_2;
+ script_instance_extension->native_info = info_3;
script_instance_extension->free_native_info = true;
- script_instance_extension->deprecated_native_info.notification_func = p_info->notification_func;
+ script_instance_extension->deprecated_native_info = memnew(ScriptInstanceExtension::DeprecatedNativeInfo);
+ script_instance_extension->deprecated_native_info->notification_func = p_info->notification_func;
+ script_instance_extension->deprecated_native_info->free_property_list_func = p_info->free_property_list_func;
+ script_instance_extension->deprecated_native_info->free_method_list_func = p_info->free_method_list_func;
return reinterpret_cast<GDExtensionScriptInstancePtr>(script_instance_extension);
}
-#endif // DISABLE_DEPRECATED
static GDExtensionScriptInstancePtr gdextension_script_instance_create2(const GDExtensionScriptInstanceInfo2 *p_info, GDExtensionScriptInstanceDataPtr p_instance_data) {
+ GDExtensionScriptInstanceInfo3 *info_3 = memnew(GDExtensionScriptInstanceInfo3);
+ info_3->set_func = p_info->set_func;
+ info_3->get_func = p_info->get_func;
+ info_3->get_property_list_func = p_info->get_property_list_func;
+ info_3->free_property_list_func = nullptr;
+ info_3->get_class_category_func = nullptr;
+ info_3->property_can_revert_func = p_info->property_can_revert_func;
+ info_3->property_get_revert_func = p_info->property_get_revert_func;
+ info_3->get_owner_func = p_info->get_owner_func;
+ info_3->get_property_state_func = p_info->get_property_state_func;
+ info_3->get_method_list_func = p_info->get_method_list_func;
+ info_3->free_method_list_func = nullptr;
+ info_3->get_property_type_func = p_info->get_property_type_func;
+ info_3->validate_property_func = nullptr;
+ info_3->has_method_func = p_info->has_method_func;
+ info_3->call_func = p_info->call_func;
+ info_3->notification_func = p_info->notification_func;
+ info_3->to_string_func = p_info->to_string_func;
+ info_3->refcount_incremented_func = p_info->refcount_incremented_func;
+ info_3->refcount_decremented_func = p_info->refcount_decremented_func;
+ info_3->get_script_func = p_info->get_script_func;
+ info_3->is_placeholder_func = p_info->is_placeholder_func;
+ info_3->set_fallback_func = p_info->set_fallback_func;
+ info_3->get_fallback_func = p_info->get_fallback_func;
+ info_3->get_language_func = p_info->get_language_func;
+ info_3->free_func = p_info->free_func;
+
+ ScriptInstanceExtension *script_instance_extension = memnew(ScriptInstanceExtension);
+ script_instance_extension->instance = p_instance_data;
+ script_instance_extension->native_info = info_3;
+ script_instance_extension->free_native_info = true;
+ script_instance_extension->deprecated_native_info = memnew(ScriptInstanceExtension::DeprecatedNativeInfo);
+ script_instance_extension->deprecated_native_info->free_property_list_func = p_info->free_property_list_func;
+ script_instance_extension->deprecated_native_info->free_method_list_func = p_info->free_method_list_func;
+ return reinterpret_cast<GDExtensionScriptInstancePtr>(script_instance_extension);
+}
+#endif // DISABLE_DEPRECATED
+
+static GDExtensionScriptInstancePtr gdextension_script_instance_create3(const GDExtensionScriptInstanceInfo3 *p_info, GDExtensionScriptInstanceDataPtr p_instance_data) {
ScriptInstanceExtension *script_instance_extension = memnew(ScriptInstanceExtension);
script_instance_extension->instance = p_instance_data;
script_instance_extension->native_info = p_info;
@@ -1548,8 +1589,9 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(ref_set_object);
#ifndef DISABLE_DEPRECATED
REGISTER_INTERFACE_FUNC(script_instance_create);
-#endif // DISABLE_DEPRECATED
REGISTER_INTERFACE_FUNC(script_instance_create2);
+#endif // DISABLE_DEPRECATED
+ REGISTER_INTERFACE_FUNC(script_instance_create3);
REGISTER_INTERFACE_FUNC(placeholder_script_instance_create);
REGISTER_INTERFACE_FUNC(placeholder_script_instance_update);
REGISTER_INTERFACE_FUNC(object_get_script_instance);
diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h
index 65ee647a51..e7497a9d4c 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/gdextension_interface.h
@@ -480,7 +480,8 @@ typedef void *GDExtensionScriptInstanceDataPtr; // Pointer to custom ScriptInsta
typedef GDExtensionBool (*GDExtensionScriptInstanceSet)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionConstVariantPtr p_value);
typedef GDExtensionBool (*GDExtensionScriptInstanceGet)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret);
typedef const GDExtensionPropertyInfo *(*GDExtensionScriptInstanceGetPropertyList)(GDExtensionScriptInstanceDataPtr p_instance, uint32_t *r_count);
-typedef void (*GDExtensionScriptInstanceFreePropertyList)(GDExtensionScriptInstanceDataPtr p_instance, const GDExtensionPropertyInfo *p_list);
+typedef void (*GDExtensionScriptInstanceFreePropertyList)(GDExtensionScriptInstanceDataPtr p_instance, const GDExtensionPropertyInfo *p_list); // Deprecated. Use GDExtensionScriptInstanceFreePropertyList2 instead.
+typedef void (*GDExtensionScriptInstanceFreePropertyList2)(GDExtensionScriptInstanceDataPtr p_instance, const GDExtensionPropertyInfo *p_list, uint32_t p_count);
typedef GDExtensionBool (*GDExtensionScriptInstanceGetClassCategory)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionPropertyInfo *p_class_category);
typedef GDExtensionVariantType (*GDExtensionScriptInstanceGetPropertyType)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionBool *r_is_valid);
@@ -494,7 +495,8 @@ typedef void (*GDExtensionScriptInstancePropertyStateAdd)(GDExtensionConstString
typedef void (*GDExtensionScriptInstanceGetPropertyState)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionScriptInstancePropertyStateAdd p_add_func, void *p_userdata);
typedef const GDExtensionMethodInfo *(*GDExtensionScriptInstanceGetMethodList)(GDExtensionScriptInstanceDataPtr p_instance, uint32_t *r_count);
-typedef void (*GDExtensionScriptInstanceFreeMethodList)(GDExtensionScriptInstanceDataPtr p_instance, const GDExtensionMethodInfo *p_list);
+typedef void (*GDExtensionScriptInstanceFreeMethodList)(GDExtensionScriptInstanceDataPtr p_instance, const GDExtensionMethodInfo *p_list); // Deprecated. Use GDExtensionScriptInstanceFreeMethodList2 instead.
+typedef void (*GDExtensionScriptInstanceFreeMethodList2)(GDExtensionScriptInstanceDataPtr p_instance, const GDExtensionMethodInfo *p_list, uint32_t p_count);
typedef GDExtensionBool (*GDExtensionScriptInstanceHasMethod)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name);
@@ -554,7 +556,7 @@ typedef struct {
GDExtensionScriptInstanceFree free_func;
-} GDExtensionScriptInstanceInfo; // Deprecated. Use GDExtensionScriptInstanceInfo2 instead.
+} GDExtensionScriptInstanceInfo; // Deprecated. Use GDExtensionScriptInstanceInfo3 instead.
typedef struct {
GDExtensionScriptInstanceSet set_func;
@@ -595,7 +597,48 @@ typedef struct {
GDExtensionScriptInstanceFree free_func;
-} GDExtensionScriptInstanceInfo2;
+} GDExtensionScriptInstanceInfo2; // Deprecated. Use GDExtensionScriptInstanceInfo3 instead.
+
+typedef struct {
+ GDExtensionScriptInstanceSet set_func;
+ GDExtensionScriptInstanceGet get_func;
+ GDExtensionScriptInstanceGetPropertyList get_property_list_func;
+ GDExtensionScriptInstanceFreePropertyList2 free_property_list_func;
+ GDExtensionScriptInstanceGetClassCategory get_class_category_func; // Optional. Set to NULL for the default behavior.
+
+ GDExtensionScriptInstancePropertyCanRevert property_can_revert_func;
+ GDExtensionScriptInstancePropertyGetRevert property_get_revert_func;
+
+ GDExtensionScriptInstanceGetOwner get_owner_func;
+ GDExtensionScriptInstanceGetPropertyState get_property_state_func;
+
+ GDExtensionScriptInstanceGetMethodList get_method_list_func;
+ GDExtensionScriptInstanceFreeMethodList2 free_method_list_func;
+ GDExtensionScriptInstanceGetPropertyType get_property_type_func;
+ GDExtensionScriptInstanceValidateProperty validate_property_func;
+
+ GDExtensionScriptInstanceHasMethod has_method_func;
+
+ GDExtensionScriptInstanceCall call_func;
+ GDExtensionScriptInstanceNotification2 notification_func;
+
+ GDExtensionScriptInstanceToString to_string_func;
+
+ GDExtensionScriptInstanceRefCountIncremented refcount_incremented_func;
+ GDExtensionScriptInstanceRefCountDecremented refcount_decremented_func;
+
+ GDExtensionScriptInstanceGetScript get_script_func;
+
+ GDExtensionScriptInstanceIsPlaceholder is_placeholder_func;
+
+ GDExtensionScriptInstanceSet set_fallback_func;
+ GDExtensionScriptInstanceGet get_fallback_func;
+
+ GDExtensionScriptInstanceGetLanguage get_language_func;
+
+ GDExtensionScriptInstanceFree free_func;
+
+} GDExtensionScriptInstanceInfo3;
/* INITIALIZATION */
@@ -2380,7 +2423,7 @@ typedef void (*GDExtensionInterfaceRefSetObject)(GDExtensionRefPtr p_ref, GDExte
/**
* @name script_instance_create
* @since 4.1
- * @deprecated in Godot 4.2. Use `script_instance_create2` instead.
+ * @deprecated in Godot 4.2. Use `script_instance_create3` instead.
*
* Creates a script instance that contains the given info and instance data.
*
@@ -2394,6 +2437,7 @@ typedef GDExtensionScriptInstancePtr (*GDExtensionInterfaceScriptInstanceCreate)
/**
* @name script_instance_create2
* @since 4.2
+ * @deprecated in Godot 4.3. Use `script_instance_create3` instead.
*
* Creates a script instance that contains the given info and instance data.
*
@@ -2405,6 +2449,19 @@ typedef GDExtensionScriptInstancePtr (*GDExtensionInterfaceScriptInstanceCreate)
typedef GDExtensionScriptInstancePtr (*GDExtensionInterfaceScriptInstanceCreate2)(const GDExtensionScriptInstanceInfo2 *p_info, GDExtensionScriptInstanceDataPtr p_instance_data);
/**
+ * @name script_instance_create3
+ * @since 4.3
+ *
+ * Creates a script instance that contains the given info and instance data.
+ *
+ * @param p_info A pointer to a GDExtensionScriptInstanceInfo3 struct.
+ * @param p_instance_data A pointer to a data representing the script instance in the GDExtension. This will be passed to all the function pointers on p_info.
+ *
+ * @return A pointer to a ScriptInstanceExtension object.
+ */
+typedef GDExtensionScriptInstancePtr (*GDExtensionInterfaceScriptInstanceCreate3)(const GDExtensionScriptInstanceInfo3 *p_info, GDExtensionScriptInstanceDataPtr p_instance_data);
+
+/**
* @name placeholder_script_instance_create
* @since 4.2
*
diff --git a/core/input/SCsub b/core/input/SCsub
index b12bf561de..da29637135 100644
--- a/core/input/SCsub
+++ b/core/input/SCsub
@@ -14,7 +14,7 @@ controller_databases = [
gensource = env.CommandNoCache(
"default_controller_mappings.gen.cpp",
controller_databases,
- env.Run(input_builders.make_default_controller_mappings, "Generating default controller mappings."),
+ env.Run(input_builders.make_default_controller_mappings),
)
env.add_source_files(env.core_sources, "*.cpp")
diff --git a/core/math/random_number_generator.h b/core/math/random_number_generator.h
index bedeb56ce4..7ec4cdffb0 100644
--- a/core/math/random_number_generator.h
+++ b/core/math/random_number_generator.h
@@ -57,7 +57,7 @@ public:
_FORCE_INLINE_ real_t randfn(real_t p_mean = 0.0, real_t p_deviation = 1.0) { return randbase.randfn(p_mean, p_deviation); }
_FORCE_INLINE_ int randi_range(int p_from, int p_to) { return randbase.random(p_from, p_to); }
- _FORCE_INLINE_ int rand_weighted(const Vector<float> &p_weights) { return randbase.rand_weighted(p_weights); }
+ _FORCE_INLINE_ int64_t rand_weighted(const Vector<float> &p_weights) { return randbase.rand_weighted(p_weights); }
RandomNumberGenerator() { randbase.randomize(); }
};
diff --git a/core/math/random_pcg.cpp b/core/math/random_pcg.cpp
index e754a34271..e083820494 100644
--- a/core/math/random_pcg.cpp
+++ b/core/math/random_pcg.cpp
@@ -43,7 +43,7 @@ void RandomPCG::randomize() {
seed(((uint64_t)OS::get_singleton()->get_unix_time() + OS::get_singleton()->get_ticks_usec()) * pcg.state + PCG_DEFAULT_INC_64);
}
-int RandomPCG::rand_weighted(const Vector<float> &p_weights) {
+int64_t RandomPCG::rand_weighted(const Vector<float> &p_weights) {
ERR_FAIL_COND_V_MSG(p_weights.is_empty(), -1, "Weights array is empty.");
int64_t weights_size = p_weights.size();
const float *weights = p_weights.ptr();
diff --git a/core/math/random_pcg.h b/core/math/random_pcg.h
index fa8ad3cfb3..fd0934b24a 100644
--- a/core/math/random_pcg.h
+++ b/core/math/random_pcg.h
@@ -90,7 +90,7 @@ public:
return pcg32_boundedrand_r(&pcg, bounds);
}
- int rand_weighted(const Vector<float> &p_weights);
+ int64_t rand_weighted(const Vector<float> &p_weights);
// Obtaining floating point numbers in [0, 1] range with "good enough" uniformity.
// These functions sample the output of rand() as the fraction part of an infinite binary number,
diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h
index b7222a159a..aa0788b8bf 100644
--- a/core/object/script_language_extension.h
+++ b/core/object/script_language_extension.h
@@ -655,11 +655,17 @@ VARIANT_ENUM_CAST(ScriptLanguageExtension::CodeCompletionLocation)
class ScriptInstanceExtension : public ScriptInstance {
public:
- const GDExtensionScriptInstanceInfo2 *native_info;
+ const GDExtensionScriptInstanceInfo3 *native_info;
+
+#ifndef DISABLE_DEPRECATED
bool free_native_info = false;
- struct {
+ struct DeprecatedNativeInfo {
GDExtensionScriptInstanceNotification notification_func = nullptr;
- } deprecated_native_info;
+ GDExtensionScriptInstanceFreePropertyList free_property_list_func = nullptr;
+ GDExtensionScriptInstanceFreeMethodList free_method_list_func = nullptr;
+ };
+ DeprecatedNativeInfo *deprecated_native_info = nullptr;
+#endif // DISABLE_DEPRECATED
GDExtensionScriptInstanceDataPtr instance = nullptr;
@@ -706,7 +712,11 @@ public:
p_list->push_back(PropertyInfo(pinfo[i]));
}
if (native_info->free_property_list_func) {
- native_info->free_property_list_func(instance, pinfo);
+ native_info->free_property_list_func(instance, pinfo, pcount);
+#ifndef DISABLE_DEPRECATED
+ } else if (deprecated_native_info && deprecated_native_info->free_property_list_func) {
+ deprecated_native_info->free_property_list_func(instance, pinfo);
+#endif // DISABLE_DEPRECATED
}
}
}
@@ -781,7 +791,11 @@ public:
p_list->push_back(MethodInfo(minfo[i]));
}
if (native_info->free_method_list_func) {
- native_info->free_method_list_func(instance, minfo);
+ native_info->free_method_list_func(instance, minfo, mcount);
+#ifndef DISABLE_DEPRECATED
+ } else if (deprecated_native_info && deprecated_native_info->free_method_list_func) {
+ deprecated_native_info->free_method_list_func(instance, minfo);
+#endif // DISABLE_DEPRECATED
}
}
}
@@ -808,8 +822,8 @@ public:
if (native_info->notification_func) {
native_info->notification_func(instance, p_notification, p_reversed);
#ifndef DISABLE_DEPRECATED
- } else if (deprecated_native_info.notification_func) {
- deprecated_native_info.notification_func(instance, p_notification);
+ } else if (deprecated_native_info && deprecated_native_info->notification_func) {
+ deprecated_native_info->notification_func(instance, p_notification);
#endif // DISABLE_DEPRECATED
}
}
@@ -885,9 +899,14 @@ public:
if (native_info->free_func) {
native_info->free_func(instance);
}
+#ifndef DISABLE_DEPRECATED
if (free_native_info) {
- memfree(const_cast<GDExtensionScriptInstanceInfo2 *>(native_info));
+ memfree(const_cast<GDExtensionScriptInstanceInfo3 *>(native_info));
}
+ if (deprecated_native_info) {
+ memfree(deprecated_native_info);
+ }
+#endif // DISABLE_DEPRECATED
}
#if defined(__GNUC__) && !defined(__clang__)
diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml
index 6913d1f75c..bf91d02b3a 100644
--- a/doc/classes/Environment.xml
+++ b/doc/classes/Environment.xml
@@ -127,13 +127,15 @@
</member>
<member name="glow_blend_mode" type="int" setter="set_glow_blend_mode" getter="get_glow_blend_mode" enum="Environment.GlowBlendMode" default="2">
The glow blending mode.
+ [b]Note:[/b] [member glow_blend_mode] has no effect when using the Compatibility rendering method, due to this rendering method using a simpler glow implementation optimized for low-end devices.
</member>
<member name="glow_bloom" type="float" setter="set_glow_bloom" getter="get_glow_bloom" default="0.0">
The bloom's intensity. If set to a value higher than [code]0[/code], this will make glow visible in areas darker than the [member glow_hdr_threshold].
</member>
<member name="glow_enabled" type="bool" setter="set_glow_enabled" getter="is_glow_enabled" default="false">
- If [code]true[/code], the glow effect is enabled.
- [b]Note:[/b] Glow is only supported in the Forward+ and Mobile rendering methods, not Compatibility. When using the Mobile rendering method, glow will look different due to the lower dynamic range available in the Mobile rendering method.
+ If [code]true[/code], the glow effect is enabled. This simulates real world eye/camera behavior where bright pixels bleed onto surrounding pixels.
+ [b]Note:[/b] When using the Mobile rendering method, glow looks different due to the lower dynamic range available in the Mobile rendering method.
+ [b]Note:[/b] When using the Compatibility rendering method, glow uses a different implementation with some properties being unavailable and hidden from the inspector: [code]glow_levels/*[/code], [member glow_normalized], [member glow_strength], [member glow_blend_mode], [member glow_mix], [member glow_map], and [member glow_map_strength]. This implementation is optimized to run on low-end devices and is less flexible as a result.
</member>
<member name="glow_hdr_luminance_cap" type="float" setter="set_glow_hdr_luminance_cap" getter="get_glow_hdr_luminance_cap" default="12.0">
The higher threshold of the HDR glow. Areas brighter than this threshold will be clamped for the purposes of the glow effect.
@@ -149,40 +151,52 @@
</member>
<member name="glow_levels/1" type="float" setter="set_glow_level" getter="get_glow_level" default="0.0">
The intensity of the 1st level of glow. This is the most "local" level (least blurry).
+ [b]Note:[/b] [member glow_levels/1] has no effect when using the Compatibility rendering method, due to this rendering method using a simpler glow implementation optimized for low-end devices.
</member>
<member name="glow_levels/2" type="float" setter="set_glow_level" getter="get_glow_level" default="0.0">
The intensity of the 2nd level of glow.
+ [b]Note:[/b] [member glow_levels/2] has no effect when using the Compatibility rendering method, due to this rendering method using a simpler glow implementation optimized for low-end devices.
</member>
<member name="glow_levels/3" type="float" setter="set_glow_level" getter="get_glow_level" default="1.0">
The intensity of the 3rd level of glow.
+ [b]Note:[/b] [member glow_levels/3] has no effect when using the Compatibility rendering method, due to this rendering method using a simpler glow implementation optimized for low-end devices.
</member>
<member name="glow_levels/4" type="float" setter="set_glow_level" getter="get_glow_level" default="0.0">
The intensity of the 4th level of glow.
+ [b]Note:[/b] [member glow_levels/4] has no effect when using the Compatibility rendering method, due to this rendering method using a simpler glow implementation optimized for low-end devices.
</member>
<member name="glow_levels/5" type="float" setter="set_glow_level" getter="get_glow_level" default="1.0">
The intensity of the 5th level of glow.
+ [b]Note:[/b] [member glow_levels/5] has no effect when using the Compatibility rendering method, due to this rendering method using a simpler glow implementation optimized for low-end devices.
</member>
<member name="glow_levels/6" type="float" setter="set_glow_level" getter="get_glow_level" default="0.0">
The intensity of the 6th level of glow.
+ [b]Note:[/b] [member glow_levels/6] has no effect when using the Compatibility rendering method, due to this rendering method using a simpler glow implementation optimized for low-end devices.
</member>
<member name="glow_levels/7" type="float" setter="set_glow_level" getter="get_glow_level" default="0.0">
The intensity of the 7th level of glow. This is the most "global" level (blurriest).
+ [b]Note:[/b] [member glow_levels/7] has no effect when using the Compatibility rendering method, due to this rendering method using a simpler glow implementation optimized for low-end devices.
</member>
<member name="glow_map" type="Texture" setter="set_glow_map" getter="get_glow_map">
The texture that should be used as a glow map to [i]multiply[/i] the resulting glow color according to [member glow_map_strength]. This can be used to create a "lens dirt" effect. The texture's RGB color channels are used for modulation, but the alpha channel is ignored.
[b]Note:[/b] The texture will be stretched to fit the screen. Therefore, it's recommended to use a texture with an aspect ratio that matches your project's base aspect ratio (typically 16:9).
+ [b]Note:[/b] [member glow_map] has no effect when using the Compatibility rendering method, due to this rendering method using a simpler glow implementation optimized for low-end devices.
</member>
<member name="glow_map_strength" type="float" setter="set_glow_map_strength" getter="get_glow_map_strength" default="0.8">
How strong of an impact the [member glow_map] should have on the overall glow effect. A strength of [code]0.0[/code] means the glow map has no effect on the overall glow effect. A strength of [code]1.0[/code] means the glow has a full effect on the overall glow effect (and can turn off glow entirely in specific areas of the screen if the glow map has black areas).
+ [b]Note:[/b] [member glow_map_strength] has no effect when using the Compatibility rendering method, due to this rendering method using a simpler glow implementation optimized for low-end devices.
</member>
<member name="glow_mix" type="float" setter="set_glow_mix" getter="get_glow_mix" default="0.05">
When using the [constant GLOW_BLEND_MODE_MIX] [member glow_blend_mode], this controls how much the source image is blended with the glow layer. A value of [code]0.0[/code] makes the glow rendering invisible, while a value of [code]1.0[/code] is equivalent to [constant GLOW_BLEND_MODE_REPLACE].
+ [b]Note:[/b] [member glow_mix] has no effect when using the Compatibility rendering method, due to this rendering method using a simpler glow implementation optimized for low-end devices.
</member>
<member name="glow_normalized" type="bool" setter="set_glow_normalized" getter="is_glow_normalized" default="false">
If [code]true[/code], glow levels will be normalized so that summed together their intensities equal [code]1.0[/code].
+ [b]Note:[/b] [member glow_normalized] has no effect when using the Compatibility rendering method, due to this rendering method using a simpler glow implementation optimized for low-end devices.
</member>
<member name="glow_strength" type="float" setter="set_glow_strength" getter="get_glow_strength" default="1.0">
The strength of the glow effect. This applies as the glow is blurred across the screen and increases the distance and intensity of the blur. When using the Mobile rendering method, this should be increased to compensate for the lower dynamic range.
+ [b]Note:[/b] [member glow_strength] has no effect when using the Compatibility rendering method, due to this rendering method using a simpler glow implementation optimized for low-end devices.
</member>
<member name="reflected_light_source" type="int" setter="set_reflection_source" getter="get_reflection_source" enum="Environment.ReflectionSource" default="0">
The reflected (specular) light source.
diff --git a/doc/classes/Parallax2D.xml b/doc/classes/Parallax2D.xml
new file mode 100644
index 0000000000..6db29b7a33
--- /dev/null
+++ b/doc/classes/Parallax2D.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="Parallax2D" inherits="Node2D" experimental="This node is meant to replace [ParallaxBackground] and [ParallaxLayer]. The implementation may change in the future." xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ A node used to create a parallax scrolling background.
+ </brief_description>
+ <description>
+ A [Parallax2D] is used to create a parallax effect. It can move at a different speed relative to the camera movement using [member scroll_scale]. This creates an illusion of depth in a 2D game. If manual scrolling is desired, the [Camera2D] position can be ignored with [member ignore_camera_scroll].
+ [b]Note:[/b] Any changes to this node's position made after it enters the scene tree will be overridden if [member ignore_camera_scroll] is [code]false[/code] or [member screen_offset] is modified.
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="autoscroll" type="Vector2" setter="set_autoscroll" getter="get_autoscroll" default="Vector2(0, 0)">
+ Velocity at which the offset scrolls automatically, in pixels per second.
+ </member>
+ <member name="follow_viewport" type="bool" setter="set_follow_viewport" getter="get_follow_viewport" default="true">
+ If [code]true[/code], this [Parallax2D] is offset by the current camera's position. If the [Parallax2D] is in a [CanvasLayer] separate from the current camera, it may be desired to match the value with [member CanvasLayer.follow_viewport_enabled].
+ </member>
+ <member name="ignore_camera_scroll" type="bool" setter="set_ignore_camera_scroll" getter="is_ignore_camera_scroll" default="false">
+ If [code]true[/code], [Parallax2D]'s position is not affected by the position of the camera.
+ </member>
+ <member name="limit_begin" type="Vector2" setter="set_limit_begin" getter="get_limit_begin" default="Vector2(-1e+07, -1e+07)">
+ Top-left limits for scrolling to begin. If the camera is outside of this limit, the [Parallax2D] stops scrolling. Must be lower than [member limit_end] minus the viewport size to work.
+ </member>
+ <member name="limit_end" type="Vector2" setter="set_limit_end" getter="get_limit_end" default="Vector2(1e+07, 1e+07)">
+ Bottom-right limits for scrolling to end. If the camera is outside of this limit, the [Parallax2D] will stop scrolling. Must be higher than [member limit_begin] and the viewport size combined to work.
+ </member>
+ <member name="repeat_size" type="Vector2" setter="set_repeat_size" getter="get_repeat_size" default="Vector2(0, 0)">
+ Repeats the [Texture2D] of each of this node's children and offsets them by this value. When scrolling, the node's position loops, giving the illusion of an infinite scrolling background if the values are larger than the screen size. If an axis is set to [code]0[/code], the [Texture2D] will not be repeated.
+ </member>
+ <member name="repeat_times" type="int" setter="set_repeat_times" getter="get_repeat_times" default="1">
+ Overrides the amount of times the texture repeats. Each texture copy spreads evenly from the original by [member repeat_size]. Useful for when zooming out with a camera.
+ </member>
+ <member name="screen_offset" type="Vector2" setter="set_screen_offset" getter="get_screen_offset" default="Vector2(0, 0)">
+ Offset used to scroll this [Parallax2D]. This value is updated automatically unless [member ignore_camera_scroll] is [code]true[/code].
+ </member>
+ <member name="scroll_offset" type="Vector2" setter="set_scroll_offset" getter="get_scroll_offset" default="Vector2(0, 0)">
+ The [Parallax2D]'s offset. Similar to [member screen_offset] and [member Node2D.position], but will not be overridden.
+ [b]Note:[/b] Values will loop if [member repeat_size] is set higher than [code]0[/code].
+ </member>
+ <member name="scroll_scale" type="Vector2" setter="set_scroll_scale" getter="get_scroll_scale" default="Vector2(1, 1)">
+ Multiplier to the final [Parallax2D]'s offset. Can be used to simulate distance from the camera.
+ For example, a value of [code]1[/code] scrolls at the same speed as the camera. A value greater than [code]1[/code] scrolls faster, making objects appear closer. Less than [code]1[/code] scrolls slower, making object appear closer and a value of [code]0[/code] stops the objects completely.
+ </member>
+ </members>
+</class>
diff --git a/doc/classes/RandomNumberGenerator.xml b/doc/classes/RandomNumberGenerator.xml
index e005c1c22c..44e29d2322 100644
--- a/doc/classes/RandomNumberGenerator.xml
+++ b/doc/classes/RandomNumberGenerator.xml
@@ -24,13 +24,13 @@
Returns a random index with non-uniform weights. Prints an error and returns [code]-1[/code] if the array is empty.
[codeblocks]
[gdscript]
- var rnd = RandomNumberGenerator.new()
+ var rng = RandomNumberGenerator.new()
- var my_array = ["one", "two", "three, "four"]
+ var my_array = ["one", "two", "three", "four"]
var weights = PackedFloat32Array([0.5, 1, 1, 2])
# Prints one of the four elements in `my_array`.
- # It is more likely to print "four", and less likely to print "two".
+ # It is more likely to print "four", and less likely to print "one".
print(my_array[rng.rand_weighted(weights)])
[/gdscript]
[/codeblocks]
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index bc4f3a9052..152518e7da 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -869,6 +869,15 @@
A copy of the canvas item will be drawn with a local offset of the mirroring [Vector2].
</description>
</method>
+ <method name="canvas_set_item_repeat">
+ <return type="void" />
+ <param index="0" name="item" type="RID" />
+ <param index="1" name="repeat_size" type="Vector2" />
+ <param index="2" name="repeat_times" type="int" />
+ <description>
+ A copy of the canvas item will be drawn with a local offset of the [param repeat_size] by the number of times of the [param repeat_times]. As the [param repeat_times] increases, the copies will spread away from the origin texture.
+ </description>
+ </method>
<method name="canvas_set_modulate">
<return type="void" />
<param index="0" name="canvas" type="RID" />
diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml
index 6e37e395a2..b9a6b06fe3 100644
--- a/doc/classes/RichTextLabel.xml
+++ b/doc/classes/RichTextLabel.xml
@@ -55,8 +55,8 @@
<method name="clear">
<return type="void" />
<description>
- Clears the tag stack.
- [b]Note:[/b] This method will not modify [member text], but setting [member text] to an empty string also clears the stack.
+ Clears the tag stack, causing the label to display nothing.
+ [b]Note:[/b] This method does not affect [member text], and its contents will show again if the label is redrawn. However, setting [member text] to an empty [String] also clears the stack.
</description>
</method>
<method name="deselect">
@@ -594,6 +594,7 @@
</member>
<member name="bbcode_enabled" type="bool" setter="set_use_bbcode" getter="is_using_bbcode" default="false">
If [code]true[/code], the label uses BBCode formatting.
+ [b]Note:[/b] This only affects the contents of [member text], not the tag stack.
</member>
<member name="clip_contents" type="bool" setter="set_clip_contents" getter="is_clipping_contents" overrides="Control" default="true" />
<member name="context_menu_enabled" type="bool" setter="set_context_menu_enabled" getter="is_context_menu_enabled" default="false">
diff --git a/doc/classes/TextMesh.xml b/doc/classes/TextMesh.xml
index 00c6b0b1a8..9e705311c5 100644
--- a/doc/classes/TextMesh.xml
+++ b/doc/classes/TextMesh.xml
@@ -53,6 +53,7 @@
</member>
<member name="text" type="String" setter="set_text" getter="get_text" default="&quot;&quot;">
The text to generate mesh from.
+ [b]Note:[/b] Due to being a [Resource], it doesn't follow the rules of [member Node.auto_translate_mode]. If disabling translation is desired, it should be done manually with [method Object.set_message_translation].
</member>
<member name="text_direction" type="int" setter="set_text_direction" getter="get_text_direction" enum="TextServer.Direction" default="0">
Base text writing direction.
diff --git a/doc/classes/Transform3D.xml b/doc/classes/Transform3D.xml
index a1ddb75bc2..e4394e9c70 100644
--- a/doc/classes/Transform3D.xml
+++ b/doc/classes/Transform3D.xml
@@ -4,8 +4,9 @@
A 3×4 matrix representing a 3D transformation.
</brief_description>
<description>
- A 3×4 matrix (3 rows, 4 columns) used for 3D linear transformations. It can represent transformations such as translation, rotation, and scaling. It consists of a [member basis] (first 3 columns) and a [Vector3] for the [member origin] (last column).
+ The [Transform3D] built-in [Variant] type is a 3×4 matrix representing a transformation in 3D space. It contains a [Basis], which on its own can represent rotation, scale, and shear. Additionally, combined with its own [member origin], the transform can also represent a translation.
For a general introduction, see the [url=$DOCS_URL/tutorials/math/matrices_and_transforms.html]Matrices and transforms[/url] tutorial.
+ [b]Note:[/b] Godot uses a [url=https://en.wikipedia.org/wiki/Right-hand_rule]right-handed coordinate system[/url], which is a common standard. For directions, the convention for built-in types like [Camera3D] is for -Z to point forward (+X is right, +Y is up, and +Z is back). Other objects may use different direction conventions. For more information, see the [url=$DOCS_URL/tutorials/assets_pipeline/importing_scenes.html#d-asset-direction-conventions]Importing 3D Scenes[/url] tutorial.
</description>
<tutorials>
<link title="Math documentation index">$DOCS_URL/tutorials/math/index.html</link>
@@ -19,7 +20,7 @@
<constructor name="Transform3D">
<return type="Transform3D" />
<description>
- Constructs a default-initialized [Transform3D] set to [constant IDENTITY].
+ Constructs a [Transform3D] identical to the [constant IDENTITY].
</description>
</constructor>
<constructor name="Transform3D">
@@ -34,14 +35,14 @@
<param index="0" name="basis" type="Basis" />
<param index="1" name="origin" type="Vector3" />
<description>
- Constructs a Transform3D from a [Basis] and [Vector3].
+ Constructs a [Transform3D] from a [Basis] and [Vector3].
</description>
</constructor>
<constructor name="Transform3D">
<return type="Transform3D" />
<param index="0" name="from" type="Projection" />
<description>
- Constructs a Transform3D from a [Projection] by trimming the last row of the projection matrix ([code]from.x.w[/code], [code]from.y.w[/code], [code]from.z.w[/code], and [code]from.w.w[/code] are not copied over).
+ Constructs a [Transform3D] from a [Projection]. Because [Transform3D] is a 3×4 matrix and [Projection] is a 4×4 matrix, this operation trims the last row of the projection matrix ([code]from.x.w[/code], [code]from.y.w[/code], [code]from.z.w[/code], and [code]from.w.w[/code] are not included in the new transform).
</description>
</constructor>
<constructor name="Transform3D">
@@ -51,7 +52,8 @@
<param index="2" name="z_axis" type="Vector3" />
<param index="3" name="origin" type="Vector3" />
<description>
- Constructs a Transform3D from four [Vector3] values (matrix columns). Each axis corresponds to local basis vectors (some of which may be scaled).
+ Constructs a [Transform3D] from four [Vector3] values (also called matrix columns).
+ The first three arguments are the [member basis]'s axes ([member Basis.x], [member Basis.y], and [member Basis.z]).
</description>
</constructor>
</constructors>
@@ -59,7 +61,8 @@
<method name="affine_inverse" qualifiers="const">
<return type="Transform3D" />
<description>
- Returns the inverse of the transform, under the assumption that the basis is invertible (must have non-zero determinant).
+ Returns the inverted version of this transform. Unlike [method inverse], this method works with almost any [member basis], including non-uniform ones, but is slower. See also [method Basis.inverse].
+ [b]Note:[/b] For this method to return correctly, the transform's [member basis] needs to not have a determinant of exactly [code]0[/code] (see [method Basis.determinant]).
</description>
</method>
<method name="interpolate_with" qualifiers="const">
@@ -67,13 +70,15 @@
<param index="0" name="xform" type="Transform3D" />
<param index="1" name="weight" type="float" />
<description>
- Returns a transform interpolated between this transform and another by a given [param weight] (on the range of 0.0 to 1.0).
+ Returns the result of the linear interpolation between this transform and [param xform] by the given [param weight].
+ The [param weight] should be between [code]0.0[/code] and [code]1.0[/code] (inclusive). Values outside this range are allowed and can be used to perform [i]extrapolation[/i], instead.
</description>
</method>
<method name="inverse" qualifiers="const">
<return type="Transform3D" />
<description>
- Returns the inverse of the transform, under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection is fine, scaling/skew is not). Use [method affine_inverse] for non-orthonormal transforms (e.g. with scaling).
+ Returns the inverted version of this transform. See also [method Basis.inverse].
+ [b]Note:[/b] For this method to return correctly, the transform's [member basis] needs to be [i]orthonormal[/i] (see [method Basis.orthonormalized]). That means, the basis should only represent a rotation. If it does not, use [method affine_inverse] instead.
</description>
</method>
<method name="is_equal_approx" qualifiers="const">
@@ -95,7 +100,7 @@
<param index="1" name="up" type="Vector3" default="Vector3(0, 1, 0)" />
<param index="2" name="use_model_front" type="bool" default="false" />
<description>
- Returns a copy of the transform rotated such that the forward axis (-Z) points towards the [param target] position.
+ Returns a copy of this transform rotated so that the forward axis (-Z) points towards the [param target] position.
The up axis (+Y) points as close to the [param up] vector as possible while staying perpendicular to the forward axis. The resulting transform is orthonormalized. The existing rotation, scale, and skew information from the original transform is discarded. The [param target] and [param up] vectors cannot be zero, cannot be parallel to each other, and are defined in global/parent space.
If [param use_model_front] is [code]true[/code], the +Z axis (asset front) is treated as forward (implies +X is left) and points toward the [param target] position. By default, the -Z axis (camera forward) is treated as forward (implies +X is right).
</description>
@@ -103,7 +108,7 @@
<method name="orthonormalized" qualifiers="const">
<return type="Transform3D" />
<description>
- Returns the transform with the basis orthogonal (90 degrees), and normalized axis vectors (scale of 1 or -1).
+ Returns a copy of this transform with its [member basis] orthonormalized. An orthonormal basis is both [i]orthogonal[/i] (the axes are perpendicular to each other) and [i]normalized[/i] (the axes have a length of [code]1[/code]), which also means it can only represent rotation. See also [method Basis.orthonormalized].
</description>
</method>
<method name="rotated" qualifiers="const">
@@ -111,7 +116,7 @@
<param index="0" name="axis" type="Vector3" />
<param index="1" name="angle" type="float" />
<description>
- Returns a copy of the transform rotated around the given [param axis] by the given [param angle] (in radians).
+ Returns a copy of this transform rotated around the given [param axis] by the given [param angle] (in radians).
The [param axis] must be a normalized vector.
This method is an optimized version of multiplying the given transform [code]X[/code] with a corresponding rotation transform [code]R[/code] from the left, i.e., [code]R * X[/code].
This can be seen as transforming with respect to the global/parent frame.
@@ -122,7 +127,7 @@
<param index="0" name="axis" type="Vector3" />
<param index="1" name="angle" type="float" />
<description>
- Returns a copy of the transform rotated around the given [param axis] by the given [param angle] (in radians).
+ Returns a copy of this transform rotated around the given [param axis] by the given [param angle] (in radians).
The [param axis] must be a normalized vector.
This method is an optimized version of multiplying the given transform [code]X[/code] with a corresponding rotation transform [code]R[/code] from the right, i.e., [code]X * R[/code].
This can be seen as transforming with respect to the local frame.
@@ -132,7 +137,7 @@
<return type="Transform3D" />
<param index="0" name="scale" type="Vector3" />
<description>
- Returns a copy of the transform scaled by the given [param scale] factor.
+ Returns a copy of this transform scaled by the given [param scale] factor.
This method is an optimized version of multiplying the given transform [code]X[/code] with a corresponding scaling transform [code]S[/code] from the left, i.e., [code]S * X[/code].
This can be seen as transforming with respect to the global/parent frame.
</description>
@@ -141,7 +146,7 @@
<return type="Transform3D" />
<param index="0" name="scale" type="Vector3" />
<description>
- Returns a copy of the transform scaled by the given [param scale] factor.
+ Returns a copy of this transform scaled by the given [param scale] factor.
This method is an optimized version of multiplying the given transform [code]X[/code] with a corresponding scaling transform [code]S[/code] from the right, i.e., [code]X * S[/code].
This can be seen as transforming with respect to the local frame.
</description>
@@ -150,7 +155,7 @@
<return type="Transform3D" />
<param index="0" name="offset" type="Vector3" />
<description>
- Returns a copy of the transform translated by the given [param offset].
+ Returns a copy of this transform translated by the given [param offset].
This method is an optimized version of multiplying the given transform [code]X[/code] with a corresponding translation transform [code]T[/code] from the left, i.e., [code]T * X[/code].
This can be seen as transforming with respect to the global/parent frame.
</description>
@@ -159,7 +164,7 @@
<return type="Transform3D" />
<param index="0" name="offset" type="Vector3" />
<description>
- Returns a copy of the transform translated by the given [param offset].
+ Returns a copy of this transform translated by the given [param offset].
This method is an optimized version of multiplying the given transform [code]X[/code] with a corresponding translation transform [code]T[/code] from the right, i.e., [code]X * T[/code].
This can be seen as transforming with respect to the local frame.
</description>
@@ -167,24 +172,25 @@
</methods>
<members>
<member name="basis" type="Basis" setter="" getter="" default="Basis(1, 0, 0, 0, 1, 0, 0, 0, 1)">
- The basis is a matrix containing 3 [Vector3] as its columns: X axis, Y axis, and Z axis. These vectors can be interpreted as the basis vectors of local coordinate system traveling with the object.
+ The [Basis] of this transform. It is composed by 3 axes ([member Basis.x], [member Basis.y], and [member Basis.z]). Together, these represent the transform's rotation, scale, and shearing.
</member>
<member name="origin" type="Vector3" setter="" getter="" default="Vector3(0, 0, 0)">
- The translation offset of the transform (column 3, the fourth column). Equivalent to array index [code]3[/code].
+ The translation offset of this transform. In 3D space, this can be seen as the position.
</member>
</members>
<constants>
<constant name="IDENTITY" value="Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)">
- [Transform3D] with no translation, rotation or scaling applied. When applied to other data structures, [constant IDENTITY] performs no transformation.
+ A transform with no translation, no rotation, and its scale being [code]1[/code]. Its [member basis] is equal to [constant Basis.IDENTITY].
+ When multiplied by another [Variant] such as [AABB] or another [Transform3D], no transformation occurs.
</constant>
<constant name="FLIP_X" value="Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)">
- [Transform3D] with mirroring applied perpendicular to the YZ plane.
+ [Transform3D] with mirroring applied perpendicular to the YZ plane. Its [member basis] is equal to [constant Basis.FLIP_X].
</constant>
<constant name="FLIP_Y" value="Transform3D(1, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 0)">
- [Transform3D] with mirroring applied perpendicular to the XZ plane.
+ [Transform3D] with mirroring applied perpendicular to the XZ plane. Its [member basis] is equal to [constant Basis.FLIP_Y].
</constant>
<constant name="FLIP_Z" value="Transform3D(1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0)">
- [Transform3D] with mirroring applied perpendicular to the XY plane.
+ [Transform3D] with mirroring applied perpendicular to the XY plane. Its [member basis] is equal to [constant Basis.FLIP_Z].
</constant>
</constants>
<operators>
@@ -192,7 +198,7 @@
<return type="bool" />
<param index="0" name="right" type="Transform3D" />
<description>
- Returns [code]true[/code] if the transforms are not equal.
+ Returns [code]true[/code] if the components of both transforms are not equal.
[b]Note:[/b] Due to floating-point precision errors, consider using [method is_equal_approx] instead, which is more reliable.
</description>
</operator>
@@ -200,70 +206,76 @@
<return type="AABB" />
<param index="0" name="right" type="AABB" />
<description>
- Transforms (multiplies) the [AABB] by the given [Transform3D] matrix.
+ Transforms (multiplies) the [AABB] by this transformation matrix.
</description>
</operator>
<operator name="operator *">
<return type="PackedVector3Array" />
<param index="0" name="right" type="PackedVector3Array" />
<description>
- Transforms (multiplies) each element of the [Vector3] array by the given [Transform3D] matrix.
+ Transforms (multiplies) every [Vector3] element of the given [PackedVector3Array] by this transformation matrix.
+ On larger arrays, this operation is much faster than transforming each [Vector3] individually.
</description>
</operator>
<operator name="operator *">
<return type="Plane" />
<param index="0" name="right" type="Plane" />
<description>
- Transforms (multiplies) the [Plane] by the given [Transform3D] transformation matrix.
+ Transforms (multiplies) the [Plane] by this transformation matrix.
</description>
</operator>
<operator name="operator *">
<return type="Transform3D" />
<param index="0" name="right" type="Transform3D" />
<description>
- Composes these two transformation matrices by multiplying them together. This has the effect of transforming the second transform (the child) by the first transform (the parent).
+ Transforms (multiplies) this transform by the [param right] transform.
+ This is the operation performed between parent and child [Node3D]s.
+ [b]Note:[/b] If you need to only modify one attribute of this transform, consider using one of the following methods, instead:
+ - For translation, see [method translated] or [method translated_local].
+ - For rotation, see [method rotated] or [method rotated_local].
+ - For scale, see [method scaled] or [method scaled_local].
</description>
</operator>
<operator name="operator *">
<return type="Vector3" />
<param index="0" name="right" type="Vector3" />
<description>
- Transforms (multiplies) the [Vector3] by the given [Transform3D] matrix.
+ Transforms (multiplies) the [Vector3] by this transformation matrix.
</description>
</operator>
<operator name="operator *">
<return type="Transform3D" />
<param index="0" name="right" type="float" />
<description>
- This operator multiplies all components of the [Transform3D], including the [member origin] vector, which scales it uniformly.
+ Multiplies all components of the [Transform3D] by the given [float], including the [member origin]. This affects the transform's scale uniformly, also resizing the [member basis].
</description>
</operator>
<operator name="operator *">
<return type="Transform3D" />
<param index="0" name="right" type="int" />
<description>
- This operator multiplies all components of the [Transform3D], including the [member origin] vector, which scales it uniformly.
+ Multiplies all components of the [Transform3D] by the given [int], including the [member origin]. This affects the transform's scale uniformly, also resizing the [member basis].
</description>
</operator>
<operator name="operator /">
<return type="Transform3D" />
<param index="0" name="right" type="float" />
<description>
- This operator divides all components of the [Transform3D], including the [member origin] vector, which inversely scales it uniformly.
+ Divides all components of the [Transform3D] by the given [float], including the [member origin]. This affects the transform's scale uniformly, also resizing the [member basis].
</description>
</operator>
<operator name="operator /">
<return type="Transform3D" />
<param index="0" name="right" type="int" />
<description>
- This operator divides all components of the [Transform3D], including the [member origin] vector, which inversely scales it uniformly.
+ Divides all components of the [Transform3D] by the given [int], including the [member origin]. This affects the transform's scale uniformly, also resizing the [member basis].
</description>
</operator>
<operator name="operator ==">
<return type="bool" />
<param index="0" name="right" type="Transform3D" />
<description>
- Returns [code]true[/code] if the transforms are exactly equal.
+ Returns [code]true[/code] if the components of both transforms are exactly equal.
[b]Note:[/b] Due to floating-point precision errors, consider using [method is_equal_approx] instead, which is more reliable.
</description>
</operator>
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index 198160939a..de990a4222 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -634,7 +634,23 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
GLES3::CanvasShaderData::BlendMode blend_mode = shader_data_cache ? shader_data_cache->blend_mode : GLES3::CanvasShaderData::BLEND_MODE_MIX;
- _record_item_commands(ci, p_to_render_target, p_canvas_transform_inverse, current_clip, blend_mode, p_lights, index, batch_broken, r_sdf_used);
+ if (!ci->repeat_size.x && !ci->repeat_size.y) {
+ _record_item_commands(ci, p_to_render_target, p_canvas_transform_inverse, current_clip, blend_mode, p_lights, index, batch_broken, r_sdf_used, Point2());
+ } else {
+ Point2 start_pos = ci->repeat_size * -(ci->repeat_times / 2);
+ Point2 end_pos = ci->repeat_size * ci->repeat_times + ci->repeat_size + start_pos;
+ Point2 pos = start_pos;
+
+ do {
+ do {
+ _record_item_commands(ci, p_to_render_target, p_canvas_transform_inverse, current_clip, blend_mode, p_lights, index, batch_broken, r_sdf_used, pos);
+ pos.y += ci->repeat_size.y;
+ } while (pos.y < end_pos.y);
+
+ pos.x += ci->repeat_size.x;
+ pos.y = start_pos.y;
+ } while (pos.x < end_pos.x);
+ }
}
if (index == 0) {
@@ -784,7 +800,7 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
state.last_item_index += index;
}
-void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used) {
+void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used, const Point2 &p_offset) {
RenderingServer::CanvasItemTextureFilter texture_filter = p_item->texture_filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT ? state.default_filter : p_item->texture_filter;
if (texture_filter != state.canvas_instance_batches[state.current_batch_index].filter) {
@@ -802,6 +818,11 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
}
Transform2D base_transform = p_canvas_transform_inverse * p_item->final_transform;
+
+ if (p_offset.x || p_offset.y) {
+ base_transform *= Transform2D(0, p_offset / p_item->xform.get_scale());
+ }
+
Transform2D draw_transform; // Used by transform command
Color base_color = p_item->final_modulate;
diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h
index 88befa7883..a3762e828e 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.h
+++ b/drivers/gles3/rasterizer_canvas_gles3.h
@@ -357,7 +357,7 @@ public:
void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used, RenderingMethod::RenderInfo *r_render_info = nullptr) override;
void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false, RenderingMethod::RenderInfo *r_render_info = nullptr);
- void _record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_break_batch, bool &r_sdf_used);
+ void _record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_break_batch, bool &r_sdf_used, const Point2 &p_offset);
void _render_batch(Light *p_lights, uint32_t p_index, RenderingMethod::RenderInfo *r_render_info = nullptr);
bool _bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant, uint64_t p_specialization);
void _new_batch(bool &r_batch_broken);
diff --git a/drivers/gles3/storage/particles_storage.cpp b/drivers/gles3/storage/particles_storage.cpp
index c5a97bdbd5..4d563ab28b 100644
--- a/drivers/gles3/storage/particles_storage.cpp
+++ b/drivers/gles3/storage/particles_storage.cpp
@@ -395,7 +395,7 @@ AABB ParticlesStorage::particles_get_current_aabb(RID p_particles) {
bool first = true;
const uint8_t *data_ptr = (const uint8_t *)buffer.ptr();
- uint32_t particle_data_size = sizeof(ParticleInstanceData3D) + sizeof(float) * particles->userdata_count;
+ uint32_t particle_data_size = sizeof(ParticleInstanceData3D);
for (int i = 0; i < total_amount; i++) {
const ParticleInstanceData3D &particle_data = *(const ParticleInstanceData3D *)&data_ptr[particle_data_size * i];
diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp
index d1e4d207e7..a35d8bfdde 100644
--- a/drivers/unix/file_access_unix.cpp
+++ b/drivers/unix/file_access_unix.cpp
@@ -395,7 +395,7 @@ Error FileAccessUnix::_set_unix_permissions(const String &p_file, BitField<FileA
}
bool FileAccessUnix::_get_hidden_attribute(const String &p_file) {
-#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__)
String file = fix_path(p_file);
struct stat st = {};
@@ -409,7 +409,7 @@ bool FileAccessUnix::_get_hidden_attribute(const String &p_file) {
}
Error FileAccessUnix::_set_hidden_attribute(const String &p_file, bool p_hidden) {
-#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__)
String file = fix_path(p_file);
struct stat st = {};
diff --git a/editor/SCsub b/editor/SCsub
index bbb40ff303..c88fd5e16a 100644
--- a/editor/SCsub
+++ b/editor/SCsub
@@ -68,7 +68,7 @@ if env.editor_build:
env.CommandNoCache(
"#editor/doc_data_compressed.gen.h",
docs,
- env.Run(editor_builders.make_doc_header, "Generating documentation header."),
+ env.Run(editor_builders.make_doc_header),
)
# Editor interface and class reference translations incur a significant size
@@ -83,7 +83,7 @@ if env.editor_build:
env.CommandNoCache(
"#editor/editor_translations.gen.h",
tlist,
- env.Run(editor_builders.make_editor_translations_header, "Generating editor translations header."),
+ env.Run(editor_builders.make_editor_translations_header),
)
# Property translations
@@ -92,7 +92,7 @@ if env.editor_build:
env.CommandNoCache(
"#editor/property_translations.gen.h",
tlist,
- env.Run(editor_builders.make_property_translations_header, "Generating property translations header."),
+ env.Run(editor_builders.make_property_translations_header),
)
# Documentation translations
@@ -101,7 +101,7 @@ if env.editor_build:
env.CommandNoCache(
"#editor/doc_translations.gen.h",
tlist,
- env.Run(editor_builders.make_doc_translations_header, "Generating translations header."),
+ env.Run(editor_builders.make_doc_translations_header),
)
# Extractable translations
@@ -110,7 +110,7 @@ if env.editor_build:
env.CommandNoCache(
"#editor/extractable_translations.gen.h",
tlist,
- env.Run(editor_builders.make_extractable_translations_header, "Generating extractable translations header."),
+ env.Run(editor_builders.make_extractable_translations_header),
)
env.add_source_files(env.editor_sources, "*.cpp")
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index 3efefe83c3..08299da260 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -932,9 +932,6 @@ void CodeTextEditor::_complete_request() {
if (code_complete_func) {
code_complete_func(code_complete_ud, ctext, &entries, forced);
}
- if (entries.size() == 0) {
- return;
- }
for (const ScriptLanguage::CodeCompletionOption &e : entries) {
Color font_color = completion_font_color;
diff --git a/editor/debugger/debug_adapter/debug_adapter_parser.cpp b/editor/debugger/debug_adapter/debug_adapter_parser.cpp
index e854681010..5009de4ac4 100644
--- a/editor/debugger/debug_adapter/debug_adapter_parser.cpp
+++ b/editor/debugger/debug_adapter/debug_adapter_parser.cpp
@@ -485,6 +485,7 @@ Dictionary DebugAdapterParser::req_evaluate(const Dictionary &p_params) const {
String value = EditorDebuggerNode::get_singleton()->get_var_value(args["expression"]);
body["result"] = value;
+ body["variablesReference"] = 0;
return response;
}
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index dc90256490..d2d10efaf3 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -6191,7 +6191,9 @@ EditorNode::EditorNode() {
// Only if no touchscreen ui hint, disable emulation just in case.
Input::get_singleton()->set_emulate_touch_from_mouse(false);
}
- DisplayServer::get_singleton()->cursor_set_custom_image(Ref<Resource>());
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CUSTOM_CURSOR_SHAPE)) {
+ DisplayServer::get_singleton()->cursor_set_custom_image(Ref<Resource>());
+ }
}
SceneState::set_disable_placeholders(true);
diff --git a/editor/export/editor_export.cpp b/editor/export/editor_export.cpp
index 0c1cce9f3e..1be9fe5740 100644
--- a/editor/export/editor_export.cpp
+++ b/editor/export/editor_export.cpp
@@ -219,6 +219,12 @@ void EditorExport::load_config() {
}
String platform = config->get_value(section, "platform");
+#ifndef DISABLE_DEPRECATED
+ // Compatibility with Linux platform before 4.3.
+ if (platform == "Linux/X11") {
+ platform = "Linux";
+ }
+#endif
Ref<EditorExportPreset> preset;
diff --git a/editor/icons/Parallax2D.svg b/editor/icons/Parallax2D.svg
new file mode 100644
index 0000000000..8d41102a86
--- /dev/null
+++ b/editor/icons/Parallax2D.svg
@@ -0,0 +1 @@
+<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><path d="m3 1a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-10a2 2 0 00-2-2zm10 2v10h-10v-10zM7 5v6l-3-3zm2 0v6l3-3z" fill="#8da5f3"/></svg> \ No newline at end of file
diff --git a/editor/icons/SCsub b/editor/icons/SCsub
index cbbfe1d7ea..d2d752cff4 100644
--- a/editor/icons/SCsub
+++ b/editor/icons/SCsub
@@ -7,7 +7,7 @@ import editor_icons_builders
env["BUILDERS"]["MakeEditorIconsBuilder"] = Builder(
- action=env.Run(editor_icons_builders.make_editor_icons_action, "Generating editor icons header."),
+ action=env.Run(editor_icons_builders.make_editor_icons_action),
suffix=".h",
src_suffix=".svg",
)
diff --git a/editor/import/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp
index c97b6a7579..ab14a5f01d 100644
--- a/editor/import/resource_importer_wav.cpp
+++ b/editor/import/resource_importer_wav.cpp
@@ -328,7 +328,7 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
int ipos = 0;
for (int i = 0; i < new_data_frames; i++) {
- //simple cubic interpolation should be enough.
+ // Cubic interpolation should be enough.
float mu = frac;
@@ -337,15 +337,7 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
float y2 = data[MIN(frames - 1, ipos + 1) * format_channels + c];
float y3 = data[MIN(frames - 1, ipos + 2) * format_channels + c];
- float mu2 = mu * mu;
- float a0 = y3 - y2 - y0 + y1;
- float a1 = y0 - y1 - a0;
- float a2 = y2 - y0;
- float a3 = y1;
-
- float res = (a0 * mu * mu2 + a1 * mu2 + a2 * mu + a3);
-
- new_data.write[i * format_channels + c] = res;
+ new_data.write[i * format_channels + c] = Math::cubic_interpolate(y1, y2, y0, y3, mu);
// update position and always keep fractional part within ]0...1]
// in order to avoid 32bit floating point precision errors
diff --git a/editor/plugins/parallax_background_editor_plugin.cpp b/editor/plugins/parallax_background_editor_plugin.cpp
new file mode 100644
index 0000000000..e14a81778e
--- /dev/null
+++ b/editor/plugins/parallax_background_editor_plugin.cpp
@@ -0,0 +1,137 @@
+/**************************************************************************/
+/* parallax_background_editor_plugin.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "parallax_background_editor_plugin.h"
+
+#include "canvas_item_editor_plugin.h"
+#include "editor/editor_node.h"
+#include "editor/editor_undo_redo_manager.h"
+#include "editor/scene_tree_dock.h"
+#include "scene/2d/parallax_2d.h"
+#include "scene/2d/parallax_background.h"
+#include "scene/2d/parallax_layer.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/menu_button.h"
+
+void ParallaxBackgroundEditorPlugin::edit(Object *p_object) {
+ parallax_background = Object::cast_to<ParallaxBackground>(p_object);
+}
+
+bool ParallaxBackgroundEditorPlugin::handles(Object *p_object) const {
+ return Object::cast_to<ParallaxBackground>(p_object) != nullptr;
+}
+
+void ParallaxBackgroundEditorPlugin::make_visible(bool p_visible) {
+ if (p_visible) {
+ toolbar->show();
+ } else {
+ toolbar->hide();
+ }
+}
+
+void ParallaxBackgroundEditorPlugin::_menu_callback(int p_idx) {
+ if (p_idx == MENU_CONVERT_TO_PARALLAX_2D) {
+ convert_to_parallax2d();
+ }
+}
+
+void ParallaxBackgroundEditorPlugin::convert_to_parallax2d() {
+ ParallaxBackground *parallax_bg = parallax_background;
+ TypedArray<Node> children = parallax_bg->get_children();
+
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
+ ur->create_action(TTR("Convert to Parallax2D"), UndoRedo::MERGE_DISABLE, parallax_bg);
+
+ for (int i = 0; i < children.size(); i++) {
+ ParallaxLayer *parallax_layer = Object::cast_to<ParallaxLayer>(children[i]);
+
+ if (!parallax_layer) {
+ continue;
+ }
+
+ Parallax2D *parallax2d = memnew(Parallax2D);
+
+ Point2 offset = parallax_bg->get_scroll_base_offset() * parallax_layer->get_motion_scale();
+ offset += parallax_layer->get_motion_offset() + parallax_layer->get_position();
+ parallax2d->set_scroll_offset(offset);
+
+ Point2 limit_begin = parallax2d->get_limit_begin();
+ Point2 limit_end = parallax2d->get_limit_end();
+
+ if (parallax_bg->get_limit_begin().x != 0 || parallax_bg->get_limit_end().x != 0) {
+ limit_begin.x = parallax_bg->get_limit_begin().x;
+ limit_end.x = parallax_bg->get_limit_end().x;
+ }
+
+ if (parallax_bg->get_limit_begin().y != 0 || parallax_bg->get_limit_end().y != 0) {
+ limit_begin.y = parallax_bg->get_limit_begin().y;
+ limit_end.y = parallax_bg->get_limit_end().y;
+ }
+
+ parallax2d->set_limit_begin(limit_begin);
+ parallax2d->set_limit_end(limit_end);
+ parallax2d->set_follow_viewport(!parallax_bg->is_ignore_camera_zoom());
+ parallax2d->set_repeat_size(parallax_layer->get_mirroring());
+ parallax2d->set_scroll_scale(parallax_bg->get_scroll_base_scale() * parallax_layer->get_motion_scale());
+
+ SceneTreeDock::get_singleton()->replace_node(parallax_layer, parallax2d);
+ }
+
+ if (parallax_bg->is_ignore_camera_zoom()) {
+ CanvasLayer *canvas_layer = memnew(CanvasLayer);
+ SceneTreeDock::get_singleton()->replace_node(parallax_bg, canvas_layer);
+ } else {
+ Node2D *node2d = memnew(Node2D);
+ SceneTreeDock::get_singleton()->replace_node(parallax_bg, node2d);
+ }
+
+ ur->commit_action(false);
+}
+
+void ParallaxBackgroundEditorPlugin::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ menu->get_popup()->connect("id_pressed", callable_mp(this, &ParallaxBackgroundEditorPlugin::_menu_callback));
+ menu->set_icon(menu->get_editor_theme_icon(SNAME("ParallaxBackground")));
+ } break;
+ }
+}
+
+ParallaxBackgroundEditorPlugin::ParallaxBackgroundEditorPlugin() {
+ toolbar = memnew(HBoxContainer);
+ toolbar->hide();
+ add_control_to_container(CONTAINER_CANVAS_EDITOR_MENU, toolbar);
+
+ menu = memnew(MenuButton);
+ menu->get_popup()->add_item(TTR("Convert to Parallax2D"), MENU_CONVERT_TO_PARALLAX_2D);
+ menu->set_text(TTR("ParallaxBackground"));
+ menu->set_switch_on_hover(true);
+ toolbar->add_child(menu);
+}
diff --git a/editor/plugins/parallax_background_editor_plugin.h b/editor/plugins/parallax_background_editor_plugin.h
new file mode 100644
index 0000000000..ba394d04dc
--- /dev/null
+++ b/editor/plugins/parallax_background_editor_plugin.h
@@ -0,0 +1,67 @@
+/**************************************************************************/
+/* parallax_background_editor_plugin.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 PARALLAX_BACKGROUND_EDITOR_PLUGIN_H
+#define PARALLAX_BACKGROUND_EDITOR_PLUGIN_H
+
+#include "editor/editor_plugin.h"
+
+class HBoxContainer;
+class MenuButton;
+class ParallaxBackground;
+
+class ParallaxBackgroundEditorPlugin : public EditorPlugin {
+ GDCLASS(ParallaxBackgroundEditorPlugin, EditorPlugin);
+
+ enum {
+ MENU_CONVERT_TO_PARALLAX_2D,
+ };
+
+ ParallaxBackground *parallax_background = nullptr;
+ HBoxContainer *toolbar = nullptr;
+ MenuButton *menu = nullptr;
+
+ void _menu_callback(int p_idx);
+ void convert_to_parallax2d();
+
+protected:
+ void _notification(int p_what);
+
+public:
+ virtual String get_name() const override { return "ParallaxBackground"; }
+ bool has_main_screen() const override { return false; }
+ virtual void edit(Object *p_object) override;
+ virtual bool handles(Object *p_object) const override;
+ virtual void make_visible(bool p_visible) override;
+
+ ParallaxBackgroundEditorPlugin();
+};
+
+#endif // PARALLAX_BACKGROUND_EDITOR_PLUGIN_H
diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp
index d60345c906..9c1170492b 100644
--- a/editor/plugins/sprite_frames_editor_plugin.cpp
+++ b/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -283,7 +283,7 @@ void SpriteFramesEditor::_sheet_add_frames() {
const Size2i separation = _get_separation();
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Add Frame"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene());
+ undo_redo->create_action(TTR("Add Frame"), UndoRedo::MERGE_DISABLE, frames.ptr());
int fc = frames->get_frame_count(edited_anim);
_sheet_sort_frames();
@@ -625,7 +625,7 @@ void SpriteFramesEditor::_file_load_request(const Vector<String> &p_path, int p_
}
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Add Frame"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene());
+ undo_redo->create_action(TTR("Add Frame"), UndoRedo::MERGE_DISABLE, frames.ptr());
int fc = frames->get_frame_count(edited_anim);
int count = 0;
@@ -675,30 +675,57 @@ void SpriteFramesEditor::_load_pressed() {
void SpriteFramesEditor::_paste_pressed() {
ERR_FAIL_COND(!frames->has_animation(edited_anim));
+ Ref<ClipboardSpriteFrames> clipboard_frames = EditorSettings::get_singleton()->get_resource_clipboard();
+ if (clipboard_frames.is_valid()) {
+ _paste_frame_array(clipboard_frames);
+ return;
+ }
+
+ Ref<Texture2D> texture = EditorSettings::get_singleton()->get_resource_clipboard();
+ if (texture.is_valid()) {
+ _paste_texture(texture);
+ return;
+ }
+}
+
+void SpriteFramesEditor::_paste_frame_array(const Ref<ClipboardSpriteFrames> &p_clipboard_frames) {
+ if (p_clipboard_frames->frames.is_empty()) {
+ return;
+ }
+
Ref<Texture2D> texture;
float duration = 1.0;
- Ref<EditorSpriteFramesFrame> frame = EditorSettings::get_singleton()->get_resource_clipboard();
- if (frame.is_valid()) {
- texture = frame->texture;
- duration = frame->duration;
- } else {
- texture = EditorSettings::get_singleton()->get_resource_clipboard();
- }
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->create_action(TTR("Paste Frame(s)"), UndoRedo::MERGE_DISABLE, frames.ptr());
- if (texture.is_null()) {
- dialog->set_text(TTR("Resource clipboard is empty or not a texture!"));
- dialog->set_title(TTR("Error!"));
- //dialog->get_cancel()->set_text("Close");
- dialog->set_ok_button_text(TTR("Close"));
- dialog->popup_centered();
- return; ///beh should show an error i guess
+ int undo_index = frames->get_frame_count(edited_anim);
+
+ for (int index = 0; index < p_clipboard_frames->frames.size(); index++) {
+ const ClipboardSpriteFrames::Frame &frame = p_clipboard_frames->frames[index];
+ texture = frame.texture;
+ duration = frame.duration;
+
+ undo_redo->add_do_method(frames.ptr(), "add_frame", edited_anim, texture, duration);
+ undo_redo->add_undo_method(frames.ptr(), "remove_frame", edited_anim, undo_index);
}
+ undo_redo->add_do_method(this, "_update_library");
+ undo_redo->add_undo_method(this, "_update_library");
+ undo_redo->commit_action();
+}
+
+void SpriteFramesEditor::_paste_texture(const Ref<Texture2D> &p_texture) {
+ float duration = 1.0;
+
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Paste Frame"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene());
- undo_redo->add_do_method(frames.ptr(), "add_frame", edited_anim, texture, duration);
- undo_redo->add_undo_method(frames.ptr(), "remove_frame", edited_anim, frames->get_frame_count(edited_anim));
+ undo_redo->create_action(TTR("Paste Texture"), UndoRedo::MERGE_DISABLE, frames.ptr());
+
+ int undo_index = frames->get_frame_count(edited_anim);
+
+ undo_redo->add_do_method(frames.ptr(), "add_frame", edited_anim, p_texture, duration);
+ undo_redo->add_undo_method(frames.ptr(), "remove_frame", edited_anim, undo_index);
+
undo_redo->add_do_method(this, "_update_library");
undo_redo->add_undo_method(this, "_update_library");
undo_redo->commit_action();
@@ -707,31 +734,39 @@ void SpriteFramesEditor::_paste_pressed() {
void SpriteFramesEditor::_copy_pressed() {
ERR_FAIL_COND(!frames->has_animation(edited_anim));
- if (frame_list->get_current() < 0) {
- return;
- }
+ Vector<int> selected_items = frame_list->get_selected_items();
- Ref<Texture2D> texture = frames->get_frame_texture(edited_anim, frame_list->get_current());
- if (texture.is_null()) {
+ if (selected_items.is_empty()) {
return;
}
- Ref<EditorSpriteFramesFrame> frame = memnew(EditorSpriteFramesFrame);
- frame->texture = texture;
- frame->duration = frames->get_frame_duration(edited_anim, frame_list->get_current());
+ Ref<ClipboardSpriteFrames> clipboard_frames = memnew(ClipboardSpriteFrames);
- EditorSettings::get_singleton()->set_resource_clipboard(frame);
+ for (const int &frame_index : selected_items) {
+ Ref<Texture2D> texture = frames->get_frame_texture(edited_anim, frame_index);
+ if (texture.is_null()) {
+ continue;
+ }
+
+ ClipboardSpriteFrames::Frame frame;
+ frame.texture = texture;
+ frame.duration = frames->get_frame_duration(edited_anim, frame_index);
+
+ clipboard_frames->frames.push_back(frame);
+ }
+ EditorSettings::get_singleton()->set_resource_clipboard(clipboard_frames);
}
void SpriteFramesEditor::_empty_pressed() {
ERR_FAIL_COND(!frames->has_animation(edited_anim));
int from = -1;
+ Vector<int> selected_items = frame_list->get_selected_items();
- if (frame_list->get_current() >= 0) {
- from = frame_list->get_current();
- sel = from;
-
+ if (!selected_items.is_empty()) {
+ from = selected_items[0];
+ selection.clear();
+ selection.push_back(from + 1);
} else {
from = frames->get_frame_count(edited_anim);
}
@@ -739,7 +774,7 @@ void SpriteFramesEditor::_empty_pressed() {
Ref<Texture2D> texture;
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Add Empty"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene());
+ undo_redo->create_action(TTR("Add Empty"), UndoRedo::MERGE_DISABLE, frames.ptr());
undo_redo->add_do_method(frames.ptr(), "add_frame", edited_anim, texture, 1.0, from);
undo_redo->add_undo_method(frames.ptr(), "remove_frame", edited_anim, from);
undo_redo->add_do_method(this, "_update_library");
@@ -751,11 +786,12 @@ void SpriteFramesEditor::_empty2_pressed() {
ERR_FAIL_COND(!frames->has_animation(edited_anim));
int from = -1;
+ Vector<int> selected_items = frame_list->get_selected_items();
- if (frame_list->get_current() >= 0) {
- from = frame_list->get_current();
- sel = from;
-
+ if (!selected_items.is_empty()) {
+ from = selected_items[selected_items.size() - 1];
+ selection.clear();
+ selection.push_back(from);
} else {
from = frames->get_frame_count(edited_anim);
}
@@ -763,7 +799,7 @@ void SpriteFramesEditor::_empty2_pressed() {
Ref<Texture2D> texture;
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Add Empty"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene());
+ undo_redo->create_action(TTR("Add Empty"), UndoRedo::MERGE_DISABLE, frames.ptr());
undo_redo->add_do_method(frames.ptr(), "add_frame", edited_anim, texture, 1.0, from + 1);
undo_redo->add_undo_method(frames.ptr(), "remove_frame", edited_anim, from + 1);
undo_redo->add_do_method(this, "_update_library");
@@ -774,24 +810,44 @@ void SpriteFramesEditor::_empty2_pressed() {
void SpriteFramesEditor::_up_pressed() {
ERR_FAIL_COND(!frames->has_animation(edited_anim));
- if (frame_list->get_current() < 0) {
+ Vector<int> selected_items = frame_list->get_selected_items();
+
+ int nb_selected_items = selected_items.size();
+ if (nb_selected_items <= 0) {
return;
}
- int to_move = frame_list->get_current();
- if (to_move < 1) {
+ int first_selected_frame_index = selected_items[0];
+ if (first_selected_frame_index < 1) {
return;
}
- sel = to_move;
- sel -= 1;
-
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Move Frame"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene());
- undo_redo->add_do_method(frames.ptr(), "set_frame", edited_anim, to_move, frames->get_frame_texture(edited_anim, to_move - 1), frames->get_frame_duration(edited_anim, to_move - 1));
- undo_redo->add_do_method(frames.ptr(), "set_frame", edited_anim, to_move - 1, frames->get_frame_texture(edited_anim, to_move), frames->get_frame_duration(edited_anim, to_move));
- undo_redo->add_undo_method(frames.ptr(), "set_frame", edited_anim, to_move, frames->get_frame_texture(edited_anim, to_move), frames->get_frame_duration(edited_anim, to_move));
- undo_redo->add_undo_method(frames.ptr(), "set_frame", edited_anim, to_move - 1, frames->get_frame_texture(edited_anim, to_move - 1), frames->get_frame_duration(edited_anim, to_move - 1));
+ undo_redo->create_action(TTR("Move Frame"), UndoRedo::MERGE_DISABLE, frames.ptr());
+
+ int last_overwritten_frame = -1;
+
+ for (int selected_index = 0; selected_index < nb_selected_items; selected_index++) {
+ int to_move = selected_items[selected_index];
+ int new_index = to_move - 1;
+ selected_items.set(selected_index, new_index);
+
+ undo_redo->add_do_method(frames.ptr(), "set_frame", edited_anim, new_index, frames->get_frame_texture(edited_anim, to_move), frames->get_frame_duration(edited_anim, to_move));
+ undo_redo->add_undo_method(frames.ptr(), "set_frame", edited_anim, new_index, frames->get_frame_texture(edited_anim, new_index), frames->get_frame_duration(edited_anim, new_index));
+
+ bool is_next_item_in_selection = selected_index + 1 < nb_selected_items && selected_items[selected_index + 1] == to_move + 1;
+ if (last_overwritten_frame == -1) {
+ last_overwritten_frame = new_index;
+ }
+
+ if (!is_next_item_in_selection) {
+ undo_redo->add_do_method(frames.ptr(), "set_frame", edited_anim, to_move, frames->get_frame_texture(edited_anim, last_overwritten_frame), frames->get_frame_duration(edited_anim, last_overwritten_frame));
+ undo_redo->add_undo_method(frames.ptr(), "set_frame", edited_anim, to_move, frames->get_frame_texture(edited_anim, to_move), frames->get_frame_duration(edited_anim, to_move));
+ last_overwritten_frame = -1;
+ }
+ }
+ selection = selected_items;
+
undo_redo->add_do_method(this, "_update_library");
undo_redo->add_undo_method(this, "_update_library");
undo_redo->commit_action();
@@ -800,24 +856,44 @@ void SpriteFramesEditor::_up_pressed() {
void SpriteFramesEditor::_down_pressed() {
ERR_FAIL_COND(!frames->has_animation(edited_anim));
- if (frame_list->get_current() < 0) {
+ Vector<int> selected_items = frame_list->get_selected_items();
+
+ int nb_selected_items = selected_items.size();
+ if (nb_selected_items <= 0) {
return;
}
- int to_move = frame_list->get_current();
- if (to_move < 0 || to_move >= frames->get_frame_count(edited_anim) - 1) {
+ int last_selected_frame_index = selected_items[nb_selected_items - 1];
+ if (last_selected_frame_index >= frames->get_frame_count(edited_anim) - 1) {
return;
}
- sel = to_move;
- sel += 1;
-
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Move Frame"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene());
- undo_redo->add_do_method(frames.ptr(), "set_frame", edited_anim, to_move, frames->get_frame_texture(edited_anim, to_move + 1), frames->get_frame_duration(edited_anim, to_move + 1));
- undo_redo->add_do_method(frames.ptr(), "set_frame", edited_anim, to_move + 1, frames->get_frame_texture(edited_anim, to_move), frames->get_frame_duration(edited_anim, to_move));
- undo_redo->add_undo_method(frames.ptr(), "set_frame", edited_anim, to_move, frames->get_frame_texture(edited_anim, to_move), frames->get_frame_duration(edited_anim, to_move));
- undo_redo->add_undo_method(frames.ptr(), "set_frame", edited_anim, to_move + 1, frames->get_frame_texture(edited_anim, to_move + 1), frames->get_frame_duration(edited_anim, to_move + 1));
+ undo_redo->create_action(TTR("Move Frame"), UndoRedo::MERGE_DISABLE, frames.ptr());
+
+ int first_moved_frame = -1;
+
+ for (int selected_index = 0; selected_index < nb_selected_items; selected_index++) {
+ int to_move = selected_items[selected_index];
+ int new_index = to_move + 1;
+ selected_items.set(selected_index, new_index);
+
+ undo_redo->add_do_method(frames.ptr(), "set_frame", edited_anim, new_index, frames->get_frame_texture(edited_anim, to_move), frames->get_frame_duration(edited_anim, to_move));
+ undo_redo->add_undo_method(frames.ptr(), "set_frame", edited_anim, new_index, frames->get_frame_texture(edited_anim, new_index), frames->get_frame_duration(edited_anim, new_index));
+
+ bool is_next_item_in_selection = selected_index + 1 < nb_selected_items && selected_items[selected_index + 1] == new_index;
+ if (first_moved_frame == -1) {
+ first_moved_frame = to_move;
+ }
+
+ if (!is_next_item_in_selection) {
+ undo_redo->add_do_method(frames.ptr(), "set_frame", edited_anim, first_moved_frame, frames->get_frame_texture(edited_anim, new_index), frames->get_frame_duration(edited_anim, new_index));
+ undo_redo->add_undo_method(frames.ptr(), "set_frame", edited_anim, first_moved_frame, frames->get_frame_texture(edited_anim, first_moved_frame), frames->get_frame_duration(edited_anim, first_moved_frame));
+ first_moved_frame = -1;
+ }
+ }
+ selection = selected_items;
+
undo_redo->add_do_method(this, "_update_library");
undo_redo->add_undo_method(this, "_update_library");
undo_redo->commit_action();
@@ -826,19 +902,21 @@ void SpriteFramesEditor::_down_pressed() {
void SpriteFramesEditor::_delete_pressed() {
ERR_FAIL_COND(!frames->has_animation(edited_anim));
- if (frame_list->get_current() < 0) {
- return;
- }
+ Vector<int> selected_items = frame_list->get_selected_items();
- int to_delete = frame_list->get_current();
- if (to_delete < 0 || to_delete >= frames->get_frame_count(edited_anim)) {
+ int nb_selected_items = selected_items.size();
+ if (nb_selected_items <= 0) {
return;
}
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Delete Resource"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene());
- undo_redo->add_do_method(frames.ptr(), "remove_frame", edited_anim, to_delete);
- undo_redo->add_undo_method(frames.ptr(), "add_frame", edited_anim, frames->get_frame_texture(edited_anim, to_delete), frames->get_frame_duration(edited_anim, to_delete), to_delete);
+ undo_redo->create_action(TTR("Delete Resource"), UndoRedo::MERGE_DISABLE, frames.ptr());
+ for (int selected_index = 0; selected_index < nb_selected_items; selected_index++) {
+ int to_delete = selected_items[selected_index];
+ undo_redo->add_do_method(frames.ptr(), "remove_frame", edited_anim, to_delete - selected_index);
+ undo_redo->add_undo_method(frames.ptr(), "add_frame", edited_anim, frames->get_frame_texture(edited_anim, to_delete), frames->get_frame_duration(edited_anim, to_delete), to_delete);
+ }
+
undo_redo->add_do_method(this, "_update_library");
undo_redo->add_undo_method(this, "_update_library");
undo_redo->commit_action();
@@ -952,7 +1030,7 @@ void SpriteFramesEditor::_animation_name_edited() {
edited->set_text(0, name);
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Rename Animation"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene());
+ undo_redo->create_action(TTR("Rename Animation"), UndoRedo::MERGE_DISABLE, frames.ptr());
undo_redo->add_do_method(frames.ptr(), "rename_animation", edited_anim, name);
undo_redo->add_undo_method(frames.ptr(), "rename_animation", name, edited_anim);
_rename_node_animation(undo_redo, false, edited_anim, name, name);
@@ -1004,7 +1082,7 @@ void SpriteFramesEditor::_animation_add() {
}
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Add Animation"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene());
+ undo_redo->create_action(TTR("Add Animation"), UndoRedo::MERGE_DISABLE, frames.ptr());
undo_redo->add_do_method(frames.ptr(), "add_animation", name);
undo_redo->add_undo_method(frames.ptr(), "remove_animation", name);
undo_redo->add_do_method(this, "_select_animation", name);
@@ -1045,7 +1123,7 @@ void SpriteFramesEditor::_animation_remove_confirmed() {
}
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Remove Animation"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene());
+ undo_redo->create_action(TTR("Remove Animation"), UndoRedo::MERGE_DISABLE, frames.ptr());
_rename_node_animation(undo_redo, false, edited_anim, new_edited, "");
undo_redo->add_do_method(frames.ptr(), "remove_animation", edited_anim);
undo_redo->add_undo_method(frames.ptr(), "add_animation", edited_anim);
@@ -1075,7 +1153,7 @@ void SpriteFramesEditor::_animation_loop_changed() {
}
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Change Animation Loop"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene());
+ undo_redo->create_action(TTR("Change Animation Loop"), UndoRedo::MERGE_DISABLE, frames.ptr());
undo_redo->add_do_method(frames.ptr(), "set_animation_loop", edited_anim, anim_loop->is_pressed());
undo_redo->add_undo_method(frames.ptr(), "set_animation_loop", edited_anim, frames->get_animation_loop(edited_anim));
undo_redo->add_do_method(this, "_update_library", true);
@@ -1089,7 +1167,7 @@ void SpriteFramesEditor::_animation_speed_changed(double p_value) {
}
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Change Animation FPS"), UndoRedo::MERGE_ENDS, EditorNode::get_singleton()->get_edited_scene());
+ undo_redo->create_action(TTR("Change Animation FPS"), UndoRedo::MERGE_ENDS, frames.ptr());
undo_redo->add_do_method(frames.ptr(), "set_animation_speed", edited_anim, p_value);
undo_redo->add_undo_method(frames.ptr(), "set_animation_speed", edited_anim, frames->get_animation_speed(edited_anim));
undo_redo->add_do_method(this, "_update_library", true);
@@ -1113,15 +1191,18 @@ void SpriteFramesEditor::_frame_list_gui_input(const Ref<InputEvent> &p_event) {
}
}
-void SpriteFramesEditor::_frame_list_item_selected(int p_index) {
+void SpriteFramesEditor::_frame_list_item_selected(int p_index, bool p_selected) {
if (updating) {
return;
}
- sel = p_index;
+ selection = frame_list->get_selected_items();
+ if (selection.is_empty() || !p_selected) {
+ return;
+ }
updating = true;
- frame_duration->set_value(frames->get_frame_duration(edited_anim, p_index));
+ frame_duration->set_value(frames->get_frame_duration(edited_anim, selection[0]));
updating = false;
}
@@ -1130,18 +1211,21 @@ void SpriteFramesEditor::_frame_duration_changed(double p_value) {
return;
}
- int index = sel;
- if (index < 0) {
+ if (selection.is_empty()) {
return;
}
- Ref<Texture2D> texture = frames->get_frame_texture(edited_anim, index);
- float old_duration = frames->get_frame_duration(edited_anim, index);
-
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Set Frame Duration"), UndoRedo::MERGE_ENDS, EditorNode::get_singleton()->get_edited_scene());
- undo_redo->add_do_method(frames.ptr(), "set_frame", edited_anim, index, texture, p_value);
- undo_redo->add_undo_method(frames.ptr(), "set_frame", edited_anim, index, texture, old_duration);
+ undo_redo->create_action(TTR("Set Frame Duration"), UndoRedo::MERGE_ENDS, frames.ptr());
+
+ for (const int &index : selection) {
+ Ref<Texture2D> texture = frames->get_frame_texture(edited_anim, index);
+ float old_duration = frames->get_frame_duration(edited_anim, index);
+
+ undo_redo->add_do_method(frames.ptr(), "set_frame", edited_anim, index, texture, p_value);
+ undo_redo->add_undo_method(frames.ptr(), "set_frame", edited_anim, index, texture, old_duration);
+ }
+
undo_redo->add_do_method(this, "_update_library");
undo_redo->add_undo_method(this, "_update_library");
undo_redo->commit_action();
@@ -1262,12 +1346,31 @@ void SpriteFramesEditor::_update_library_impl() {
return;
}
- if (sel >= frames->get_frame_count(edited_anim)) {
- sel = frames->get_frame_count(edited_anim) - 1;
- } else if (sel < 0 && frames->get_frame_count(edited_anim)) {
- sel = 0;
+ int anim_frame_count = frames->get_frame_count(edited_anim);
+ if (anim_frame_count == 0) {
+ selection.clear();
+ }
+
+ for (int index = 0; index < selection.size(); index++) {
+ int sel = selection[index];
+ if (sel == -1) {
+ selection.remove_at(index);
+ index--;
+ }
+ if (sel >= anim_frame_count) {
+ selection.set(index, anim_frame_count - 1);
+ // Since selection is ordered, if we get a frame that is outside of the range
+ // we can clip all the other one.
+ selection.resize(index + 1);
+ break;
+ }
}
+ if (selection.is_empty() && frames->get_frame_count(edited_anim)) {
+ selection.push_back(0);
+ }
+
+ bool is_first_selection = true;
for (int i = 0; i < frames->get_frame_count(edited_anim); i++) {
String name = itos(i);
Ref<Texture2D> texture = frames->get_frame_texture(edited_anim, i);
@@ -1301,9 +1404,12 @@ void SpriteFramesEditor::_update_library_impl() {
frame_list->set_item_tooltip(-1, tooltip);
}
- if (sel == i) {
- frame_list->select(frame_list->get_item_count() - 1);
- frame_duration->set_value_no_signal(frames->get_frame_duration(edited_anim, i));
+ if (selection.has(i)) {
+ frame_list->select(frame_list->get_item_count() - 1, is_first_selection);
+ if (is_first_selection) {
+ frame_duration->set_value_no_signal(frames->get_frame_duration(edited_anim, i));
+ }
+ is_first_selection = false;
}
}
@@ -1472,7 +1578,7 @@ void SpriteFramesEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
duration = frames->get_frame_duration(edited_anim, from_frame);
}
- undo_redo->create_action(TTR("Move Frame"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene());
+ undo_redo->create_action(TTR("Move Frame"), UndoRedo::MERGE_DISABLE, frames.ptr());
undo_redo->add_do_method(frames.ptr(), "remove_frame", edited_anim, from_frame == -1 ? frames->get_frame_count(edited_anim) : from_frame);
undo_redo->add_do_method(frames.ptr(), "add_frame", edited_anim, texture, duration, at_pos == -1 ? -1 : at_pos);
undo_redo->add_undo_method(frames.ptr(), "remove_frame", edited_anim, at_pos == -1 ? frames->get_frame_count(edited_anim) - 1 : at_pos);
@@ -1481,7 +1587,7 @@ void SpriteFramesEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
undo_redo->add_undo_method(this, "_update_library");
undo_redo->commit_action();
} else {
- undo_redo->create_action(TTR("Add Frame"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene());
+ undo_redo->create_action(TTR("Add Frame"), UndoRedo::MERGE_DISABLE, frames.ptr());
undo_redo->add_do_method(frames.ptr(), "add_frame", edited_anim, texture, 1.0, at_pos == -1 ? -1 : at_pos);
undo_redo->add_undo_method(frames.ptr(), "remove_frame", edited_anim, at_pos == -1 ? frames->get_frame_count(edited_anim) : at_pos);
undo_redo->add_do_method(this, "_update_library");
@@ -1619,7 +1725,7 @@ void SpriteFramesEditor::_autoplay_pressed() {
if (animated_sprite) {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Toggle Autoplay"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene());
+ undo_redo->create_action(TTR("Toggle Autoplay"), UndoRedo::MERGE_DISABLE, frames.ptr());
String current = animated_sprite->call("get_animation");
String current_auto = animated_sprite->call("get_autoplay");
if (current == current_auto) {
@@ -1885,6 +1991,7 @@ SpriteFramesEditor::SpriteFramesEditor() {
frame_list->set_v_size_flags(SIZE_EXPAND_FILL);
frame_list->set_icon_mode(ItemList::ICON_MODE_TOP);
frame_list->set_texture_filter(TEXTURE_FILTER_NEAREST_WITH_MIPMAPS);
+ frame_list->set_select_mode(ItemList::SELECT_MULTI);
frame_list->set_max_columns(0);
frame_list->set_icon_mode(ItemList::ICON_MODE_TOP);
@@ -1892,7 +1999,8 @@ SpriteFramesEditor::SpriteFramesEditor() {
SET_DRAG_FORWARDING_GCD(frame_list, SpriteFramesEditor);
frame_list->connect("gui_input", callable_mp(this, &SpriteFramesEditor::_frame_list_gui_input));
// HACK: The item_selected signal is emitted before the Frame Duration spinbox loses focus and applies the change.
- frame_list->connect("item_selected", callable_mp(this, &SpriteFramesEditor::_frame_list_item_selected), CONNECT_DEFERRED);
+ frame_list->connect("multi_selected", callable_mp(this, &SpriteFramesEditor::_frame_list_item_selected), CONNECT_DEFERRED);
+
sub_vb->add_child(frame_list);
dialog = memnew(AcceptDialog);
@@ -1915,9 +2023,9 @@ SpriteFramesEditor::SpriteFramesEditor() {
delete_frame->set_shortcut_context(frame_list);
delete_frame->set_shortcut(ED_SHORTCUT("sprite_frames/delete", TTR("Delete Frame"), Key::KEY_DELETE));
copy->set_shortcut_context(frame_list);
- copy->set_shortcut(ED_SHORTCUT("sprite_frames/copy", TTR("Copy Frame"), KeyModifierMask::CMD_OR_CTRL | Key::C));
+ copy->set_shortcut(ED_SHORTCUT("sprite_frames/copy", TTR("Copy Frame(s)"), KeyModifierMask::CMD_OR_CTRL | Key::C));
paste->set_shortcut_context(frame_list);
- paste->set_shortcut(ED_SHORTCUT("sprite_frames/paste", TTR("Paste Frame"), KeyModifierMask::CMD_OR_CTRL | Key::V));
+ paste->set_shortcut(ED_SHORTCUT("sprite_frames/paste", TTR("Paste Frame(s)"), KeyModifierMask::CMD_OR_CTRL | Key::V));
empty_before->set_shortcut_context(frame_list);
empty_before->set_shortcut(ED_SHORTCUT("sprite_frames/empty_before", TTR("Insert Empty (Before Selected)"), KeyModifierMask::ALT | Key::LEFT));
empty_after->set_shortcut_context(frame_list);
@@ -1935,7 +2043,6 @@ SpriteFramesEditor::SpriteFramesEditor() {
{ int32_t(KeyModifierMask::CMD_OR_CTRL | Key::EQUAL), int32_t(KeyModifierMask::CMD_OR_CTRL | Key::KP_ADD) }));
loading_scene = false;
- sel = -1;
updating = false;
diff --git a/editor/plugins/sprite_frames_editor_plugin.h b/editor/plugins/sprite_frames_editor_plugin.h
index f14c2203df..730dddade1 100644
--- a/editor/plugins/sprite_frames_editor_plugin.h
+++ b/editor/plugins/sprite_frames_editor_plugin.h
@@ -49,12 +49,15 @@
class OptionButton;
class EditorFileDialog;
-class EditorSpriteFramesFrame : public Resource {
- GDCLASS(EditorSpriteFramesFrame, Resource);
+class ClipboardSpriteFrames : public Resource {
+ GDCLASS(ClipboardSpriteFrames, Resource);
public:
- Ref<Texture2D> texture;
- float duration;
+ struct Frame {
+ Ref<Texture2D> texture;
+ float duration;
+ };
+ Vector<Frame> frames;
};
class SpriteFramesEditor : public HSplitContainer {
@@ -115,7 +118,7 @@ class SpriteFramesEditor : public HSplitContainer {
SpinBox *frame_duration = nullptr;
ItemList *frame_list = nullptr;
bool loading_scene;
- int sel;
+ Vector<int> selection;
Button *add_anim = nullptr;
Button *delete_anim = nullptr;
@@ -182,6 +185,9 @@ class SpriteFramesEditor : public HSplitContainer {
void _file_load_request(const Vector<String> &p_path, int p_at_pos = -1);
void _copy_pressed();
void _paste_pressed();
+ void _paste_frame_array(const Ref<ClipboardSpriteFrames> &p_clipboard_frames);
+ void _paste_texture(const Ref<Texture2D> &p_texture);
+
void _empty_pressed();
void _empty2_pressed();
void _delete_pressed();
@@ -209,7 +215,7 @@ class SpriteFramesEditor : public HSplitContainer {
void _animation_speed_changed(double p_value);
void _frame_list_gui_input(const Ref<InputEvent> &p_event);
- void _frame_list_item_selected(int p_index);
+ void _frame_list_item_selected(int p_index, bool p_selected);
void _zoom_in();
void _zoom_out();
diff --git a/editor/register_editor_types.cpp b/editor/register_editor_types.cpp
index db1f5b439d..ab8aa4b68f 100644
--- a/editor/register_editor_types.cpp
+++ b/editor/register_editor_types.cpp
@@ -101,6 +101,7 @@
#include "editor/plugins/node_3d_editor_gizmos.h"
#include "editor/plugins/occluder_instance_3d_editor_plugin.h"
#include "editor/plugins/packed_scene_editor_plugin.h"
+#include "editor/plugins/parallax_background_editor_plugin.h"
#include "editor/plugins/path_2d_editor_plugin.h"
#include "editor/plugins/path_3d_editor_plugin.h"
#include "editor/plugins/physical_bone_3d_editor_plugin.h"
@@ -252,6 +253,7 @@ void register_editor_types() {
EditorPlugins::add_by_type<NavigationLink2DEditorPlugin>();
EditorPlugins::add_by_type<NavigationObstacle2DEditorPlugin>();
EditorPlugins::add_by_type<NavigationPolygonEditorPlugin>();
+ EditorPlugins::add_by_type<ParallaxBackgroundEditorPlugin>();
EditorPlugins::add_by_type<Path2DEditorPlugin>();
EditorPlugins::add_by_type<Polygon2DEditorPlugin>();
EditorPlugins::add_by_type<Cast2DEditorPlugin>();
diff --git a/editor/themes/SCsub b/editor/themes/SCsub
index 41b20f8a78..65cfb6a8be 100644
--- a/editor/themes/SCsub
+++ b/editor/themes/SCsub
@@ -16,7 +16,7 @@ env.Depends("#editor/themes/builtin_fonts.gen.h", flist)
env.CommandNoCache(
"#editor/themes/builtin_fonts.gen.h",
flist,
- env.Run(editor_theme_builders.make_fonts_header, "Generating builtin fonts header."),
+ env.Run(editor_theme_builders.make_fonts_header),
)
env.add_source_files(env.editor_sources, "*.cpp")
diff --git a/main/SCsub b/main/SCsub
index 79dc4bff15..3e7680bf89 100644
--- a/main/SCsub
+++ b/main/SCsub
@@ -17,7 +17,7 @@ env_main.Depends("#main/splash.gen.h", "#main/splash.png")
env_main.CommandNoCache(
"#main/splash.gen.h",
"#main/splash.png",
- env.Run(main_builders.make_splash, "Building splash screen header."),
+ env.Run(main_builders.make_splash),
)
if not env_main["no_editor_splash"]:
@@ -25,14 +25,14 @@ if not env_main["no_editor_splash"]:
env_main.CommandNoCache(
"#main/splash_editor.gen.h",
"#main/splash_editor.png",
- env.Run(main_builders.make_splash_editor, "Building editor splash screen header."),
+ env.Run(main_builders.make_splash_editor),
)
env_main.Depends("#main/app_icon.gen.h", "#main/app_icon.png")
env_main.CommandNoCache(
"#main/app_icon.gen.h",
"#main/app_icon.png",
- env.Run(main_builders.make_app_icon, "Building application icon."),
+ env.Run(main_builders.make_app_icon),
)
lib = env_main.add_library("main", env.main_sources)
diff --git a/methods.py b/methods.py
index 88c8e7043e..7e889195c0 100644
--- a/methods.py
+++ b/methods.py
@@ -607,6 +607,12 @@ def no_verbose(sys, env):
java_library_message = "{}Creating Java Archive {}$TARGET{} ...{}".format(
colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
)
+ compiled_resource_message = "{}Creating Compiled Resource {}$TARGET{} ...{}".format(
+ colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
+ )
+ generated_file_message = "{}Generating {}$TARGET{} ...{}".format(
+ colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
+ )
env.Append(CXXCOMSTR=[compile_source_message])
env.Append(CCCOMSTR=[compile_source_message])
@@ -618,6 +624,8 @@ def no_verbose(sys, env):
env.Append(LINKCOMSTR=[link_program_message])
env.Append(JARCOMSTR=[java_library_message])
env.Append(JAVACCOMSTR=[java_compile_source_message])
+ env.Append(RCCOMSTR=[compiled_resource_message])
+ env.Append(GENCOMSTR=[generated_file_message])
def detect_visual_c_compiler_version(tools_env):
@@ -817,15 +825,14 @@ def CommandNoCache(env, target, sources, command, **args):
return result
-def Run(env, function, short_message, subprocess=True):
+def Run(env, function, subprocess=True):
from SCons.Script import Action
from platform_methods import run_in_subprocess
- output_print = short_message if not env["verbose"] else ""
if not subprocess:
- return Action(function, output_print)
+ return Action(function, "$GENCOMSTR")
else:
- return Action(run_in_subprocess(function), output_print)
+ return Action(run_in_subprocess(function), "$GENCOMSTR")
def detect_darwin_sdk_path(platform, env):
diff --git a/modules/SCsub b/modules/SCsub
index 7c9946170f..87c59c3bcc 100644
--- a/modules/SCsub
+++ b/modules/SCsub
@@ -19,7 +19,6 @@ env.CommandNoCache(
Value(env.module_list),
env.Run(
modules_builders.generate_modules_enabled,
- "Generating enabled modules header.",
# NOTE: No need to run in subprocess since this is still executed serially.
subprocess=False,
),
@@ -58,7 +57,6 @@ if env["tests"]:
test_headers,
env.Run(
modules_builders.generate_modules_tests,
- "Generating modules tests header.",
# NOTE: No need to run in subprocess since this is still executed serially.
subprocess=False,
),
diff --git a/modules/gdscript/editor/script_templates/SCsub b/modules/gdscript/editor/script_templates/SCsub
index 2266ef2d01..5db7e3fc3b 100644
--- a/modules/gdscript/editor/script_templates/SCsub
+++ b/modules/gdscript/editor/script_templates/SCsub
@@ -5,7 +5,7 @@ Import("env")
import editor.template_builders as build_template_gd
env["BUILDERS"]["MakeGDTemplateBuilder"] = Builder(
- action=env.Run(build_template_gd.make_templates, "Generating GDScript templates header."),
+ action=env.Run(build_template_gd.make_templates),
suffix=".h",
src_suffix=".gd",
)
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 98d4a19a87..3ec5098c21 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -3360,12 +3360,8 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
parser->push_warning(p_call, GDScriptWarning::RETURN_VALUE_DISCARDED, p_call->function_name);
}
- if (method_flags.has_flag(METHOD_FLAG_STATIC) && !is_constructor && !base_type.is_meta_type && !(is_self && static_context)) {
- String caller_type = String(base_type.native_type);
-
- if (caller_type.is_empty()) {
- caller_type = base_type.to_string();
- }
+ if (method_flags.has_flag(METHOD_FLAG_STATIC) && !is_constructor && !base_type.is_meta_type && !is_self) {
+ String caller_type = base_type.to_string();
parser->push_warning(p_call, GDScriptWarning::STATIC_CALLED_ON_INSTANCE, p_call->function_name, caller_type);
}
diff --git a/modules/gdscript/tests/scripts/completion/class_a.notest.gd b/modules/gdscript/tests/scripts/completion/class_a.notest.gd
new file mode 100644
index 0000000000..47c64dc674
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/class_a.notest.gd
@@ -0,0 +1,8 @@
+extends Node
+
+signal signal_of_a
+
+var property_of_a
+
+func func_of_a():
+ pass
diff --git a/modules/gdscript/tests/scripts/completion/get_node/get_node.tscn b/modules/gdscript/tests/scripts/completion/get_node/get_node.tscn
new file mode 100644
index 0000000000..35ac666941
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/get_node.tscn
@@ -0,0 +1,19 @@
+[gd_scene load_steps=2 format=3 uid="uid://c8wekfd5ql7bc"]
+
+[ext_resource type="Script" path="res://completion/class_a.notest.gd" id="1_ldc4g"]
+
+[node name="GetNode" type="Node"]
+
+[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
+
+[node name="UniqueNames" type="Node" parent="."]
+
+[node name="UniqueAnimationPlayer" type="AnimationPlayer" parent="UniqueNames"]
+unique_name_in_owner = true
+
+[node name="UniqueA" type="Node" parent="UniqueNames"]
+unique_name_in_owner = true
+script = ExtResource("1_ldc4g")
+
+[node name="A" type="Node" parent="."]
+script = ExtResource("1_ldc4g")
diff --git a/modules/gdscript/tests/scripts/completion/get_node/get_node_member_annotated.cfg b/modules/gdscript/tests/scripts/completion/get_node/get_node_member_annotated.cfg
deleted file mode 100644
index 27e695d245..0000000000
--- a/modules/gdscript/tests/scripts/completion/get_node/get_node_member_annotated.cfg
+++ /dev/null
@@ -1,4 +0,0 @@
-[output]
-include=[
- {"display": "autoplay"},
-]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal/dollar.cfg b/modules/gdscript/tests/scripts/completion/get_node/literal/dollar.cfg
new file mode 100644
index 0000000000..ae7d34d87d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/literal/dollar.cfg
@@ -0,0 +1,7 @@
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal/dollar.gd b/modules/gdscript/tests/scripts/completion/get_node/literal/dollar.gd
new file mode 100644
index 0000000000..df458a9435
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/literal/dollar.gd
@@ -0,0 +1,4 @@
+extends Node
+
+func a():
+ %AnimationPlayer.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal/percent.cfg b/modules/gdscript/tests/scripts/completion/get_node/literal/percent.cfg
new file mode 100644
index 0000000000..ae7d34d87d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/literal/percent.cfg
@@ -0,0 +1,7 @@
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal/percent.gd b/modules/gdscript/tests/scripts/completion/get_node/literal/percent.gd
new file mode 100644
index 0000000000..7050761b86
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/literal/percent.gd
@@ -0,0 +1,4 @@
+extends Node
+
+func a():
+ $UniqueAnimationPlayer.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_class_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_class_scene.cfg
new file mode 100644
index 0000000000..009ab9f9ce
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_class_scene.cfg
@@ -0,0 +1,14 @@
+[input]
+scene="res://completion/get_node/get_node.tscn"
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+ ; GDScript: class_a.notest.gd
+ {"display": "property_of_a"},
+ {"display": "func_of_a"},
+ {"display": "signal_of_a"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_class_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_class_scene.gd
new file mode 100644
index 0000000000..a84283a1de
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_class_scene.gd
@@ -0,0 +1,4 @@
+extends Node
+
+func a():
+ $A.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_native_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_native_scene.cfg
new file mode 100644
index 0000000000..0fb46a4704
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_native_scene.cfg
@@ -0,0 +1,14 @@
+[input]
+scene="res://completion/get_node/get_node.tscn"
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+ ; AnimationPlayer
+ {"display": "autoplay"},
+ {"display": "play"},
+ {"display": "animation_changed"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_native_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_native_scene.gd
new file mode 100644
index 0000000000..6e3fee1696
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_native_scene.gd
@@ -0,0 +1,4 @@
+extends Node
+
+func a():
+ $AnimationPlayer.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_class_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_class_scene.cfg
new file mode 100644
index 0000000000..009ab9f9ce
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_class_scene.cfg
@@ -0,0 +1,14 @@
+[input]
+scene="res://completion/get_node/get_node.tscn"
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+ ; GDScript: class_a.notest.gd
+ {"display": "property_of_a"},
+ {"display": "func_of_a"},
+ {"display": "signal_of_a"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_class_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_class_scene.gd
new file mode 100644
index 0000000000..27f059c944
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_class_scene.gd
@@ -0,0 +1,4 @@
+extends Node
+
+func a():
+ %UniqueA.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_native_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_native_scene.cfg
new file mode 100644
index 0000000000..0fb46a4704
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_native_scene.cfg
@@ -0,0 +1,14 @@
+[input]
+scene="res://completion/get_node/get_node.tscn"
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+ ; AnimationPlayer
+ {"display": "autoplay"},
+ {"display": "play"},
+ {"display": "animation_changed"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_native_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_native_scene.gd
new file mode 100644
index 0000000000..07068fc5a4
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_native_scene.gd
@@ -0,0 +1,4 @@
+extends Node
+
+func a():
+ %UniqueAnimationPlayer.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local/local.cfg b/modules/gdscript/tests/scripts/completion/get_node/local/local.cfg
new file mode 100644
index 0000000000..ae7d34d87d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local/local.cfg
@@ -0,0 +1,7 @@
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local/local.gd b/modules/gdscript/tests/scripts/completion/get_node/local/local.gd
new file mode 100644
index 0000000000..596ad80ef2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local/local.gd
@@ -0,0 +1,5 @@
+extends Node
+
+func a():
+ var test = $AnimationPlayer
+ test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_interfered/local_interfered.cfg b/modules/gdscript/tests/scripts/completion/get_node/local_interfered/local_interfered.cfg
new file mode 100644
index 0000000000..ae7d34d87d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local_interfered/local_interfered.cfg
@@ -0,0 +1,7 @@
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_interfered/local_interfered.gd b/modules/gdscript/tests/scripts/completion/get_node/local_interfered/local_interfered.gd
new file mode 100644
index 0000000000..6f87af3c85
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local_interfered/local_interfered.gd
@@ -0,0 +1,5 @@
+extends Node
+
+func a():
+ var test := $AnimationPlayer
+ test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/class_local_interfered_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/class_local_interfered_scene.cfg
new file mode 100644
index 0000000000..009ab9f9ce
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/class_local_interfered_scene.cfg
@@ -0,0 +1,14 @@
+[input]
+scene="res://completion/get_node/get_node.tscn"
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+ ; GDScript: class_a.notest.gd
+ {"display": "property_of_a"},
+ {"display": "func_of_a"},
+ {"display": "signal_of_a"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/class_local_interfered_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/class_local_interfered_scene.gd
new file mode 100644
index 0000000000..a710c8bbd7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/class_local_interfered_scene.gd
@@ -0,0 +1,5 @@
+extends Node
+
+func a():
+ var test := $A
+ test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/native_local_interfered_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/native_local_interfered_scene.cfg
new file mode 100644
index 0000000000..0fb46a4704
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/native_local_interfered_scene.cfg
@@ -0,0 +1,14 @@
+[input]
+scene="res://completion/get_node/get_node.tscn"
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+ ; AnimationPlayer
+ {"display": "autoplay"},
+ {"display": "play"},
+ {"display": "animation_changed"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/native_local_interfered_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/native_local_interfered_scene.gd
new file mode 100644
index 0000000000..6f87af3c85
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/native_local_interfered_scene.gd
@@ -0,0 +1,5 @@
+extends Node
+
+func a():
+ var test := $AnimationPlayer
+ test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_scene/class_local_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/local_scene/class_local_scene.cfg
new file mode 100644
index 0000000000..009ab9f9ce
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local_scene/class_local_scene.cfg
@@ -0,0 +1,14 @@
+[input]
+scene="res://completion/get_node/get_node.tscn"
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+ ; GDScript: class_a.notest.gd
+ {"display": "property_of_a"},
+ {"display": "func_of_a"},
+ {"display": "signal_of_a"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_scene/class_local_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/local_scene/class_local_scene.gd
new file mode 100644
index 0000000000..2fc88f93dd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local_scene/class_local_scene.gd
@@ -0,0 +1,5 @@
+extends Node
+
+func a():
+ var test = $A
+ test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_scene/native_local_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/local_scene/native_local_scene.cfg
new file mode 100644
index 0000000000..0fb46a4704
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local_scene/native_local_scene.cfg
@@ -0,0 +1,14 @@
+[input]
+scene="res://completion/get_node/get_node.tscn"
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+ ; AnimationPlayer
+ {"display": "autoplay"},
+ {"display": "play"},
+ {"display": "animation_changed"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_scene/native_local_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/local_scene/native_local_scene.gd
new file mode 100644
index 0000000000..596ad80ef2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local_scene/native_local_scene.gd
@@ -0,0 +1,5 @@
+extends Node
+
+func a():
+ var test = $AnimationPlayer
+ test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint/class_local_typehint.cfg b/modules/gdscript/tests/scripts/completion/get_node/local_typehint/class_local_typehint.cfg
new file mode 100644
index 0000000000..a72b489be6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint/class_local_typehint.cfg
@@ -0,0 +1,12 @@
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+ ; GDScript: class_a.notest.gd
+ {"display": "property_of_a"},
+ {"display": "func_of_a"},
+ {"display": "signal_of_a"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint/class_local_typehint.gd b/modules/gdscript/tests/scripts/completion/get_node/local_typehint/class_local_typehint.gd
new file mode 100644
index 0000000000..b6d2074939
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint/class_local_typehint.gd
@@ -0,0 +1,7 @@
+extends Node
+
+const A := preload("res://completion/class_a.notest.gd")
+
+func a():
+ var test: A = $A
+ test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint/native_local_typehint.cfg b/modules/gdscript/tests/scripts/completion/get_node/local_typehint/native_local_typehint.cfg
new file mode 100644
index 0000000000..adf06c8707
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint/native_local_typehint.cfg
@@ -0,0 +1,12 @@
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+ ; AnimationPlayer
+ {"display": "autoplay"},
+ {"display": "play"},
+ {"display": "animation_changed"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint/native_local_typehint.gd b/modules/gdscript/tests/scripts/completion/get_node/local_typehint/native_local_typehint.gd
new file mode 100644
index 0000000000..13b541a35d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint/native_local_typehint.gd
@@ -0,0 +1,5 @@
+extends Node
+
+func a():
+ var test: AnimationPlayer = $AnimationPlayer
+ test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/class_local_typehint_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/class_local_typehint_scene.cfg
new file mode 100644
index 0000000000..9c580b711d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/class_local_typehint_scene.cfg
@@ -0,0 +1,14 @@
+[input]
+scene="res://completion/get_node/get_node.tscn"
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+ ; GDScript: class_a.notest.gd
+ {"display": "property_of_a"},
+ {"display": "func_of_a"},
+ {"display": "signal_of_a"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/class_local_typehint_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/class_local_typehint_scene.gd
new file mode 100644
index 0000000000..b6d2074939
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/class_local_typehint_scene.gd
@@ -0,0 +1,7 @@
+extends Node
+
+const A := preload("res://completion/class_a.notest.gd")
+
+func a():
+ var test: A = $A
+ test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/native_local_typehint_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/native_local_typehint_scene.cfg
new file mode 100644
index 0000000000..446198dd35
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/native_local_typehint_scene.cfg
@@ -0,0 +1,14 @@
+[input]
+scene="res://completion/get_node/get_node.tscn"
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+ ; AnimationPlayer
+ {"display": "autoplay"},
+ {"display": "play"},
+ {"display": "animation_changed"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/native_local_typehint_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/native_local_typehint_scene.gd
new file mode 100644
index 0000000000..13b541a35d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/native_local_typehint_scene.gd
@@ -0,0 +1,5 @@
+extends Node
+
+func a():
+ var test: AnimationPlayer = $AnimationPlayer
+ test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/class_local_typehint_scene_broad.cfg b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/class_local_typehint_scene_broad.cfg
new file mode 100644
index 0000000000..9c580b711d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/class_local_typehint_scene_broad.cfg
@@ -0,0 +1,14 @@
+[input]
+scene="res://completion/get_node/get_node.tscn"
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+ ; GDScript: class_a.notest.gd
+ {"display": "property_of_a"},
+ {"display": "func_of_a"},
+ {"display": "signal_of_a"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/class_local_typehint_scene_broad.notest.gd b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/class_local_typehint_scene_broad.notest.gd
new file mode 100644
index 0000000000..5c785b3ddc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/class_local_typehint_scene_broad.notest.gd
@@ -0,0 +1,6 @@
+# TODO
+extends Node
+
+func a():
+ var test: Node = $A
+ test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/native_local_typehint_scene_broad.cfg b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/native_local_typehint_scene_broad.cfg
new file mode 100644
index 0000000000..446198dd35
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/native_local_typehint_scene_broad.cfg
@@ -0,0 +1,14 @@
+[input]
+scene="res://completion/get_node/get_node.tscn"
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+ ; AnimationPlayer
+ {"display": "autoplay"},
+ {"display": "play"},
+ {"display": "animation_changed"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/native_local_typehint_scene_broad.notest.gd b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/native_local_typehint_scene_broad.notest.gd
new file mode 100644
index 0000000000..57f4e16e3c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/native_local_typehint_scene_broad.notest.gd
@@ -0,0 +1,6 @@
+# TODO
+extends Node
+
+func a():
+ var test: Node = $AnimationPlayer
+ test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/class_local_typehint_scene_incompatible.cfg b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/class_local_typehint_scene_incompatible.cfg
new file mode 100644
index 0000000000..1894e72c65
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/class_local_typehint_scene_incompatible.cfg
@@ -0,0 +1,20 @@
+[input]
+scene="res://completion/get_node/get_node.tscn"
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+ ; Area2D
+ {"display": "get_overlapping_areas"},
+ {"display": "linear_damp"},
+ {"display": "area_entered"},
+]
+exclude=[
+ ; GDScript: class_a.notest.gd
+ {"display": "property_of_a"},
+ {"display": "func_of_a"},
+ {"display": "signal_of_a"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/class_local_typehint_scene_incompatible.gd b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/class_local_typehint_scene_incompatible.gd
new file mode 100644
index 0000000000..c6adfe0dd3
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/class_local_typehint_scene_incompatible.gd
@@ -0,0 +1,5 @@
+extends Node
+
+func a():
+ var test: Area2D = $A
+ test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/native_local_typehint_scene_incompatible.cfg b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/native_local_typehint_scene_incompatible.cfg
new file mode 100644
index 0000000000..c8ab63f6d6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/native_local_typehint_scene_incompatible.cfg
@@ -0,0 +1,20 @@
+[input]
+scene="res://completion/get_node/get_node.tscn"
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+ ; Area2D
+ {"display": "get_overlapping_areas"},
+ {"display": "linear_damp"},
+ {"display": "area_entered"},
+]
+exclude=[
+ ; AnimationPlayer
+ {"display": "autoplay"},
+ {"display": "play"},
+ {"display": "animation_changed"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/native_local_typehint_scene_incompatible.gd b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/native_local_typehint_scene_incompatible.gd
new file mode 100644
index 0000000000..f53fce9bfe
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/native_local_typehint_scene_incompatible.gd
@@ -0,0 +1,5 @@
+extends Node
+
+func a():
+ var test: Area2D = $AnimationPlayer
+ test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member/member.cfg b/modules/gdscript/tests/scripts/completion/get_node/member/member.cfg
new file mode 100644
index 0000000000..ae7d34d87d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member/member.cfg
@@ -0,0 +1,7 @@
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member/member.gd b/modules/gdscript/tests/scripts/completion/get_node/member/member.gd
new file mode 100644
index 0000000000..6bcc0a0298
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member/member.gd
@@ -0,0 +1,6 @@
+extends Node
+
+var test = $AnimationPlayer
+
+func a():
+ test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_interfered/member_interfered.cfg b/modules/gdscript/tests/scripts/completion/get_node/member_interfered/member_interfered.cfg
new file mode 100644
index 0000000000..ae7d34d87d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member_interfered/member_interfered.cfg
@@ -0,0 +1,7 @@
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_interfered/member_interfered.gd b/modules/gdscript/tests/scripts/completion/get_node/member_interfered/member_interfered.gd
new file mode 100644
index 0000000000..542197e643
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member_interfered/member_interfered.gd
@@ -0,0 +1,6 @@
+extends Node
+
+var test := $AnimationPlayer
+
+func a():
+ test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/class_member_interfered_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/class_member_interfered_scene.cfg
new file mode 100644
index 0000000000..009ab9f9ce
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/class_member_interfered_scene.cfg
@@ -0,0 +1,14 @@
+[input]
+scene="res://completion/get_node/get_node.tscn"
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+ ; GDScript: class_a.notest.gd
+ {"display": "property_of_a"},
+ {"display": "func_of_a"},
+ {"display": "signal_of_a"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/class_member_interfered_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/class_member_interfered_scene.gd
new file mode 100644
index 0000000000..da0b1b11d4
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/class_member_interfered_scene.gd
@@ -0,0 +1,6 @@
+extends Node
+
+var test := $A
+
+func a():
+ test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/native_member_interfered_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/native_member_interfered_scene.cfg
new file mode 100644
index 0000000000..0fb46a4704
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/native_member_interfered_scene.cfg
@@ -0,0 +1,14 @@
+[input]
+scene="res://completion/get_node/get_node.tscn"
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+ ; AnimationPlayer
+ {"display": "autoplay"},
+ {"display": "play"},
+ {"display": "animation_changed"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/native_member_interfered_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/native_member_interfered_scene.gd
new file mode 100644
index 0000000000..542197e643
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/native_member_interfered_scene.gd
@@ -0,0 +1,6 @@
+extends Node
+
+var test := $AnimationPlayer
+
+func a():
+ test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_scene/class_member_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/member_scene/class_member_scene.cfg
new file mode 100644
index 0000000000..009ab9f9ce
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member_scene/class_member_scene.cfg
@@ -0,0 +1,14 @@
+[input]
+scene="res://completion/get_node/get_node.tscn"
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+ ; GDScript: class_a.notest.gd
+ {"display": "property_of_a"},
+ {"display": "func_of_a"},
+ {"display": "signal_of_a"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_scene/class_member_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_scene/class_member_scene.gd
new file mode 100644
index 0000000000..4a35661e94
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member_scene/class_member_scene.gd
@@ -0,0 +1,6 @@
+extends Node
+
+var test = $A
+
+func a():
+ test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_scene/native_member_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/member_scene/native_member_scene.cfg
new file mode 100644
index 0000000000..0fb46a4704
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member_scene/native_member_scene.cfg
@@ -0,0 +1,14 @@
+[input]
+scene="res://completion/get_node/get_node.tscn"
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+ ; AnimationPlayer
+ {"display": "autoplay"},
+ {"display": "play"},
+ {"display": "animation_changed"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_scene/native_member_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_scene/native_member_scene.gd
new file mode 100644
index 0000000000..6bcc0a0298
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member_scene/native_member_scene.gd
@@ -0,0 +1,6 @@
+extends Node
+
+var test = $AnimationPlayer
+
+func a():
+ test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint/class_member_typehint.cfg b/modules/gdscript/tests/scripts/completion/get_node/member_typehint/class_member_typehint.cfg
new file mode 100644
index 0000000000..a72b489be6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint/class_member_typehint.cfg
@@ -0,0 +1,12 @@
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+ ; GDScript: class_a.notest.gd
+ {"display": "property_of_a"},
+ {"display": "func_of_a"},
+ {"display": "signal_of_a"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint/class_member_typehint.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint/class_member_typehint.gd
new file mode 100644
index 0000000000..e4edc3a4e4
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint/class_member_typehint.gd
@@ -0,0 +1,8 @@
+extends Node
+
+const A := preload("res://completion/class_a.notest.gd")
+
+var test: A = $A
+
+func a():
+ test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint/native_member_typehint.cfg b/modules/gdscript/tests/scripts/completion/get_node/member_typehint/native_member_typehint.cfg
new file mode 100644
index 0000000000..adf06c8707
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint/native_member_typehint.cfg
@@ -0,0 +1,12 @@
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+ ; AnimationPlayer
+ {"display": "autoplay"},
+ {"display": "play"},
+ {"display": "animation_changed"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/get_node_member_annotated.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint/native_member_typehint.gd
index d41bbb970c..eda94ae34d 100644
--- a/modules/gdscript/tests/scripts/completion/get_node/get_node_member_annotated.gd
+++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint/native_member_typehint.gd
@@ -2,5 +2,5 @@ extends Node
var test: AnimationPlayer = $AnimationPlayer
-func _ready():
+func a():
test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/class_member_typehint_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/class_member_typehint_scene.cfg
new file mode 100644
index 0000000000..9c580b711d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/class_member_typehint_scene.cfg
@@ -0,0 +1,14 @@
+[input]
+scene="res://completion/get_node/get_node.tscn"
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+ ; GDScript: class_a.notest.gd
+ {"display": "property_of_a"},
+ {"display": "func_of_a"},
+ {"display": "signal_of_a"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/class_member_typehint_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/class_member_typehint_scene.gd
new file mode 100644
index 0000000000..8f68f54072
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/class_member_typehint_scene.gd
@@ -0,0 +1,8 @@
+extends Node
+
+const A := preload("res://completion/class_a.notest.gd")
+
+@onready var test: A = $A
+
+func a():
+ test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/native_member_typehint_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/native_member_typehint_scene.cfg
new file mode 100644
index 0000000000..446198dd35
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/native_member_typehint_scene.cfg
@@ -0,0 +1,14 @@
+[input]
+scene="res://completion/get_node/get_node.tscn"
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+ ; AnimationPlayer
+ {"display": "autoplay"},
+ {"display": "play"},
+ {"display": "animation_changed"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/native_member_typehint_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/native_member_typehint_scene.gd
new file mode 100644
index 0000000000..eda94ae34d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/native_member_typehint_scene.gd
@@ -0,0 +1,6 @@
+extends Node
+
+var test: AnimationPlayer = $AnimationPlayer
+
+func a():
+ test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/class_member_typehint_scene_broad.cfg b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/class_member_typehint_scene_broad.cfg
new file mode 100644
index 0000000000..502038bef7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/class_member_typehint_scene_broad.cfg
@@ -0,0 +1,16 @@
+[input]
+scene="res://completion/get_node/get_node.tscn"
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+]
+exclude=[
+ ; GDScript: class_a.notest.gd
+ {"display": "property_of_a"},
+ {"display": "func_of_a"},
+ {"display": "signal_of_a"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/class_member_typehint_scene_broad.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/class_member_typehint_scene_broad.gd
new file mode 100644
index 0000000000..7b0ed4ecd8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/class_member_typehint_scene_broad.gd
@@ -0,0 +1,6 @@
+extends Node
+
+var test: Node = $A
+
+func a():
+ test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/native_member_typehint_scene_broad.cfg b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/native_member_typehint_scene_broad.cfg
new file mode 100644
index 0000000000..1810e9fe5f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/native_member_typehint_scene_broad.cfg
@@ -0,0 +1,16 @@
+[input]
+scene="res://completion/get_node/get_node.tscn"
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+]
+exclude=[
+ ; AnimationPlayer
+ {"display": "autoplay"},
+ {"display": "play"},
+ {"display": "animation_changed"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/native_member_typehint_scene_broad.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/native_member_typehint_scene_broad.gd
new file mode 100644
index 0000000000..87342f9a21
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/native_member_typehint_scene_broad.gd
@@ -0,0 +1,6 @@
+extends Node
+
+var test: Node = $AnimationPlayer
+
+func a():
+ test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/class_member_typehint_scene_incompatible.cfg b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/class_member_typehint_scene_incompatible.cfg
new file mode 100644
index 0000000000..1894e72c65
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/class_member_typehint_scene_incompatible.cfg
@@ -0,0 +1,20 @@
+[input]
+scene="res://completion/get_node/get_node.tscn"
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+ ; Area2D
+ {"display": "get_overlapping_areas"},
+ {"display": "linear_damp"},
+ {"display": "area_entered"},
+]
+exclude=[
+ ; GDScript: class_a.notest.gd
+ {"display": "property_of_a"},
+ {"display": "func_of_a"},
+ {"display": "signal_of_a"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/class_member_typehint_scene_incompatible.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/class_member_typehint_scene_incompatible.gd
new file mode 100644
index 0000000000..5f78bcdf04
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/class_member_typehint_scene_incompatible.gd
@@ -0,0 +1,6 @@
+extends Node
+
+var test: Area2D = $A
+
+func a():
+ test.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/native_member_typehint_scene_incompatible.cfg b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/native_member_typehint_scene_incompatible.cfg
new file mode 100644
index 0000000000..c8ab63f6d6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/native_member_typehint_scene_incompatible.cfg
@@ -0,0 +1,20 @@
+[input]
+scene="res://completion/get_node/get_node.tscn"
+[output]
+include=[
+ ; Node
+ {"display": "add_child"},
+ {"display": "owner"},
+ {"display": "child_entered_tree"},
+
+ ; Area2D
+ {"display": "get_overlapping_areas"},
+ {"display": "linear_damp"},
+ {"display": "area_entered"},
+]
+exclude=[
+ ; AnimationPlayer
+ {"display": "autoplay"},
+ {"display": "play"},
+ {"display": "animation_changed"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/native_member_typehint_scene_incompatible.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/native_member_typehint_scene_incompatible.gd
new file mode 100644
index 0000000000..c14df5cd1b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/native_member_typehint_scene_incompatible.gd
@@ -0,0 +1,6 @@
+extends Node
+
+var test: Area2D = $AnimationPlayer
+
+func a():
+ test.➡
diff --git a/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.gd b/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.gd
index 29d8501b78..193faab5d0 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.gd
+++ b/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.gd
@@ -1,11 +1,23 @@
-class Player:
- var x = 3
+class_name TestStaticCalledOnInstance
+
+class Inner:
+ static func static_func():
+ pass
+
+static func static_func():
+ pass
func test():
- # These should not emit a warning.
- var _player = Player.new()
- print(String.num_uint64(8589934592)) # 2 ^ 33
+ print(String.num_uint64(8589934592))
+ var some_string := String()
+ print(some_string.num_uint64(8589934592)) # Warning.
+
+ TestStaticCalledOnInstance.static_func()
+ static_func()
+ self.static_func()
+ var other := TestStaticCalledOnInstance.new()
+ other.static_func() # Warning.
- # This should emit a warning.
- var some_string = String()
- print(some_string.num_uint64(8589934592)) # 2 ^ 33
+ Inner.static_func()
+ var inner := Inner.new()
+ inner.static_func() # Warning.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.out b/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.out
index 77994ce9ba..c00f3d093a 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.out
+++ b/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.out
@@ -1,7 +1,15 @@
GDTEST_OK
>> WARNING
->> Line: 11
+>> Line: 13
>> STATIC_CALLED_ON_INSTANCE
>> The function "num_uint64()" is a static function but was called from an instance. Instead, it should be directly called from the type: "String.num_uint64()".
+>> WARNING
+>> Line: 19
+>> STATIC_CALLED_ON_INSTANCE
+>> The function "static_func()" is a static function but was called from an instance. Instead, it should be directly called from the type: "TestStaticCalledOnInstance.static_func()".
+>> WARNING
+>> Line: 23
+>> STATIC_CALLED_ON_INSTANCE
+>> The function "static_func()" is a static function but was called from an instance. Instead, it should be directly called from the type: "Inner.static_func()".
8589934592
8589934592
diff --git a/modules/gdscript/tests/test_completion.h b/modules/gdscript/tests/test_completion.h
index fd6b5321e6..ac9ffcd915 100644
--- a/modules/gdscript/tests/test_completion.h
+++ b/modules/gdscript/tests/test_completion.h
@@ -139,7 +139,7 @@ static void test_directory(const String &p_dir) {
Node *owner = nullptr;
if (conf.has_section_key("input", "scene")) {
- Ref<PackedScene> scene = ResourceLoader::load(conf.get_value("input", "scene"), "PackedScene");
+ Ref<PackedScene> scene = ResourceLoader::load(conf.get_value("input", "scene"), "PackedScene", ResourceFormatLoader::CACHE_MODE_IGNORE_DEEP);
if (scene.is_valid()) {
owner = scene->instantiate();
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedComplexStrings.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedComplexStrings.cs
new file mode 100644
index 0000000000..907257b125
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedComplexStrings.cs
@@ -0,0 +1,26 @@
+using Godot;
+
+namespace Godot.SourceGenerators.Sample
+{
+ public partial class ExportedComplexStrings : Node
+ {
+ [Export]
+ private string _fieldInterpolated1 = $"The quick brown fox jumps over ({Engine.GetVersionInfo()})";
+
+ [Export]
+ private string _fieldInterpolated2 = $"The quick brown fox jumps over ({Engine.GetVersionInfo()["major"],0:G}) the lazy dog.";
+
+ [Export]
+ private string _fieldInterpolated3 = $"{((int)Engine.GetVersionInfo()["major"]) * -1 * -1:G} the lazy dog.";
+
+ [Export]
+ private string _fieldInterpolated4 = $"{":::fff,,}<,<}},,}]"}";
+
+ [Export]
+ public string PropertyInterpolated1
+ {
+ get;
+ private set;
+ } = $"The quick brown fox jumps over {GD.VarToStr($"the lazy {Engine.GetVersionInfo()} do")}g.";
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPropertyDefValGeneratorTests.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPropertyDefValGeneratorTests.cs
index ae5fb86d77..7711bce1c7 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPropertyDefValGeneratorTests.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPropertyDefValGeneratorTests.cs
@@ -21,4 +21,13 @@ public class ScriptPropertyDefValGeneratorTests
"ExportedProperties_ScriptPropertyDefVal.generated.cs"
);
}
+
+ [Fact]
+ public async void ExportedComplexStrings()
+ {
+ await CSharpSourceGeneratorVerifier<ScriptPropertyDefValGenerator>.Verify(
+ "ExportedComplexStrings.cs",
+ "ExportedComplexStrings_ScriptPropertyDefVal.generated.cs"
+ );
+ }
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedComplexStrings_ScriptPropertyDefVal.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedComplexStrings_ScriptPropertyDefVal.generated.cs
new file mode 100644
index 0000000000..69e85b4467
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedComplexStrings_ScriptPropertyDefVal.generated.cs
@@ -0,0 +1,29 @@
+partial class ExportedComplexStrings
+{
+#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword
+#if TOOLS
+ /// <summary>
+ /// Get the default values for all properties declared in this class.
+ /// This method is used by Godot to determine the value that will be
+ /// used by the inspector when resetting properties.
+ /// Do not call this method.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ internal new static global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant> GetGodotPropertyDefaultValues()
+ {
+ var values = new global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant>(5);
+ string __PropertyInterpolated1_default_value = $"The quick brown fox jumps over {(global::Godot.GD.VarToStr($"the lazy {(global::Godot.Engine.GetVersionInfo())} do"))}g.";
+ values.Add(PropertyName.PropertyInterpolated1, global::Godot.Variant.From<string>(__PropertyInterpolated1_default_value));
+ string ___fieldInterpolated1_default_value = $"The quick brown fox jumps over ({(global::Godot.Engine.GetVersionInfo())})";
+ values.Add(PropertyName._fieldInterpolated1, global::Godot.Variant.From<string>(___fieldInterpolated1_default_value));
+ string ___fieldInterpolated2_default_value = $"The quick brown fox jumps over ({(global::Godot.Engine.GetVersionInfo()["major"]),0:G}) the lazy dog.";
+ values.Add(PropertyName._fieldInterpolated2, global::Godot.Variant.From<string>(___fieldInterpolated2_default_value));
+ string ___fieldInterpolated3_default_value = $"{(((int)global::Godot.Engine.GetVersionInfo()["major"]) * -1 * -1):G} the lazy dog.";
+ values.Add(PropertyName._fieldInterpolated3, global::Godot.Variant.From<string>(___fieldInterpolated3_default_value));
+ string ___fieldInterpolated4_default_value = $"{(":::fff,,}<,<}},,}]")}";
+ values.Add(PropertyName._fieldInterpolated4, global::Godot.Variant.From<string>(___fieldInterpolated4_default_value));
+ return values;
+ }
+#endif // TOOLS
+#pragma warning restore CS0109
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportedComplexStrings.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportedComplexStrings.cs
new file mode 100644
index 0000000000..06a34bdc4a
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportedComplexStrings.cs
@@ -0,0 +1,23 @@
+using Godot;
+
+public partial class ExportedComplexStrings : Node
+{
+ [Export]
+ private string _fieldInterpolated1 = $"The quick brown fox jumps over ({Engine.GetVersionInfo()})";
+
+ [Export]
+ private string _fieldInterpolated2 = $"The quick brown fox jumps over ({Engine.GetVersionInfo()["major"],0:G}) the lazy dog.";
+
+ [Export]
+ private string _fieldInterpolated3 = $"{((int)Engine.GetVersionInfo()["major"]) * -1 * -1:G} the lazy dog.";
+
+ [Export]
+ private string _fieldInterpolated4 = $"{":::fff,,}<,<}},,}]"}";
+
+ [Export]
+ public string PropertyInterpolated1
+ {
+ get;
+ private set;
+ } = $"The quick brown fox jumps over {GD.VarToStr($"the lazy {Engine.GetVersionInfo()} do")}g.";
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
index 9784bd0b78..957d5789df 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
@@ -208,7 +208,17 @@ namespace Godot.SourceGenerators
if (child.IsNode)
{
- FullQualifiedSyntax(child.AsNode()!, sm, sb, isFirstNode: innerIsFirstNode);
+ var childNode = child.AsNode()!;
+
+ if (node is InterpolationSyntax && childNode is ExpressionSyntax)
+ {
+ ParenEnclosedFullQualifiedSyntax(childNode, sm, sb, isFirstNode: innerIsFirstNode);
+ }
+ else
+ {
+ FullQualifiedSyntax(childNode, sm, sb, isFirstNode: innerIsFirstNode);
+ }
+
innerIsFirstNode = false;
}
else
@@ -221,6 +231,13 @@ namespace Godot.SourceGenerators
sb.Append(child.GetTrailingTrivia());
}
}
+
+ static void ParenEnclosedFullQualifiedSyntax(SyntaxNode node, SemanticModel sm, StringBuilder sb, bool isFirstNode)
+ {
+ sb.Append(SyntaxFactory.Token(SyntaxKind.OpenParenToken));
+ FullQualifiedSyntax(node, sm, sb, isFirstNode);
+ sb.Append(SyntaxFactory.Token(SyntaxKind.CloseParenToken));
+ }
}
public static string SanitizeQualifiedNameForUniqueHint(this string qualifiedName)
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 0a9162bd28..5cb177676c 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -121,6 +121,10 @@ StringBuilder &operator<<(StringBuilder &r_sb, const char *p_cstring) {
// This must be kept in sync with `ignored_types` in csharp_script.cpp
const Vector<String> ignored_types = {};
+// Special [code] keywords to wrap with <see langword="code"/> instead of <c>code</c>.
+// Don't check against all C# reserved words, as many cases are GDScript-specific.
+const Vector<String> langword_check = { "true", "false", "null" };
+
void BindingsGenerator::TypeInterface::postsetup_enum_type(BindingsGenerator::TypeInterface &r_enum_itype) {
// C interface for enums is the same as that of 'uint32_t'. Remember to apply
// any of the changes done here to the 'uint32_t' type interface as well.
@@ -670,11 +674,24 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "code" || tag.begins_with("code ")) {
- xml_output.append("<c>");
+ int end = bbcode.find("[", brk_end);
+ if (end == -1) {
+ end = bbcode.length();
+ }
+ String code = bbcode.substr(brk_end + 1, end - brk_end - 1);
+ if (langword_check.has(code)) {
+ xml_output.append("<see langword=\"");
+ xml_output.append(code);
+ xml_output.append("\"/>");
- code_tag = true;
- pos = brk_end + 1;
- tag_stack.push_front("code");
+ pos = brk_end + code.length() + 8;
+ } else {
+ xml_output.append("<c>");
+
+ code_tag = true;
+ pos = brk_end + 1;
+ tag_stack.push_front("code");
+ }
} else if (tag == "codeblock" || tag.begins_with("codeblock ")) {
xml_output.append("<code>");
diff --git a/modules/mono/editor/script_templates/SCsub b/modules/mono/editor/script_templates/SCsub
index 39f6cb5c01..01c293c25d 100644
--- a/modules/mono/editor/script_templates/SCsub
+++ b/modules/mono/editor/script_templates/SCsub
@@ -5,7 +5,7 @@ Import("env")
import editor.template_builders as build_template_cs
env["BUILDERS"]["MakeCSharpTemplateBuilder"] = Builder(
- action=env.Run(build_template_cs.make_templates, "Generating C# templates header."),
+ action=env.Run(build_template_cs.make_templates),
suffix=".h",
src_suffix=".cs",
)
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
index 72a3fe3ed0..a1f1ade9b8 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
@@ -336,7 +336,7 @@ namespace Godot
/// by the specified ratio (on the range of 0 to 1).
/// </summary>
/// <param name="amount">The ratio to lighten by.</param>
- /// <returns>The darkened color.</returns>
+ /// <returns>The lightened color.</returns>
public readonly Color Lightened(float amount)
{
Color res = this;
diff --git a/modules/multiplayer/scene_replication_config.cpp b/modules/multiplayer/scene_replication_config.cpp
index 836fa1014d..733540a0e4 100644
--- a/modules/multiplayer/scene_replication_config.cpp
+++ b/modules/multiplayer/scene_replication_config.cpp
@@ -103,6 +103,14 @@ void SceneReplicationConfig::_get_property_list(List<PropertyInfo> *p_list) cons
}
}
+void SceneReplicationConfig::reset_state() {
+ dirty = false;
+ properties.clear();
+ sync_props.clear();
+ spawn_props.clear();
+ watch_props.clear();
+}
+
TypedArray<NodePath> SceneReplicationConfig::get_properties() const {
TypedArray<NodePath> paths;
for (const ReplicationProperty &prop : properties) {
diff --git a/modules/multiplayer/scene_replication_config.h b/modules/multiplayer/scene_replication_config.h
index 3f870ba2d8..969a23d788 100644
--- a/modules/multiplayer/scene_replication_config.h
+++ b/modules/multiplayer/scene_replication_config.h
@@ -79,6 +79,8 @@ protected:
void _get_property_list(List<PropertyInfo> *p_list) const;
public:
+ virtual void reset_state() override; // Required since we use variable amount of properties.
+
TypedArray<NodePath> get_properties() const;
void add_property(const NodePath &p_path, int p_index = -1);
diff --git a/modules/text_server_adv/gdextension_build/methods.py b/modules/text_server_adv/gdextension_build/methods.py
index e58bc3abec..327097a3df 100644
--- a/modules/text_server_adv/gdextension_build/methods.py
+++ b/modules/text_server_adv/gdextension_build/methods.py
@@ -42,6 +42,12 @@ def no_verbose(sys, env):
java_library_message = "{}Creating Java Archive {}$TARGET{} ...{}".format(
colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
)
+ compiled_resource_message = "{}Creating Compiled Resource {}$TARGET{} ...{}".format(
+ colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
+ )
+ generated_file_message = "{}Generating {}$TARGET{} ...{}".format(
+ colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
+ )
env.Append(CXXCOMSTR=[compile_source_message])
env.Append(CCCOMSTR=[compile_source_message])
@@ -53,6 +59,8 @@ def no_verbose(sys, env):
env.Append(LINKCOMSTR=[link_program_message])
env.Append(JARCOMSTR=[java_library_message])
env.Append(JAVACCOMSTR=[java_compile_source_message])
+ env.Append(RCCOMSTR=[compiled_resource_message])
+ env.Append(GENCOMSTR=[generated_file_message])
def disable_warnings(self):
diff --git a/modules/text_server_fb/gdextension_build/methods.py b/modules/text_server_fb/gdextension_build/methods.py
index e58bc3abec..327097a3df 100644
--- a/modules/text_server_fb/gdextension_build/methods.py
+++ b/modules/text_server_fb/gdextension_build/methods.py
@@ -42,6 +42,12 @@ def no_verbose(sys, env):
java_library_message = "{}Creating Java Archive {}$TARGET{} ...{}".format(
colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
)
+ compiled_resource_message = "{}Creating Compiled Resource {}$TARGET{} ...{}".format(
+ colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
+ )
+ generated_file_message = "{}Generating {}$TARGET{} ...{}".format(
+ colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
+ )
env.Append(CXXCOMSTR=[compile_source_message])
env.Append(CCCOMSTR=[compile_source_message])
@@ -53,6 +59,8 @@ def no_verbose(sys, env):
env.Append(LINKCOMSTR=[link_program_message])
env.Append(JARCOMSTR=[java_library_message])
env.Append(JAVACCOMSTR=[java_compile_source_message])
+ env.Append(RCCOMSTR=[compiled_resource_message])
+ env.Append(GENCOMSTR=[generated_file_message])
def disable_warnings(self):
diff --git a/platform/ios/app_delegate.mm b/platform/ios/app_delegate.mm
index 32ebf7be44..5a0c57be93 100644
--- a/platform/ios/app_delegate.mm
+++ b/platform/ios/app_delegate.mm
@@ -105,11 +105,17 @@ static ViewController *mainViewController = nil;
// Initialize with default Ambient category.
AVAudioSessionCategory category = AVAudioSessionCategoryAmbient;
+ AVAudioSessionCategoryOptions options = 0;
+
+ if (GLOBAL_GET("audio/general/ios/mix_with_others")) {
+ options |= AVAudioSessionCategoryOptionMixWithOthers;
+ }
if (sessionCategorySetting == SESSION_CATEGORY_MULTI_ROUTE) {
category = AVAudioSessionCategoryMultiRoute;
} else if (sessionCategorySetting == SESSION_CATEGORY_PLAY_AND_RECORD) {
category = AVAudioSessionCategoryPlayAndRecord;
+ options |= AVAudioSessionCategoryOptionDefaultToSpeaker;
} else if (sessionCategorySetting == SESSION_CATEGORY_PLAYBACK) {
category = AVAudioSessionCategoryPlayback;
} else if (sessionCategorySetting == SESSION_CATEGORY_RECORD) {
@@ -118,11 +124,7 @@ static ViewController *mainViewController = nil;
category = AVAudioSessionCategorySoloAmbient;
}
- if (GLOBAL_GET("audio/general/ios/mix_with_others")) {
- [[AVAudioSession sharedInstance] setCategory:category withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
- } else {
- [[AVAudioSession sharedInstance] setCategory:category error:nil];
- }
+ [[AVAudioSession sharedInstance] setCategory:category withOptions:options error:nil];
return YES;
}
diff --git a/platform/linuxbsd/wayland/wayland_thread.cpp b/platform/linuxbsd/wayland/wayland_thread.cpp
index 7fa69a3bed..8167642345 100644
--- a/platform/linuxbsd/wayland/wayland_thread.cpp
+++ b/platform/linuxbsd/wayland/wayland_thread.cpp
@@ -2424,8 +2424,7 @@ void WaylandThread::_wp_tablet_tool_on_frame(void *data, struct zwp_tablet_tool_
// According to the tablet proto spec, tilt is expressed in degrees relative
// to the Z axis of the tablet, so it shouldn't go over 90 degrees either way,
// I think. We'll clamp it just in case.
- td.tilt.x = CLAMP(td.tilt.x, -90, 90);
- td.tilt.y = CLAMP(td.tilt.x, -90, 90);
+ td.tilt = td.tilt.clamp(Vector2(-90, -90), Vector2(90, 90));
mm->set_tilt(td.tilt / 90);
diff --git a/platform/linuxbsd/wayland/wayland_thread.h b/platform/linuxbsd/wayland/wayland_thread.h
index b9e7e0437a..d49f0c9d34 100644
--- a/platform/linuxbsd/wayland/wayland_thread.h
+++ b/platform/linuxbsd/wayland/wayland_thread.h
@@ -308,7 +308,7 @@ public:
struct TabletToolData {
Point2i position;
- Vector2i tilt;
+ Vector2 tilt;
uint32_t pressure = 0;
BitField<MouseButtonMask> pressed_button_mask;
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index 4585884859..ca81bb615e 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -202,6 +202,7 @@ def get_opts():
BoolVariable("use_asan", "Use address sanitizer (ASAN)", False),
BoolVariable("debug_crt", "Compile with MSVC's debug CRT (/MDd)", False),
BoolVariable("incremental_link", "Use MSVC incremental linking. May increase or decrease build times.", False),
+ BoolVariable("silence_msvc", "Silence MSVC's stdout. Decreases output log bloat by roughly half.", True),
("angle_libs", "Path to the ANGLE static libraries", ""),
# Direct3D 12 support.
(
@@ -392,6 +393,20 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config):
## Compile/link flags
+ env["MAXLINELENGTH"] = 8192 # Windows Vista and beyond, so always applicable.
+
+ if env["silence_msvc"]:
+ env.Prepend(CCFLAGS=[">", "NUL"])
+ env.Prepend(LINKFLAGS=[">", "NUL"])
+
+ # "> NUL" fails if using a tempfile, circumvent by removing the argument altogether.
+ old_esc_func = env["TEMPFILEARGESCFUNC"]
+
+ def trim_nul(arg):
+ return "" if arg in [">", "NUL"] else old_esc_func(arg)
+
+ env["TEMPFILEARGESCFUNC"] = trim_nul
+
if env["debug_crt"]:
# Always use dynamic runtime, static debug CRT breaks thread_local.
env.AppendUnique(CCFLAGS=["/MDd"])
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
index 08baa7e387..722858b674 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -60,9 +60,11 @@ void Camera2D::_update_scroll() {
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);
- get_tree()->call_group(group_name, "_camera_moved", xform, screen_offset);
- };
+ // TODO: Remove xform and screen_offset when ParallaxBackground/ParallaxLayer is removed.
+ get_tree()->call_group(group_name, SNAME("_camera_moved"), xform, screen_offset, adj_screen_pos);
+ }
}
void Camera2D::_update_process_callback() {
diff --git a/scene/2d/parallax_2d.cpp b/scene/2d/parallax_2d.cpp
new file mode 100644
index 0000000000..f516fd41ac
--- /dev/null
+++ b/scene/2d/parallax_2d.cpp
@@ -0,0 +1,290 @@
+/**************************************************************************/
+/* parallax_2d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "parallax_2d.h"
+
+#include "core/config/project_settings.h"
+
+void Parallax2D::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ group_name = "__cameras_" + itos(get_viewport_rid().get_id());
+ add_to_group(group_name);
+ _update_repeat();
+ _update_scroll();
+ } break;
+
+ case NOTIFICATION_READY: {
+ _update_process();
+ } break;
+
+ case NOTIFICATION_INTERNAL_PROCESS: {
+ autoscroll_offset += autoscroll * get_process_delta_time();
+ autoscroll_offset = autoscroll_offset.posmodv(repeat_size);
+
+ _update_scroll();
+ } break;
+
+ case NOTIFICATION_EXIT_TREE: {
+ remove_from_group(group_name);
+ } break;
+ }
+}
+
+#ifdef TOOLS_ENABLED
+void Parallax2D::_edit_set_position(const Point2 &p_position) {
+ set_scroll_offset(p_position);
+}
+#endif // TOOLS_ENABLED
+
+void Parallax2D::_validate_property(PropertyInfo &p_property) const {
+ if (p_property.name == "position") {
+ p_property.usage = PROPERTY_USAGE_NONE;
+ }
+}
+
+void Parallax2D::_camera_moved(const Transform2D &p_transform, const Point2 &p_screen_offset, const Point2 &p_adj_screen_pos) {
+ if (!ignore_camera_scroll) {
+ set_screen_offset(p_adj_screen_pos);
+ }
+}
+
+void Parallax2D::_update_process() {
+ set_process_internal(!Engine::get_singleton()->is_editor_hint() && (repeat_size.x || repeat_size.y) && (autoscroll.x || autoscroll.y));
+}
+
+void Parallax2D::_update_scroll() {
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ Point2 scroll_ofs = screen_offset;
+ Size2 vps = get_viewport_rect().size;
+
+ if (Engine::get_singleton()->is_editor_hint()) {
+ vps = Size2(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height"));
+ } else {
+ if (limit_begin.x <= limit_end.x - vps.x) {
+ scroll_ofs.x = CLAMP(scroll_ofs.x, limit_begin.x, limit_end.x - vps.x);
+ }
+ if (limit_begin.y <= limit_end.y - vps.y) {
+ scroll_ofs.y = CLAMP(scroll_ofs.y, limit_begin.y, limit_end.y - vps.y);
+ }
+ }
+
+ scroll_ofs *= scroll_scale;
+
+ if (repeat_size.x) {
+ real_t mod = Math::fposmod(scroll_ofs.x - scroll_offset.x - autoscroll_offset.x, repeat_size.x);
+ scroll_ofs.x = screen_offset.x - mod;
+ } else {
+ scroll_ofs.x = screen_offset.x + scroll_offset.x - scroll_ofs.x;
+ }
+
+ if (repeat_size.y) {
+ real_t mod = Math::fposmod(scroll_ofs.y - scroll_offset.y - autoscroll_offset.y, repeat_size.y);
+ scroll_ofs.y = screen_offset.y - mod;
+ } else {
+ scroll_ofs.y = screen_offset.y + scroll_offset.y - scroll_ofs.y;
+ }
+
+ if (!follow_viewport) {
+ scroll_ofs -= screen_offset;
+ }
+
+ set_position(scroll_ofs);
+}
+
+void Parallax2D::_update_repeat() {
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ Point2 repeat_scale = repeat_size * get_scale();
+ RenderingServer::get_singleton()->canvas_set_item_repeat(get_canvas_item(), repeat_scale, repeat_times);
+}
+
+void Parallax2D::set_scroll_scale(const Size2 &p_scale) {
+ scroll_scale = p_scale;
+}
+
+Size2 Parallax2D::get_scroll_scale() const {
+ return scroll_scale;
+}
+
+void Parallax2D::set_repeat_size(const Size2 &p_repeat_size) {
+ if (p_repeat_size == repeat_size) {
+ return;
+ }
+
+ repeat_size = p_repeat_size.max(Vector2(0, 0));
+
+ _update_process();
+ _update_repeat();
+ _update_scroll();
+}
+
+Size2 Parallax2D::get_repeat_size() const {
+ return repeat_size;
+}
+
+void Parallax2D::set_repeat_times(int p_repeat_times) {
+ if (p_repeat_times == repeat_times) {
+ return;
+ }
+
+ repeat_times = MAX(p_repeat_times, 1);
+
+ _update_repeat();
+}
+
+int Parallax2D::get_repeat_times() const {
+ return repeat_times;
+}
+
+void Parallax2D::set_scroll_offset(const Point2 &p_offset) {
+ if (p_offset == scroll_offset) {
+ return;
+ }
+
+ scroll_offset = p_offset;
+
+ _update_scroll();
+}
+
+Point2 Parallax2D::get_scroll_offset() const {
+ return scroll_offset;
+}
+
+void Parallax2D::set_autoscroll(const Point2 &p_autoscroll) {
+ if (p_autoscroll == autoscroll) {
+ return;
+ }
+
+ autoscroll = p_autoscroll;
+ autoscroll_offset = Point2();
+
+ _update_process();
+ _update_scroll();
+}
+
+Point2 Parallax2D::get_autoscroll() const {
+ return autoscroll;
+}
+
+void Parallax2D::set_screen_offset(const Point2 &p_offset) {
+ if (p_offset == screen_offset) {
+ return;
+ }
+
+ screen_offset = p_offset;
+
+ _update_scroll();
+}
+
+Point2 Parallax2D::get_screen_offset() const {
+ return screen_offset;
+}
+
+void Parallax2D::set_limit_begin(const Point2 &p_offset) {
+ limit_begin = p_offset;
+}
+
+Point2 Parallax2D::get_limit_begin() const {
+ return limit_begin;
+}
+
+void Parallax2D::set_limit_end(const Point2 &p_offset) {
+ limit_end = p_offset;
+}
+
+Point2 Parallax2D::get_limit_end() const {
+ return limit_end;
+}
+
+void Parallax2D::set_follow_viewport(bool p_follow) {
+ follow_viewport = p_follow;
+}
+
+bool Parallax2D::get_follow_viewport() {
+ return follow_viewport;
+}
+
+void Parallax2D::set_ignore_camera_scroll(bool p_ignore) {
+ ignore_camera_scroll = p_ignore;
+}
+
+bool Parallax2D::is_ignore_camera_scroll() {
+ return ignore_camera_scroll;
+}
+
+void Parallax2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_camera_moved", "transform", "screen_offset", "adj_screen_offset"), &Parallax2D::_camera_moved);
+ ClassDB::bind_method(D_METHOD("set_scroll_scale", "scale"), &Parallax2D::set_scroll_scale);
+ ClassDB::bind_method(D_METHOD("get_scroll_scale"), &Parallax2D::get_scroll_scale);
+ ClassDB::bind_method(D_METHOD("set_repeat_size", "repeat_size"), &Parallax2D::set_repeat_size);
+ ClassDB::bind_method(D_METHOD("get_repeat_size"), &Parallax2D::get_repeat_size);
+ ClassDB::bind_method(D_METHOD("set_repeat_times", "repeat_times"), &Parallax2D::set_repeat_times);
+ ClassDB::bind_method(D_METHOD("get_repeat_times"), &Parallax2D::get_repeat_times);
+ ClassDB::bind_method(D_METHOD("set_autoscroll", "autoscroll"), &Parallax2D::set_autoscroll);
+ ClassDB::bind_method(D_METHOD("get_autoscroll"), &Parallax2D::get_autoscroll);
+ ClassDB::bind_method(D_METHOD("set_scroll_offset", "offset"), &Parallax2D::set_scroll_offset);
+ ClassDB::bind_method(D_METHOD("get_scroll_offset"), &Parallax2D::get_scroll_offset);
+ ClassDB::bind_method(D_METHOD("set_screen_offset", "offset"), &Parallax2D::set_screen_offset);
+ ClassDB::bind_method(D_METHOD("get_screen_offset"), &Parallax2D::get_screen_offset);
+ ClassDB::bind_method(D_METHOD("set_limit_begin", "offset"), &Parallax2D::set_limit_begin);
+ ClassDB::bind_method(D_METHOD("get_limit_begin"), &Parallax2D::get_limit_begin);
+ ClassDB::bind_method(D_METHOD("set_limit_end", "offset"), &Parallax2D::set_limit_end);
+ ClassDB::bind_method(D_METHOD("get_limit_end"), &Parallax2D::get_limit_end);
+ ClassDB::bind_method(D_METHOD("set_follow_viewport", "follow"), &Parallax2D::set_follow_viewport);
+ ClassDB::bind_method(D_METHOD("get_follow_viewport"), &Parallax2D::get_follow_viewport);
+ ClassDB::bind_method(D_METHOD("set_ignore_camera_scroll", "ignore"), &Parallax2D::set_ignore_camera_scroll);
+ ClassDB::bind_method(D_METHOD("is_ignore_camera_scroll"), &Parallax2D::is_ignore_camera_scroll);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scroll_scale", PROPERTY_HINT_LINK), "set_scroll_scale", "get_scroll_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scroll_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_scroll_offset", "get_scroll_offset");
+
+ ADD_GROUP("Repeat", "");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "repeat_size"), "set_repeat_size", "get_repeat_size");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "autoscroll", PROPERTY_HINT_NONE, "suffix:px/s"), "set_autoscroll", "get_autoscroll");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "repeat_times"), "set_repeat_times", "get_repeat_times");
+
+ ADD_GROUP("Limit", "limit_");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "limit_begin", PROPERTY_HINT_NONE, "suffix:px"), "set_limit_begin", "get_limit_begin");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "limit_end", PROPERTY_HINT_NONE, "suffix:px"), "set_limit_end", "get_limit_end");
+
+ ADD_GROUP("Override", "");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_viewport"), "set_follow_viewport", "get_follow_viewport");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_camera_scroll"), "set_ignore_camera_scroll", "is_ignore_camera_scroll");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "screen_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_screen_offset", "get_screen_offset");
+}
+
+Parallax2D::Parallax2D() {
+}
diff --git a/scene/2d/parallax_2d.h b/scene/2d/parallax_2d.h
new file mode 100644
index 0000000000..5fbc3a20c8
--- /dev/null
+++ b/scene/2d/parallax_2d.h
@@ -0,0 +1,101 @@
+/**************************************************************************/
+/* parallax_2d.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 PARALLAX_2D_H
+#define PARALLAX_2D_H
+
+#include "scene/2d/node_2d.h"
+
+class Parallax2D : public Node2D {
+ GDCLASS(Parallax2D, Node2D);
+
+ static constexpr real_t DEFAULT_LIMIT = 10000000;
+
+ String group_name;
+ Size2 scroll_scale = Size2(1, 1);
+ Point2 scroll_offset;
+ Point2 screen_offset;
+ Vector2 repeat_size;
+ int repeat_times = 1;
+ Point2 limit_begin = Point2(-DEFAULT_LIMIT, -DEFAULT_LIMIT);
+ Point2 limit_end = Point2(DEFAULT_LIMIT, DEFAULT_LIMIT);
+ Point2 autoscroll;
+ Point2 autoscroll_offset;
+ bool follow_viewport = true;
+ bool ignore_camera_scroll = false;
+
+ void _update_process();
+ void _update_repeat();
+ void _update_scroll();
+
+protected:
+#ifdef TOOLS_ENABLED
+ void _edit_set_position(const Point2 &p_position) override;
+#endif // TOOLS_ENABLED
+ void _validate_property(PropertyInfo &p_property) const;
+ void _camera_moved(const Transform2D &p_transform, const Point2 &p_screen_offset, const Point2 &p_adj_screen_offset);
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ void set_scroll_scale(const Size2 &p_scale);
+ Size2 get_scroll_scale() const;
+
+ void set_repeat_size(const Size2 &p_repeat_size);
+ Size2 get_repeat_size() const;
+
+ void set_repeat_times(int p_repeat_times);
+ int get_repeat_times() const;
+
+ void set_autoscroll(const Point2 &p_autoscroll);
+ Point2 get_autoscroll() const;
+
+ void set_scroll_offset(const Point2 &p_offset);
+ Point2 get_scroll_offset() const;
+
+ void set_screen_offset(const Point2 &p_offset);
+ Point2 get_screen_offset() const;
+
+ void set_limit_begin(const Point2 &p_offset);
+ Point2 get_limit_begin() const;
+
+ void set_limit_end(const Point2 &p_offset);
+ Point2 get_limit_end() const;
+
+ void set_follow_viewport(bool p_follow);
+ bool get_follow_viewport();
+
+ void set_ignore_camera_scroll(bool p_ignore);
+ bool is_ignore_camera_scroll();
+
+ Parallax2D();
+};
+
+#endif // PARALLAX_2D_H
diff --git a/scene/2d/parallax_background.cpp b/scene/2d/parallax_background.cpp
index 3d57aaf6aa..777a525135 100644
--- a/scene/2d/parallax_background.cpp
+++ b/scene/2d/parallax_background.cpp
@@ -45,7 +45,7 @@ void ParallaxBackground::_notification(int p_what) {
}
}
-void ParallaxBackground::_camera_moved(const Transform2D &p_transform, const Point2 &p_screen_offset) {
+void ParallaxBackground::_camera_moved(const Transform2D &p_transform, const Point2 &p_screen_offset, const Point2 &p_adj_screen_offset) {
screen_offset = p_screen_offset;
set_scroll_scale(p_transform.get_scale().dot(Vector2(0.5, 0.5)));
diff --git a/scene/2d/parallax_background.h b/scene/2d/parallax_background.h
index 70766ae7fa..89a87221eb 100644
--- a/scene/2d/parallax_background.h
+++ b/scene/2d/parallax_background.h
@@ -50,7 +50,7 @@ class ParallaxBackground : public CanvasLayer {
void _update_scroll();
protected:
- void _camera_moved(const Transform2D &p_transform, const Point2 &p_screen_offset);
+ void _camera_moved(const Transform2D &p_transform, const Point2 &p_screen_offset, const Point2 &p_adj_screen_offset);
void _notification(int p_what);
static void _bind_methods();
diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp
index 5683fb7306..76e89f24d8 100644
--- a/scene/3d/bone_attachment_3d.cpp
+++ b/scene/3d/bone_attachment_3d.cpp
@@ -244,7 +244,7 @@ int BoneAttachment3D::get_bone_idx() const {
void BoneAttachment3D::set_override_pose(bool p_override) {
override_pose = p_override;
- set_notify_local_transform(override_pose);
+ set_notify_transform(override_pose);
set_process_internal(override_pose);
if (!override_pose) {
@@ -301,7 +301,7 @@ void BoneAttachment3D::_notification(int p_what) {
_check_unbind();
} break;
- case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
+ case NOTIFICATION_TRANSFORM_CHANGED: {
_transform_changed();
} break;
diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp
index 3fb472335e..718ddd4982 100644
--- a/scene/3d/label_3d.cpp
+++ b/scene/3d/label_3d.cpp
@@ -208,7 +208,7 @@ void Label3D::_notification(int p_what) {
viewport->disconnect("size_changed", callable_mp(this, &Label3D::_font_changed));
} break;
case NOTIFICATION_TRANSLATION_CHANGED: {
- String new_text = tr(text);
+ String new_text = atr(text);
if (new_text == xl_text) {
return; // Nothing new.
}
@@ -636,7 +636,7 @@ void Label3D::_shape() {
void Label3D::set_text(const String &p_string) {
text = p_string;
- xl_text = tr(p_string);
+ xl_text = atr(p_string);
dirty_text = true;
_queue_update();
}
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 4226ecbdb3..7230f87359 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -1901,7 +1901,11 @@ void RichTextLabel::_notification(int p_what) {
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_TRANSLATION_CHANGED: {
- _apply_translation();
+ // If `text` is empty, it could mean that the tag stack is being used instead. Leave it be.
+ if (!text.is_empty()) {
+ _apply_translation();
+ }
+
queue_redraw();
} break;
@@ -5667,19 +5671,16 @@ int RichTextLabel::get_selection_to() const {
}
void RichTextLabel::set_text(const String &p_bbcode) {
- if (text == p_bbcode) {
+ // Allow clearing the tag stack.
+ if (!p_bbcode.is_empty() && text == p_bbcode) {
return;
}
+
text = p_bbcode;
_apply_translation();
}
void RichTextLabel::_apply_translation() {
- // If `text` is empty, it could mean that the tag stack is being used instead. Leave it be.
- if (text.is_empty()) {
- return;
- }
-
String xl_text = atr(text);
if (use_bbcode) {
parse_bbcode(xl_text);
@@ -5700,7 +5701,10 @@ void RichTextLabel::set_use_bbcode(bool p_enable) {
use_bbcode = p_enable;
notify_property_list_changed();
- _apply_translation();
+ // If `text` is empty, it could mean that the tag stack is being used instead. Leave it be.
+ if (!text.is_empty()) {
+ _apply_translation();
+ }
}
bool RichTextLabel::is_using_bbcode() const {
diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp
index 69d281e373..f984d781d3 100644
--- a/scene/gui/slider.cpp
+++ b/scene/gui/slider.cpp
@@ -67,8 +67,8 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) {
grab.value_before_dragging = get_as_ratio();
emit_signal(SNAME("drag_started"));
- double grab_width = (double)grabber->get_width();
- double grab_height = (double)grabber->get_height();
+ double grab_width = theme_cache.center_grabber ? 0.0 : (double)grabber->get_width();
+ double grab_height = theme_cache.center_grabber ? 0.0 : (double)grabber->get_height();
double max = orientation == VERTICAL ? get_size().height - grab_height : get_size().width - grab_width;
set_block_signals(true);
if (orientation == VERTICAL) {
@@ -107,12 +107,14 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) {
if (mm.is_valid()) {
if (grab.active) {
Size2i size = get_size();
- Ref<Texture2D> grabber = theme_cache.grabber_icon;
+ Ref<Texture2D> grabber = theme_cache.grabber_hl_icon;
+ double grab_width = theme_cache.center_grabber ? 0.0 : (double)grabber->get_width();
+ double grab_height = theme_cache.center_grabber ? 0.0 : (double)grabber->get_height();
double motion = (orientation == VERTICAL ? mm->get_position().y : mm->get_position().x) - grab.pos;
if (orientation == VERTICAL) {
motion = -motion;
}
- double areasize = orientation == VERTICAL ? size.height - grabber->get_height() : size.width - grabber->get_width();
+ double areasize = orientation == VERTICAL ? size.height - grab_height : size.width - grab_width;
if (areasize <= 0) {
return;
}
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 9f741db7da..3583dace2a 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -52,6 +52,7 @@
#include "scene/2d/navigation_link_2d.h"
#include "scene/2d/navigation_obstacle_2d.h"
#include "scene/2d/navigation_region_2d.h"
+#include "scene/2d/parallax_2d.h"
#include "scene/2d/parallax_background.h"
#include "scene/2d/parallax_layer.h"
#include "scene/2d/path_2d.h"
@@ -813,6 +814,7 @@ void register_scene_types() {
GDREGISTER_CLASS(TileData);
GDREGISTER_CLASS(TileMap);
GDREGISTER_ABSTRACT_CLASS(TileMapLayerGroup);
+ GDREGISTER_CLASS(Parallax2D);
GDREGISTER_CLASS(ParallaxBackground);
GDREGISTER_CLASS(ParallaxLayer);
GDREGISTER_CLASS(TouchScreenButton);
diff --git a/scene/resources/2d/world_boundary_shape_2d.cpp b/scene/resources/2d/world_boundary_shape_2d.cpp
index 294653659a..574b10c448 100644
--- a/scene/resources/2d/world_boundary_shape_2d.cpp
+++ b/scene/resources/2d/world_boundary_shape_2d.cpp
@@ -86,10 +86,41 @@ void WorldBoundaryShape2D::draw(const RID &p_to_rid, const Color &p_color) {
Vector2 point = distance * normal;
real_t line_width = 3.0;
- Vector2 l1[2] = { point - normal.orthogonal() * 100, point + normal.orthogonal() * 100 };
- RS::get_singleton()->canvas_item_add_line(p_to_rid, l1[0], l1[1], p_color, line_width);
- Vector2 l2[2] = { point + normal.normalized() * (0.5 * line_width), point + normal * 30 };
- RS::get_singleton()->canvas_item_add_line(p_to_rid, l2[0], l2[1], p_color, line_width);
+ // Draw collision shape line.
+ PackedVector2Array line_points = {
+ point - normal.orthogonal() * 100,
+ point - normal.orthogonal() * 60,
+ point + normal.orthogonal() * 60,
+ point + normal.orthogonal() * 100
+ };
+
+ Color transparent_color = Color(p_color, 0);
+ PackedColorArray line_colors = {
+ transparent_color,
+ p_color,
+ p_color,
+ transparent_color
+ };
+
+ RS::get_singleton()->canvas_item_add_polyline(p_to_rid, line_points, line_colors, line_width);
+
+ // Draw arrow.
+ Color arrow_color = p_color.inverted();
+
+ Transform2D xf;
+ xf.rotate(normal.angle());
+
+ Vector<Vector2> arrow_points = {
+ xf.xform(Vector2(distance + line_width / 2, -2.5)),
+ xf.xform(Vector2(distance + 20, -2.5)),
+ xf.xform(Vector2(distance + 20, -10)),
+ xf.xform(Vector2(distance + 40, 0)),
+ xf.xform(Vector2(distance + 20, 10)),
+ xf.xform(Vector2(distance + 20, 2.5)),
+ xf.xform(Vector2(distance + line_width / 2, 2.5)),
+ };
+
+ RS::get_singleton()->canvas_item_add_polyline(p_to_rid, arrow_points, { arrow_color }, line_width / 2);
}
Rect2 WorldBoundaryShape2D::get_rect() const {
diff --git a/scene/theme/icons/SCsub b/scene/theme/icons/SCsub
index fa9f3cb1ce..46133ccceb 100644
--- a/scene/theme/icons/SCsub
+++ b/scene/theme/icons/SCsub
@@ -6,9 +6,7 @@ import default_theme_icons_builders
env["BUILDERS"]["MakeDefaultThemeIconsBuilder"] = Builder(
- action=env.Run(
- default_theme_icons_builders.make_default_theme_icons_action, "Generating default project theme icons header."
- ),
+ action=env.Run(default_theme_icons_builders.make_default_theme_icons_action),
suffix=".h",
src_suffix=".svg",
)
diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp
index 65fcdb9751..cd38aa05f3 100644
--- a/servers/rendering/renderer_canvas_cull.cpp
+++ b/servers/rendering/renderer_canvas_cull.cpp
@@ -37,17 +37,14 @@
#include "rendering_server_globals.h"
#include "servers/rendering/storage/texture_storage.h"
-void RendererCanvasCull::_render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RendererCanvasRender::Light *p_lights, RendererCanvasRender::Light *p_directional_lights, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, uint32_t p_canvas_cull_mask, RenderingMethod::RenderInfo *r_render_info) {
+void RendererCanvasCull::_render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RendererCanvasRender::Light *p_lights, RendererCanvasRender::Light *p_directional_lights, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, uint32_t p_canvas_cull_mask, RenderingMethod::RenderInfo *r_render_info) {
RENDER_TIMESTAMP("Cull CanvasItem Tree");
memset(z_list, 0, z_range * sizeof(RendererCanvasRender::Item *));
memset(z_last_list, 0, z_range * sizeof(RendererCanvasRender::Item *));
for (int i = 0; i < p_child_item_count; i++) {
- _cull_canvas_item(p_child_items[i].item, p_transform, p_clip_rect, Color(1, 1, 1, 1), 0, z_list, z_last_list, nullptr, nullptr, true, p_canvas_cull_mask);
- }
- if (p_canvas_item) {
- _cull_canvas_item(p_canvas_item, p_transform, p_clip_rect, Color(1, 1, 1, 1), 0, z_list, z_last_list, nullptr, nullptr, true, p_canvas_cull_mask);
+ _cull_canvas_item(p_child_items[i].item, p_transform, p_clip_rect, Color(1, 1, 1, 1), 0, z_list, z_last_list, nullptr, nullptr, true, p_canvas_cull_mask, p_child_items[i].mirror, 1);
}
RendererCanvasRender::Item *list = nullptr;
@@ -223,7 +220,7 @@ void RendererCanvasCull::_attach_canvas_item_for_draw(RendererCanvasCull::Item *
}
}
-void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2D &p_parent_xform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool p_allow_y_sort, uint32_t p_canvas_cull_mask) {
+void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2D &p_parent_xform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool p_allow_y_sort, uint32_t p_canvas_cull_mask, const Point2 &p_repeat_size, int p_repeat_times) {
Item *ci = p_canvas_item;
if (!ci->visible) {
@@ -250,6 +247,22 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
Transform2D xform = ci->xform;
Transform2D parent_xform = p_parent_xform;
+ Point2 repeat_size = p_repeat_size;
+ int repeat_times = p_repeat_times;
+
+ if (ci->repeat_source) {
+ repeat_size = ci->repeat_size;
+ repeat_times = ci->repeat_times;
+ } else {
+ ci->repeat_size = repeat_size;
+ ci->repeat_times = repeat_times;
+
+ if (repeat_size.x || repeat_size.y) {
+ rect.size += repeat_size * repeat_times / ci->xform.get_scale();
+ rect.position -= repeat_size * (repeat_times / 2);
+ }
+ }
+
if (snapping_2d_transforms_to_pixel) {
xform.columns[2] = xform.columns[2].round();
parent_xform.columns[2] = parent_xform.columns[2].round();
@@ -324,7 +337,7 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
sorter.sort(child_items, child_item_count);
for (i = 0; i < child_item_count; i++) {
- _cull_canvas_item(child_items[i], xform * child_items[i]->ysort_xform, p_clip_rect, modulate * child_items[i]->ysort_modulate, child_items[i]->ysort_parent_abs_z_index, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, (Item *)child_items[i]->material_owner, false, p_canvas_cull_mask);
+ _cull_canvas_item(child_items[i], xform * child_items[i]->ysort_xform, p_clip_rect, modulate * child_items[i]->ysort_modulate, child_items[i]->ysort_parent_abs_z_index, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, (Item *)child_items[i]->material_owner, false, p_canvas_cull_mask, repeat_size, repeat_times);
}
} else {
RendererCanvasRender::Item *canvas_group_from = nullptr;
@@ -348,14 +361,14 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
if (!child_items[i]->behind && !use_canvas_group) {
continue;
}
- _cull_canvas_item(child_items[i], xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, p_canvas_cull_mask);
+ _cull_canvas_item(child_items[i], xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, p_canvas_cull_mask, repeat_size, repeat_times);
}
_attach_canvas_item_for_draw(ci, p_canvas_clip, r_z_list, r_z_last_list, xform, p_clip_rect, global_rect, modulate, p_z, p_material_owner, use_canvas_group, canvas_group_from);
for (int i = 0; i < child_item_count; i++) {
if (child_items[i]->behind || use_canvas_group) {
continue;
}
- _cull_canvas_item(child_items[i], xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, p_canvas_cull_mask);
+ _cull_canvas_item(child_items[i], xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, p_canvas_cull_mask, repeat_size, repeat_times);
}
}
}
@@ -374,38 +387,7 @@ void RendererCanvasCull::render_canvas(RID p_render_target, Canvas *p_canvas, co
int l = p_canvas->child_items.size();
Canvas::ChildItem *ci = p_canvas->child_items.ptrw();
- bool has_mirror = false;
- for (int i = 0; i < l; i++) {
- if (ci[i].mirror.x || ci[i].mirror.y) {
- has_mirror = true;
- break;
- }
- }
-
- if (!has_mirror) {
- _render_canvas_item_tree(p_render_target, ci, l, nullptr, p_transform, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel, canvas_cull_mask, r_render_info);
-
- } else {
- //used for parallaxlayer mirroring
- for (int i = 0; i < l; i++) {
- const Canvas::ChildItem &ci2 = p_canvas->child_items[i];
- _render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, p_transform, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel, canvas_cull_mask, r_render_info);
-
- //mirroring (useful for scrolling backgrounds)
- if (ci2.mirror.x != 0) {
- Transform2D xform2 = p_transform * Transform2D(0, Vector2(ci2.mirror.x, 0));
- _render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel, canvas_cull_mask, r_render_info);
- }
- if (ci2.mirror.y != 0) {
- Transform2D xform2 = p_transform * Transform2D(0, Vector2(0, ci2.mirror.y));
- _render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel, canvas_cull_mask, r_render_info);
- }
- if (ci2.mirror.y != 0 && ci2.mirror.x != 0) {
- Transform2D xform2 = p_transform * Transform2D(0, ci2.mirror);
- _render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel, canvas_cull_mask, r_render_info);
- }
- }
- }
+ _render_canvas_item_tree(p_render_target, ci, l, p_transform, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel, canvas_cull_mask, r_render_info);
RENDER_TIMESTAMP("< Render Canvas");
}
@@ -432,6 +414,15 @@ void RendererCanvasCull::canvas_set_item_mirroring(RID p_canvas, RID p_item, con
canvas->child_items.write[idx].mirror = p_mirroring;
}
+void RendererCanvasCull::canvas_set_item_repeat(RID p_item, const Point2 &p_repeat_size, int p_repeat_times) {
+ Item *canvas_item = canvas_item_owner.get_or_null(p_item);
+ ERR_FAIL_NULL(canvas_item);
+
+ canvas_item->repeat_source = true;
+ canvas_item->repeat_size = p_repeat_size;
+ canvas_item->repeat_times = p_repeat_times;
+}
+
void RendererCanvasCull::canvas_set_modulate(RID p_canvas, const Color &p_color) {
Canvas *canvas = canvas_owner.get_or_null(p_canvas);
ERR_FAIL_NULL(canvas);
diff --git a/servers/rendering/renderer_canvas_cull.h b/servers/rendering/renderer_canvas_cull.h
index 00f69766b1..8d40c2fbaa 100644
--- a/servers/rendering/renderer_canvas_cull.h
+++ b/servers/rendering/renderer_canvas_cull.h
@@ -187,8 +187,8 @@ public:
_FORCE_INLINE_ void _attach_canvas_item_for_draw(Item *ci, Item *p_canvas_clip, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, const Transform2D &p_transform, const Rect2 &p_clip_rect, Rect2 p_global_rect, const Color &modulate, int p_z, RendererCanvasCull::Item *p_material_owner, bool p_use_canvas_group, RendererCanvasRender::Item *r_canvas_group_from);
private:
- void _render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RendererCanvasRender::Light *p_lights, RendererCanvasRender::Light *p_directional_lights, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, uint32_t p_canvas_cull_mask, RenderingMethod::RenderInfo *r_render_info = nullptr);
- void _cull_canvas_item(Item *p_canvas_item, const Transform2D &p_parent_xform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool p_allow_y_sort, uint32_t p_canvas_cull_mask);
+ void _render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RendererCanvasRender::Light *p_lights, RendererCanvasRender::Light *p_directional_lights, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, uint32_t p_canvas_cull_mask, RenderingMethod::RenderInfo *r_render_info = nullptr);
+ void _cull_canvas_item(Item *p_canvas_item, const Transform2D &p_parent_xform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool p_allow_y_sort, uint32_t p_canvas_cull_mask, const Point2 &p_repeat_size, int p_repeat_times);
static constexpr int z_range = RS::CANVAS_ITEM_Z_MAX - RS::CANVAS_ITEM_Z_MIN + 1;
@@ -204,6 +204,7 @@ public:
void canvas_initialize(RID p_rid);
void canvas_set_item_mirroring(RID p_canvas, RID p_item, const Point2 &p_mirroring);
+ void canvas_set_item_repeat(RID p_item, const Point2 &p_repeat_size, int p_repeat_times);
void canvas_set_modulate(RID p_canvas, const Color &p_color);
void canvas_set_parent(RID p_canvas, RID p_parent, float p_scale);
void canvas_set_disable_scale(bool p_disable);
diff --git a/servers/rendering/renderer_canvas_render.h b/servers/rendering/renderer_canvas_render.h
index 3b78df5fab..fd31a40a3e 100644
--- a/servers/rendering/renderer_canvas_render.h
+++ b/servers/rendering/renderer_canvas_render.h
@@ -350,6 +350,9 @@ public:
ViewportRender *vp_render = nullptr;
bool distance_field;
bool light_masked;
+ bool repeat_source;
+ Point2 repeat_size;
+ int repeat_times = 1;
Rect2 global_rect_cache;
@@ -468,6 +471,7 @@ public:
z_final = 0;
texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT;
texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT;
+ repeat_source = false;
}
virtual ~Item() {
clear();
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index 00604d0d4b..673afc53e5 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -406,7 +406,7 @@ _FORCE_INLINE_ static uint32_t _indices_to_primitives(RS::PrimitiveType p_primit
return (p_indices - subtractor[p_primitive]) / divisor[p_primitive];
}
-void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RD::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used, RenderingMethod::RenderInfo *r_render_info) {
+void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RD::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used, const Point2 &p_offset, RenderingMethod::RenderInfo *r_render_info) {
//create an empty push constant
RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton();
@@ -425,6 +425,11 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
PushConstant push_constant;
Transform2D base_transform = p_canvas_transform_inverse * p_item->final_transform;
+
+ if (p_offset.x || p_offset.y) {
+ base_transform *= Transform2D(0, p_offset / p_item->xform.get_scale());
+ }
+
Transform2D draw_transform;
_update_transform_2d_to_mat2x3(base_transform, push_constant.world);
@@ -1240,7 +1245,23 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co
}
}
- _render_item(draw_list, p_to_render_target, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants, r_sdf_used, r_render_info);
+ if (!ci->repeat_size.x && !ci->repeat_size.y) {
+ _render_item(draw_list, p_to_render_target, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants, r_sdf_used, Point2(), r_render_info);
+ } else {
+ Point2 start_pos = ci->repeat_size * -(ci->repeat_times / 2);
+ Point2 end_pos = ci->repeat_size * ci->repeat_times + ci->repeat_size + start_pos;
+ Point2 pos = start_pos;
+
+ do {
+ do {
+ _render_item(draw_list, p_to_render_target, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants, r_sdf_used, pos, r_render_info);
+ pos.y += ci->repeat_size.y;
+ } while (pos.y < end_pos.y);
+
+ pos.x += ci->repeat_size.x;
+ pos.y = start_pos.y;
+ } while (pos.x < end_pos.x);
+ }
prev_material = material;
}
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
index 6da3774fc2..c7c5d34314 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
@@ -424,7 +424,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
double debug_redraw_time = 1.0;
inline void _bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size, bool p_texture_is_data = false); //recursive, so regular inline used instead.
- void _render_item(RenderingDevice::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used, RenderingMethod::RenderInfo *r_render_info = nullptr);
+ void _render_item(RenderingDevice::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used, const Point2 &p_offset, RenderingMethod::RenderInfo *r_render_info = nullptr);
void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false, RenderingMethod::RenderInfo *r_render_info = nullptr);
_FORCE_INLINE_ void _update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4);
diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
index a854e78f53..28b2c9b40c 100644
--- a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
@@ -620,8 +620,9 @@ AABB ParticlesStorage::particles_get_current_aabb(RID p_particles) {
total_amount *= particles->trail_bind_poses.size();
}
+ uint32_t particle_data_size = sizeof(ParticleData) + sizeof(float) * 4 * particles->userdata_count;
Vector<uint8_t> buffer = RD::get_singleton()->buffer_get_data(particles->particle_buffer);
- ERR_FAIL_COND_V(buffer.size() != (int)(total_amount * sizeof(ParticleData)), AABB());
+ ERR_FAIL_COND_V(buffer.size() != (int)(total_amount * particle_data_size), AABB());
Transform3D inv = particles->emission_transform.affine_inverse();
@@ -630,7 +631,6 @@ AABB ParticlesStorage::particles_get_current_aabb(RID p_particles) {
bool first = true;
const uint8_t *data_ptr = (const uint8_t *)buffer.ptr();
- uint32_t particle_data_size = sizeof(ParticleData) + sizeof(float) * particles->userdata_count;
for (int i = 0; i < total_amount; i++) {
const ParticleData &particle_data = *(const ParticleData *)&data_ptr[particle_data_size * i];
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index 17622ba311..99fd683e1d 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -852,6 +852,7 @@ public:
FUNCRIDSPLIT(canvas)
FUNC3(canvas_set_item_mirroring, RID, RID, const Point2 &)
+ FUNC3(canvas_set_item_repeat, RID, const Point2 &, int)
FUNC2(canvas_set_modulate, RID, const Color &)
FUNC3(canvas_set_parent, RID, RID, float)
FUNC1(canvas_set_disable_scale, bool)
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index e0bf4fde42..1ed0424839 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -3186,6 +3186,7 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("canvas_create"), &RenderingServer::canvas_create);
ClassDB::bind_method(D_METHOD("canvas_set_item_mirroring", "canvas", "item", "mirroring"), &RenderingServer::canvas_set_item_mirroring);
+ ClassDB::bind_method(D_METHOD("canvas_set_item_repeat", "item", "repeat_size", "repeat_times"), &RenderingServer::canvas_set_item_repeat);
ClassDB::bind_method(D_METHOD("canvas_set_modulate", "canvas", "color"), &RenderingServer::canvas_set_modulate);
ClassDB::bind_method(D_METHOD("canvas_set_disable_scale", "disable"), &RenderingServer::canvas_set_disable_scale);
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index 02a90dad3b..a67ae0a66c 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -1378,6 +1378,7 @@ public:
virtual RID canvas_create() = 0;
virtual void canvas_set_item_mirroring(RID p_canvas, RID p_item, const Point2 &p_mirroring) = 0;
+ virtual void canvas_set_item_repeat(RID p_item, const Point2 &p_repeat_size, int p_repeat_times) = 0;
virtual void canvas_set_modulate(RID p_canvas, const Color &p_color) = 0;
virtual void canvas_set_parent(RID p_canvas, RID p_parent, float p_scale) = 0;