summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--COPYRIGHT.txt2
-rw-r--r--SConstruct6
-rw-r--r--core/extension/extension_api_dump.cpp17
-rw-r--r--core/extension/gdnative_interface.cpp134
-rw-r--r--core/extension/gdnative_interface.h213
-rw-r--r--core/extension/native_extension.cpp48
-rw-r--r--core/extension/native_extension.h18
-rw-r--r--core/io/image.cpp13
-rw-r--r--core/io/image.h1
-rw-r--r--core/object/make_virtuals.py4
-rw-r--r--core/object/method_bind.h34
-rw-r--r--core/object/script_language_extension.h18
-rw-r--r--doc/classes/Animation.xml9
-rw-r--r--doc/classes/AnimationLibrary.xml7
-rw-r--r--doc/classes/AnimationNode.xml3
-rw-r--r--doc/classes/BaseMaterial3D.xml4
-rw-r--r--doc/classes/CanvasItem.xml13
-rw-r--r--doc/classes/CharFXTransform.xml3
-rw-r--r--doc/classes/Curve3D.xml2
-rw-r--r--doc/classes/GraphEdit.xml2
-rw-r--r--doc/classes/Node2D.xml10
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp40
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp5
-rw-r--r--drivers/gles3/shaders/SCsub1
-rw-r--r--drivers/gles3/shaders/canvas.glsl9
-rw-r--r--drivers/gles3/shaders/skeleton.glsl269
-rw-r--r--drivers/gles3/shaders/stdlib_inc.glsl3
-rw-r--r--drivers/gles3/storage/mesh_storage.cpp581
-rw-r--r--drivers/gles3/storage/mesh_storage.h38
-rw-r--r--drivers/gles3/storage/texture_storage.cpp69
-rw-r--r--drivers/gles3/storage/texture_storage.h3
-rw-r--r--drivers/gles3/storage/utilities.cpp2
-rw-r--r--editor/editor_node.cpp4
-rw-r--r--editor/export/editor_export_plugin.cpp24
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp7
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp293
-rw-r--r--editor/plugins/node_3d_editor_plugin.h37
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp2
-rw-r--r--editor/project_manager.cpp3
-rw-r--r--editor/script_create_dialog.cpp36
-rw-r--r--editor/script_create_dialog.h1
-rw-r--r--main/main.cpp1
-rw-r--r--modules/etcpak/image_compress_etcpak.cpp3
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml2
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp25
-rw-r--r--modules/gdscript/gdscript_compiler.cpp9
-rw-r--r--modules/gdscript/gdscript_parser.cpp8
-rw-r--r--modules/gdscript/gdscript_parser.h2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_less.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_more.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_default_values.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_return_type.out2
-rw-r--r--modules/mono/csharp_script.cpp14
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props3
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs9
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs6
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs374
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs8
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs14
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs16
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs6
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs8
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs16
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs28
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs10
-rw-r--r--modules/mono/editor/bindings_generator.cpp61
-rw-r--r--modules/mono/editor/bindings_generator.h9
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs14
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs59
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs275
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs10
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs513
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs52
-rw-r--r--modules/openxr/config.py2
-rw-r--r--modules/openxr/extensions/openxr_android_extension.cpp6
-rw-r--r--modules/openxr/extensions/openxr_android_extension.h1
-rw-r--r--modules/openxr/extensions/openxr_opengl_extension.cpp2
-rw-r--r--modules/openxr/openxr_api.cpp4
-rw-r--r--modules/webxr/doc_classes/WebXRInterface.xml105
-rw-r--r--modules/webxr/godot_webxr.h41
-rw-r--r--modules/webxr/native/library_godot_webxr.js460
-rw-r--r--modules/webxr/native/webxr.externs.js682
-rw-r--r--modules/webxr/webxr_interface.cpp23
-rw-r--r--modules/webxr/webxr_interface.h14
-rw-r--r--modules/webxr/webxr_interface_js.cpp487
-rw-r--r--modules/webxr/webxr_interface_js.h156
-rw-r--r--platform/android/detect.py32
-rw-r--r--platform/android/display_server_android.cpp33
-rw-r--r--platform/android/export/export_plugin.cpp2
-rw-r--r--platform/android/java/app/AndroidManifest.xml2
-rw-r--r--platform/android/java/app/config.gradle9
-rw-r--r--platform/android/java/editor/src/main/AndroidManifest.xml2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java14
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java11
-rw-r--r--platform/android/java/nativeSrcsConfigs/CMakeLists.txt2
-rw-r--r--platform/android/os_android.cpp1
-rw-r--r--platform/macos/display_server_macos.mm12
-rw-r--r--platform/macos/export/export_plugin.cpp2
-rw-r--r--platform/web/SCsub9
-rw-r--r--platform/web/js/libs/library_godot_webgl2.externs.js36
-rw-r--r--platform/web/js/libs/library_godot_webgl2.js5
-rw-r--r--scene/2d/node_2d.cpp46
-rw-r--r--scene/2d/node_2d.h12
-rw-r--r--scene/animation/animation_blend_tree.cpp54
-rw-r--r--scene/animation/animation_node_state_machine.cpp8
-rw-r--r--scene/animation/animation_player.cpp120
-rw-r--r--scene/animation/animation_player.h6
-rw-r--r--scene/animation/animation_tree.cpp23
-rw-r--r--scene/animation/animation_tree.h4
-rw-r--r--scene/gui/control.cpp12
-rw-r--r--scene/gui/graph_edit.cpp89
-rw-r--r--scene/gui/graph_edit.h4
-rw-r--r--scene/gui/range.cpp21
-rw-r--r--scene/gui/range.h1
-rw-r--r--scene/gui/rich_text_effect.cpp4
-rw-r--r--scene/gui/rich_text_effect.h4
-rw-r--r--scene/gui/rich_text_label.cpp10
-rw-r--r--scene/gui/tree.cpp64
-rw-r--r--scene/gui/tree.h4
-rw-r--r--scene/main/canvas_item.cpp48
-rw-r--r--scene/main/canvas_item.h15
-rw-r--r--scene/main/viewport.cpp22
-rw-r--r--scene/main/viewport.h1
-rw-r--r--scene/resources/animation.cpp486
-rw-r--r--scene/resources/animation.h12
-rw-r--r--scene/resources/animation_library.cpp13
-rw-r--r--scene/resources/animation_library.h2
-rw-r--r--scene/resources/curve.cpp22
-rw-r--r--scene/resources/material.cpp15
-rw-r--r--scene/resources/mesh.cpp100
-rw-r--r--scene/resources/style_box.cpp12
-rw-r--r--scene/resources/texture.cpp126
-rw-r--r--servers/audio/audio_effect.cpp10
-rw-r--r--servers/audio/audio_stream.cpp27
-rw-r--r--servers/movie_writer/movie_writer.cpp43
-rw-r--r--servers/rendering/renderer_canvas_cull.cpp10
-rw-r--r--servers/rendering/renderer_rd/storage_rd/mesh_storage.h1
-rw-r--r--servers/rendering/renderer_scene_cull.cpp2
-rw-r--r--thirdparty/README.md4
-rw-r--r--thirdparty/miniupnpc/LICENSE39
-rw-r--r--thirdparty/miniupnpc/include/miniupnpc.h8
-rw-r--r--thirdparty/miniupnpc/src/miniupnpcstrings.h2
-rw-r--r--thirdparty/miniupnpc/src/minixml.c2
-rw-r--r--thirdparty/thorvg/inc/config.h2
-rw-r--r--thirdparty/thorvg/inc/thorvg.h18
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp6
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h2
-rw-r--r--thirdparty/thorvg/src/lib/tvgLoadModule.h1
-rw-r--r--thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp6
-rw-r--r--thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp2
-rw-r--r--thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp6
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp20
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp164
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h3
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h26
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp148
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h2
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp6
-rw-r--r--thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp6
-rw-r--r--thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp6
-rwxr-xr-xthirdparty/thorvg/update-thorvg.sh5
164 files changed, 4828 insertions, 2821 deletions
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index 2dc58158ef..02cd733c92 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -293,7 +293,7 @@ License: CC0-1.0
Files: ./thirdparty/miniupnpc/
Comment: MiniUPnP Project
-Copyright: 2005-2021, Thomas Bernard
+Copyright: 2005-2022, Thomas Bernard
License: BSD-3-clause
Files: ./thirdparty/minizip/
diff --git a/SConstruct b/SConstruct
index 6847a7c937..03043ee110 100644
--- a/SConstruct
+++ b/SConstruct
@@ -544,6 +544,12 @@ if selected_platform in platform_list:
env.Append(CCFLAGS=["-g3"])
else:
env.Append(CCFLAGS=["-g2"])
+ else:
+ if methods.using_clang(env) and not methods.is_vanilla_clang(env):
+ # Apple Clang, its linker doesn't like -s.
+ env.Append(LINKFLAGS=["-Wl,-S", "-Wl,-x", "-Wl,-dead_strip"])
+ else:
+ env.Append(LINKFLAGS=["-s"])
if env["optimize"] == "speed":
env.Append(CCFLAGS=["-O3"])
diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp
index a2c507e350..196bb68b2b 100644
--- a/core/extension/extension_api_dump.cpp
+++ b/core/extension/extension_api_dump.cpp
@@ -266,7 +266,7 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() {
{ Variant::VECTOR2I, "x", 0, 0, 0, 0 },
{ Variant::VECTOR2I, "y", sizeof(int32_t), sizeof(int32_t), sizeof(int32_t), sizeof(int32_t) },
{ Variant::RECT2, "position", 0, 0, 0, 0 },
- { Variant::RECT2, "size", 2 * sizeof(Vector2), 2 * sizeof(float), 2 * sizeof(double), 2 * sizeof(double) },
+ { Variant::RECT2, "size", 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(double), 2 * sizeof(double) },
{ Variant::RECT2I, "position", 0, 0, 0, 0 },
{ Variant::RECT2I, "size", 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t) },
{ Variant::VECTOR3, "x", 0, 0, 0, 0 },
@@ -871,9 +871,18 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() {
Dictionary d2;
d2["type"] = get_property_info_type_name(F);
d2["name"] = String(property_name);
- d2["setter"] = ClassDB::get_property_setter(class_name, F.name);
- d2["getter"] = ClassDB::get_property_getter(class_name, F.name);
- d2["index"] = ClassDB::get_property_index(class_name, F.name);
+ StringName setter = ClassDB::get_property_setter(class_name, F.name);
+ if (!(setter == "")) {
+ d2["setter"] = setter;
+ }
+ StringName getter = ClassDB::get_property_getter(class_name, F.name);
+ if (!(getter == "")) {
+ d2["getter"] = getter;
+ }
+ int index = ClassDB::get_property_index(class_name, F.name);
+ if (index != -1) {
+ d2["index"] = index;
+ }
properties.push_back(d2);
}
diff --git a/core/extension/gdnative_interface.cpp b/core/extension/gdnative_interface.cpp
index 864f2fa86b..78dc6dac0b 100644
--- a/core/extension/gdnative_interface.cpp
+++ b/core/extension/gdnative_interface.cpp
@@ -61,15 +61,15 @@ static void gdnative_print_script_error(const char *p_description, const char *p
_err_print_error(p_function, p_file, p_line, p_description, false, ERR_HANDLER_SCRIPT);
}
-uint64_t gdnative_get_native_struct_size(const GDNativeStringNamePtr p_name) {
+uint64_t gdnative_get_native_struct_size(GDNativeConstStringNamePtr p_name) {
const StringName name = *reinterpret_cast<const StringName *>(p_name);
return ClassDB::get_native_struct_size(name);
}
// Variant functions
-static void gdnative_variant_new_copy(GDNativeVariantPtr r_dest, const GDNativeVariantPtr p_src) {
- memnew_placement(reinterpret_cast<Variant *>(r_dest), Variant(*reinterpret_cast<Variant *>(p_src)));
+static void gdnative_variant_new_copy(GDNativeVariantPtr r_dest, GDNativeConstVariantPtr p_src) {
+ memnew_placement(reinterpret_cast<Variant *>(r_dest), Variant(*reinterpret_cast<const Variant *>(p_src)));
}
static void gdnative_variant_new_nil(GDNativeVariantPtr r_dest) {
memnew_placement(reinterpret_cast<Variant *>(r_dest), Variant);
@@ -80,7 +80,7 @@ static void gdnative_variant_destroy(GDNativeVariantPtr p_self) {
// variant type
-static void gdnative_variant_call(GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_method, const GDNativeVariantPtr *p_args, const GDNativeInt p_argcount, GDNativeVariantPtr r_return, GDNativeCallError *r_error) {
+static void gdnative_variant_call(GDNativeVariantPtr p_self, GDNativeConstStringNamePtr p_method, GDNativeConstVariantPtr *p_args, GDNativeInt p_argcount, GDNativeVariantPtr r_return, GDNativeCallError *r_error) {
Variant *self = (Variant *)p_self;
const StringName method = *reinterpret_cast<const StringName *>(p_method);
const Variant **args = (const Variant **)p_args;
@@ -96,7 +96,7 @@ static void gdnative_variant_call(GDNativeVariantPtr p_self, const GDNativeStrin
}
}
-static void gdnative_variant_call_static(GDNativeVariantType p_type, const GDNativeStringNamePtr p_method, const GDNativeVariantPtr *p_args, const GDNativeInt p_argcount, GDNativeVariantPtr r_return, GDNativeCallError *r_error) {
+static void gdnative_variant_call_static(GDNativeVariantType p_type, GDNativeConstStringNamePtr p_method, GDNativeConstVariantPtr *p_args, GDNativeInt p_argcount, GDNativeVariantPtr r_return, GDNativeCallError *r_error) {
Variant::Type type = (Variant::Type)p_type;
const StringName method = *reinterpret_cast<const StringName *>(p_method);
const Variant **args = (const Variant **)p_args;
@@ -112,7 +112,7 @@ static void gdnative_variant_call_static(GDNativeVariantType p_type, const GDNat
}
}
-static void gdnative_variant_evaluate(GDNativeVariantOperator p_op, const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, GDNativeVariantPtr r_return, GDNativeBool *r_valid) {
+static void gdnative_variant_evaluate(GDNativeVariantOperator p_op, GDNativeConstVariantPtr p_a, GDNativeConstVariantPtr p_b, GDNativeVariantPtr r_return, GDNativeBool *r_valid) {
Variant::Operator op = (Variant::Operator)p_op;
const Variant *a = (const Variant *)p_a;
const Variant *b = (const Variant *)p_b;
@@ -122,7 +122,7 @@ static void gdnative_variant_evaluate(GDNativeVariantOperator p_op, const GDNati
*r_valid = valid;
}
-static void gdnative_variant_set(GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid) {
+static void gdnative_variant_set(GDNativeVariantPtr p_self, GDNativeConstVariantPtr p_key, GDNativeConstVariantPtr p_value, GDNativeBool *r_valid) {
Variant *self = (Variant *)p_self;
const Variant *key = (const Variant *)p_key;
const Variant *value = (const Variant *)p_value;
@@ -132,7 +132,7 @@ static void gdnative_variant_set(GDNativeVariantPtr p_self, const GDNativeVarian
*r_valid = valid;
}
-static void gdnative_variant_set_named(GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid) {
+static void gdnative_variant_set_named(GDNativeVariantPtr p_self, GDNativeConstStringNamePtr p_key, GDNativeConstVariantPtr p_value, GDNativeBool *r_valid) {
Variant *self = (Variant *)p_self;
const StringName *key = (const StringName *)p_key;
const Variant *value = (const Variant *)p_value;
@@ -142,7 +142,7 @@ static void gdnative_variant_set_named(GDNativeVariantPtr p_self, const GDNative
*r_valid = valid;
}
-static void gdnative_variant_set_keyed(GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid) {
+static void gdnative_variant_set_keyed(GDNativeVariantPtr p_self, GDNativeConstVariantPtr p_key, GDNativeConstVariantPtr p_value, GDNativeBool *r_valid) {
Variant *self = (Variant *)p_self;
const Variant *key = (const Variant *)p_key;
const Variant *value = (const Variant *)p_value;
@@ -152,7 +152,7 @@ static void gdnative_variant_set_keyed(GDNativeVariantPtr p_self, const GDNative
*r_valid = valid;
}
-static void gdnative_variant_set_indexed(GDNativeVariantPtr p_self, GDNativeInt p_index, const GDNativeVariantPtr p_value, GDNativeBool *r_valid, GDNativeBool *r_oob) {
+static void gdnative_variant_set_indexed(GDNativeVariantPtr p_self, GDNativeInt p_index, GDNativeConstVariantPtr p_value, GDNativeBool *r_valid, GDNativeBool *r_oob) {
Variant *self = (Variant *)p_self;
const Variant *value = (const Variant *)p_value;
@@ -163,7 +163,7 @@ static void gdnative_variant_set_indexed(GDNativeVariantPtr p_self, GDNativeInt
*r_oob = oob;
}
-static void gdnative_variant_get(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid) {
+static void gdnative_variant_get(GDNativeConstVariantPtr p_self, GDNativeConstVariantPtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid) {
const Variant *self = (const Variant *)p_self;
const Variant *key = (const Variant *)p_key;
@@ -172,7 +172,7 @@ static void gdnative_variant_get(const GDNativeVariantPtr p_self, const GDNative
*r_valid = valid;
}
-static void gdnative_variant_get_named(const GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid) {
+static void gdnative_variant_get_named(GDNativeConstVariantPtr p_self, GDNativeConstStringNamePtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid) {
const Variant *self = (const Variant *)p_self;
const StringName *key = (const StringName *)p_key;
@@ -181,7 +181,7 @@ static void gdnative_variant_get_named(const GDNativeVariantPtr p_self, const GD
*r_valid = valid;
}
-static void gdnative_variant_get_keyed(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid) {
+static void gdnative_variant_get_keyed(GDNativeConstVariantPtr p_self, GDNativeConstVariantPtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid) {
const Variant *self = (const Variant *)p_self;
const Variant *key = (const Variant *)p_key;
@@ -190,7 +190,7 @@ static void gdnative_variant_get_keyed(const GDNativeVariantPtr p_self, const GD
*r_valid = valid;
}
-static void gdnative_variant_get_indexed(const GDNativeVariantPtr p_self, GDNativeInt p_index, GDNativeVariantPtr r_ret, GDNativeBool *r_valid, GDNativeBool *r_oob) {
+static void gdnative_variant_get_indexed(GDNativeConstVariantPtr p_self, GDNativeInt p_index, GDNativeVariantPtr r_ret, GDNativeBool *r_valid, GDNativeBool *r_oob) {
const Variant *self = (const Variant *)p_self;
bool valid;
@@ -201,7 +201,7 @@ static void gdnative_variant_get_indexed(const GDNativeVariantPtr p_self, GDNati
}
/// Iteration.
-static GDNativeBool gdnative_variant_iter_init(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid) {
+static GDNativeBool gdnative_variant_iter_init(GDNativeConstVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid) {
const Variant *self = (const Variant *)p_self;
Variant *iter = (Variant *)r_iter;
@@ -211,7 +211,7 @@ static GDNativeBool gdnative_variant_iter_init(const GDNativeVariantPtr p_self,
return ret;
}
-static GDNativeBool gdnative_variant_iter_next(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid) {
+static GDNativeBool gdnative_variant_iter_next(GDNativeConstVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid) {
const Variant *self = (const Variant *)p_self;
Variant *iter = (Variant *)r_iter;
@@ -221,7 +221,7 @@ static GDNativeBool gdnative_variant_iter_next(const GDNativeVariantPtr p_self,
return ret;
}
-static void gdnative_variant_iter_get(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeVariantPtr r_ret, GDNativeBool *r_valid) {
+static void gdnative_variant_iter_get(GDNativeConstVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeVariantPtr r_ret, GDNativeBool *r_valid) {
const Variant *self = (const Variant *)p_self;
Variant *iter = (Variant *)r_iter;
@@ -231,53 +231,53 @@ static void gdnative_variant_iter_get(const GDNativeVariantPtr p_self, GDNativeV
}
/// Variant functions.
-static GDNativeInt gdnative_variant_hash(const GDNativeVariantPtr p_self) {
+static GDNativeInt gdnative_variant_hash(GDNativeConstVariantPtr p_self) {
const Variant *self = (const Variant *)p_self;
return self->hash();
}
-static GDNativeInt gdnative_variant_recursive_hash(const GDNativeVariantPtr p_self, GDNativeInt p_recursion_count) {
+static GDNativeInt gdnative_variant_recursive_hash(GDNativeConstVariantPtr p_self, GDNativeInt p_recursion_count) {
const Variant *self = (const Variant *)p_self;
return self->recursive_hash(p_recursion_count);
}
-static GDNativeBool gdnative_variant_hash_compare(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_other) {
+static GDNativeBool gdnative_variant_hash_compare(GDNativeConstVariantPtr p_self, GDNativeConstVariantPtr p_other) {
const Variant *self = (const Variant *)p_self;
const Variant *other = (const Variant *)p_other;
return self->hash_compare(*other);
}
-static GDNativeBool gdnative_variant_booleanize(const GDNativeVariantPtr p_self) {
+static GDNativeBool gdnative_variant_booleanize(GDNativeConstVariantPtr p_self) {
const Variant *self = (const Variant *)p_self;
return self->booleanize();
}
-static void gdnative_variant_duplicate(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_ret, GDNativeBool p_deep) {
+static void gdnative_variant_duplicate(GDNativeConstVariantPtr p_self, GDNativeVariantPtr r_ret, GDNativeBool p_deep) {
const Variant *self = (const Variant *)p_self;
memnew_placement(r_ret, Variant(self->duplicate(p_deep)));
}
-static void gdnative_variant_stringify(const GDNativeVariantPtr p_self, GDNativeStringPtr r_ret) {
+static void gdnative_variant_stringify(GDNativeConstVariantPtr p_self, GDNativeStringPtr r_ret) {
const Variant *self = (const Variant *)p_self;
memnew_placement(r_ret, String(*self));
}
-static GDNativeVariantType gdnative_variant_get_type(const GDNativeVariantPtr p_self) {
+static GDNativeVariantType gdnative_variant_get_type(GDNativeConstVariantPtr p_self) {
const Variant *self = (const Variant *)p_self;
return (GDNativeVariantType)self->get_type();
}
-static GDNativeBool gdnative_variant_has_method(const GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_method) {
+static GDNativeBool gdnative_variant_has_method(GDNativeConstVariantPtr p_self, GDNativeConstStringNamePtr p_method) {
const Variant *self = (const Variant *)p_self;
const StringName *method = (const StringName *)p_method;
return self->has_method(*method);
}
-static GDNativeBool gdnative_variant_has_member(GDNativeVariantType p_type, const GDNativeStringNamePtr p_member) {
+static GDNativeBool gdnative_variant_has_member(GDNativeVariantType p_type, GDNativeConstStringNamePtr p_member) {
return Variant::has_member((Variant::Type)p_type, *((const StringName *)p_member));
}
-static GDNativeBool gdnative_variant_has_key(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeBool *r_valid) {
+static GDNativeBool gdnative_variant_has_key(GDNativeConstVariantPtr p_self, GDNativeConstVariantPtr p_key, GDNativeBool *r_valid) {
const Variant *self = (const Variant *)p_self;
const Variant *key = (const Variant *)p_key;
bool valid;
@@ -470,7 +470,7 @@ static GDNativeTypeFromVariantConstructorFunc gdnative_get_type_from_variant_con
static GDNativePtrOperatorEvaluator gdnative_variant_get_ptr_operator_evaluator(GDNativeVariantOperator p_operator, GDNativeVariantType p_type_a, GDNativeVariantType p_type_b) {
return (GDNativePtrOperatorEvaluator)Variant::get_ptr_operator_evaluator(Variant::Operator(p_operator), Variant::Type(p_type_a), Variant::Type(p_type_b));
}
-static GDNativePtrBuiltInMethod gdnative_variant_get_ptr_builtin_method(GDNativeVariantType p_type, const GDNativeStringNamePtr p_method, GDNativeInt p_hash) {
+static GDNativePtrBuiltInMethod gdnative_variant_get_ptr_builtin_method(GDNativeVariantType p_type, GDNativeConstStringNamePtr p_method, GDNativeInt p_hash) {
const StringName method = *reinterpret_cast<const StringName *>(p_method);
uint32_t hash = Variant::get_builtin_method_hash(Variant::Type(p_type), method);
if (hash != p_hash) {
@@ -486,7 +486,7 @@ static GDNativePtrConstructor gdnative_variant_get_ptr_constructor(GDNativeVaria
static GDNativePtrDestructor gdnative_variant_get_ptr_destructor(GDNativeVariantType p_type) {
return (GDNativePtrDestructor)Variant::get_ptr_destructor(Variant::Type(p_type));
}
-static void gdnative_variant_construct(GDNativeVariantType p_type, GDNativeVariantPtr p_base, const GDNativeVariantPtr *p_args, int32_t p_argument_count, GDNativeCallError *r_error) {
+static void gdnative_variant_construct(GDNativeVariantType p_type, GDNativeVariantPtr p_base, GDNativeConstVariantPtr *p_args, int32_t p_argument_count, GDNativeCallError *r_error) {
memnew_placement(p_base, Variant);
Callable::CallError error;
@@ -498,11 +498,11 @@ static void gdnative_variant_construct(GDNativeVariantType p_type, GDNativeVaria
r_error->expected = error.expected;
}
}
-static GDNativePtrSetter gdnative_variant_get_ptr_setter(GDNativeVariantType p_type, const GDNativeStringNamePtr p_member) {
+static GDNativePtrSetter gdnative_variant_get_ptr_setter(GDNativeVariantType p_type, GDNativeConstStringNamePtr p_member) {
const StringName member = *reinterpret_cast<const StringName *>(p_member);
return (GDNativePtrSetter)Variant::get_member_ptr_setter(Variant::Type(p_type), member);
}
-static GDNativePtrGetter gdnative_variant_get_ptr_getter(GDNativeVariantType p_type, const GDNativeStringNamePtr p_member) {
+static GDNativePtrGetter gdnative_variant_get_ptr_getter(GDNativeVariantType p_type, GDNativeConstStringNamePtr p_member) {
const StringName member = *reinterpret_cast<const StringName *>(p_member);
return (GDNativePtrGetter)Variant::get_member_ptr_getter(Variant::Type(p_type), member);
}
@@ -521,11 +521,11 @@ static GDNativePtrKeyedGetter gdnative_variant_get_ptr_keyed_getter(GDNativeVari
static GDNativePtrKeyedChecker gdnative_variant_get_ptr_keyed_checker(GDNativeVariantType p_type) {
return (GDNativePtrKeyedChecker)Variant::get_member_ptr_keyed_checker(Variant::Type(p_type));
}
-static void gdnative_variant_get_constant_value(GDNativeVariantType p_type, const GDNativeStringNamePtr p_constant, GDNativeVariantPtr r_ret) {
+static void gdnative_variant_get_constant_value(GDNativeVariantType p_type, GDNativeConstStringNamePtr p_constant, GDNativeVariantPtr r_ret) {
StringName constant = *reinterpret_cast<const StringName *>(p_constant);
memnew_placement(r_ret, Variant(Variant::get_constant_value(Variant::Type(p_type), constant)));
}
-static GDNativePtrUtilityFunction gdnative_variant_get_ptr_utility_function(const GDNativeStringNamePtr p_function, GDNativeInt p_hash) {
+static GDNativePtrUtilityFunction gdnative_variant_get_ptr_utility_function(GDNativeConstStringNamePtr p_function, GDNativeInt p_hash) {
StringName function = *reinterpret_cast<const StringName *>(p_function);
uint32_t hash = Variant::get_utility_function_hash(function);
if (hash != p_hash) {
@@ -574,31 +574,31 @@ static void gdnative_string_new_with_wide_chars(GDNativeStringPtr r_dest, const
}
}
-static void gdnative_string_new_with_latin1_chars_and_len(GDNativeStringPtr r_dest, const char *p_contents, const GDNativeInt p_size) {
+static void gdnative_string_new_with_latin1_chars_and_len(GDNativeStringPtr r_dest, const char *p_contents, GDNativeInt p_size) {
String *dest = (String *)r_dest;
memnew_placement(dest, String);
*dest = String(p_contents, p_size);
}
-static void gdnative_string_new_with_utf8_chars_and_len(GDNativeStringPtr r_dest, const char *p_contents, const GDNativeInt p_size) {
+static void gdnative_string_new_with_utf8_chars_and_len(GDNativeStringPtr r_dest, const char *p_contents, GDNativeInt p_size) {
String *dest = (String *)r_dest;
memnew_placement(dest, String);
dest->parse_utf8(p_contents, p_size);
}
-static void gdnative_string_new_with_utf16_chars_and_len(GDNativeStringPtr r_dest, const char16_t *p_contents, const GDNativeInt p_size) {
+static void gdnative_string_new_with_utf16_chars_and_len(GDNativeStringPtr r_dest, const char16_t *p_contents, GDNativeInt p_size) {
String *dest = (String *)r_dest;
memnew_placement(dest, String);
dest->parse_utf16(p_contents, p_size);
}
-static void gdnative_string_new_with_utf32_chars_and_len(GDNativeStringPtr r_dest, const char32_t *p_contents, const GDNativeInt p_size) {
+static void gdnative_string_new_with_utf32_chars_and_len(GDNativeStringPtr r_dest, const char32_t *p_contents, GDNativeInt p_size) {
String *dest = (String *)r_dest;
memnew_placement(dest, String);
*dest = String((const char32_t *)p_contents, p_size);
}
-static void gdnative_string_new_with_wide_chars_and_len(GDNativeStringPtr r_dest, const wchar_t *p_contents, const GDNativeInt p_size) {
+static void gdnative_string_new_with_wide_chars_and_len(GDNativeStringPtr r_dest, const wchar_t *p_contents, GDNativeInt p_size) {
String *dest = (String *)r_dest;
if constexpr (sizeof(wchar_t) == 2) {
// wchar_t is 16 bit, parse.
@@ -611,7 +611,7 @@ static void gdnative_string_new_with_wide_chars_and_len(GDNativeStringPtr r_dest
}
}
-static GDNativeInt gdnative_string_to_latin1_chars(const GDNativeStringPtr p_self, char *r_text, GDNativeInt p_max_write_length) {
+static GDNativeInt gdnative_string_to_latin1_chars(GDNativeConstStringPtr p_self, char *r_text, GDNativeInt p_max_write_length) {
String *self = (String *)p_self;
CharString cs = self->ascii(true);
GDNativeInt len = cs.length();
@@ -623,7 +623,7 @@ static GDNativeInt gdnative_string_to_latin1_chars(const GDNativeStringPtr p_sel
}
return len;
}
-static GDNativeInt gdnative_string_to_utf8_chars(const GDNativeStringPtr p_self, char *r_text, GDNativeInt p_max_write_length) {
+static GDNativeInt gdnative_string_to_utf8_chars(GDNativeConstStringPtr p_self, char *r_text, GDNativeInt p_max_write_length) {
String *self = (String *)p_self;
CharString cs = self->utf8();
GDNativeInt len = cs.length();
@@ -635,7 +635,7 @@ static GDNativeInt gdnative_string_to_utf8_chars(const GDNativeStringPtr p_self,
}
return len;
}
-static GDNativeInt gdnative_string_to_utf16_chars(const GDNativeStringPtr p_self, char16_t *r_text, GDNativeInt p_max_write_length) {
+static GDNativeInt gdnative_string_to_utf16_chars(GDNativeConstStringPtr p_self, char16_t *r_text, GDNativeInt p_max_write_length) {
String *self = (String *)p_self;
Char16String cs = self->utf16();
GDNativeInt len = cs.length();
@@ -647,7 +647,7 @@ static GDNativeInt gdnative_string_to_utf16_chars(const GDNativeStringPtr p_self
}
return len;
}
-static GDNativeInt gdnative_string_to_utf32_chars(const GDNativeStringPtr p_self, char32_t *r_text, GDNativeInt p_max_write_length) {
+static GDNativeInt gdnative_string_to_utf32_chars(GDNativeConstStringPtr p_self, char32_t *r_text, GDNativeInt p_max_write_length) {
String *self = (String *)p_self;
GDNativeInt len = self->length();
if (r_text) {
@@ -658,7 +658,7 @@ static GDNativeInt gdnative_string_to_utf32_chars(const GDNativeStringPtr p_self
}
return len;
}
-static GDNativeInt gdnative_string_to_wide_chars(const GDNativeStringPtr p_self, wchar_t *r_text, GDNativeInt p_max_write_length) {
+static GDNativeInt gdnative_string_to_wide_chars(GDNativeConstStringPtr p_self, wchar_t *r_text, GDNativeInt p_max_write_length) {
if constexpr (sizeof(wchar_t) == 4) {
return gdnative_string_to_utf32_chars(p_self, (char32_t *)r_text, p_max_write_length);
} else {
@@ -672,7 +672,7 @@ static char32_t *gdnative_string_operator_index(GDNativeStringPtr p_self, GDNati
return &self->ptrw()[p_index];
}
-static const char32_t *gdnative_string_operator_index_const(const GDNativeStringPtr p_self, GDNativeInt p_index) {
+static const char32_t *gdnative_string_operator_index_const(GDNativeConstStringPtr p_self, GDNativeInt p_index) {
const String *self = (const String *)p_self;
ERR_FAIL_INDEX_V(p_index, self->length() + 1, nullptr);
return &self->ptr()[p_index];
@@ -686,7 +686,7 @@ static uint8_t *gdnative_packed_byte_array_operator_index(GDNativeTypePtr p_self
return &self->ptrw()[p_index];
}
-static const uint8_t *gdnative_packed_byte_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) {
+static const uint8_t *gdnative_packed_byte_array_operator_index_const(GDNativeConstTypePtr p_self, GDNativeInt p_index) {
const PackedByteArray *self = (const PackedByteArray *)p_self;
ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
return &self->ptr()[p_index];
@@ -698,7 +698,7 @@ static GDNativeTypePtr gdnative_packed_color_array_operator_index(GDNativeTypePt
return (GDNativeTypePtr)&self->ptrw()[p_index];
}
-static GDNativeTypePtr gdnative_packed_color_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) {
+static GDNativeTypePtr gdnative_packed_color_array_operator_index_const(GDNativeConstTypePtr p_self, GDNativeInt p_index) {
const PackedColorArray *self = (const PackedColorArray *)p_self;
ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
return (GDNativeTypePtr)&self->ptr()[p_index];
@@ -710,7 +710,7 @@ static float *gdnative_packed_float32_array_operator_index(GDNativeTypePtr p_sel
return &self->ptrw()[p_index];
}
-static const float *gdnative_packed_float32_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) {
+static const float *gdnative_packed_float32_array_operator_index_const(GDNativeConstTypePtr p_self, GDNativeInt p_index) {
const PackedFloat32Array *self = (const PackedFloat32Array *)p_self;
ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
return &self->ptr()[p_index];
@@ -722,7 +722,7 @@ static double *gdnative_packed_float64_array_operator_index(GDNativeTypePtr p_se
return &self->ptrw()[p_index];
}
-static const double *gdnative_packed_float64_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) {
+static const double *gdnative_packed_float64_array_operator_index_const(GDNativeConstTypePtr p_self, GDNativeInt p_index) {
const PackedFloat64Array *self = (const PackedFloat64Array *)p_self;
ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
return &self->ptr()[p_index];
@@ -734,7 +734,7 @@ static int32_t *gdnative_packed_int32_array_operator_index(GDNativeTypePtr p_sel
return &self->ptrw()[p_index];
}
-static const int32_t *gdnative_packed_int32_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) {
+static const int32_t *gdnative_packed_int32_array_operator_index_const(GDNativeConstTypePtr p_self, GDNativeInt p_index) {
const PackedInt32Array *self = (const PackedInt32Array *)p_self;
ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
return &self->ptr()[p_index];
@@ -746,7 +746,7 @@ static int64_t *gdnative_packed_int64_array_operator_index(GDNativeTypePtr p_sel
return &self->ptrw()[p_index];
}
-static const int64_t *gdnative_packed_int64_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) {
+static const int64_t *gdnative_packed_int64_array_operator_index_const(GDNativeConstTypePtr p_self, GDNativeInt p_index) {
const PackedInt64Array *self = (const PackedInt64Array *)p_self;
ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
return &self->ptr()[p_index];
@@ -758,7 +758,7 @@ static GDNativeStringPtr gdnative_packed_string_array_operator_index(GDNativeTyp
return (GDNativeStringPtr)&self->ptrw()[p_index];
}
-static GDNativeStringPtr gdnative_packed_string_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) {
+static GDNativeStringPtr gdnative_packed_string_array_operator_index_const(GDNativeConstTypePtr p_self, GDNativeInt p_index) {
const PackedStringArray *self = (const PackedStringArray *)p_self;
ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
return (GDNativeStringPtr)&self->ptr()[p_index];
@@ -770,7 +770,7 @@ static GDNativeTypePtr gdnative_packed_vector2_array_operator_index(GDNativeType
return (GDNativeTypePtr)&self->ptrw()[p_index];
}
-static GDNativeTypePtr gdnative_packed_vector2_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) {
+static GDNativeTypePtr gdnative_packed_vector2_array_operator_index_const(GDNativeConstTypePtr p_self, GDNativeInt p_index) {
const PackedVector2Array *self = (const PackedVector2Array *)p_self;
ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
return (GDNativeTypePtr)&self->ptr()[p_index];
@@ -782,7 +782,7 @@ static GDNativeTypePtr gdnative_packed_vector3_array_operator_index(GDNativeType
return (GDNativeTypePtr)&self->ptrw()[p_index];
}
-static GDNativeTypePtr gdnative_packed_vector3_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) {
+static GDNativeTypePtr gdnative_packed_vector3_array_operator_index_const(GDNativeConstTypePtr p_self, GDNativeInt p_index) {
const PackedVector3Array *self = (const PackedVector3Array *)p_self;
ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
return (GDNativeTypePtr)&self->ptr()[p_index];
@@ -794,7 +794,7 @@ static GDNativeVariantPtr gdnative_array_operator_index(GDNativeTypePtr p_self,
return (GDNativeVariantPtr)&self->operator[](p_index);
}
-static GDNativeVariantPtr gdnative_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) {
+static GDNativeVariantPtr gdnative_array_operator_index_const(GDNativeConstTypePtr p_self, GDNativeInt p_index) {
const Array *self = (const Array *)p_self;
ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
return (GDNativeVariantPtr)&self->operator[](p_index);
@@ -802,20 +802,20 @@ static GDNativeVariantPtr gdnative_array_operator_index_const(const GDNativeType
/* Dictionary functions */
-static GDNativeVariantPtr gdnative_dictionary_operator_index(GDNativeTypePtr p_self, const GDNativeVariantPtr p_key) {
+static GDNativeVariantPtr gdnative_dictionary_operator_index(GDNativeTypePtr p_self, GDNativeConstVariantPtr p_key) {
Dictionary *self = (Dictionary *)p_self;
return (GDNativeVariantPtr)&self->operator[](*(const Variant *)p_key);
}
-static GDNativeVariantPtr gdnative_dictionary_operator_index_const(const GDNativeTypePtr p_self, const GDNativeVariantPtr p_key) {
+static GDNativeVariantPtr gdnative_dictionary_operator_index_const(GDNativeConstTypePtr p_self, GDNativeConstVariantPtr p_key) {
const Dictionary *self = (const Dictionary *)p_self;
return (GDNativeVariantPtr)&self->operator[](*(const Variant *)p_key);
}
/* OBJECT API */
-static void gdnative_object_method_bind_call(const GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeVariantPtr *p_args, GDNativeInt p_arg_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error) {
- MethodBind *mb = (MethodBind *)p_method_bind;
+static void gdnative_object_method_bind_call(GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, GDNativeConstVariantPtr *p_args, GDNativeInt p_arg_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error) {
+ const MethodBind *mb = reinterpret_cast<const MethodBind *>(p_method_bind);
Object *o = (Object *)p_instance;
const Variant **args = (const Variant **)p_args;
Callable::CallError error;
@@ -830,8 +830,8 @@ static void gdnative_object_method_bind_call(const GDNativeMethodBindPtr p_metho
}
}
-static void gdnative_object_method_bind_ptrcall(const GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr p_ret) {
- MethodBind *mb = (MethodBind *)p_method_bind;
+static void gdnative_object_method_bind_ptrcall(GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, GDNativeConstTypePtr *p_args, GDNativeTypePtr p_ret) {
+ const MethodBind *mb = reinterpret_cast<const MethodBind *>(p_method_bind);
Object *o = (Object *)p_instance;
mb->ptrcall(o, (const void **)p_args, p_ret);
}
@@ -840,7 +840,7 @@ static void gdnative_object_destroy(GDNativeObjectPtr p_o) {
memdelete((Object *)p_o);
}
-static GDNativeObjectPtr gdnative_global_get_singleton(const GDNativeStringNamePtr p_name) {
+static GDNativeObjectPtr gdnative_global_get_singleton(GDNativeConstStringNamePtr p_name) {
const StringName name = *reinterpret_cast<const StringName *>(p_name);
return (GDNativeObjectPtr)Engine::get_singleton()->get_singleton_object(name);
}
@@ -855,7 +855,7 @@ static void gdnative_object_set_instance_binding(GDNativeObjectPtr p_object, voi
o->set_instance_binding(p_token, p_binding, p_callbacks);
}
-static void gdnative_object_set_instance(GDNativeObjectPtr p_object, const GDNativeStringNamePtr p_classname, GDExtensionClassInstancePtr p_instance) {
+static void gdnative_object_set_instance(GDNativeObjectPtr p_object, GDNativeConstStringNamePtr p_classname, GDExtensionClassInstancePtr p_instance) {
const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
Object *o = (Object *)p_object;
ClassDB::set_object_extension_instance(o, classname, p_instance);
@@ -865,7 +865,7 @@ static GDNativeObjectPtr gdnative_object_get_instance_from_id(GDObjectInstanceID
return (GDNativeObjectPtr)ObjectDB::get_instance(ObjectID(p_instance_id));
}
-static GDNativeObjectPtr gdnative_object_cast_to(const GDNativeObjectPtr p_object, void *p_class_tag) {
+static GDNativeObjectPtr gdnative_object_cast_to(GDNativeConstObjectPtr p_object, void *p_class_tag) {
if (!p_object) {
return nullptr;
}
@@ -874,7 +874,7 @@ static GDNativeObjectPtr gdnative_object_cast_to(const GDNativeObjectPtr p_objec
return o->is_class_ptr(p_class_tag) ? (GDNativeObjectPtr)o : (GDNativeObjectPtr) nullptr;
}
-static GDObjectInstanceID gdnative_object_get_instance_id(const GDNativeObjectPtr p_object) {
+static GDObjectInstanceID gdnative_object_get_instance_id(GDNativeConstObjectPtr p_object) {
const Object *o = (const Object *)p_object;
return (GDObjectInstanceID)o->get_instance_id();
}
@@ -886,7 +886,7 @@ static GDNativeScriptInstancePtr gdnative_script_instance_create(const GDNativeE
return reinterpret_cast<GDNativeScriptInstancePtr>(script_instance_extension);
}
-static GDNativeMethodBindPtr gdnative_classdb_get_method_bind(const GDNativeStringNamePtr p_classname, const GDNativeStringNamePtr p_methodname, GDNativeInt p_hash) {
+static GDNativeMethodBindPtr gdnative_classdb_get_method_bind(GDNativeConstStringNamePtr p_classname, GDNativeConstStringNamePtr p_methodname, GDNativeInt p_hash) {
const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
const StringName methodname = *reinterpret_cast<const StringName *>(p_methodname);
MethodBind *mb = ClassDB::get_method(classname, methodname);
@@ -898,12 +898,12 @@ static GDNativeMethodBindPtr gdnative_classdb_get_method_bind(const GDNativeStri
return (GDNativeMethodBindPtr)mb;
}
-static GDNativeObjectPtr gdnative_classdb_construct_object(const GDNativeStringNamePtr p_classname) {
+static GDNativeObjectPtr gdnative_classdb_construct_object(GDNativeConstStringNamePtr p_classname) {
const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
return (GDNativeObjectPtr)ClassDB::instantiate(classname);
}
-static void *gdnative_classdb_get_class_tag(const GDNativeStringNamePtr p_classname) {
+static void *gdnative_classdb_get_class_tag(GDNativeConstStringNamePtr p_classname) {
const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
ClassDB::ClassInfo *class_info = ClassDB::classes.getptr(classname);
return class_info ? class_info->class_ptr : nullptr;
diff --git a/core/extension/gdnative_interface.h b/core/extension/gdnative_interface.h
index 50410c4857..486f5be344 100644
--- a/core/extension/gdnative_interface.h
+++ b/core/extension/gdnative_interface.h
@@ -141,12 +141,16 @@ typedef enum {
} GDNativeVariantOperator;
typedef void *GDNativeVariantPtr;
+typedef const void *GDNativeConstVariantPtr;
typedef void *GDNativeStringNamePtr;
+typedef const void *GDNativeConstStringNamePtr;
typedef void *GDNativeStringPtr;
+typedef const void *GDNativeConstStringPtr;
typedef void *GDNativeObjectPtr;
+typedef const void *GDNativeConstObjectPtr;
typedef void *GDNativeTypePtr;
-typedef void *GDNativeExtensionPtr;
-typedef void *GDNativeMethodBindPtr;
+typedef const void *GDNativeConstTypePtr;
+typedef const void *GDNativeMethodBindPtr;
typedef int64_t GDNativeInt;
typedef uint8_t GDNativeBool;
typedef uint64_t GDObjectInstanceID;
@@ -171,18 +175,18 @@ typedef struct {
typedef void (*GDNativeVariantFromTypeConstructorFunc)(GDNativeVariantPtr, GDNativeTypePtr);
typedef void (*GDNativeTypeFromVariantConstructorFunc)(GDNativeTypePtr, GDNativeVariantPtr);
-typedef void (*GDNativePtrOperatorEvaluator)(const GDNativeTypePtr p_left, const GDNativeTypePtr p_right, GDNativeTypePtr r_result);
-typedef void (*GDNativePtrBuiltInMethod)(GDNativeTypePtr p_base, const GDNativeTypePtr *p_args, GDNativeTypePtr r_return, int p_argument_count);
-typedef void (*GDNativePtrConstructor)(GDNativeTypePtr p_base, const GDNativeTypePtr *p_args);
+typedef void (*GDNativePtrOperatorEvaluator)(GDNativeConstTypePtr p_left, GDNativeConstTypePtr p_right, GDNativeTypePtr r_result);
+typedef void (*GDNativePtrBuiltInMethod)(GDNativeTypePtr p_base, GDNativeConstTypePtr *p_args, GDNativeTypePtr r_return, int p_argument_count);
+typedef void (*GDNativePtrConstructor)(GDNativeTypePtr p_base, GDNativeConstTypePtr *p_args);
typedef void (*GDNativePtrDestructor)(GDNativeTypePtr p_base);
-typedef void (*GDNativePtrSetter)(GDNativeTypePtr p_base, const GDNativeTypePtr p_value);
-typedef void (*GDNativePtrGetter)(const GDNativeTypePtr p_base, GDNativeTypePtr r_value);
-typedef void (*GDNativePtrIndexedSetter)(GDNativeTypePtr p_base, GDNativeInt p_index, const GDNativeTypePtr p_value);
-typedef void (*GDNativePtrIndexedGetter)(const GDNativeTypePtr p_base, GDNativeInt p_index, GDNativeTypePtr r_value);
-typedef void (*GDNativePtrKeyedSetter)(GDNativeTypePtr p_base, const GDNativeTypePtr p_key, const GDNativeTypePtr p_value);
-typedef void (*GDNativePtrKeyedGetter)(const GDNativeTypePtr p_base, const GDNativeTypePtr p_key, GDNativeTypePtr r_value);
-typedef uint32_t (*GDNativePtrKeyedChecker)(const GDNativeVariantPtr p_base, const GDNativeVariantPtr p_key);
-typedef void (*GDNativePtrUtilityFunction)(GDNativeTypePtr r_return, const GDNativeTypePtr *p_arguments, int p_argument_count);
+typedef void (*GDNativePtrSetter)(GDNativeTypePtr p_base, GDNativeConstTypePtr p_value);
+typedef void (*GDNativePtrGetter)(GDNativeConstTypePtr p_base, GDNativeTypePtr r_value);
+typedef void (*GDNativePtrIndexedSetter)(GDNativeTypePtr p_base, GDNativeInt p_index, GDNativeConstTypePtr p_value);
+typedef void (*GDNativePtrIndexedGetter)(GDNativeConstTypePtr p_base, GDNativeInt p_index, GDNativeTypePtr r_value);
+typedef void (*GDNativePtrKeyedSetter)(GDNativeTypePtr p_base, GDNativeConstTypePtr p_key, GDNativeConstTypePtr p_value);
+typedef void (*GDNativePtrKeyedGetter)(GDNativeConstTypePtr p_base, GDNativeConstTypePtr p_key, GDNativeTypePtr r_value);
+typedef uint32_t (*GDNativePtrKeyedChecker)(GDNativeConstVariantPtr p_base, GDNativeConstVariantPtr p_key);
+typedef void (*GDNativePtrUtilityFunction)(GDNativeTypePtr r_return, GDNativeConstTypePtr *p_arguments, int p_argument_count);
typedef GDNativeObjectPtr (*GDNativeClassConstructor)();
@@ -200,8 +204,8 @@ typedef struct {
typedef void *GDExtensionClassInstancePtr;
-typedef GDNativeBool (*GDNativeExtensionClassSet)(GDExtensionClassInstancePtr p_instance, const GDNativeStringNamePtr p_name, const GDNativeVariantPtr p_value);
-typedef GDNativeBool (*GDNativeExtensionClassGet)(GDExtensionClassInstancePtr p_instance, const GDNativeStringNamePtr p_name, GDNativeVariantPtr r_ret);
+typedef GDNativeBool (*GDNativeExtensionClassSet)(GDExtensionClassInstancePtr p_instance, GDNativeConstStringNamePtr p_name, GDNativeConstVariantPtr p_value);
+typedef GDNativeBool (*GDNativeExtensionClassGet)(GDExtensionClassInstancePtr p_instance, GDNativeConstStringNamePtr p_name, GDNativeVariantPtr r_ret);
typedef uint64_t (*GDNativeExtensionClassGetRID)(GDExtensionClassInstancePtr p_instance);
typedef struct {
@@ -230,16 +234,16 @@ typedef struct {
typedef const GDNativePropertyInfo *(*GDNativeExtensionClassGetPropertyList)(GDExtensionClassInstancePtr p_instance, uint32_t *r_count);
typedef void (*GDNativeExtensionClassFreePropertyList)(GDExtensionClassInstancePtr p_instance, const GDNativePropertyInfo *p_list);
-typedef GDNativeBool (*GDNativeExtensionClassPropertyCanRevert)(GDExtensionClassInstancePtr p_instance, const GDNativeStringNamePtr p_name);
-typedef GDNativeBool (*GDNativeExtensionClassPropertyGetRevert)(GDExtensionClassInstancePtr p_instance, const GDNativeStringNamePtr p_name, GDNativeVariantPtr r_ret);
+typedef GDNativeBool (*GDNativeExtensionClassPropertyCanRevert)(GDExtensionClassInstancePtr p_instance, GDNativeConstStringNamePtr p_name);
+typedef GDNativeBool (*GDNativeExtensionClassPropertyGetRevert)(GDExtensionClassInstancePtr p_instance, GDNativeConstStringNamePtr p_name, GDNativeVariantPtr r_ret);
typedef void (*GDNativeExtensionClassNotification)(GDExtensionClassInstancePtr p_instance, int32_t p_what);
typedef void (*GDNativeExtensionClassToString)(GDExtensionClassInstancePtr p_instance, GDNativeBool *r_is_valid, GDNativeStringPtr p_out);
typedef void (*GDNativeExtensionClassReference)(GDExtensionClassInstancePtr p_instance);
typedef void (*GDNativeExtensionClassUnreference)(GDExtensionClassInstancePtr p_instance);
-typedef void (*GDNativeExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret);
+typedef void (*GDNativeExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, GDNativeConstTypePtr *p_args, GDNativeTypePtr r_ret);
typedef GDNativeObjectPtr (*GDNativeExtensionClassCreateInstance)(void *p_userdata);
typedef void (*GDNativeExtensionClassFreeInstance)(void *p_userdata, GDExtensionClassInstancePtr p_instance);
-typedef GDNativeExtensionClassCallVirtual (*GDNativeExtensionClassGetVirtual)(void *p_userdata, const GDNativeStringNamePtr p_name);
+typedef GDNativeExtensionClassCallVirtual (*GDNativeExtensionClassGetVirtual)(void *p_userdata, GDNativeConstStringNamePtr p_name);
typedef struct {
GDNativeBool is_virtual;
@@ -289,8 +293,8 @@ typedef enum {
GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE
} GDNativeExtensionClassMethodArgumentMetadata;
-typedef void (*GDNativeExtensionClassMethodCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDNativeVariantPtr *p_args, const GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error);
-typedef void (*GDNativeExtensionClassMethodPtrCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret);
+typedef void (*GDNativeExtensionClassMethodCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, GDNativeConstVariantPtr *p_args, GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error);
+typedef void (*GDNativeExtensionClassMethodPtrCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, GDNativeConstTypePtr *p_args, GDNativeTypePtr r_ret);
typedef struct {
GDNativeStringNamePtr name;
@@ -320,25 +324,25 @@ typedef struct {
typedef void *GDNativeExtensionScriptInstanceDataPtr; // Pointer to custom ScriptInstance native implementation.
-typedef GDNativeBool (*GDNativeExtensionScriptInstanceSet)(GDNativeExtensionScriptInstanceDataPtr p_instance, const GDNativeStringNamePtr p_name, const GDNativeVariantPtr p_value);
-typedef GDNativeBool (*GDNativeExtensionScriptInstanceGet)(GDNativeExtensionScriptInstanceDataPtr p_instance, const GDNativeStringNamePtr p_name, GDNativeVariantPtr r_ret);
+typedef GDNativeBool (*GDNativeExtensionScriptInstanceSet)(GDNativeExtensionScriptInstanceDataPtr p_instance, GDNativeConstStringNamePtr p_name, GDNativeConstVariantPtr p_value);
+typedef GDNativeBool (*GDNativeExtensionScriptInstanceGet)(GDNativeExtensionScriptInstanceDataPtr p_instance, GDNativeConstStringNamePtr p_name, GDNativeVariantPtr r_ret);
typedef const GDNativePropertyInfo *(*GDNativeExtensionScriptInstanceGetPropertyList)(GDNativeExtensionScriptInstanceDataPtr p_instance, uint32_t *r_count);
typedef void (*GDNativeExtensionScriptInstanceFreePropertyList)(GDNativeExtensionScriptInstanceDataPtr p_instance, const GDNativePropertyInfo *p_list);
-typedef GDNativeVariantType (*GDNativeExtensionScriptInstanceGetPropertyType)(GDNativeExtensionScriptInstanceDataPtr p_instance, const GDNativeStringNamePtr p_name, GDNativeBool *r_is_valid);
+typedef GDNativeVariantType (*GDNativeExtensionScriptInstanceGetPropertyType)(GDNativeExtensionScriptInstanceDataPtr p_instance, GDNativeConstStringNamePtr p_name, GDNativeBool *r_is_valid);
-typedef GDNativeBool (*GDNativeExtensionScriptInstancePropertyCanRevert)(GDNativeExtensionScriptInstanceDataPtr p_instance, const GDNativeStringNamePtr p_name);
-typedef GDNativeBool (*GDNativeExtensionScriptInstancePropertyGetRevert)(GDNativeExtensionScriptInstanceDataPtr p_instance, const GDNativeStringNamePtr p_name, GDNativeVariantPtr r_ret);
+typedef GDNativeBool (*GDNativeExtensionScriptInstancePropertyCanRevert)(GDNativeExtensionScriptInstanceDataPtr p_instance, GDNativeConstStringNamePtr p_name);
+typedef GDNativeBool (*GDNativeExtensionScriptInstancePropertyGetRevert)(GDNativeExtensionScriptInstanceDataPtr p_instance, GDNativeConstStringNamePtr p_name, GDNativeVariantPtr r_ret);
typedef GDNativeObjectPtr (*GDNativeExtensionScriptInstanceGetOwner)(GDNativeExtensionScriptInstanceDataPtr p_instance);
-typedef void (*GDNativeExtensionScriptInstancePropertyStateAdd)(const GDNativeStringNamePtr p_name, const GDNativeVariantPtr p_value, void *p_userdata);
+typedef void (*GDNativeExtensionScriptInstancePropertyStateAdd)(GDNativeConstStringNamePtr p_name, GDNativeConstVariantPtr p_value, void *p_userdata);
typedef void (*GDNativeExtensionScriptInstanceGetPropertyState)(GDNativeExtensionScriptInstanceDataPtr p_instance, GDNativeExtensionScriptInstancePropertyStateAdd p_add_func, void *p_userdata);
typedef const GDNativeMethodInfo *(*GDNativeExtensionScriptInstanceGetMethodList)(GDNativeExtensionScriptInstanceDataPtr p_instance, uint32_t *r_count);
typedef void (*GDNativeExtensionScriptInstanceFreeMethodList)(GDNativeExtensionScriptInstanceDataPtr p_instance, const GDNativeMethodInfo *p_list);
-typedef GDNativeBool (*GDNativeExtensionScriptInstanceHasMethod)(GDNativeExtensionScriptInstanceDataPtr p_instance, const GDNativeStringNamePtr p_name);
+typedef GDNativeBool (*GDNativeExtensionScriptInstanceHasMethod)(GDNativeExtensionScriptInstanceDataPtr p_instance, GDNativeConstStringNamePtr p_name);
-typedef void (*GDNativeExtensionScriptInstanceCall)(GDNativeExtensionScriptInstanceDataPtr p_self, const GDNativeStringNamePtr p_method, const GDNativeVariantPtr *p_args, const GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error);
+typedef void (*GDNativeExtensionScriptInstanceCall)(GDNativeExtensionScriptInstanceDataPtr p_self, GDNativeConstStringNamePtr p_method, GDNativeConstVariantPtr *p_args, GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error);
typedef void (*GDNativeExtensionScriptInstanceNotification)(GDNativeExtensionScriptInstanceDataPtr p_instance, int32_t p_what);
typedef void (*GDNativeExtensionScriptInstanceToString)(GDNativeExtensionScriptInstanceDataPtr p_instance, GDNativeBool *r_is_valid, GDNativeStringPtr r_out);
@@ -413,41 +417,41 @@ typedef struct {
void (*print_warning)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line);
void (*print_script_error)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line);
- uint64_t (*get_native_struct_size)(const GDNativeStringNamePtr p_name);
+ uint64_t (*get_native_struct_size)(GDNativeConstStringNamePtr p_name);
/* GODOT VARIANT */
/* variant general */
- void (*variant_new_copy)(GDNativeVariantPtr r_dest, const GDNativeVariantPtr p_src);
+ void (*variant_new_copy)(GDNativeVariantPtr r_dest, GDNativeConstVariantPtr p_src);
void (*variant_new_nil)(GDNativeVariantPtr r_dest);
void (*variant_destroy)(GDNativeVariantPtr p_self);
/* variant type */
- void (*variant_call)(GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_method, const GDNativeVariantPtr *p_args, const GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error);
- void (*variant_call_static)(GDNativeVariantType p_type, const GDNativeStringNamePtr p_method, const GDNativeVariantPtr *p_args, const GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error);
- void (*variant_evaluate)(GDNativeVariantOperator p_op, const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, GDNativeVariantPtr r_return, GDNativeBool *r_valid);
- void (*variant_set)(GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid);
- void (*variant_set_named)(GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid);
- void (*variant_set_keyed)(GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid);
- void (*variant_set_indexed)(GDNativeVariantPtr p_self, GDNativeInt p_index, const GDNativeVariantPtr p_value, GDNativeBool *r_valid, GDNativeBool *r_oob);
- void (*variant_get)(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid);
- void (*variant_get_named)(const GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid);
- void (*variant_get_keyed)(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid);
- void (*variant_get_indexed)(const GDNativeVariantPtr p_self, GDNativeInt p_index, GDNativeVariantPtr r_ret, GDNativeBool *r_valid, GDNativeBool *r_oob);
- GDNativeBool (*variant_iter_init)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid);
- GDNativeBool (*variant_iter_next)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid);
- void (*variant_iter_get)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeVariantPtr r_ret, GDNativeBool *r_valid);
- GDNativeInt (*variant_hash)(const GDNativeVariantPtr p_self);
- GDNativeInt (*variant_recursive_hash)(const GDNativeVariantPtr p_self, GDNativeInt p_recursion_count);
- GDNativeBool (*variant_hash_compare)(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_other);
- GDNativeBool (*variant_booleanize)(const GDNativeVariantPtr p_self);
- void (*variant_duplicate)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_ret, GDNativeBool p_deep);
- void (*variant_stringify)(const GDNativeVariantPtr p_self, GDNativeStringPtr r_ret);
-
- GDNativeVariantType (*variant_get_type)(const GDNativeVariantPtr p_self);
- GDNativeBool (*variant_has_method)(const GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_method);
- GDNativeBool (*variant_has_member)(GDNativeVariantType p_type, const GDNativeStringNamePtr p_member);
- GDNativeBool (*variant_has_key)(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeBool *r_valid);
+ void (*variant_call)(GDNativeVariantPtr p_self, GDNativeConstStringNamePtr p_method, GDNativeConstVariantPtr *p_args, GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error);
+ void (*variant_call_static)(GDNativeVariantType p_type, GDNativeConstStringNamePtr p_method, GDNativeConstVariantPtr *p_args, GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error);
+ void (*variant_evaluate)(GDNativeVariantOperator p_op, GDNativeConstVariantPtr p_a, GDNativeConstVariantPtr p_b, GDNativeVariantPtr r_return, GDNativeBool *r_valid);
+ void (*variant_set)(GDNativeVariantPtr p_self, GDNativeConstVariantPtr p_key, GDNativeConstVariantPtr p_value, GDNativeBool *r_valid);
+ void (*variant_set_named)(GDNativeVariantPtr p_self, GDNativeConstStringNamePtr p_key, GDNativeConstVariantPtr p_value, GDNativeBool *r_valid);
+ void (*variant_set_keyed)(GDNativeVariantPtr p_self, GDNativeConstVariantPtr p_key, GDNativeConstVariantPtr p_value, GDNativeBool *r_valid);
+ void (*variant_set_indexed)(GDNativeVariantPtr p_self, GDNativeInt p_index, GDNativeConstVariantPtr p_value, GDNativeBool *r_valid, GDNativeBool *r_oob);
+ void (*variant_get)(GDNativeConstVariantPtr p_self, GDNativeConstVariantPtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid);
+ void (*variant_get_named)(GDNativeConstVariantPtr p_self, GDNativeConstStringNamePtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid);
+ void (*variant_get_keyed)(GDNativeConstVariantPtr p_self, GDNativeConstVariantPtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid);
+ void (*variant_get_indexed)(GDNativeConstVariantPtr p_self, GDNativeInt p_index, GDNativeVariantPtr r_ret, GDNativeBool *r_valid, GDNativeBool *r_oob);
+ GDNativeBool (*variant_iter_init)(GDNativeConstVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid);
+ GDNativeBool (*variant_iter_next)(GDNativeConstVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid);
+ void (*variant_iter_get)(GDNativeConstVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeVariantPtr r_ret, GDNativeBool *r_valid);
+ GDNativeInt (*variant_hash)(GDNativeConstVariantPtr p_self);
+ GDNativeInt (*variant_recursive_hash)(GDNativeConstVariantPtr p_self, GDNativeInt p_recursion_count);
+ GDNativeBool (*variant_hash_compare)(GDNativeConstVariantPtr p_self, GDNativeConstVariantPtr p_other);
+ GDNativeBool (*variant_booleanize)(GDNativeConstVariantPtr p_self);
+ void (*variant_duplicate)(GDNativeConstVariantPtr p_self, GDNativeVariantPtr r_ret, GDNativeBool p_deep);
+ void (*variant_stringify)(GDNativeConstVariantPtr p_self, GDNativeStringPtr r_ret);
+
+ GDNativeVariantType (*variant_get_type)(GDNativeConstVariantPtr p_self);
+ GDNativeBool (*variant_has_method)(GDNativeConstVariantPtr p_self, GDNativeConstStringNamePtr p_method);
+ GDNativeBool (*variant_has_member)(GDNativeVariantType p_type, GDNativeConstStringNamePtr p_member);
+ GDNativeBool (*variant_has_key)(GDNativeConstVariantPtr p_self, GDNativeConstVariantPtr p_key, GDNativeBool *r_valid);
void (*variant_get_type_name)(GDNativeVariantType p_type, GDNativeStringPtr r_name);
GDNativeBool (*variant_can_convert)(GDNativeVariantType p_from, GDNativeVariantType p_to);
GDNativeBool (*variant_can_convert_strict)(GDNativeVariantType p_from, GDNativeVariantType p_to);
@@ -456,19 +460,19 @@ typedef struct {
GDNativeVariantFromTypeConstructorFunc (*get_variant_from_type_constructor)(GDNativeVariantType p_type);
GDNativeTypeFromVariantConstructorFunc (*get_variant_to_type_constructor)(GDNativeVariantType p_type);
GDNativePtrOperatorEvaluator (*variant_get_ptr_operator_evaluator)(GDNativeVariantOperator p_operator, GDNativeVariantType p_type_a, GDNativeVariantType p_type_b);
- GDNativePtrBuiltInMethod (*variant_get_ptr_builtin_method)(GDNativeVariantType p_type, const GDNativeStringNamePtr p_method, GDNativeInt p_hash);
+ GDNativePtrBuiltInMethod (*variant_get_ptr_builtin_method)(GDNativeVariantType p_type, GDNativeConstStringNamePtr p_method, GDNativeInt p_hash);
GDNativePtrConstructor (*variant_get_ptr_constructor)(GDNativeVariantType p_type, int32_t p_constructor);
GDNativePtrDestructor (*variant_get_ptr_destructor)(GDNativeVariantType p_type);
- void (*variant_construct)(GDNativeVariantType p_type, GDNativeVariantPtr p_base, const GDNativeVariantPtr *p_args, int32_t p_argument_count, GDNativeCallError *r_error);
- GDNativePtrSetter (*variant_get_ptr_setter)(GDNativeVariantType p_type, const GDNativeStringNamePtr p_member);
- GDNativePtrGetter (*variant_get_ptr_getter)(GDNativeVariantType p_type, const GDNativeStringNamePtr p_member);
+ void (*variant_construct)(GDNativeVariantType p_type, GDNativeVariantPtr p_base, GDNativeConstVariantPtr *p_args, int32_t p_argument_count, GDNativeCallError *r_error);
+ GDNativePtrSetter (*variant_get_ptr_setter)(GDNativeVariantType p_type, GDNativeConstStringNamePtr p_member);
+ GDNativePtrGetter (*variant_get_ptr_getter)(GDNativeVariantType p_type, GDNativeConstStringNamePtr p_member);
GDNativePtrIndexedSetter (*variant_get_ptr_indexed_setter)(GDNativeVariantType p_type);
GDNativePtrIndexedGetter (*variant_get_ptr_indexed_getter)(GDNativeVariantType p_type);
GDNativePtrKeyedSetter (*variant_get_ptr_keyed_setter)(GDNativeVariantType p_type);
GDNativePtrKeyedGetter (*variant_get_ptr_keyed_getter)(GDNativeVariantType p_type);
GDNativePtrKeyedChecker (*variant_get_ptr_keyed_checker)(GDNativeVariantType p_type);
- void (*variant_get_constant_value)(GDNativeVariantType p_type, const GDNativeStringNamePtr p_constant, GDNativeVariantPtr r_ret);
- GDNativePtrUtilityFunction (*variant_get_ptr_utility_function)(const GDNativeStringNamePtr p_function, GDNativeInt p_hash);
+ void (*variant_get_constant_value)(GDNativeVariantType p_type, GDNativeConstStringNamePtr p_constant, GDNativeVariantPtr r_ret);
+ GDNativePtrUtilityFunction (*variant_get_ptr_utility_function)(GDNativeConstStringNamePtr p_function, GDNativeInt p_hash);
/* extra utilities */
void (*string_new_with_latin1_chars)(GDNativeStringPtr r_dest, const char *p_contents);
@@ -476,12 +480,11 @@ typedef struct {
void (*string_new_with_utf16_chars)(GDNativeStringPtr r_dest, const char16_t *p_contents);
void (*string_new_with_utf32_chars)(GDNativeStringPtr r_dest, const char32_t *p_contents);
void (*string_new_with_wide_chars)(GDNativeStringPtr r_dest, const wchar_t *p_contents);
- void (*string_new_with_latin1_chars_and_len)(GDNativeStringPtr r_dest, const char *p_contents, const GDNativeInt p_size);
- void (*string_new_with_utf8_chars_and_len)(GDNativeStringPtr r_dest, const char *p_contents, const GDNativeInt p_size);
- void (*string_new_with_utf16_chars_and_len)(GDNativeStringPtr r_dest, const char16_t *p_contents, const GDNativeInt p_size);
- void (*string_new_with_utf32_chars_and_len)(GDNativeStringPtr r_dest, const char32_t *p_contents, const GDNativeInt p_size);
- void (*string_new_with_wide_chars_and_len)(GDNativeStringPtr r_dest, const wchar_t *p_contents, const GDNativeInt p_size);
-
+ void (*string_new_with_latin1_chars_and_len)(GDNativeStringPtr r_dest, const char *p_contents, GDNativeInt p_size);
+ void (*string_new_with_utf8_chars_and_len)(GDNativeStringPtr r_dest, const char *p_contents, GDNativeInt p_size);
+ void (*string_new_with_utf16_chars_and_len)(GDNativeStringPtr r_dest, const char16_t *p_contents, GDNativeInt p_size);
+ void (*string_new_with_utf32_chars_and_len)(GDNativeStringPtr r_dest, const char32_t *p_contents, GDNativeInt p_size);
+ void (*string_new_with_wide_chars_and_len)(GDNativeStringPtr r_dest, const wchar_t *p_contents, GDNativeInt p_size);
/* Information about the following functions:
* - The return value is the resulting encoded string length.
* - The length returned is in characters, not in bytes. It also does not include a trailing zero.
@@ -490,63 +493,63 @@ typedef struct {
* - p_max_write_length argument is in characters, not bytes. It will be ignored if r_text is NULL.
* - p_max_write_length argument does not affect the return value, it's only to cap write length.
*/
- GDNativeInt (*string_to_latin1_chars)(const GDNativeStringPtr p_self, char *r_text, GDNativeInt p_max_write_length);
- GDNativeInt (*string_to_utf8_chars)(const GDNativeStringPtr p_self, char *r_text, GDNativeInt p_max_write_length);
- GDNativeInt (*string_to_utf16_chars)(const GDNativeStringPtr p_self, char16_t *r_text, GDNativeInt p_max_write_length);
- GDNativeInt (*string_to_utf32_chars)(const GDNativeStringPtr p_self, char32_t *r_text, GDNativeInt p_max_write_length);
- GDNativeInt (*string_to_wide_chars)(const GDNativeStringPtr p_self, wchar_t *r_text, GDNativeInt p_max_write_length);
+ GDNativeInt (*string_to_latin1_chars)(GDNativeConstStringPtr p_self, char *r_text, GDNativeInt p_max_write_length);
+ GDNativeInt (*string_to_utf8_chars)(GDNativeConstStringPtr p_self, char *r_text, GDNativeInt p_max_write_length);
+ GDNativeInt (*string_to_utf16_chars)(GDNativeConstStringPtr p_self, char16_t *r_text, GDNativeInt p_max_write_length);
+ GDNativeInt (*string_to_utf32_chars)(GDNativeConstStringPtr p_self, char32_t *r_text, GDNativeInt p_max_write_length);
+ GDNativeInt (*string_to_wide_chars)(GDNativeConstStringPtr p_self, wchar_t *r_text, GDNativeInt p_max_write_length);
char32_t *(*string_operator_index)(GDNativeStringPtr p_self, GDNativeInt p_index);
- const char32_t *(*string_operator_index_const)(const GDNativeStringPtr p_self, GDNativeInt p_index);
+ const char32_t *(*string_operator_index_const)(GDNativeConstStringPtr p_self, GDNativeInt p_index);
/* Packed array functions */
uint8_t *(*packed_byte_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedByteArray
- const uint8_t *(*packed_byte_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedByteArray
+ const uint8_t *(*packed_byte_array_operator_index_const)(GDNativeConstTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedByteArray
GDNativeTypePtr (*packed_color_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedColorArray, returns Color ptr
- GDNativeTypePtr (*packed_color_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedColorArray, returns Color ptr
+ GDNativeTypePtr (*packed_color_array_operator_index_const)(GDNativeConstTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedColorArray, returns Color ptr
float *(*packed_float32_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedFloat32Array
- const float *(*packed_float32_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedFloat32Array
+ const float *(*packed_float32_array_operator_index_const)(GDNativeConstTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedFloat32Array
double *(*packed_float64_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedFloat64Array
- const double *(*packed_float64_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedFloat64Array
+ const double *(*packed_float64_array_operator_index_const)(GDNativeConstTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedFloat64Array
int32_t *(*packed_int32_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedInt32Array
- const int32_t *(*packed_int32_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedInt32Array
+ const int32_t *(*packed_int32_array_operator_index_const)(GDNativeConstTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedInt32Array
int64_t *(*packed_int64_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedInt32Array
- const int64_t *(*packed_int64_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedInt32Array
+ const int64_t *(*packed_int64_array_operator_index_const)(GDNativeConstTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedInt32Array
GDNativeStringPtr (*packed_string_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedStringArray
- GDNativeStringPtr (*packed_string_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedStringArray
+ GDNativeStringPtr (*packed_string_array_operator_index_const)(GDNativeConstTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedStringArray
GDNativeTypePtr (*packed_vector2_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedVector2Array, returns Vector2 ptr
- GDNativeTypePtr (*packed_vector2_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedVector2Array, returns Vector2 ptr
+ GDNativeTypePtr (*packed_vector2_array_operator_index_const)(GDNativeConstTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedVector2Array, returns Vector2 ptr
GDNativeTypePtr (*packed_vector3_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedVector3Array, returns Vector3 ptr
- GDNativeTypePtr (*packed_vector3_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedVector3Array, returns Vector3 ptr
+ GDNativeTypePtr (*packed_vector3_array_operator_index_const)(GDNativeConstTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedVector3Array, returns Vector3 ptr
GDNativeVariantPtr (*array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be an Array ptr
- GDNativeVariantPtr (*array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be an Array ptr
+ GDNativeVariantPtr (*array_operator_index_const)(GDNativeConstTypePtr p_self, GDNativeInt p_index); // p_self should be an Array ptr
/* Dictionary functions */
- GDNativeVariantPtr (*dictionary_operator_index)(GDNativeTypePtr p_self, const GDNativeVariantPtr p_key); // p_self should be an Dictionary ptr
- GDNativeVariantPtr (*dictionary_operator_index_const)(const GDNativeTypePtr p_self, const GDNativeVariantPtr p_key); // p_self should be an Dictionary ptr
+ GDNativeVariantPtr (*dictionary_operator_index)(GDNativeTypePtr p_self, GDNativeConstVariantPtr p_key); // p_self should be an Dictionary ptr
+ GDNativeVariantPtr (*dictionary_operator_index_const)(GDNativeConstTypePtr p_self, GDNativeConstVariantPtr p_key); // p_self should be an Dictionary ptr
/* OBJECT */
- void (*object_method_bind_call)(const GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeVariantPtr *p_args, GDNativeInt p_arg_count, GDNativeVariantPtr r_ret, GDNativeCallError *r_error);
- void (*object_method_bind_ptrcall)(const GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret);
+ void (*object_method_bind_call)(GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, GDNativeConstVariantPtr *p_args, GDNativeInt p_arg_count, GDNativeVariantPtr r_ret, GDNativeCallError *r_error);
+ void (*object_method_bind_ptrcall)(GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, GDNativeConstTypePtr *p_args, GDNativeTypePtr r_ret);
void (*object_destroy)(GDNativeObjectPtr p_o);
- GDNativeObjectPtr (*global_get_singleton)(const GDNativeStringNamePtr p_name);
+ GDNativeObjectPtr (*global_get_singleton)(GDNativeConstStringNamePtr p_name);
void *(*object_get_instance_binding)(GDNativeObjectPtr p_o, void *p_token, const GDNativeInstanceBindingCallbacks *p_callbacks);
void (*object_set_instance_binding)(GDNativeObjectPtr p_o, void *p_token, void *p_binding, const GDNativeInstanceBindingCallbacks *p_callbacks);
- void (*object_set_instance)(GDNativeObjectPtr p_o, const GDNativeStringNamePtr p_classname, GDExtensionClassInstancePtr p_instance); /* p_classname should be a registered extension class and should extend the p_o object's class. */
+ void (*object_set_instance)(GDNativeObjectPtr p_o, GDNativeConstStringNamePtr p_classname, GDExtensionClassInstancePtr p_instance); /* p_classname should be a registered extension class and should extend the p_o object's class. */
- GDNativeObjectPtr (*object_cast_to)(const GDNativeObjectPtr p_object, void *p_class_tag);
+ GDNativeObjectPtr (*object_cast_to)(GDNativeConstObjectPtr p_object, void *p_class_tag);
GDNativeObjectPtr (*object_get_instance_from_id)(GDObjectInstanceID p_instance_id);
- GDObjectInstanceID (*object_get_instance_id)(const GDNativeObjectPtr p_object);
+ GDObjectInstanceID (*object_get_instance_id)(GDNativeConstObjectPtr p_object);
/* SCRIPT INSTANCE */
@@ -554,23 +557,23 @@ typedef struct {
/* CLASSDB */
- GDNativeObjectPtr (*classdb_construct_object)(const GDNativeStringNamePtr p_classname); /* The passed class must be a built-in godot class, or an already-registered extension class. In both case, object_set_instance should be called to fully initialize the object. */
- GDNativeMethodBindPtr (*classdb_get_method_bind)(const GDNativeStringNamePtr p_classname, const GDNativeStringNamePtr p_methodname, GDNativeInt p_hash);
- void *(*classdb_get_class_tag)(const GDNativeStringNamePtr p_classname);
+ GDNativeObjectPtr (*classdb_construct_object)(GDNativeConstStringNamePtr p_classname); /* The passed class must be a built-in godot class, or an already-registered extension class. In both case, object_set_instance should be called to fully initialize the object. */
+ GDNativeMethodBindPtr (*classdb_get_method_bind)(GDNativeConstStringNamePtr p_classname, GDNativeConstStringNamePtr p_methodname, GDNativeInt p_hash);
+ void *(*classdb_get_class_tag)(GDNativeConstStringNamePtr p_classname);
/* CLASSDB EXTENSION */
/* Provided parameters for `classdb_register_extension_*` can be safely freed once the function returns. */
- void (*classdb_register_extension_class)(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeStringNamePtr p_parent_class_name, const GDNativeExtensionClassCreationInfo *p_extension_funcs);
- void (*classdb_register_extension_class_method)(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeExtensionClassMethodInfo *p_method_info);
- void (*classdb_register_extension_class_integer_constant)(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeStringNamePtr p_enum_name, const GDNativeStringNamePtr p_constant_name, GDNativeInt p_constant_value, GDNativeBool p_is_bitfield);
- void (*classdb_register_extension_class_property)(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativePropertyInfo *p_info, const GDNativeStringNamePtr p_setter, const GDNativeStringNamePtr p_getter);
- void (*classdb_register_extension_class_property_group)(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeStringPtr p_group_name, const GDNativeStringPtr p_prefix);
- void (*classdb_register_extension_class_property_subgroup)(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeStringPtr p_subgroup_name, const GDNativeStringPtr p_prefix);
- void (*classdb_register_extension_class_signal)(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeStringNamePtr p_signal_name, const GDNativePropertyInfo *p_argument_info, GDNativeInt p_argument_count);
- void (*classdb_unregister_extension_class)(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name); /* Unregistering a parent class before a class that inherits it will result in failure. Inheritors must be unregistered first. */
+ void (*classdb_register_extension_class)(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, GDNativeConstStringNamePtr p_parent_class_name, const GDNativeExtensionClassCreationInfo *p_extension_funcs);
+ void (*classdb_register_extension_class_method)(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, const GDNativeExtensionClassMethodInfo *p_method_info);
+ void (*classdb_register_extension_class_integer_constant)(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, GDNativeConstStringNamePtr p_enum_name, GDNativeConstStringNamePtr p_constant_name, GDNativeInt p_constant_value, GDNativeBool p_is_bitfield);
+ void (*classdb_register_extension_class_property)(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, const GDNativePropertyInfo *p_info, GDNativeConstStringNamePtr p_setter, GDNativeConstStringNamePtr p_getter);
+ void (*classdb_register_extension_class_property_group)(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, GDNativeConstStringPtr p_group_name, GDNativeConstStringPtr p_prefix);
+ void (*classdb_register_extension_class_property_subgroup)(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, GDNativeConstStringPtr p_subgroup_name, GDNativeConstStringPtr p_prefix);
+ void (*classdb_register_extension_class_signal)(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, GDNativeConstStringNamePtr p_signal_name, const GDNativePropertyInfo *p_argument_info, GDNativeInt p_argument_count);
+ void (*classdb_unregister_extension_class)(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name); /* Unregistering a parent class before a class that inherits it will result in failure. Inheritors must be unregistered first. */
- void (*get_library_path)(const GDNativeExtensionClassLibraryPtr p_library, GDNativeStringPtr r_path);
+ void (*get_library_path)(GDNativeExtensionClassLibraryPtr p_library, GDNativeStringPtr r_path);
} GDNativeInterface;
@@ -600,7 +603,7 @@ typedef struct {
* It can be used to set up different init levels, which are called during various stages of initialization/shutdown.
* The function name must be a unique one specified in the .gdextension config file.
*/
-typedef GDNativeBool (*GDNativeInitializationFunction)(const GDNativeInterface *p_interface, const GDNativeExtensionClassLibraryPtr p_library, GDNativeInitialization *r_initialization);
+typedef GDNativeBool (*GDNativeInitializationFunction)(const GDNativeInterface *p_interface, GDNativeExtensionClassLibraryPtr p_library, GDNativeInitialization *r_initialization);
#ifdef __cplusplus
}
diff --git a/core/extension/native_extension.cpp b/core/extension/native_extension.cpp
index 83a2e80793..37967cdb48 100644
--- a/core/extension/native_extension.cpp
+++ b/core/extension/native_extension.cpp
@@ -76,20 +76,20 @@ public:
}
#endif
- virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) override {
+ virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override {
Variant ret;
GDExtensionClassInstancePtr extension_instance = is_static() ? nullptr : p_object->_get_extension_instance();
GDNativeCallError ce{ GDNATIVE_CALL_OK, 0, 0 };
- call_func(method_userdata, extension_instance, (const GDNativeVariantPtr *)p_args, p_arg_count, (GDNativeVariantPtr)&ret, &ce);
+ call_func(method_userdata, extension_instance, reinterpret_cast<GDNativeConstVariantPtr *>(p_args), p_arg_count, (GDNativeVariantPtr)&ret, &ce);
r_error.error = Callable::CallError::Error(ce.error);
r_error.argument = ce.argument;
r_error.expected = ce.expected;
return ret;
}
- virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) override {
+ virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override {
ERR_FAIL_COND_MSG(vararg, "Vararg methods don't have ptrcall support. This is most likely an engine bug.");
GDExtensionClassInstancePtr extension_instance = p_object->_get_extension_instance();
- ptrcall_func(method_userdata, extension_instance, (const GDNativeTypePtr *)p_args, (GDNativeTypePtr)r_ret);
+ ptrcall_func(method_userdata, extension_instance, reinterpret_cast<GDNativeConstTypePtr *>(p_args), (GDNativeTypePtr)r_ret);
}
virtual bool is_vararg() const override {
@@ -135,11 +135,11 @@ public:
static GDNativeInterface gdnative_interface;
-void NativeExtension::_register_extension_class(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeStringNamePtr p_parent_class_name, const GDNativeExtensionClassCreationInfo *p_extension_funcs) {
- NativeExtension *self = static_cast<NativeExtension *>(p_library);
+void NativeExtension::_register_extension_class(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, GDNativeConstStringNamePtr p_parent_class_name, const GDNativeExtensionClassCreationInfo *p_extension_funcs) {
+ NativeExtension *self = reinterpret_cast<NativeExtension *>(p_library);
- StringName class_name = *reinterpret_cast<StringName *>(p_class_name);
- StringName parent_class_name = *reinterpret_cast<StringName *>(p_parent_class_name);
+ StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
+ StringName parent_class_name = *reinterpret_cast<const StringName *>(p_parent_class_name);
ERR_FAIL_COND_MSG(!String(class_name).is_valid_identifier(), "Attempt to register extension class '" + class_name + "', which is not a valid class identifier.");
ERR_FAIL_COND_MSG(ClassDB::class_exists(class_name), "Attempt to register extension class '" + class_name + "', which appears to be already registered.");
@@ -190,8 +190,8 @@ void NativeExtension::_register_extension_class(const GDNativeExtensionClassLibr
ClassDB::register_extension_class(&extension->native_extension);
}
-void NativeExtension::_register_extension_class_method(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeExtensionClassMethodInfo *p_method_info) {
- NativeExtension *self = static_cast<NativeExtension *>(p_library);
+void NativeExtension::_register_extension_class_method(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, const GDNativeExtensionClassMethodInfo *p_method_info) {
+ NativeExtension *self = reinterpret_cast<NativeExtension *>(p_library);
StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
StringName method_name = *reinterpret_cast<const StringName *>(p_method_info->name);
@@ -204,8 +204,8 @@ void NativeExtension::_register_extension_class_method(const GDNativeExtensionCl
ClassDB::bind_method_custom(class_name, method);
}
-void NativeExtension::_register_extension_class_integer_constant(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeStringNamePtr p_enum_name, const GDNativeStringNamePtr p_constant_name, GDNativeInt p_constant_value, GDNativeBool p_is_bitfield) {
- NativeExtension *self = static_cast<NativeExtension *>(p_library);
+void NativeExtension::_register_extension_class_integer_constant(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, GDNativeConstStringNamePtr p_enum_name, GDNativeConstStringNamePtr p_constant_name, GDNativeInt p_constant_value, GDNativeBool p_is_bitfield) {
+ NativeExtension *self = reinterpret_cast<NativeExtension *>(p_library);
StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
StringName enum_name = *reinterpret_cast<const StringName *>(p_enum_name);
@@ -215,8 +215,8 @@ void NativeExtension::_register_extension_class_integer_constant(const GDNativeE
ClassDB::bind_integer_constant(class_name, enum_name, constant_name, p_constant_value, p_is_bitfield);
}
-void NativeExtension::_register_extension_class_property(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativePropertyInfo *p_info, const GDNativeStringNamePtr p_setter, const GDNativeStringNamePtr p_getter) {
- NativeExtension *self = static_cast<NativeExtension *>(p_library);
+void NativeExtension::_register_extension_class_property(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, const GDNativePropertyInfo *p_info, GDNativeConstStringNamePtr p_setter, GDNativeConstStringNamePtr p_getter) {
+ NativeExtension *self = reinterpret_cast<NativeExtension *>(p_library);
StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
StringName setter = *reinterpret_cast<const StringName *>(p_setter);
@@ -230,8 +230,8 @@ void NativeExtension::_register_extension_class_property(const GDNativeExtension
ClassDB::add_property(class_name, pinfo, setter, getter);
}
-void NativeExtension::_register_extension_class_property_group(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeStringPtr p_group_name, const GDNativeStringPtr p_prefix) {
- NativeExtension *self = static_cast<NativeExtension *>(p_library);
+void NativeExtension::_register_extension_class_property_group(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, GDNativeConstStringPtr p_group_name, GDNativeConstStringPtr p_prefix) {
+ NativeExtension *self = reinterpret_cast<NativeExtension *>(p_library);
StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
String group_name = *reinterpret_cast<const String *>(p_group_name);
@@ -241,8 +241,8 @@ void NativeExtension::_register_extension_class_property_group(const GDNativeExt
ClassDB::add_property_group(class_name, group_name, prefix);
}
-void NativeExtension::_register_extension_class_property_subgroup(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeStringPtr p_subgroup_name, const GDNativeStringPtr p_prefix) {
- NativeExtension *self = static_cast<NativeExtension *>(p_library);
+void NativeExtension::_register_extension_class_property_subgroup(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, GDNativeConstStringPtr p_subgroup_name, GDNativeConstStringPtr p_prefix) {
+ NativeExtension *self = reinterpret_cast<NativeExtension *>(p_library);
StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
String subgroup_name = *reinterpret_cast<const String *>(p_subgroup_name);
@@ -252,8 +252,8 @@ void NativeExtension::_register_extension_class_property_subgroup(const GDNative
ClassDB::add_property_subgroup(class_name, subgroup_name, prefix);
}
-void NativeExtension::_register_extension_class_signal(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeStringNamePtr p_signal_name, const GDNativePropertyInfo *p_argument_info, GDNativeInt p_argument_count) {
- NativeExtension *self = static_cast<NativeExtension *>(p_library);
+void NativeExtension::_register_extension_class_signal(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, GDNativeConstStringNamePtr p_signal_name, const GDNativePropertyInfo *p_argument_info, GDNativeInt p_argument_count) {
+ NativeExtension *self = reinterpret_cast<NativeExtension *>(p_library);
StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
StringName signal_name = *reinterpret_cast<const StringName *>(p_signal_name);
@@ -268,8 +268,8 @@ void NativeExtension::_register_extension_class_signal(const GDNativeExtensionCl
ClassDB::add_signal(class_name, s);
}
-void NativeExtension::_unregister_extension_class(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name) {
- NativeExtension *self = static_cast<NativeExtension *>(p_library);
+void NativeExtension::_unregister_extension_class(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name) {
+ NativeExtension *self = reinterpret_cast<NativeExtension *>(p_library);
StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to unregister unexisting extension class '" + class_name + "'.");
@@ -283,8 +283,8 @@ void NativeExtension::_unregister_extension_class(const GDNativeExtensionClassLi
self->extension_classes.erase(class_name);
}
-void NativeExtension::_get_library_path(const GDNativeExtensionClassLibraryPtr p_library, GDNativeStringPtr r_path) {
- NativeExtension *self = static_cast<NativeExtension *>(p_library);
+void NativeExtension::_get_library_path(GDNativeExtensionClassLibraryPtr p_library, GDNativeStringPtr r_path) {
+ NativeExtension *self = reinterpret_cast<NativeExtension *>(p_library);
*(String *)r_path = self->library_path;
}
diff --git a/core/extension/native_extension.h b/core/extension/native_extension.h
index 70f6f9f039..ca6200cd1e 100644
--- a/core/extension/native_extension.h
+++ b/core/extension/native_extension.h
@@ -47,15 +47,15 @@ class NativeExtension : public Resource {
HashMap<StringName, Extension> extension_classes;
- static void _register_extension_class(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeStringNamePtr p_parent_class_name, const GDNativeExtensionClassCreationInfo *p_extension_funcs);
- static void _register_extension_class_method(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeExtensionClassMethodInfo *p_method_info);
- static void _register_extension_class_integer_constant(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeStringNamePtr p_enum_name, const GDNativeStringNamePtr p_constant_name, GDNativeInt p_constant_value, GDNativeBool p_is_bitfield);
- static void _register_extension_class_property(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativePropertyInfo *p_info, const GDNativeStringNamePtr p_setter, const GDNativeStringNamePtr p_getter);
- static void _register_extension_class_property_group(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeStringNamePtr p_group_name, const GDNativeStringNamePtr p_prefix);
- static void _register_extension_class_property_subgroup(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeStringNamePtr p_subgroup_name, const GDNativeStringNamePtr p_prefix);
- static void _register_extension_class_signal(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeStringNamePtr p_signal_name, const GDNativePropertyInfo *p_argument_info, GDNativeInt p_argument_count);
- static void _unregister_extension_class(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name);
- static void _get_library_path(const GDNativeExtensionClassLibraryPtr p_library, GDNativeStringPtr r_path);
+ static void _register_extension_class(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, GDNativeConstStringNamePtr p_parent_class_name, const GDNativeExtensionClassCreationInfo *p_extension_funcs);
+ static void _register_extension_class_method(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, const GDNativeExtensionClassMethodInfo *p_method_info);
+ static void _register_extension_class_integer_constant(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, GDNativeConstStringNamePtr p_enum_name, GDNativeConstStringNamePtr p_constant_name, GDNativeInt p_constant_value, GDNativeBool p_is_bitfield);
+ static void _register_extension_class_property(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, const GDNativePropertyInfo *p_info, GDNativeConstStringNamePtr p_setter, GDNativeConstStringNamePtr p_getter);
+ static void _register_extension_class_property_group(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, GDNativeConstStringNamePtr p_group_name, GDNativeConstStringNamePtr p_prefix);
+ static void _register_extension_class_property_subgroup(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, GDNativeConstStringNamePtr p_subgroup_name, GDNativeConstStringNamePtr p_prefix);
+ static void _register_extension_class_signal(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, GDNativeConstStringNamePtr p_signal_name, const GDNativePropertyInfo *p_argument_info, GDNativeInt p_argument_count);
+ static void _unregister_extension_class(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name);
+ static void _get_library_path(GDNativeExtensionClassLibraryPtr p_library, GDNativeStringPtr r_path);
GDNativeInitialization initialization;
int32_t level_initialized = -1;
diff --git a/core/io/image.cpp b/core/io/image.cpp
index 21146dd80c..1b9538794a 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -3798,6 +3798,19 @@ void Image::convert_ra_rgba8_to_rg() {
}
}
+void Image::convert_rgba8_to_bgra8() {
+ ERR_FAIL_COND(format != FORMAT_RGBA8);
+ ERR_FAIL_COND(!data.size());
+
+ int s = data.size();
+ uint8_t *w = data.ptrw();
+ for (int i = 0; i < s; i += 4) {
+ uint8_t r = w[i];
+ w[i] = w[i + 2]; // Swap R to B
+ w[i + 2] = r; // Swap B to R
+ }
+}
+
Error Image::_load_from_buffer(const Vector<uint8_t> &p_array, ImageMemLoadFunc p_loader) {
int buffer_size = p_array.size();
diff --git a/core/io/image.h b/core/io/image.h
index 62df81e7c8..ad5c0b4a04 100644
--- a/core/io/image.h
+++ b/core/io/image.h
@@ -391,6 +391,7 @@ public:
void convert_rg_to_ra_rgba8();
void convert_ra_rgba8_to_rg();
+ void convert_rgba8_to_bgra8();
Image(const uint8_t *p_mem_png_jpg, int p_len = -1);
Image(const char **p_xpm);
diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py
index 61bf6d900a..c92ac0961e 100644
--- a/core/object/make_virtuals.py
+++ b/core/object/make_virtuals.py
@@ -94,7 +94,7 @@ def generate_version(argcount, const=False, returns=False):
argtext += ", "
callsiargs = "Variant vargs[" + str(argcount) + "]={"
callsiargptrs = "\t\tconst Variant *vargptrs[" + str(argcount) + "]={"
- callptrargsptr = "\t\tconst GDNativeTypePtr argptrs[" + str(argcount) + "]={"
+ callptrargsptr = "\t\tGDNativeConstTypePtr argptrs[" + str(argcount) + "]={"
callptrargs = ""
for i in range(argcount):
if i > 0:
@@ -121,7 +121,7 @@ def generate_version(argcount, const=False, returns=False):
s = s.replace("$CALLSIARGPASS", "(const Variant **)vargptrs," + str(argcount))
callptrargsptr += "};\\\n"
s = s.replace("$CALLPTRARGS", callptrargs + callptrargsptr)
- s = s.replace("$CALLPTRARGPASS", "(const GDNativeTypePtr*)argptrs")
+ s = s.replace("$CALLPTRARGPASS", "reinterpret_cast<GDNativeConstTypePtr*>(argptrs)")
else:
s = s.replace("$CALLSIARGS", "")
s = s.replace("$CALLSIARGPASS", "nullptr, 0")
diff --git a/core/object/method_bind.h b/core/object/method_bind.h
index 598e8a224d..0f1366aefd 100644
--- a/core/object/method_bind.h
+++ b/core/object/method_bind.h
@@ -110,8 +110,8 @@ public:
_FORCE_INLINE_ int get_argument_count() const { return argument_count; };
- virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) = 0;
- virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) = 0;
+ virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const = 0;
+ virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const = 0;
StringName get_name() const;
void set_name(const StringName &p_name);
@@ -158,7 +158,7 @@ public:
}
#endif
- virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) override {
+ virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override {
ERR_FAIL(); // Can't call.
}
@@ -210,7 +210,7 @@ class MethodBindVarArgT : public MethodBindVarArgBase<MethodBindVarArgT<T>, T, v
friend class MethodBindVarArgBase<MethodBindVarArgT<T>, T, void, false>;
public:
- virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) override {
+ virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override {
(static_cast<T *>(p_object)->*MethodBindVarArgBase<MethodBindVarArgT<T>, T, void, false>::method)(p_args, p_arg_count, r_error);
return {};
}
@@ -246,7 +246,7 @@ public:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
- virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) override {
+ virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override {
return (static_cast<T *>(p_object)->*MethodBindVarArgBase<MethodBindVarArgTR<T, R>, T, R, true>::method)(p_args, p_arg_count, r_error);
}
#if defined(SANITIZERS_ENABLED) && defined(__GNUC__) && !defined(__clang__)
@@ -313,7 +313,7 @@ public:
}
#endif
- virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) override {
+ virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override {
#ifdef TYPED_METHOD_BIND
call_with_variant_args_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, r_error, get_default_arguments());
#else
@@ -322,7 +322,7 @@ public:
return Variant();
}
- virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) override {
+ virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override {
#ifdef TYPED_METHOD_BIND
call_with_ptr_args<T, P...>(static_cast<T *>(p_object), method, p_args);
#else
@@ -380,7 +380,7 @@ public:
}
#endif
- virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) override {
+ virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override {
#ifdef TYPED_METHOD_BIND
call_with_variant_argsc_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, r_error, get_default_arguments());
#else
@@ -389,7 +389,7 @@ public:
return Variant();
}
- virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) override {
+ virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override {
#ifdef TYPED_METHOD_BIND
call_with_ptr_argsc<T, P...>(static_cast<T *>(p_object), method, p_args);
#else
@@ -457,7 +457,7 @@ public:
}
#endif
- virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) override {
+ virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override {
Variant ret;
#ifdef TYPED_METHOD_BIND
call_with_variant_args_ret_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, ret, r_error, get_default_arguments());
@@ -467,7 +467,7 @@ public:
return ret;
}
- virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) override {
+ virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override {
#ifdef TYPED_METHOD_BIND
call_with_ptr_args_ret<T, R, P...>(static_cast<T *>(p_object), method, p_args, r_ret);
#else
@@ -536,7 +536,7 @@ public:
}
#endif
- virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) override {
+ virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override {
Variant ret;
#ifdef TYPED_METHOD_BIND
call_with_variant_args_retc_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, ret, r_error, get_default_arguments());
@@ -546,7 +546,7 @@ public:
return ret;
}
- virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) override {
+ virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override {
#ifdef TYPED_METHOD_BIND
call_with_ptr_args_retc<T, R, P...>(static_cast<T *>(p_object), method, p_args, r_ret);
#else
@@ -604,13 +604,13 @@ public:
}
#endif
- virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) override {
+ virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override {
(void)p_object; // unused
call_with_variant_args_static_dv(function, p_args, p_arg_count, r_error, get_default_arguments());
return Variant();
}
- virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) override {
+ virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override {
(void)p_object;
(void)r_ret;
call_with_ptr_args_static_method(function, p_args);
@@ -667,13 +667,13 @@ public:
}
#endif
- virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) override {
+ virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override {
Variant ret;
call_with_variant_args_static_ret_dv(function, p_args, p_arg_count, ret, r_error, get_default_arguments());
return ret;
}
- virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) override {
+ virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override {
(void)p_object;
call_with_ptr_args_static_method_ret(function, p_args, r_ret);
}
diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h
index 9a2a176096..5262c41644 100644
--- a/core/object/script_language_extension.h
+++ b/core/object/script_language_extension.h
@@ -650,13 +650,13 @@ public:
virtual bool set(const StringName &p_name, const Variant &p_value) override {
if (native_info->set_func) {
- return native_info->set_func(instance, (const GDNativeStringNamePtr)&p_name, (const GDNativeVariantPtr)&p_value);
+ return native_info->set_func(instance, (GDNativeConstStringNamePtr)&p_name, (GDNativeConstVariantPtr)&p_value);
}
return false;
}
virtual bool get(const StringName &p_name, Variant &r_ret) const override {
if (native_info->get_func) {
- return native_info->get_func(instance, (const GDNativeStringNamePtr)&p_name, (GDNativeVariantPtr)&r_ret);
+ return native_info->get_func(instance, (GDNativeConstStringNamePtr)&p_name, (GDNativeVariantPtr)&r_ret);
}
return false;
}
@@ -683,7 +683,7 @@ public:
virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = nullptr) const override {
if (native_info->get_property_type_func) {
GDNativeBool is_valid = 0;
- GDNativeVariantType type = native_info->get_property_type_func(instance, (const GDNativeStringNamePtr)&p_name, &is_valid);
+ GDNativeVariantType type = native_info->get_property_type_func(instance, (GDNativeConstStringNamePtr)&p_name, &is_valid);
if (r_is_valid) {
*r_is_valid = is_valid != 0;
}
@@ -694,13 +694,13 @@ public:
virtual bool property_can_revert(const StringName &p_name) const override {
if (native_info->property_can_revert_func) {
- return native_info->property_can_revert_func(instance, (const GDNativeStringNamePtr)&p_name);
+ return native_info->property_can_revert_func(instance, (GDNativeConstStringNamePtr)&p_name);
}
return false;
}
virtual bool property_get_revert(const StringName &p_name, Variant &r_ret) const override {
if (native_info->property_get_revert_func) {
- return native_info->property_get_revert_func(instance, (const GDNativeStringNamePtr)&p_name, (GDNativeVariantPtr)&r_ret);
+ return native_info->property_get_revert_func(instance, (GDNativeConstStringNamePtr)&p_name, (GDNativeVariantPtr)&r_ret);
}
return false;
}
@@ -711,7 +711,7 @@ public:
}
return nullptr;
}
- static void _add_property_with_state(const GDNativeStringNamePtr p_name, const GDNativeVariantPtr p_value, void *p_userdata) {
+ static void _add_property_with_state(GDNativeConstStringNamePtr p_name, GDNativeConstVariantPtr p_value, void *p_userdata) {
List<Pair<StringName, Variant>> *state = (List<Pair<StringName, Variant>> *)p_userdata;
state->push_back(Pair<StringName, Variant>(*(const StringName *)p_name, *(const Variant *)p_value));
}
@@ -744,7 +744,7 @@ public:
Variant ret;
if (native_info->call_func) {
GDNativeCallError ce;
- native_info->call_func(instance, (const GDNativeStringNamePtr)&p_method, (const GDNativeVariantPtr *)p_args, p_argcount, (GDNativeVariantPtr)&ret, &ce);
+ native_info->call_func(instance, (GDNativeConstStringNamePtr)&p_method, (GDNativeConstVariantPtr *)p_args, p_argcount, (GDNativeVariantPtr)&ret, &ce);
r_error.error = Callable::CallError::Error(ce.error);
r_error.argument = ce.argument;
r_error.expected = ce.expected;
@@ -799,7 +799,7 @@ public:
virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid) override {
if (native_info->set_fallback_func) {
- bool ret = native_info->set_fallback_func(instance, (const GDNativeStringNamePtr)&p_name, (const GDNativeVariantPtr)&p_value);
+ bool ret = native_info->set_fallback_func(instance, (GDNativeConstStringNamePtr)&p_name, (GDNativeConstVariantPtr)&p_value);
if (r_valid) {
*r_valid = ret;
}
@@ -808,7 +808,7 @@ public:
virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid) override {
Variant ret;
if (native_info->get_fallback_func) {
- bool valid = native_info->get_fallback_func(instance, (const GDNativeStringNamePtr)&p_name, (GDNativeVariantPtr)&ret);
+ bool valid = native_info->get_fallback_func(instance, (GDNativeConstStringNamePtr)&p_name, (GDNativeVariantPtr)&ret);
if (r_valid) {
*r_valid = valid;
}
diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml
index 80e0c81509..58681090b4 100644
--- a/doc/classes/Animation.xml
+++ b/doc/classes/Animation.xml
@@ -616,5 +616,14 @@
<constant name="LOOP_PINGPONG" value="2" enum="LoopMode">
Repeats playback and reverse playback at both ends of the animation.
</constant>
+ <constant name="LOOPED_FLAG_NONE" value="0" enum="LoopedFlag">
+ This flag indicates that the animation proceeds without any looping.
+ </constant>
+ <constant name="LOOPED_FLAG_END" value="1" enum="LoopedFlag">
+ This flag indicates that the animation has reached the end of the animation and just after loop processed.
+ </constant>
+ <constant name="LOOPED_FLAG_START" value="2" enum="LoopedFlag">
+ This flag indicates that the animation has reached the start of the animation and just after loop processed.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/AnimationLibrary.xml b/doc/classes/AnimationLibrary.xml
index 769b338063..9be4cda5d6 100644
--- a/doc/classes/AnimationLibrary.xml
+++ b/doc/classes/AnimationLibrary.xml
@@ -65,6 +65,13 @@
Emitted when an [Animation] is added, under the key [param name].
</description>
</signal>
+ <signal name="animation_changed">
+ <param index="0" name="name" type="StringName" />
+ <description>
+ Emitted when there's a change in one of the animations, e.g. tracks are added, moved or have changed paths. [param name] is the key of the animation that was changed.
+ See also [signal Resource.changed], which this acts as a relay for.
+ </description>
+ </signal>
<signal name="animation_removed">
<param index="0" name="name" type="StringName" />
<description>
diff --git a/doc/classes/AnimationNode.xml b/doc/classes/AnimationNode.xml
index 915fbf53cd..79deb008d2 100644
--- a/doc/classes/AnimationNode.xml
+++ b/doc/classes/AnimationNode.xml
@@ -75,9 +75,10 @@
<param index="3" name="seeked" type="bool" />
<param index="4" name="is_external_seeking" type="bool" />
<param index="5" name="blend" type="float" />
- <param index="6" name="pingponged" type="int" default="0" />
+ <param index="6" name="looped_flag" type="int" enum="Animation.LoopedFlag" default="0" />
<description>
Blend an animation by [param blend] amount (name must be valid in the linked [AnimationPlayer]). A [param time] and [param delta] may be passed, as well as whether [param seeked] happened.
+ A [param looped_flag] is used by internal processing immediately after the loop. See also [enum Animation.LoopedFlag].
</description>
</method>
<method name="blend_input">
diff --git a/doc/classes/BaseMaterial3D.xml b/doc/classes/BaseMaterial3D.xml
index fbb31cadcd..8a5048de6b 100644
--- a/doc/classes/BaseMaterial3D.xml
+++ b/doc/classes/BaseMaterial3D.xml
@@ -319,7 +319,7 @@
Texture that controls the strength of the refraction per-pixel. Multiplied by [member refraction_scale].
</member>
<member name="refraction_texture_channel" type="int" setter="set_refraction_texture_channel" getter="get_refraction_texture_channel" enum="BaseMaterial3D.TextureChannel" default="0">
- Specifies the channel of the [member ao_texture] in which the ambient occlusion information is stored. This is useful when you store the information for multiple effects in a single texture. For example if you stored metallic in the red channel, roughness in the blue, and ambient occlusion in the green you could reduce the number of textures you use.
+ Specifies the channel of the [member refraction_texture] in which the refraction information is stored. This is useful when you store the information for multiple effects in a single texture. For example if you stored refraction in the red channel, roughness in the blue, and ambient occlusion in the green you could reduce the number of textures you use.
</member>
<member name="rim" type="float" setter="set_rim" getter="get_rim" default="1.0">
Sets the strength of the rim lighting effect.
@@ -341,7 +341,7 @@
Texture used to control the roughness per-pixel. Multiplied by [member roughness].
</member>
<member name="roughness_texture_channel" type="int" setter="set_roughness_texture_channel" getter="get_roughness_texture_channel" enum="BaseMaterial3D.TextureChannel" default="0">
- Specifies the channel of the [member ao_texture] in which the ambient occlusion information is stored. This is useful when you store the information for multiple effects in a single texture. For example if you stored metallic in the red channel, roughness in the blue, and ambient occlusion in the green you could reduce the number of textures you use.
+ Specifies the channel of the [member roughness_texture] in which the roughness information is stored. This is useful when you store the information for multiple effects in a single texture. For example if you stored metallic in the red channel, roughness in the blue, and ambient occlusion in the green you could reduce the number of textures you use.
</member>
<member name="shading_mode" type="int" setter="set_shading_mode" getter="get_shading_mode" enum="BaseMaterial3D.ShadingMode" default="1">
Sets whether the shading takes place per-pixel or per-vertex. Per-vertex lighting is faster, making it the best choice for mobile applications, however it looks considerably worse than per-pixel.
diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml
index 50bbbc71a0..499d53e63c 100644
--- a/doc/classes/CanvasItem.xml
+++ b/doc/classes/CanvasItem.xml
@@ -510,7 +510,7 @@
<method name="move_to_front">
<return type="void" />
<description>
- Moves this node to display on top of its siblings. This has more use in [Control], as [Node2D] can be ordered with [member Node2D.z_index].
+ Moves this node to display on top of its siblings.
Internally, the node is moved to the bottom of parent's children list. The method has no effect on nodes without a parent.
</description>
</method>
@@ -587,6 +587,17 @@
If [code]true[/code], this [CanvasItem] is drawn. The node is only visible if all of its antecedents are visible as well (in other words, [method is_visible_in_tree] must return [code]true[/code]).
[b]Note:[/b] For controls that inherit [Popup], the correct way to make them visible is to call one of the multiple [code]popup*()[/code] functions instead.
</member>
+ <member name="y_sort_enabled" type="bool" setter="set_y_sort_enabled" getter="is_y_sort_enabled" default="false">
+ If [code]true[/code], child nodes with the lowest Y position are drawn before those with a higher Y position. If [code]false[/code], Y-sorting is disabled. Y-sorting only affects children that inherit from [CanvasItem].
+ You can nest nodes with Y-sorting. Child Y-sorted nodes are sorted in the same space as the parent Y-sort. This feature allows you to organize a scene better or divide it into multiple ones without changing your scene tree.
+ </member>
+ <member name="z_as_relative" type="bool" setter="set_z_as_relative" getter="is_z_relative" default="true">
+ If [code]true[/code], the node's Z index is relative to its parent's Z index. If this node's Z index is 2 and its parent's effective Z index is 3, then this node's effective Z index will be 2 + 3 = 5.
+ </member>
+ <member name="z_index" type="int" setter="set_z_index" getter="get_z_index" default="0">
+ Z index. Controls the order in which the nodes render. A node with a higher Z index will display in front of others. Must be between [constant RenderingServer.CANVAS_ITEM_Z_MIN] and [constant RenderingServer.CANVAS_ITEM_Z_MAX] (inclusive).
+ [b]Note:[/b] Changing the Z index of a [Control] only affects the drawing order, not the order in which input events are handled. This can be useful to implement certain UI animations, e.g. a menu where hovered items are scaled and should overlap others.
+ </member>
</members>
<signals>
<signal name="draw">
diff --git a/doc/classes/CharFXTransform.xml b/doc/classes/CharFXTransform.xml
index c98b194a4d..a6f707383f 100644
--- a/doc/classes/CharFXTransform.xml
+++ b/doc/classes/CharFXTransform.xml
@@ -46,6 +46,9 @@
<member name="range" type="Vector2i" setter="set_range" getter="get_range" default="Vector2i(0, 0)">
Absolute character range in the string, corresponding to the glyph. Setting this property won't affect drawing.
</member>
+ <member name="relative_index" type="int" setter="set_relative_index" getter="get_relative_index" default="0">
+ The character offset of the glyph, relative to the current [RichTextEffect] custom block. Setting this property won't affect drawing.
+ </member>
<member name="visible" type="bool" setter="set_visibility" getter="is_visible" default="true">
If [code]true[/code], the character will be drawn. If [code]false[/code], the character will be hidden. Characters around hidden characters will reflow to take the space of hidden characters. If this is not desired, set their [member color] to [code]Color(1, 1, 1, 0)[/code] instead.
</member>
diff --git a/doc/classes/Curve3D.xml b/doc/classes/Curve3D.xml
index 67e4e45e52..851996131f 100644
--- a/doc/classes/Curve3D.xml
+++ b/doc/classes/Curve3D.xml
@@ -64,7 +64,7 @@
<return type="Vector3" />
<param index="0" name="to_point" type="Vector3" />
<description>
- Returns the closest baked point (in curve's local space) to [param to_point].
+ Returns the closest point on baked segments (in curve's local space) to [param to_point].
[param to_point] must be in this curve's local space.
</description>
</method>
diff --git a/doc/classes/GraphEdit.xml b/doc/classes/GraphEdit.xml
index 5050ce7715..ea4e4b53ba 100644
--- a/doc/classes/GraphEdit.xml
+++ b/doc/classes/GraphEdit.xml
@@ -291,7 +291,7 @@
</description>
</signal>
<signal name="connection_drag_started">
- <param index="0" name="from_node" type="String" />
+ <param index="0" name="from_node" type="StringName" />
<param index="1" name="from_port" type="int" />
<param index="2" name="is_output" type="bool" />
<description>
diff --git a/doc/classes/Node2D.xml b/doc/classes/Node2D.xml
index 054ab532af..26efe37dce 100644
--- a/doc/classes/Node2D.xml
+++ b/doc/classes/Node2D.xml
@@ -125,15 +125,5 @@
<member name="transform" type="Transform2D" setter="set_transform" getter="get_transform">
Local [Transform2D].
</member>
- <member name="y_sort_enabled" type="bool" setter="set_y_sort_enabled" getter="is_y_sort_enabled" default="false">
- If [code]true[/code], child nodes with the lowest Y position are drawn before those with a higher Y position. If [code]false[/code], Y-sorting is disabled. Y-sorting only affects children that inherit from [CanvasItem].
- You can nest nodes with Y-sorting. Child Y-sorted nodes are sorted in the same space as the parent Y-sort. This feature allows you to organize a scene better or divide it into multiple ones without changing your scene tree.
- </member>
- <member name="z_as_relative" type="bool" setter="set_z_as_relative" getter="is_z_relative" default="true">
- If [code]true[/code], the node's Z index is relative to its parent's Z index. If this node's Z index is 2 and its parent's effective Z index is 3, then this node's effective Z index will be 2 + 3 = 5.
- </member>
- <member name="z_index" type="int" setter="set_z_index" getter="get_z_index" default="0">
- Z index. Controls the order in which the nodes render. A node with a higher Z index will display in front of others. Must be between [constant RenderingServer.CANVAS_ITEM_Z_MIN] and [constant RenderingServer.CANVAS_ITEM_Z_MAX] (inclusive).
- </member>
</members>
</class>
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index 07d56b156c..e5d4077393 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -106,6 +106,7 @@ void RasterizerCanvasGLES3::_update_transform_to_mat4(const Transform3D &p_trans
void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_light_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) {
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
+ GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton();
Transform2D canvas_transform_inverse = p_canvas_transform.affine_inverse();
@@ -311,9 +312,14 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
Size2i ssize = texture_storage->render_target_get_size(p_to_render_target);
+ // If we've overridden the render target's color texture, then we need
+ // to invert the Y axis, so 2D texture appear right side up.
+ // We're probably rendering directly to an XR device.
+ float y_scale = texture_storage->render_target_get_override_color(p_to_render_target).is_valid() ? -2.0f : 2.0f;
+
Transform3D screen_transform;
screen_transform.translate_local(-(ssize.width / 2.0f), -(ssize.height / 2.0f), 0.0f);
- screen_transform.scale(Vector3(2.0f / ssize.width, 2.0f / ssize.height, 1.0f));
+ screen_transform.scale(Vector3(2.0f / ssize.width, y_scale / ssize.height, 1.0f));
_update_transform_to_mat4(screen_transform, state_buffer.screen_transform);
_update_transform_2d_to_mat4(p_canvas_transform, state_buffer.canvas_transform);
@@ -384,6 +390,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
Rect2 back_buffer_rect;
bool backbuffer_copy = false;
bool backbuffer_gen_mipmaps = false;
+ bool update_skeletons = false;
Item *ci = p_item_list;
Item *canvas_group_owner = nullptr;
@@ -425,8 +432,27 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
}
}
+ if (ci->skeleton.is_valid()) {
+ const Item::Command *c = ci->commands;
+
+ while (c) {
+ if (c->type == Item::Command::TYPE_MESH) {
+ const Item::CommandMesh *cm = static_cast<const Item::CommandMesh *>(c);
+ if (cm->mesh_instance.is_valid()) {
+ mesh_storage->mesh_instance_check_for_update(cm->mesh_instance);
+ update_skeletons = true;
+ }
+ }
+ c = c->next;
+ }
+ }
+
if (ci->canvas_group_owner != nullptr) {
if (canvas_group_owner == nullptr) {
+ if (update_skeletons) {
+ mesh_storage->update_mesh_instances();
+ update_skeletons = false;
+ }
// Canvas group begins here, render until before this item
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, false);
item_count = 0;
@@ -455,6 +481,10 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
}
if (ci == canvas_group_owner) {
+ if (update_skeletons) {
+ mesh_storage->update_mesh_instances();
+ update_skeletons = false;
+ }
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, true);
item_count = 0;
@@ -468,6 +498,10 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
}
if (backbuffer_copy) {
+ if (update_skeletons) {
+ mesh_storage->update_mesh_instances();
+ update_skeletons = false;
+ }
//render anything pending, including clearing if no items
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, false);
@@ -492,6 +526,10 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
items[item_count++] = ci;
if (!ci->next || item_count == MAX_RENDER_ITEMS - 1) {
+ if (update_skeletons) {
+ mesh_storage->update_mesh_instances();
+ update_skeletons = false;
+ }
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, false);
//then reset
item_count = 0;
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 026ec85e6b..8250140c3f 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -420,6 +420,11 @@ void RasterizerSceneGLES3::_geometry_instance_update(RenderGeometryInstance *p_g
}
} else if (ginstance->data->base_type == RS::INSTANCE_MESH) {
+ if (mesh_storage->skeleton_is_valid(ginstance->data->skeleton)) {
+ if (ginstance->data->dirty_dependencies) {
+ mesh_storage->skeleton_update_dependency(ginstance->data->skeleton, &ginstance->data->dependency_tracker);
+ }
+ }
}
ginstance->store_transform_cache = store_transform;
diff --git a/drivers/gles3/shaders/SCsub b/drivers/gles3/shaders/SCsub
index 2686b1aa48..34713e7e29 100644
--- a/drivers/gles3/shaders/SCsub
+++ b/drivers/gles3/shaders/SCsub
@@ -21,3 +21,4 @@ if "GLES3_GLSL" in env["BUILDERS"]:
env.GLES3_GLSL("canvas_sdf.glsl")
env.GLES3_GLSL("particles.glsl")
env.GLES3_GLSL("particles_copy.glsl")
+ env.GLES3_GLSL("skeleton.glsl")
diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl
index cdae05a516..60139de472 100644
--- a/drivers/gles3/shaders/canvas.glsl
+++ b/drivers/gles3/shaders/canvas.glsl
@@ -19,9 +19,6 @@ layout(location = 0) in vec2 vertex_attrib;
layout(location = 3) in vec4 color_attrib;
layout(location = 4) in vec2 uv_attrib;
-layout(location = 10) in uvec4 bone_attrib;
-layout(location = 11) in vec4 weight_attrib;
-
#ifdef USE_INSTANCING
layout(location = 1) in highp vec4 instance_xform0;
@@ -81,8 +78,6 @@ void main() {
uv = draw_data[draw_data_instance].uv_c;
color = vec4(unpackHalf2x16(draw_data[draw_data_instance].color_c_rg), unpackHalf2x16(draw_data[draw_data_instance].color_c_ba));
}
- uvec4 bones = uvec4(0, 0, 0, 0);
- vec4 bone_weights = vec4(0.0);
#elif defined(USE_ATTRIBUTES)
draw_data_instance = gl_InstanceID;
@@ -93,9 +88,6 @@ void main() {
vec4 color = color_attrib * draw_data[draw_data_instance].modulation;
vec2 uv = uv_attrib;
- uvec4 bones = bone_attrib;
- vec4 bone_weights = weight_attrib;
-
#ifdef USE_INSTANCING
vec4 instance_color = vec4(unpackHalf2x16(instance_color_custom_data.x), unpackHalf2x16(instance_color_custom_data.y));
color *= instance_color;
@@ -110,7 +102,6 @@ void main() {
vec2 uv = draw_data[draw_data_instance].src_rect.xy + abs(draw_data[draw_data_instance].src_rect.zw) * ((draw_data[draw_data_instance].flags & FLAGS_TRANSPOSE_RECT) != uint(0) ? vertex_base.yx : vertex_base.xy);
vec4 color = draw_data[draw_data_instance].modulation;
vec2 vertex = draw_data[draw_data_instance].dst_rect.xy + abs(draw_data[draw_data_instance].dst_rect.zw) * mix(vertex_base, vec2(1.0, 1.0) - vertex_base, lessThan(draw_data[draw_data_instance].src_rect.zw, vec2(0.0, 0.0)));
- uvec4 bones = uvec4(0, 0, 0, 0);
#endif
diff --git a/drivers/gles3/shaders/skeleton.glsl b/drivers/gles3/shaders/skeleton.glsl
new file mode 100644
index 0000000000..a1e3c098f4
--- /dev/null
+++ b/drivers/gles3/shaders/skeleton.glsl
@@ -0,0 +1,269 @@
+/* clang-format off */
+#[modes]
+
+mode_base_pass =
+mode_blend_pass = #define MODE_BLEND_PASS
+
+#[specializations]
+
+MODE_2D = true
+USE_BLEND_SHAPES = false
+USE_SKELETON = false
+USE_NORMAL = false
+USE_TANGENT = false
+FINAL_PASS = false
+USE_EIGHT_WEIGHTS = false
+
+#[vertex]
+
+#include "stdlib_inc.glsl"
+
+#ifdef MODE_2D
+#define VFORMAT vec2
+#else
+#define VFORMAT vec3
+#endif
+
+#ifdef FINAL_PASS
+#define OFORMAT vec2
+#else
+#define OFORMAT uvec2
+#endif
+
+// These come from the source mesh and the output from previous passes.
+layout(location = 0) in highp VFORMAT in_vertex;
+#ifdef MODE_BLEND_PASS
+#ifdef USE_NORMAL
+layout(location = 1) in highp uvec2 in_normal;
+#endif
+#ifdef USE_TANGENT
+layout(location = 2) in highp uvec2 in_tangent;
+#endif
+#else // MODE_BLEND_PASS
+#ifdef USE_NORMAL
+layout(location = 1) in highp vec2 in_normal;
+#endif
+#ifdef USE_TANGENT
+layout(location = 2) in highp vec2 in_tangent;
+#endif
+#endif // MODE_BLEND_PASS
+
+#ifdef USE_SKELETON
+#ifdef USE_EIGHT_WEIGHTS
+layout(location = 10) in highp uvec4 in_bone_attrib;
+layout(location = 11) in highp uvec4 in_bone_attrib2;
+layout(location = 12) in mediump vec4 in_weight_attrib;
+layout(location = 13) in mediump vec4 in_weight_attrib2;
+#else
+layout(location = 10) in highp uvec4 in_bone_attrib;
+layout(location = 11) in mediump vec4 in_weight_attrib;
+#endif
+
+uniform mediump sampler2D skeleton_texture; // texunit:0
+#endif
+
+/* clang-format on */
+#ifdef MODE_BLEND_PASS
+layout(location = 3) in highp VFORMAT blend_vertex;
+#ifdef USE_NORMAL
+layout(location = 4) in highp vec2 blend_normal;
+#endif
+#ifdef USE_TANGENT
+layout(location = 5) in highp vec2 blend_tangent;
+#endif
+#endif // MODE_BLEND_PASS
+
+out highp VFORMAT out_vertex; //tfb:
+
+#ifdef USE_NORMAL
+flat out highp OFORMAT out_normal; //tfb:USE_NORMAL
+#endif
+#ifdef USE_TANGENT
+flat out highp OFORMAT out_tangent; //tfb:USE_TANGENT
+#endif
+
+#ifdef USE_BLEND_SHAPES
+uniform highp float blend_weight;
+uniform lowp float blend_shape_count;
+#endif
+
+vec2 signNotZero(vec2 v) {
+ return mix(vec2(-1.0), vec2(1.0), greaterThanEqual(v.xy, vec2(0.0)));
+}
+
+vec3 oct_to_vec3(vec2 oct) {
+ oct = oct * 2.0 - 1.0;
+ vec3 v = vec3(oct.xy, 1.0 - abs(oct.x) - abs(oct.y));
+ if (v.z < 0.0) {
+ v.xy = (1.0 - abs(v.yx)) * signNotZero(v.xy);
+ }
+ return normalize(v);
+}
+
+vec2 vec3_to_oct(vec3 e) {
+ e /= abs(e.x) + abs(e.y) + abs(e.z);
+ vec2 oct = e.z >= 0.0f ? e.xy : (vec2(1.0f) - abs(e.yx)) * signNotZero(e.xy);
+ return oct * 0.5f + 0.5f;
+}
+
+vec4 oct_to_tang(vec2 oct_sign_encoded) {
+ // Binormal sign encoded in y component
+ vec2 oct = vec2(oct_sign_encoded.x, abs(oct_sign_encoded.y) * 2.0 - 1.0);
+ return vec4(oct_to_vec3(oct), sign(oct_sign_encoded.y));
+}
+
+vec2 tang_to_oct(vec4 base) {
+ vec2 oct = vec3_to_oct(base.xyz);
+ // Encode binormal sign in y component
+ oct.y = oct.y * 0.5f + 0.5f;
+ oct.y = base.w >= 0.0f ? oct.y : 1.0 - oct.y;
+ return oct;
+}
+
+// Our original input for normals and tangents is 2 16-bit floats.
+// Transform Feedback has to write out 32-bits per channel.
+// Octahedral compression requires normalized vectors, but we need to store
+// non-normalized vectors until the very end.
+// Therefore, we will compress our normals into 16 bits using signed-normalized
+// fixed point precision. This works well, because we know that each normal
+// is no larger than |1| so we can normalize by dividing by the number of blend
+// shapes.
+uvec2 vec4_to_vec2(vec4 p_vec) {
+ return uvec2(packSnorm2x16(p_vec.xy), packSnorm2x16(p_vec.zw));
+}
+
+vec4 vec2_to_vec4(uvec2 p_vec) {
+ return vec4(unpackSnorm2x16(p_vec.x), unpackSnorm2x16(p_vec.y));
+}
+
+void main() {
+#ifdef MODE_2D
+ out_vertex = in_vertex;
+
+#ifdef USE_BLEND_SHAPES
+#ifdef MODE_BLEND_PASS
+ out_vertex = in_vertex + blend_vertex * blend_weight;
+#else
+ out_vertex = in_vertex * blend_weight;
+#endif
+#ifdef FINAL_PASS
+ out_vertex = normalize(out_vertex);
+#endif
+#endif // USE_BLEND_SHAPES
+
+#ifdef USE_SKELETON
+
+#define TEX(m) texelFetch(skeleton_texture, ivec2(m % 256u, m / 256u), 0)
+#define GET_BONE_MATRIX(a, b, w) mat2x4(TEX(a), TEX(b)) * w
+
+ uvec4 bones = in_bone_attrib * uvec4(2u);
+ uvec4 bones_a = bones + uvec4(1u);
+
+ highp mat2x4 m = GET_BONE_MATRIX(bones.x, bones_a.x, in_weight_attrib.x);
+ m += GET_BONE_MATRIX(bones.y, bones_a.y, in_weight_attrib.y);
+ m += GET_BONE_MATRIX(bones.z, bones_a.z, in_weight_attrib.z);
+ m += GET_BONE_MATRIX(bones.w, bones_a.w, in_weight_attrib.w);
+
+ mat4 bone_matrix = mat4(m[0], m[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));
+
+ //reverse order because its transposed
+ out_vertex = (vec4(out_vertex, 0.0, 1.0) * bone_matrix).xy;
+#endif // USE_SKELETON
+
+#else // MODE_2D
+
+#ifdef USE_BLEND_SHAPES
+#ifdef MODE_BLEND_PASS
+ out_vertex = in_vertex + blend_vertex * blend_weight;
+
+#ifdef USE_NORMAL
+ vec3 normal = vec2_to_vec4(in_normal).xyz * blend_shape_count;
+ vec3 normal_blend = oct_to_vec3(blend_normal) * blend_weight;
+#ifdef FINAL_PASS
+ out_normal = vec3_to_oct(normalize(normal + normal_blend));
+#else
+ out_normal = vec4_to_vec2(vec4(normal + normal_blend, 0.0) / blend_shape_count);
+#endif
+#endif // USE_NORMAL
+
+#ifdef USE_TANGENT
+ vec4 tangent = vec2_to_vec4(in_tangent) * blend_shape_count;
+ vec4 tangent_blend = oct_to_tang(blend_tangent) * blend_weight;
+#ifdef FINAL_PASS
+ out_tangent = tang_to_oct(vec4(normalize(tangent.xyz + tangent_blend.xyz), tangent.w));
+#else
+ out_tangent = vec4_to_vec2(vec4((tangent.xyz + tangent_blend.xyz) / blend_shape_count, tangent.w));
+#endif
+#endif // USE_TANGENT
+
+#else // MODE_BLEND_PASS
+ out_vertex = in_vertex * blend_weight;
+
+#ifdef USE_NORMAL
+ vec3 normal = oct_to_vec3(in_normal);
+ out_normal = vec4_to_vec2(vec4(normal * blend_weight / blend_shape_count, 0.0));
+#endif
+#ifdef USE_TANGENT
+ vec4 tangent = oct_to_tang(in_tangent);
+ out_tangent = vec4_to_vec2(vec4(tangent.rgb * blend_weight / blend_shape_count, tangent.w));
+#endif
+#endif // MODE_BLEND_PASS
+#else // USE_BLEND_SHAPES
+
+ // Make attributes available to the skeleton shader if not written by blend shapes.
+ out_vertex = in_vertex;
+#ifdef USE_NORMAL
+ out_normal = in_normal;
+#endif
+#ifdef USE_TANGENT
+ out_tangent = in_tangent;
+#endif
+#endif // USE_BLEND_SHAPES
+
+#ifdef USE_SKELETON
+
+#define TEX(m) texelFetch(skeleton_texture, ivec2(m % 256u, m / 256u), 0)
+#define GET_BONE_MATRIX(a, b, c, w) mat4(TEX(a), TEX(b), TEX(c), vec4(0.0, 0.0, 0.0, 1.0)) * w
+
+ uvec4 bones = in_bone_attrib * uvec4(3);
+ uvec4 bones_a = bones + uvec4(1);
+ uvec4 bones_b = bones + uvec4(2);
+
+ highp mat4 m;
+ m = GET_BONE_MATRIX(bones.x, bones_a.x, bones_b.x, in_weight_attrib.x);
+ m += GET_BONE_MATRIX(bones.y, bones_a.y, bones_b.y, in_weight_attrib.y);
+ m += GET_BONE_MATRIX(bones.z, bones_a.z, bones_b.z, in_weight_attrib.z);
+ m += GET_BONE_MATRIX(bones.w, bones_a.w, bones_b.w, in_weight_attrib.w);
+
+#ifdef USE_EIGHT_WEIGHTS
+ bones = in_bone_attrib2 * uvec4(3);
+ bones_a = bones + uvec4(1);
+ bones_b = bones + uvec4(2);
+
+ m += GET_BONE_MATRIX(bones.x, bones_a.x, bones_b.x, in_weight_attrib2.x);
+ m += GET_BONE_MATRIX(bones.y, bones_a.y, bones_b.y, in_weight_attrib2.y);
+ m += GET_BONE_MATRIX(bones.z, bones_a.z, bones_b.z, in_weight_attrib2.z);
+ m += GET_BONE_MATRIX(bones.w, bones_a.w, bones_b.w, in_weight_attrib2.w);
+#endif
+
+ // Reverse order because its transposed.
+ out_vertex = (vec4(out_vertex, 1.0) * m).xyz;
+#ifdef USE_NORMAL
+ vec3 vertex_normal = oct_to_vec3(out_normal);
+ out_normal = vec3_to_oct(normalize((vec4(vertex_normal, 0.0) * m).xyz));
+#endif // USE_NORMAL
+#ifdef USE_TANGENT
+ vec4 vertex_tangent = oct_to_tang(out_tangent);
+ out_tangent = tang_to_oct(vec4(normalize((vec4(vertex_tangent.xyz, 0.0) * m).xyz), vertex_tangent.w));
+#endif // USE_TANGENT
+#endif // USE_SKELETON
+#endif // MODE_2D
+}
+
+/* clang-format off */
+#[fragment]
+
+void main() {
+
+}
+/* clang-format on */
diff --git a/drivers/gles3/shaders/stdlib_inc.glsl b/drivers/gles3/shaders/stdlib_inc.glsl
index d5051760d7..d819940b1d 100644
--- a/drivers/gles3/shaders/stdlib_inc.glsl
+++ b/drivers/gles3/shaders/stdlib_inc.glsl
@@ -38,7 +38,6 @@ vec2 unpackSnorm2x16(uint p) {
vec2 v = vec2(float(p & uint(0xffff)), float(p >> uint(16)));
return clamp((v - 32767.0) * vec2(0.00003051851), vec2(-1.0), vec2(1.0));
}
-#endif
uint packUnorm4x8(vec4 v) {
uvec4 uv = uvec4(round(clamp(v, vec4(0.0), vec4(1.0)) * 255.0));
@@ -58,3 +57,5 @@ vec4 unpackSnorm4x8(uint p) {
vec4 v = vec4(float(p & uint(0xff)), float((p >> uint(8)) & uint(0xff)), float((p >> uint(16)) & uint(0xff)), float(p >> uint(24)));
return clamp((v - vec4(127.0)) * vec4(0.00787401574), vec4(-1.0), vec4(1.0));
}
+
+#endif
diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp
index a47df42500..285f32f1a5 100644
--- a/drivers/gles3/storage/mesh_storage.cpp
+++ b/drivers/gles3/storage/mesh_storage.cpp
@@ -44,10 +44,16 @@ MeshStorage *MeshStorage::get_singleton() {
MeshStorage::MeshStorage() {
singleton = this;
+
+ {
+ skeleton_shader.shader.initialize();
+ skeleton_shader.shader_version = skeleton_shader.shader.version_create();
+ }
}
MeshStorage::~MeshStorage() {
singleton = nullptr;
+ skeleton_shader.shader.version_free(skeleton_shader.shader_version);
}
/* MESH API */
@@ -88,10 +94,6 @@ void MeshStorage::mesh_set_blend_shape_count(RID p_mesh, int p_blend_shape_count
ERR_FAIL_COND(mesh->surface_count > 0); //surfaces already exist
mesh->blend_shape_count = p_blend_shape_count;
-
- if (p_blend_shape_count > 0) {
- WARN_PRINT_ONCE("blend shapes not supported by GLES3 renderer yet");
- }
}
bool MeshStorage::mesh_needs_instance(RID p_mesh, bool p_has_skeleton) {
@@ -114,7 +116,6 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
uint32_t attrib_stride = 0;
uint32_t skin_stride = 0;
- // TODO: I think this should be <=, but it is copied from RendererRD, will have to verify later
for (int i = 0; i < RS::ARRAY_WEIGHTS; i++) {
if ((p_surface.format & (1 << i))) {
switch (i) {
@@ -248,8 +249,77 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
s->aabb = p_surface.aabb;
s->bone_aabbs = p_surface.bone_aabbs; //only really useful for returning them.
- if (mesh->blend_shape_count > 0) {
- //s->blend_shape_buffer = RD::get_singleton()->storage_buffer_create(p_surface.blend_shape_data.size(), p_surface.blend_shape_data);
+ if (p_surface.skin_data.size() || mesh->blend_shape_count > 0) {
+ // Size must match the size of the vertex array.
+ int size = p_surface.vertex_data.size();
+ int vertex_size = 0;
+ int stride = 0;
+ int normal_offset = 0;
+ int tangent_offset = 0;
+ if ((p_surface.format & (1 << RS::ARRAY_VERTEX))) {
+ if (p_surface.format & RS::ARRAY_FLAG_USE_2D_VERTICES) {
+ vertex_size = 2;
+ } else {
+ vertex_size = 3;
+ }
+ stride = sizeof(float) * vertex_size;
+ }
+ if ((p_surface.format & (1 << RS::ARRAY_NORMAL))) {
+ normal_offset = stride;
+ stride += sizeof(uint16_t) * 2;
+ }
+ if ((p_surface.format & (1 << RS::ARRAY_TANGENT))) {
+ tangent_offset = stride;
+ stride += sizeof(uint16_t) * 2;
+ }
+
+ if (mesh->blend_shape_count > 0) {
+ // Blend shapes are passed as one large array, for OpenGL, we need to split each of them into their own buffer
+ s->blend_shapes = memnew_arr(Mesh::Surface::BlendShape, mesh->blend_shape_count);
+
+ for (uint32_t i = 0; i < mesh->blend_shape_count; i++) {
+ glGenVertexArrays(1, &s->blend_shapes[i].vertex_array);
+ glBindVertexArray(s->blend_shapes[i].vertex_array);
+ glGenBuffers(1, &s->blend_shapes[i].vertex_buffer);
+ glBindBuffer(GL_ARRAY_BUFFER, s->blend_shapes[i].vertex_buffer);
+ glBufferData(GL_ARRAY_BUFFER, size, p_surface.blend_shape_data.ptr() + i * size, (s->format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
+
+ if ((p_surface.format & (1 << RS::ARRAY_VERTEX))) {
+ glEnableVertexAttribArray(RS::ARRAY_VERTEX + 3);
+ glVertexAttribPointer(RS::ARRAY_VERTEX + 3, vertex_size, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(0));
+ }
+ if ((p_surface.format & (1 << RS::ARRAY_NORMAL))) {
+ glEnableVertexAttribArray(RS::ARRAY_NORMAL + 3);
+ glVertexAttribPointer(RS::ARRAY_NORMAL + 3, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, CAST_INT_TO_UCHAR_PTR(normal_offset));
+ }
+ if ((p_surface.format & (1 << RS::ARRAY_TANGENT))) {
+ glEnableVertexAttribArray(RS::ARRAY_TANGENT + 3);
+ glVertexAttribPointer(RS::ARRAY_TANGENT + 3, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, CAST_INT_TO_UCHAR_PTR(tangent_offset));
+ }
+ }
+ glBindVertexArray(0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ }
+
+ // Create a vertex array to use for skeleton/blend shapes.
+ glGenVertexArrays(1, &s->skeleton_vertex_array);
+ glBindVertexArray(s->skeleton_vertex_array);
+ glBindBuffer(GL_ARRAY_BUFFER, s->vertex_buffer);
+
+ if ((p_surface.format & (1 << RS::ARRAY_VERTEX))) {
+ glEnableVertexAttribArray(RS::ARRAY_VERTEX);
+ glVertexAttribPointer(RS::ARRAY_VERTEX, vertex_size, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(0));
+ }
+ if ((p_surface.format & (1 << RS::ARRAY_NORMAL))) {
+ glEnableVertexAttribArray(RS::ARRAY_NORMAL);
+ glVertexAttribPointer(RS::ARRAY_NORMAL, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, CAST_INT_TO_UCHAR_PTR(normal_offset));
+ }
+ if ((p_surface.format & (1 << RS::ARRAY_TANGENT))) {
+ glEnableVertexAttribArray(RS::ARRAY_TANGENT);
+ glVertexAttribPointer(RS::ARRAY_TANGENT, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, CAST_INT_TO_UCHAR_PTR(tangent_offset));
+ }
+ glBindVertexArray(0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
}
if (mesh->surface_count == 0) {
@@ -412,7 +482,13 @@ RS::SurfaceData MeshStorage::mesh_get_surface(RID p_mesh, int p_surface) const {
}
sd.bone_aabbs = s.bone_aabbs;
- glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ if (mesh->blend_shape_count) {
+ sd.blend_shape_data = Vector<uint8_t>();
+ for (uint32_t i = 0; i < mesh->blend_shape_count; i++) {
+ sd.blend_shape_data.append_array(Utilities::buffer_get_data(GL_ARRAY_BUFFER, s.blend_shapes[i].vertex_buffer, s.vertex_buffer_size));
+ }
+ }
return sd;
}
@@ -608,6 +684,24 @@ void MeshStorage::mesh_clear(RID p_mesh) {
memdelete_arr(s.lods);
}
+ if (mesh->blend_shape_count) {
+ for (uint32_t j = 0; j < mesh->blend_shape_count; j++) {
+ if (s.blend_shapes[j].vertex_buffer != 0) {
+ glDeleteBuffers(1, &s.blend_shapes[j].vertex_buffer);
+ s.blend_shapes[j].vertex_buffer = 0;
+ }
+ if (s.blend_shapes[j].vertex_array != 0) {
+ glDeleteVertexArrays(1, &s.blend_shapes[j].vertex_array);
+ s.blend_shapes[j].vertex_array = 0;
+ }
+ }
+ memdelete_arr(s.blend_shapes);
+ }
+ if (s.skeleton_vertex_array != 0) {
+ glDeleteVertexArrays(1, &s.skeleton_vertex_array);
+ s.skeleton_vertex_array = 0;
+ }
+
memdelete(mesh->surfaces[i]);
}
if (mesh->surfaces) {
@@ -663,15 +757,15 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
case RS::ARRAY_NORMAL: {
attribs[i].offset = vertex_stride;
attribs[i].size = 2;
- attribs[i].type = GL_UNSIGNED_SHORT;
- vertex_stride += sizeof(uint16_t) * 2;
+ attribs[i].type = (mis ? GL_FLOAT : GL_UNSIGNED_SHORT);
+ vertex_stride += sizeof(uint16_t) * 2 * (mis ? 2 : 1);
attribs[i].normalized = GL_TRUE;
} break;
case RS::ARRAY_TANGENT: {
attribs[i].offset = vertex_stride;
attribs[i].size = 2;
- attribs[i].type = GL_UNSIGNED_SHORT;
- vertex_stride += sizeof(uint16_t) * 2;
+ attribs[i].type = (mis ? GL_FLOAT : GL_UNSIGNED_SHORT);
+ vertex_stride += sizeof(uint16_t) * 2 * (mis ? 2 : 1);
attribs[i].normalized = GL_TRUE;
} break;
case RS::ARRAY_COLOR: {
@@ -716,7 +810,7 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
attribs[i].offset = skin_stride;
attribs[i].size = 4;
attribs[i].type = GL_UNSIGNED_SHORT;
- attributes_stride += 4 * sizeof(uint16_t);
+ skin_stride += 4 * sizeof(uint16_t);
attribs[i].normalized = GL_FALSE;
attribs[i].integer = true;
} break;
@@ -724,7 +818,7 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
attribs[i].offset = skin_stride;
attribs[i].size = 4;
attribs[i].type = GL_UNSIGNED_SHORT;
- attributes_stride += 4 * sizeof(uint16_t);
+ skin_stride += 4 * sizeof(uint16_t);
attribs[i].normalized = GL_TRUE;
} break;
}
@@ -815,7 +909,7 @@ void MeshStorage::mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int
ERR_FAIL_COND(!mi);
ERR_FAIL_INDEX(p_shape, (int)mi->blend_weights.size());
mi->blend_weights[p_shape] = p_weight;
- mi->weights_dirty = true;
+ mi->dirty = true;
}
void MeshStorage::_mesh_instance_clear(MeshInstance *mi) {
@@ -827,38 +921,65 @@ void MeshStorage::_mesh_instance_clear(MeshInstance *mi) {
}
memfree(mi->surfaces[i].versions);
}
+
+ if (mi->surfaces[i].vertex_buffers[0] != 0) {
+ glDeleteBuffers(2, mi->surfaces[i].vertex_buffers);
+ mi->surfaces[i].vertex_buffers[0] = 0;
+ mi->surfaces[i].vertex_buffers[1] = 0;
+ }
+
if (mi->surfaces[i].vertex_buffer != 0) {
glDeleteBuffers(1, &mi->surfaces[i].vertex_buffer);
mi->surfaces[i].vertex_buffer = 0;
}
}
mi->surfaces.clear();
-
- if (mi->blend_weights_buffer != 0) {
- glDeleteBuffers(1, &mi->blend_weights_buffer);
- mi->blend_weights_buffer = 0;
- }
mi->blend_weights.clear();
- mi->weights_dirty = false;
mi->skeleton_version = 0;
}
void MeshStorage::_mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint32_t p_surface) {
- if (mesh->blend_shape_count > 0 && mi->blend_weights_buffer == 0) {
+ if (mesh->blend_shape_count > 0) {
mi->blend_weights.resize(mesh->blend_shape_count);
for (uint32_t i = 0; i < mi->blend_weights.size(); i++) {
- mi->blend_weights[i] = 0;
+ mi->blend_weights[i] = 0.0;
}
- // Todo allocate buffer for blend_weights and copy data to it
- //mi->blend_weights_buffer = RD::get_singleton()->storage_buffer_create(sizeof(float) * mi->blend_weights.size(), mi->blend_weights.to_byte_array());
-
- mi->weights_dirty = true;
}
MeshInstance::Surface s;
- if (mesh->blend_shape_count > 0 || (mesh->surfaces[p_surface]->format & RS::ARRAY_FORMAT_BONES)) {
- //surface warrants transform
- //s.vertex_buffer = RD::get_singleton()->vertex_buffer_create(mesh->surfaces[p_surface]->vertex_buffer_size, Vector<uint8_t>(), true);
+ if ((mesh->blend_shape_count > 0 || (mesh->surfaces[p_surface]->format & RS::ARRAY_FORMAT_BONES)) && mesh->surfaces[p_surface]->vertex_buffer_size > 0) {
+ // Cache surface properties
+ s.format_cache = mesh->surfaces[p_surface]->format;
+ if ((s.format_cache & (1 << RS::ARRAY_VERTEX))) {
+ if (s.format_cache & RS::ARRAY_FLAG_USE_2D_VERTICES) {
+ s.vertex_size_cache = 2;
+ } else {
+ s.vertex_size_cache = 3;
+ }
+ s.vertex_stride_cache = sizeof(float) * s.vertex_size_cache;
+ }
+ if ((s.format_cache & (1 << RS::ARRAY_NORMAL))) {
+ s.vertex_normal_offset_cache = s.vertex_stride_cache;
+ s.vertex_stride_cache += sizeof(uint32_t) * 2;
+ }
+ if ((s.format_cache & (1 << RS::ARRAY_TANGENT))) {
+ s.vertex_tangent_offset_cache = s.vertex_stride_cache;
+ s.vertex_stride_cache += sizeof(uint32_t) * 2;
+ }
+
+ // Buffer to be used for rendering. Final output of skeleton and blend shapes.
+ glGenBuffers(1, &s.vertex_buffer);
+ glBindBuffer(GL_ARRAY_BUFFER, s.vertex_buffer);
+ glBufferData(GL_ARRAY_BUFFER, s.vertex_stride_cache * mesh->surfaces[p_surface]->vertex_count, nullptr, GL_DYNAMIC_DRAW);
+ if (mesh->blend_shape_count > 0) {
+ // Ping-Pong buffers for processing blendshapes.
+ glGenBuffers(2, s.vertex_buffers);
+ for (uint32_t i = 0; i < 2; i++) {
+ glBindBuffer(GL_ARRAY_BUFFER, s.vertex_buffers[i]);
+ glBufferData(GL_ARRAY_BUFFER, s.vertex_stride_cache * mesh->surfaces[p_surface]->vertex_count, nullptr, GL_DYNAMIC_DRAW);
+ }
+ }
+ glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
}
mi->surfaces.push_back(s);
@@ -870,11 +991,6 @@ void MeshStorage::mesh_instance_check_for_update(RID p_mesh_instance) {
bool needs_update = mi->dirty;
- if (mi->weights_dirty && !mi->weight_update_list.in_list()) {
- dirty_mesh_instance_weights.add(&mi->weight_update_list);
- needs_update = true;
- }
-
if (mi->array_update_list.in_list()) {
return;
}
@@ -891,22 +1007,223 @@ void MeshStorage::mesh_instance_check_for_update(RID p_mesh_instance) {
}
}
-void MeshStorage::update_mesh_instances() {
- while (dirty_mesh_instance_weights.first()) {
- MeshInstance *mi = dirty_mesh_instance_weights.first()->self();
+void MeshStorage::_blend_shape_bind_mesh_instance_buffer(MeshInstance *p_mi, uint32_t p_surface) {
+ glBindBuffer(GL_ARRAY_BUFFER, p_mi->surfaces[p_surface].vertex_buffers[0]);
- if (mi->blend_weights_buffer != 0) {
- //RD::get_singleton()->buffer_update(mi->blend_weights_buffer, 0, mi->blend_weights.size() * sizeof(float), mi->blend_weights.ptr());
- }
- dirty_mesh_instance_weights.remove(&mi->weight_update_list);
- mi->weights_dirty = false;
+ if ((p_mi->surfaces[p_surface].format_cache & (1 << RS::ARRAY_VERTEX))) {
+ glEnableVertexAttribArray(RS::ARRAY_VERTEX);
+ glVertexAttribPointer(RS::ARRAY_VERTEX, p_mi->surfaces[p_surface].vertex_size_cache, GL_FLOAT, GL_FALSE, p_mi->surfaces[p_surface].vertex_stride_cache, CAST_INT_TO_UCHAR_PTR(0));
+ } else {
+ glDisableVertexAttribArray(RS::ARRAY_VERTEX);
}
+ if ((p_mi->surfaces[p_surface].format_cache & (1 << RS::ARRAY_NORMAL))) {
+ glEnableVertexAttribArray(RS::ARRAY_NORMAL);
+ glVertexAttribIPointer(RS::ARRAY_NORMAL, 2, GL_UNSIGNED_INT, p_mi->surfaces[p_surface].vertex_stride_cache, CAST_INT_TO_UCHAR_PTR(p_mi->surfaces[p_surface].vertex_normal_offset_cache));
+ } else {
+ glDisableVertexAttribArray(RS::ARRAY_NORMAL);
+ }
+ if ((p_mi->surfaces[p_surface].format_cache & (1 << RS::ARRAY_TANGENT))) {
+ glEnableVertexAttribArray(RS::ARRAY_TANGENT);
+ glVertexAttribIPointer(RS::ARRAY_TANGENT, 2, GL_UNSIGNED_INT, p_mi->surfaces[p_surface].vertex_stride_cache, CAST_INT_TO_UCHAR_PTR(p_mi->surfaces[p_surface].vertex_tangent_offset_cache));
+ } else {
+ glDisableVertexAttribArray(RS::ARRAY_TANGENT);
+ }
+}
+
+void MeshStorage::_compute_skeleton(MeshInstance *p_mi, Skeleton *p_sk, uint32_t p_surface) {
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ // Add in the bones and weights.
+ glBindBuffer(GL_ARRAY_BUFFER, p_mi->mesh->surfaces[p_surface]->skin_buffer);
+
+ bool use_8_weights = p_mi->surfaces[p_surface].format_cache & RS::ARRAY_FLAG_USE_8_BONE_WEIGHTS;
+ int skin_stride = sizeof(int16_t) * (use_8_weights ? 16 : 8);
+ glEnableVertexAttribArray(RS::ARRAY_BONES);
+ glVertexAttribIPointer(RS::ARRAY_BONES, 4, GL_UNSIGNED_SHORT, skin_stride, CAST_INT_TO_UCHAR_PTR(0));
+ if (use_8_weights) {
+ glEnableVertexAttribArray(11);
+ glVertexAttribIPointer(11, 4, GL_UNSIGNED_SHORT, skin_stride, CAST_INT_TO_UCHAR_PTR(4 * sizeof(uint16_t)));
+ glEnableVertexAttribArray(12);
+ glVertexAttribPointer(12, 4, GL_UNSIGNED_SHORT, GL_TRUE, skin_stride, CAST_INT_TO_UCHAR_PTR(8 * sizeof(uint16_t)));
+ glEnableVertexAttribArray(13);
+ glVertexAttribPointer(13, 4, GL_UNSIGNED_SHORT, GL_TRUE, skin_stride, CAST_INT_TO_UCHAR_PTR(12 * sizeof(uint16_t)));
+ } else {
+ glEnableVertexAttribArray(RS::ARRAY_WEIGHTS);
+ glVertexAttribPointer(RS::ARRAY_WEIGHTS, 4, GL_UNSIGNED_SHORT, GL_TRUE, skin_stride, CAST_INT_TO_UCHAR_PTR(4 * sizeof(uint16_t)));
+ }
+
+ glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, p_mi->surfaces[p_surface].vertex_buffer);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, p_sk->transforms_texture);
+
+ glBeginTransformFeedback(GL_POINTS);
+ glDrawArrays(GL_POINTS, 0, p_mi->mesh->surfaces[p_surface]->vertex_count);
+ glEndTransformFeedback();
+
+ glDisableVertexAttribArray(RS::ARRAY_BONES);
+ glDisableVertexAttribArray(RS::ARRAY_WEIGHTS);
+ glDisableVertexAttribArray(RS::ARRAY_BONES + 2);
+ glDisableVertexAttribArray(RS::ARRAY_WEIGHTS + 2);
+ glBindVertexArray(0);
+ glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0);
+}
+
+void MeshStorage::update_mesh_instances() {
if (dirty_mesh_instance_arrays.first() == nullptr) {
return; //nothing to do
}
+ glEnable(GL_RASTERIZER_DISCARD);
// Process skeletons and blend shapes using transform feedback
- // TODO: Implement when working on skeletons and blend shapes
+ while (dirty_mesh_instance_arrays.first()) {
+ MeshInstance *mi = dirty_mesh_instance_arrays.first()->self();
+
+ Skeleton *sk = skeleton_owner.get_or_null(mi->skeleton);
+
+ // Precompute base weight if using blend shapes.
+ float base_weight = 1.0;
+ if (mi->mesh->blend_shape_count && mi->mesh->blend_shape_mode == RS::BLEND_SHAPE_MODE_NORMALIZED) {
+ for (uint32_t i = 0; i < mi->mesh->blend_shape_count; i++) {
+ base_weight -= mi->blend_weights[i];
+ }
+ }
+
+ for (uint32_t i = 0; i < mi->surfaces.size(); i++) {
+ if (mi->surfaces[i].vertex_buffer == 0 || mi->mesh->surfaces[i]->skeleton_vertex_array == 0) {
+ continue;
+ }
+
+ bool array_is_2d = mi->surfaces[i].format_cache & RS::ARRAY_FLAG_USE_2D_VERTICES;
+ bool can_use_skeleton = sk != nullptr && sk->use_2d == array_is_2d && (mi->surfaces[i].format_cache & RS::ARRAY_FORMAT_BONES);
+ bool use_8_weights = mi->surfaces[i].format_cache & RS::ARRAY_FLAG_USE_8_BONE_WEIGHTS;
+
+ // Always process blend shapes first.
+ if (mi->mesh->blend_shape_count) {
+ SkeletonShaderGLES3::ShaderVariant variant = SkeletonShaderGLES3::MODE_BASE_PASS;
+ uint64_t specialization = 0;
+ specialization |= array_is_2d ? SkeletonShaderGLES3::MODE_2D : 0;
+ specialization |= SkeletonShaderGLES3::USE_BLEND_SHAPES;
+ if (!array_is_2d) {
+ if ((mi->surfaces[i].format_cache & (1 << RS::ARRAY_NORMAL))) {
+ specialization |= SkeletonShaderGLES3::USE_NORMAL;
+ }
+ if ((mi->surfaces[i].format_cache & (1 << RS::ARRAY_TANGENT))) {
+ specialization |= SkeletonShaderGLES3::USE_TANGENT;
+ }
+ }
+
+ bool success = skeleton_shader.shader.version_bind_shader(skeleton_shader.shader_version, variant, specialization);
+ if (!success) {
+ continue;
+ }
+
+ skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::BLEND_WEIGHT, base_weight, skeleton_shader.shader_version, variant, specialization);
+ skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::BLEND_SHAPE_COUNT, float(mi->mesh->blend_shape_count), skeleton_shader.shader_version, variant, specialization);
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindVertexArray(mi->mesh->surfaces[i]->skeleton_vertex_array);
+ glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mi->surfaces[i].vertex_buffers[0]);
+ glBeginTransformFeedback(GL_POINTS);
+ glDrawArrays(GL_POINTS, 0, mi->mesh->surfaces[i]->vertex_count);
+ glEndTransformFeedback();
+
+ variant = SkeletonShaderGLES3::MODE_BLEND_PASS;
+ success = skeleton_shader.shader.version_bind_shader(skeleton_shader.shader_version, variant, specialization);
+ if (!success) {
+ continue;
+ }
+
+ //Do the last blend shape separately, as it can be combined with the skeleton pass.
+ for (uint32_t bs = 0; bs < mi->mesh->blend_shape_count - 1; bs++) {
+ float weight = mi->blend_weights[bs];
+
+ if (Math::is_zero_approx(weight)) {
+ //not bother with this one
+ continue;
+ }
+ skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::BLEND_WEIGHT, weight, skeleton_shader.shader_version, variant, specialization);
+ skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::BLEND_SHAPE_COUNT, float(mi->mesh->blend_shape_count), skeleton_shader.shader_version, variant, specialization);
+
+ glBindVertexArray(mi->mesh->surfaces[i]->blend_shapes[bs].vertex_array);
+ _blend_shape_bind_mesh_instance_buffer(mi, i);
+ glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mi->surfaces[i].vertex_buffers[1]);
+
+ glBeginTransformFeedback(GL_POINTS);
+ glDrawArrays(GL_POINTS, 0, mi->mesh->surfaces[i]->vertex_count);
+ glEndTransformFeedback();
+
+ SWAP(mi->surfaces[i].vertex_buffers[0], mi->surfaces[i].vertex_buffers[1]);
+ }
+ uint32_t bs = mi->mesh->blend_shape_count - 1;
+
+ float weight = mi->blend_weights[bs];
+
+ glBindVertexArray(mi->mesh->surfaces[i]->blend_shapes[bs].vertex_array);
+ _blend_shape_bind_mesh_instance_buffer(mi, i);
+
+ specialization |= can_use_skeleton ? SkeletonShaderGLES3::USE_SKELETON : 0;
+ specialization |= (can_use_skeleton && use_8_weights) ? SkeletonShaderGLES3::USE_EIGHT_WEIGHTS : 0;
+ specialization |= SkeletonShaderGLES3::FINAL_PASS;
+ success = skeleton_shader.shader.version_bind_shader(skeleton_shader.shader_version, variant, specialization);
+ if (!success) {
+ continue;
+ }
+
+ skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::BLEND_WEIGHT, weight, skeleton_shader.shader_version, variant, specialization);
+ skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::BLEND_SHAPE_COUNT, float(mi->mesh->blend_shape_count), skeleton_shader.shader_version, variant, specialization);
+
+ if (can_use_skeleton) {
+ // Do last blendshape in the same pass as the Skeleton.
+ _compute_skeleton(mi, sk, i);
+ can_use_skeleton = false;
+ } else {
+ // Do last blendshape by itself and prepare vertex data for use by the renderer.
+ glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mi->surfaces[i].vertex_buffer);
+
+ glBeginTransformFeedback(GL_POINTS);
+ glDrawArrays(GL_POINTS, 0, mi->mesh->surfaces[i]->vertex_count);
+ glEndTransformFeedback();
+ }
+
+ glBindVertexArray(0);
+ glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0);
+ }
+
+ // This branch should only execute when Skeleton is run by itself.
+ if (can_use_skeleton) {
+ SkeletonShaderGLES3::ShaderVariant variant = SkeletonShaderGLES3::MODE_BASE_PASS;
+ uint64_t specialization = 0;
+ specialization |= array_is_2d ? SkeletonShaderGLES3::MODE_2D : 0;
+ specialization |= SkeletonShaderGLES3::USE_SKELETON;
+ specialization |= SkeletonShaderGLES3::FINAL_PASS;
+ specialization |= use_8_weights ? SkeletonShaderGLES3::USE_EIGHT_WEIGHTS : 0;
+ if (!array_is_2d) {
+ if ((mi->surfaces[i].format_cache & (1 << RS::ARRAY_NORMAL))) {
+ specialization |= SkeletonShaderGLES3::USE_NORMAL;
+ }
+ if ((mi->surfaces[i].format_cache & (1 << RS::ARRAY_TANGENT))) {
+ specialization |= SkeletonShaderGLES3::USE_TANGENT;
+ }
+ }
+
+ bool success = skeleton_shader.shader.version_bind_shader(skeleton_shader.shader_version, variant, specialization);
+ if (!success) {
+ continue;
+ }
+
+ glBindVertexArray(mi->mesh->surfaces[i]->skeleton_vertex_array);
+ _compute_skeleton(mi, sk, i);
+ }
+ }
+ mi->dirty = false;
+ if (sk) {
+ mi->skeleton_version = sk->version;
+ }
+ dirty_mesh_instance_arrays.remove(&mi->array_update_list);
+ }
+ glDisable(GL_RASTERIZER_DISCARD);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0);
}
/* MULTIMESH API */
@@ -1577,45 +1894,207 @@ void MeshStorage::_update_dirty_multimeshes() {
/* SKELETON API */
RID MeshStorage::skeleton_allocate() {
- return RID();
+ return skeleton_owner.allocate_rid();
}
void MeshStorage::skeleton_initialize(RID p_rid) {
+ skeleton_owner.initialize_rid(p_rid, Skeleton());
}
void MeshStorage::skeleton_free(RID p_rid) {
+ _update_dirty_skeletons();
+ skeleton_allocate_data(p_rid, 0);
+ Skeleton *skeleton = skeleton_owner.get_or_null(p_rid);
+ skeleton->dependency.deleted_notify(p_rid);
+ skeleton_owner.free(p_rid);
+}
+
+void MeshStorage::_skeleton_make_dirty(Skeleton *skeleton) {
+ if (!skeleton->dirty) {
+ skeleton->dirty = true;
+ skeleton->dirty_list = skeleton_dirty_list;
+ skeleton_dirty_list = skeleton;
+ }
}
void MeshStorage::skeleton_allocate_data(RID p_skeleton, int p_bones, bool p_2d_skeleton) {
+ Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton);
+ ERR_FAIL_COND(!skeleton);
+ ERR_FAIL_COND(p_bones < 0);
+
+ if (skeleton->size == p_bones && skeleton->use_2d == p_2d_skeleton) {
+ return;
+ }
+
+ skeleton->size = p_bones;
+ skeleton->use_2d = p_2d_skeleton;
+ skeleton->height = (p_bones * (p_2d_skeleton ? 2 : 3)) / 256;
+ if ((p_bones * (p_2d_skeleton ? 2 : 3)) % 256) {
+ skeleton->height++;
+ }
+
+ if (skeleton->transforms_texture != 0) {
+ glDeleteTextures(1, &skeleton->transforms_texture);
+ skeleton->transforms_texture = 0;
+ skeleton->data.clear();
+ }
+
+ if (skeleton->size) {
+ skeleton->data.resize(256 * skeleton->height * 4);
+ glGenTextures(1, &skeleton->transforms_texture);
+ glBindTexture(GL_TEXTURE_2D, skeleton->transforms_texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 256, skeleton->height, 0, GL_RGBA, GL_FLOAT, nullptr);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ memset(skeleton->data.ptrw(), 0, skeleton->data.size() * sizeof(float));
+
+ _skeleton_make_dirty(skeleton);
+ }
+
+ skeleton->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_SKELETON_DATA);
}
void MeshStorage::skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) {
+ Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton);
+
+ ERR_FAIL_NULL(skeleton);
+ ERR_FAIL_COND(!skeleton->use_2d);
+
+ skeleton->base_transform_2d = p_base_transform;
}
int MeshStorage::skeleton_get_bone_count(RID p_skeleton) const {
- return 0;
+ Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton);
+ ERR_FAIL_COND_V(!skeleton, 0);
+
+ return skeleton->size;
}
void MeshStorage::skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform3D &p_transform) {
+ Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton);
+
+ ERR_FAIL_COND(!skeleton);
+ ERR_FAIL_INDEX(p_bone, skeleton->size);
+ ERR_FAIL_COND(skeleton->use_2d);
+
+ float *dataptr = skeleton->data.ptrw() + p_bone * 12;
+
+ dataptr[0] = p_transform.basis.rows[0][0];
+ dataptr[1] = p_transform.basis.rows[0][1];
+ dataptr[2] = p_transform.basis.rows[0][2];
+ dataptr[3] = p_transform.origin.x;
+ dataptr[4] = p_transform.basis.rows[1][0];
+ dataptr[5] = p_transform.basis.rows[1][1];
+ dataptr[6] = p_transform.basis.rows[1][2];
+ dataptr[7] = p_transform.origin.y;
+ dataptr[8] = p_transform.basis.rows[2][0];
+ dataptr[9] = p_transform.basis.rows[2][1];
+ dataptr[10] = p_transform.basis.rows[2][2];
+ dataptr[11] = p_transform.origin.z;
+
+ _skeleton_make_dirty(skeleton);
}
Transform3D MeshStorage::skeleton_bone_get_transform(RID p_skeleton, int p_bone) const {
- return Transform3D();
+ Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton);
+
+ ERR_FAIL_COND_V(!skeleton, Transform3D());
+ ERR_FAIL_INDEX_V(p_bone, skeleton->size, Transform3D());
+ ERR_FAIL_COND_V(skeleton->use_2d, Transform3D());
+
+ const float *dataptr = skeleton->data.ptr() + p_bone * 12;
+
+ Transform3D t;
+
+ t.basis.rows[0][0] = dataptr[0];
+ t.basis.rows[0][1] = dataptr[1];
+ t.basis.rows[0][2] = dataptr[2];
+ t.origin.x = dataptr[3];
+ t.basis.rows[1][0] = dataptr[4];
+ t.basis.rows[1][1] = dataptr[5];
+ t.basis.rows[1][2] = dataptr[6];
+ t.origin.y = dataptr[7];
+ t.basis.rows[2][0] = dataptr[8];
+ t.basis.rows[2][1] = dataptr[9];
+ t.basis.rows[2][2] = dataptr[10];
+ t.origin.z = dataptr[11];
+
+ return t;
}
void MeshStorage::skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) {
+ Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton);
+
+ ERR_FAIL_COND(!skeleton);
+ ERR_FAIL_INDEX(p_bone, skeleton->size);
+ ERR_FAIL_COND(!skeleton->use_2d);
+
+ float *dataptr = skeleton->data.ptrw() + p_bone * 8;
+
+ dataptr[0] = p_transform.columns[0][0];
+ dataptr[1] = p_transform.columns[1][0];
+ dataptr[2] = 0;
+ dataptr[3] = p_transform.columns[2][0];
+ dataptr[4] = p_transform.columns[0][1];
+ dataptr[5] = p_transform.columns[1][1];
+ dataptr[6] = 0;
+ dataptr[7] = p_transform.columns[2][1];
+
+ _skeleton_make_dirty(skeleton);
}
Transform2D MeshStorage::skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const {
- return Transform2D();
+ Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton);
+
+ ERR_FAIL_COND_V(!skeleton, Transform2D());
+ ERR_FAIL_INDEX_V(p_bone, skeleton->size, Transform2D());
+ ERR_FAIL_COND_V(!skeleton->use_2d, Transform2D());
+
+ const float *dataptr = skeleton->data.ptr() + p_bone * 8;
+
+ Transform2D t;
+ t.columns[0][0] = dataptr[0];
+ t.columns[1][0] = dataptr[1];
+ t.columns[2][0] = dataptr[3];
+ t.columns[0][1] = dataptr[4];
+ t.columns[1][1] = dataptr[5];
+ t.columns[2][1] = dataptr[7];
+
+ return t;
}
-void MeshStorage::skeleton_update_dependency(RID p_base, DependencyTracker *p_instance) {
+void MeshStorage::_update_dirty_skeletons() {
+ while (skeleton_dirty_list) {
+ Skeleton *skeleton = skeleton_dirty_list;
+
+ if (skeleton->size) {
+ glBindTexture(GL_TEXTURE_2D, skeleton->transforms_texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 256, skeleton->height, 0, GL_RGBA, GL_FLOAT, skeleton->data.ptr());
+ glBindTexture(GL_TEXTURE_2D, 0);
+ }
+
+ skeleton_dirty_list = skeleton->dirty_list;
+
+ skeleton->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_SKELETON_BONES);
+
+ skeleton->version++;
+
+ skeleton->dirty = false;
+ skeleton->dirty_list = nullptr;
+ }
+
+ skeleton_dirty_list = nullptr;
}
-/* OCCLUDER */
+void MeshStorage::skeleton_update_dependency(RID p_skeleton, DependencyTracker *p_instance) {
+ Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton);
+ ERR_FAIL_COND(!skeleton);
-void MeshStorage::occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices) {
+ p_instance->update_dependency(&skeleton->dependency);
}
#endif // GLES3_ENABLED
diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h
index 1aef3cbf78..0f30814928 100644
--- a/drivers/gles3/storage/mesh_storage.h
+++ b/drivers/gles3/storage/mesh_storage.h
@@ -33,6 +33,7 @@
#ifdef GLES3_ENABLED
+#include "../shaders/skeleton.glsl.gen.h"
#include "core/templates/local_vector.h"
#include "core/templates/rid_owner.h"
#include "core/templates/self_list.h"
@@ -102,7 +103,13 @@ struct Mesh {
Vector<AABB> bone_aabbs;
- GLuint blend_shape_buffer = 0;
+ struct BlendShape {
+ GLuint vertex_buffer = 0;
+ GLuint vertex_array = 0;
+ };
+
+ BlendShape *blend_shapes = nullptr;
+ GLuint skeleton_vertex_array = 0;
RID material;
};
@@ -136,7 +143,14 @@ struct MeshInstance {
Mesh *mesh = nullptr;
RID skeleton;
struct Surface {
+ GLuint vertex_buffers[2] = { 0, 0 };
+ GLuint vertex_arrays[2] = { 0, 0 };
GLuint vertex_buffer = 0;
+ int vertex_stride_cache = 0;
+ int vertex_size_cache = 0;
+ int vertex_normal_offset_cache = 0;
+ int vertex_tangent_offset_cache = 0;
+ uint32_t format_cache = 0;
Mesh::Surface::Version *versions = nullptr; //allocated on demand
uint32_t version_count = 0;
@@ -144,7 +158,6 @@ struct MeshInstance {
LocalVector<Surface> surfaces;
LocalVector<float> blend_weights;
- GLuint blend_weights_buffer = 0;
List<MeshInstance *>::Element *I = nullptr; //used to erase itself
uint64_t skeleton_version = 0;
bool dirty = false;
@@ -186,13 +199,15 @@ struct MultiMesh {
struct Skeleton {
bool use_2d = false;
int size = 0;
+ int height = 0;
Vector<float> data;
- GLuint buffer = 0;
bool dirty = false;
Skeleton *dirty_list = nullptr;
Transform2D base_transform_2d;
+ GLuint transforms_texture = 0;
+
uint64_t version = 1;
Dependency dependency;
@@ -202,6 +217,11 @@ class MeshStorage : public RendererMeshStorage {
private:
static MeshStorage *singleton;
+ struct {
+ SkeletonShaderGLES3 shader;
+ RID shader_version;
+ } skeleton_shader;
+
/* Mesh */
mutable RID_Owner<Mesh, true> mesh_owner;
@@ -214,6 +234,7 @@ private:
void _mesh_instance_clear(MeshInstance *mi);
void _mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint32_t p_surface);
+ void _blend_shape_bind_mesh_instance_buffer(MeshInstance *p_mi, uint32_t p_surface);
SelfList<MeshInstance>::List dirty_mesh_instance_weights;
SelfList<MeshInstance>::List dirty_mesh_instance_arrays;
@@ -232,9 +253,10 @@ private:
mutable RID_Owner<Skeleton, true> skeleton_owner;
- Skeleton *skeleton_dirty_list = nullptr;
-
_FORCE_INLINE_ void _skeleton_make_dirty(Skeleton *skeleton);
+ void _compute_skeleton(MeshInstance *p_mi, Skeleton *p_sk, uint32_t p_surface);
+
+ Skeleton *skeleton_dirty_list = nullptr;
public:
static MeshStorage *get_singleton();
@@ -534,9 +556,11 @@ public:
virtual void skeleton_update_dependency(RID p_base, DependencyTracker *p_instance) override;
- /* OCCLUDER */
+ void _update_dirty_skeletons();
- void occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices);
+ _FORCE_INLINE_ bool skeleton_is_valid(RID p_skeleton) {
+ return skeleton_owner.get_or_null(p_skeleton) != nullptr;
+ }
};
} // namespace GLES3
diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp
index 15743c2d78..99908d197a 100644
--- a/drivers/gles3/storage/texture_storage.cpp
+++ b/drivers/gles3/storage/texture_storage.cpp
@@ -1694,34 +1694,51 @@ void TextureStorage::_clear_render_target(RenderTarget *rt) {
return;
}
+ // Dispose of the cached fbo's and the allocated textures
+ for (KeyValue<uint32_t, RenderTarget::RTOverridden::FBOCacheEntry> &E : rt->overridden.fbo_cache) {
+ glDeleteTextures(E.value.allocated_textures.size(), E.value.allocated_textures.ptr());
+ // Don't delete the current FBO, we'll do that a couple lines down.
+ if (E.value.fbo != rt->fbo) {
+ glDeleteFramebuffers(1, &E.value.fbo);
+ }
+ }
+ rt->overridden.fbo_cache.clear();
+
if (rt->fbo) {
glDeleteFramebuffers(1, &rt->fbo);
rt->fbo = 0;
}
if (rt->overridden.color.is_null()) {
- glDeleteTextures(1, &rt->color);
- rt->color = 0;
+ if (rt->texture.is_valid()) {
+ Texture *tex = get_texture(rt->texture);
+ tex->alloc_height = 0;
+ tex->alloc_width = 0;
+ tex->width = 0;
+ tex->height = 0;
+ tex->active = false;
+ }
+ } else {
+ Texture *tex = get_texture(rt->overridden.color);
+ tex->is_render_target = false;
}
- if (rt->overridden.depth.is_null()) {
- glDeleteTextures(1, &rt->depth);
- rt->depth = 0;
+ if (rt->overridden.color.is_valid()) {
+ rt->overridden.color = RID();
+ } else if (rt->color) {
+ glDeleteTextures(1, &rt->color);
}
+ rt->color = 0;
- if (rt->texture.is_valid()) {
- Texture *tex = get_texture(rt->texture);
- tex->alloc_height = 0;
- tex->alloc_width = 0;
- tex->width = 0;
- tex->height = 0;
- tex->active = false;
+ if (rt->overridden.depth.is_valid()) {
+ rt->overridden.depth = RID();
+ } else if (rt->depth) {
+ glDeleteTextures(1, &rt->depth);
}
+ rt->depth = 0;
- if (rt->overridden.color.is_valid()) {
- Texture *tex = get_texture(rt->overridden.color);
- tex->is_render_target = false;
- }
+ rt->overridden.velocity = RID();
+ rt->overridden.is_overridden = false;
if (rt->backbuffer_fbo != 0) {
glDeleteFramebuffers(1, &rt->backbuffer_fbo);
@@ -1732,15 +1749,6 @@ void TextureStorage::_clear_render_target(RenderTarget *rt) {
_render_target_clear_sdf(rt);
}
-void TextureStorage::_clear_render_target_overridden_fbo_cache(RenderTarget *rt) {
- // Dispose of the cached fbo's and the allocated textures
- for (KeyValue<uint32_t, RenderTarget::RTOverridden::FBOCacheEntry> &E : rt->overridden.fbo_cache) {
- glDeleteTextures(E.value.allocated_textures.size(), E.value.allocated_textures.ptr());
- glDeleteFramebuffers(1, &E.value.fbo);
- }
- rt->overridden.fbo_cache.clear();
-}
-
RID TextureStorage::render_target_create() {
RenderTarget render_target;
//render_target.was_used = false;
@@ -1759,7 +1767,6 @@ RID TextureStorage::render_target_create() {
void TextureStorage::render_target_free(RID p_rid) {
RenderTarget *rt = render_target_owner.get_or_null(p_rid);
_clear_render_target(rt);
- _clear_render_target_overridden_fbo_cache(rt);
Texture *t = get_texture(rt->texture);
if (t) {
@@ -1826,11 +1833,7 @@ void TextureStorage::render_target_set_override(RID p_render_target, RID p_color
if (p_color_texture.is_null() && p_depth_texture.is_null()) {
_clear_render_target(rt);
- rt->overridden.is_overridden = false;
- rt->overridden.color = RID();
- rt->overridden.depth = RID();
- rt->size = Size2i();
- _clear_render_target_overridden_fbo_cache(rt);
+ _update_render_target(rt);
return;
}
@@ -1849,6 +1852,8 @@ void TextureStorage::render_target_set_override(RID p_render_target, RID p_color
RBMap<uint32_t, RenderTarget::RTOverridden::FBOCacheEntry>::Element *cache;
if ((cache = rt->overridden.fbo_cache.find(hash_key)) != nullptr) {
rt->fbo = cache->get().fbo;
+ rt->color = cache->get().color;
+ rt->depth = cache->get().depth;
rt->size = cache->get().size;
rt->texture = p_color_texture;
return;
@@ -1858,6 +1863,8 @@ void TextureStorage::render_target_set_override(RID p_render_target, RID p_color
RenderTarget::RTOverridden::FBOCacheEntry new_entry;
new_entry.fbo = rt->fbo;
+ new_entry.color = rt->color;
+ new_entry.depth = rt->depth;
new_entry.size = rt->size;
// Keep track of any textures we had to allocate because they weren't overridden.
if (p_color_texture.is_null()) {
diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h
index c465576347..169c50638d 100644
--- a/drivers/gles3/storage/texture_storage.h
+++ b/drivers/gles3/storage/texture_storage.h
@@ -344,6 +344,8 @@ struct RenderTarget {
struct FBOCacheEntry {
GLuint fbo;
+ GLuint color;
+ GLuint depth;
Size2i size;
Vector<GLuint> allocated_textures;
};
@@ -412,7 +414,6 @@ private:
mutable RID_Owner<RenderTarget> render_target_owner;
void _clear_render_target(RenderTarget *rt);
- void _clear_render_target_overridden_fbo_cache(RenderTarget *rt);
void _update_render_target(RenderTarget *rt);
void _create_render_target_backbuffer(RenderTarget *rt);
void _render_target_allocate_sdf(RenderTarget *rt);
diff --git a/drivers/gles3/storage/utilities.cpp b/drivers/gles3/storage/utilities.cpp
index 393093c2a7..fe900c7cfb 100644
--- a/drivers/gles3/storage/utilities.cpp
+++ b/drivers/gles3/storage/utilities.cpp
@@ -281,7 +281,7 @@ String Utilities::get_captured_timestamp_name(uint32_t p_index) const {
void Utilities::update_dirty_resources() {
MaterialStorage::get_singleton()->_update_global_shader_uniforms();
MaterialStorage::get_singleton()->_update_queued_materials();
- //MeshStorage::get_singleton()->_update_dirty_skeletons();
+ MeshStorage::get_singleton()->_update_dirty_skeletons();
MeshStorage::get_singleton()->_update_dirty_multimeshes();
TextureStorage::get_singleton()->update_texture_atlas();
}
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index d00a64f796..06e1e8b22e 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -2274,7 +2274,9 @@ void EditorNode::_edit_current(bool p_skip_foreign) {
} else {
Node *selected_node = nullptr;
- if (current_obj->is_class("MultiNodeEdit")) {
+ if (current_obj->is_class("EditorDebuggerRemoteObject")) {
+ disable_folding = true;
+ } else if (current_obj->is_class("MultiNodeEdit")) {
Node *scene = get_edited_scene();
if (scene) {
MultiNodeEdit *multi_node_edit = Object::cast_to<MultiNodeEdit>(current_obj);
diff --git a/editor/export/editor_export_plugin.cpp b/editor/export/editor_export_plugin.cpp
index 78d4f93dfe..f0e841f307 100644
--- a/editor/export/editor_export_plugin.cpp
+++ b/editor/export/editor_export_plugin.cpp
@@ -148,10 +148,8 @@ bool EditorExportPlugin::_begin_customize_resources(const Ref<EditorExportPlatfo
Ref<Resource> EditorExportPlugin::_customize_resource(const Ref<Resource> &p_resource, const String &p_path) {
Ref<Resource> ret;
- if (GDVIRTUAL_REQUIRED_CALL(_customize_resource, p_resource, p_path, ret)) {
- return ret;
- }
- return Ref<Resource>();
+ GDVIRTUAL_REQUIRED_CALL(_customize_resource, p_resource, p_path, ret);
+ return ret;
}
bool EditorExportPlugin::_begin_customize_scenes(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) const {
@@ -162,18 +160,14 @@ bool EditorExportPlugin::_begin_customize_scenes(const Ref<EditorExportPlatform>
Node *EditorExportPlugin::_customize_scene(Node *p_root, const String &p_path) {
Node *ret = nullptr;
- if (GDVIRTUAL_REQUIRED_CALL(_customize_scene, p_root, p_path, ret)) {
- return ret;
- }
- return nullptr;
+ GDVIRTUAL_REQUIRED_CALL(_customize_scene, p_root, p_path, ret);
+ return ret;
}
uint64_t EditorExportPlugin::_get_customization_configuration_hash() const {
uint64_t ret = 0;
- if (GDVIRTUAL_REQUIRED_CALL(_get_customization_configuration_hash, ret)) {
- return ret;
- }
- return 0;
+ GDVIRTUAL_REQUIRED_CALL(_get_customization_configuration_hash, ret);
+ return ret;
}
void EditorExportPlugin::_end_customize_scenes() {
@@ -186,10 +180,8 @@ void EditorExportPlugin::_end_customize_resources() {
String EditorExportPlugin::_get_name() const {
String ret;
- if (GDVIRTUAL_REQUIRED_CALL(_get_name, ret)) {
- return ret;
- }
- return "";
+ GDVIRTUAL_REQUIRED_CALL(_get_name, ret);
+ return ret;
}
void EditorExportPlugin::_export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features) {
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 8cb9a6712d..c08c9a83a7 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -5596,7 +5596,7 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String &
// make visible for certain node type
if (Object::cast_to<Control>(child)) {
Size2 texture_size = texture->get_size();
- undo_redo->add_do_property(child, "rect_size", texture_size);
+ undo_redo->add_do_property(child, "size", texture_size);
} else if (Object::cast_to<Polygon2D>(child)) {
Size2 texture_size = texture->get_size();
Vector<Vector2> list = {
@@ -5612,6 +5612,11 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String &
Transform2D xform = canvas_item_editor->get_canvas_transform();
Point2 target_position = xform.affine_inverse().xform(p_point);
+ // Adjust position for Control and TouchScreenButton
+ if (Object::cast_to<Control>(child) || Object::cast_to<TouchScreenButton>(child)) {
+ target_position -= texture->get_size() / 2;
+ }
+
// there's nothing to be used as source position so snapping will work as absolute if enabled
target_position = canvas_item_editor->snap_point(target_position);
undo_redo->add_do_method(child, "set_global_position", target_position);
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index df1fd52b69..4194fd831b 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -86,6 +86,165 @@ constexpr real_t MAX_Z = 1000000.0;
constexpr real_t MIN_FOV = 0.01;
constexpr real_t MAX_FOV = 179;
+void ViewportNavigationControl::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ if (!is_connected("mouse_exited", callable_mp(this, &ViewportNavigationControl::_on_mouse_exited))) {
+ connect("mouse_exited", callable_mp(this, &ViewportNavigationControl::_on_mouse_exited));
+ }
+ } break;
+
+ case NOTIFICATION_DRAW: {
+ if (viewport != nullptr) {
+ _draw();
+ _update_navigation();
+ }
+ } break;
+ }
+}
+
+void ViewportNavigationControl::_draw() {
+ if (nav_mode == Node3DEditorViewport::NAVIGATION_NONE) {
+ return;
+ }
+
+ Vector2 center = get_size() / 2.0;
+ float radius = get_size().x / 2.0;
+
+ const bool focused = focused_index != -1;
+ draw_circle(center, radius, Color(0.5, 0.5, 0.5, focused ? 0.25 : 0.05));
+
+ const Color c = focused ? Color(0.9, 0.9, 0.9, 0.9) : Color(0.5, 0.5, 0.5, 0.25);
+
+ Vector2 circle_pos = focused ? center.move_toward(focused_pos, radius) : center;
+
+ draw_circle(circle_pos, AXIS_CIRCLE_RADIUS, c);
+ draw_circle(circle_pos, AXIS_CIRCLE_RADIUS * 0.8, c.darkened(0.4));
+}
+
+void ViewportNavigationControl::_process_click(int p_index, Vector2 p_position, bool p_pressed) {
+ if (focused_index != -1 && focused_index != p_index) {
+ return;
+ }
+ if (p_pressed) {
+ if (p_position.distance_to(get_size() / 2.0) < get_size().x / 2.0) {
+ focused_pos = p_position;
+ focused_index = p_index;
+ queue_redraw();
+ }
+ } else {
+ focused_index = -1;
+ if (Input::get_singleton()->get_mouse_mode() == Input::MOUSE_MODE_CAPTURED) {
+ Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
+ Input::get_singleton()->warp_mouse(focused_mouse_start);
+ }
+ }
+}
+
+void ViewportNavigationControl::_process_drag(int p_index, Vector2 p_position, Vector2 p_relative_position) {
+ if (focused_index == p_index) {
+ if (Input::get_singleton()->get_mouse_mode() == Input::MOUSE_MODE_VISIBLE) {
+ Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED);
+ focused_mouse_start = p_position;
+ }
+ focused_pos += p_relative_position;
+ queue_redraw();
+ }
+}
+
+void ViewportNavigationControl::gui_input(const Ref<InputEvent> &p_event) {
+ // Mouse events
+ const Ref<InputEventMouseButton> mouse_button = p_event;
+ if (mouse_button.is_valid() && mouse_button->get_button_index() == MouseButton::LEFT) {
+ _process_click(100, mouse_button->get_position(), mouse_button->is_pressed());
+ }
+
+ const Ref<InputEventMouseMotion> mouse_motion = p_event;
+ if (mouse_motion.is_valid()) {
+ _process_drag(100, mouse_motion->get_global_position(), viewport->_get_warped_mouse_motion(mouse_motion));
+ }
+
+ // Touch events
+ const Ref<InputEventScreenTouch> screen_touch = p_event;
+ if (screen_touch.is_valid()) {
+ _process_click(screen_touch->get_index(), screen_touch->get_position(), screen_touch->is_pressed());
+ }
+
+ const Ref<InputEventScreenDrag> screen_drag = p_event;
+ if (screen_drag.is_valid()) {
+ _process_drag(screen_drag->get_index(), screen_drag->get_position(), screen_drag->get_relative());
+ }
+}
+
+void ViewportNavigationControl::_update_navigation() {
+ if (focused_index == -1) {
+ return;
+ }
+
+ Vector2 delta = focused_pos - (get_size() / 2.0);
+ Vector2 delta_normalized = delta.normalized();
+ switch (nav_mode) {
+ case Node3DEditorViewport::NavigationMode::NAVIGATION_MOVE: {
+ real_t speed_multiplier = MIN(delta.length() / (get_size().x * 100.0), 3.0);
+ real_t speed = viewport->freelook_speed * speed_multiplier;
+
+ const Node3DEditorViewport::FreelookNavigationScheme navigation_scheme = (Node3DEditorViewport::FreelookNavigationScheme)EditorSettings::get_singleton()->get("editors/3d/freelook/freelook_navigation_scheme").operator int();
+
+ Vector3 forward;
+ if (navigation_scheme == Node3DEditorViewport::FreelookNavigationScheme::FREELOOK_FULLY_AXIS_LOCKED) {
+ // Forward/backward keys will always go straight forward/backward, never moving on the Y axis.
+ forward = Vector3(0, 0, delta_normalized.y).rotated(Vector3(0, 1, 0), viewport->camera->get_rotation().y);
+ } else {
+ // Forward/backward keys will be relative to the camera pitch.
+ forward = viewport->camera->get_transform().basis.xform(Vector3(0, 0, delta_normalized.y));
+ }
+
+ const Vector3 right = viewport->camera->get_transform().basis.xform(Vector3(delta_normalized.x, 0, 0));
+
+ const Vector3 direction = forward + right;
+ const Vector3 motion = direction * speed;
+ viewport->cursor.pos += motion;
+ viewport->cursor.eye_pos += motion;
+ } break;
+
+ case Node3DEditorViewport::NavigationMode::NAVIGATION_LOOK: {
+ real_t speed_multiplier = MIN(delta.length() / (get_size().x * 2.5), 3.0);
+ real_t speed = viewport->freelook_speed * speed_multiplier;
+ viewport->_nav_look(nullptr, delta_normalized * speed);
+ } break;
+
+ case Node3DEditorViewport::NAVIGATION_PAN: {
+ real_t speed_multiplier = MIN(delta.length() / (get_size().x), 3.0);
+ real_t speed = viewport->freelook_speed * speed_multiplier;
+ viewport->_nav_pan(nullptr, -delta_normalized * speed);
+ } break;
+ case Node3DEditorViewport::NAVIGATION_ZOOM: {
+ real_t speed_multiplier = MIN(delta.length() / (get_size().x), 3.0);
+ real_t speed = viewport->freelook_speed * speed_multiplier;
+ viewport->_nav_zoom(nullptr, delta_normalized * speed);
+ } break;
+ case Node3DEditorViewport::NAVIGATION_ORBIT: {
+ real_t speed_multiplier = MIN(delta.length() / (get_size().x), 3.0);
+ real_t speed = viewport->freelook_speed * speed_multiplier;
+ viewport->_nav_orbit(nullptr, delta_normalized * speed);
+ } break;
+ case Node3DEditorViewport::NAVIGATION_NONE: {
+ } break;
+ }
+}
+
+void ViewportNavigationControl::_on_mouse_exited() {
+ queue_redraw();
+}
+
+void ViewportNavigationControl::set_navigation_mode(Node3DEditorViewport::NavigationMode p_nav_mode) {
+ nav_mode = p_nav_mode;
+}
+
+void ViewportNavigationControl::set_viewport(Node3DEditorViewport *p_viewport) {
+ viewport = p_viewport;
+}
+
void ViewportRotationControl::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
@@ -120,7 +279,7 @@ void ViewportRotationControl::_draw() {
const Vector2i center = get_size() / 2.0;
const real_t radius = get_size().x / 2.0;
- if (focused_axis > -2 || orbiting) {
+ if (focused_axis > -2 || orbiting_index != -1) {
draw_circle(center, radius, Color(0.5, 0.5, 0.5, 0.25));
}
@@ -191,41 +350,63 @@ void ViewportRotationControl::_get_sorted_axis(Vector<Axis2D> &r_axis) {
r_axis.sort_custom<Axis2DCompare>();
}
+void ViewportRotationControl::_process_click(int p_index, Vector2 p_position, bool p_pressed) {
+ if (orbiting_index != -1 && orbiting_index != p_index) {
+ return;
+ }
+ if (p_pressed) {
+ if (p_position.distance_to(get_size() / 2.0) < get_size().x / 2.0) {
+ orbiting_index = p_index;
+ }
+ } else {
+ if (focused_axis > -1) {
+ viewport->_menu_option(axis_menu_options[focused_axis]);
+ _update_focus();
+ }
+ orbiting_index = -1;
+ if (Input::get_singleton()->get_mouse_mode() == Input::MOUSE_MODE_CAPTURED) {
+ Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
+ Input::get_singleton()->warp_mouse(orbiting_mouse_start);
+ }
+ }
+}
+
+void ViewportRotationControl::_process_drag(Ref<InputEventWithModifiers> p_event, int p_index, Vector2 p_position, Vector2 p_relative_position) {
+ if (orbiting_index == p_index) {
+ if (Input::get_singleton()->get_mouse_mode() == Input::MOUSE_MODE_VISIBLE) {
+ Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED);
+ orbiting_mouse_start = p_position;
+ }
+ viewport->_nav_orbit(p_event, p_relative_position);
+ focused_axis = -1;
+ } else {
+ _update_focus();
+ }
+}
+
void ViewportRotationControl::gui_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
+ // Mouse events
const Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) {
- Vector2 pos = mb->get_position();
- if (mb->is_pressed()) {
- if (pos.distance_to(get_size() / 2.0) < get_size().x / 2.0) {
- orbiting = true;
- }
- } else {
- if (focused_axis > -1) {
- viewport->_menu_option(axis_menu_options[focused_axis]);
- _update_focus();
- }
- orbiting = false;
- if (Input::get_singleton()->get_mouse_mode() == Input::MOUSE_MODE_CAPTURED) {
- Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
- Input::get_singleton()->warp_mouse(orbiting_mouse_start);
- }
- }
+ _process_click(100, mb->get_position(), mb->is_pressed());
}
const Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
- if (orbiting) {
- if (Input::get_singleton()->get_mouse_mode() == Input::MOUSE_MODE_VISIBLE) {
- Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED);
- orbiting_mouse_start = mm->get_global_position();
- }
- viewport->_nav_orbit(mm, viewport->_get_warped_mouse_motion(mm));
- focused_axis = -1;
- } else {
- _update_focus();
- }
+ _process_drag(mm, 100, mm->get_global_position(), viewport->_get_warped_mouse_motion(mm));
+ }
+
+ // Touch events
+ const Ref<InputEventScreenTouch> screen_touch = p_event;
+ if (screen_touch.is_valid()) {
+ _process_click(screen_touch->get_index(), screen_touch->get_position(), screen_touch->is_pressed());
+ }
+
+ const Ref<InputEventScreenDrag> screen_drag = p_event;
+ if (screen_drag.is_valid()) {
+ _process_drag(screen_drag, screen_drag->get_index(), screen_drag->get_position(), screen_drag->get_relative());
}
}
@@ -353,6 +534,8 @@ void Node3DEditorViewport::_update_camera(real_t p_interp_delta) {
update_transform_gizmo_view();
rotation_control->queue_redraw();
+ position_control->queue_redraw();
+ look_control->queue_redraw();
spatial_editor->update_grid();
}
}
@@ -2091,7 +2274,7 @@ void Node3DEditorViewport::_nav_pan(Ref<InputEventWithModifiers> p_event, const
const NavigationScheme nav_scheme = (NavigationScheme)EDITOR_GET("editors/3d/navigation/navigation_scheme").operator int();
real_t pan_speed = 1 / 150.0;
- if (nav_scheme == NAVIGATION_MAYA && p_event->is_shift_pressed()) {
+ if (p_event.is_valid() && nav_scheme == NAVIGATION_MAYA && p_event->is_shift_pressed()) {
pan_speed *= 10;
}
@@ -2115,7 +2298,7 @@ void Node3DEditorViewport::_nav_zoom(Ref<InputEventWithModifiers> p_event, const
const NavigationScheme nav_scheme = (NavigationScheme)EDITOR_GET("editors/3d/navigation/navigation_scheme").operator int();
real_t zoom_speed = 1 / 80.0;
- if (nav_scheme == NAVIGATION_MAYA && p_event->is_shift_pressed()) {
+ if (p_event.is_valid() && nav_scheme == NAVIGATION_MAYA && p_event->is_shift_pressed()) {
zoom_speed *= 10;
}
@@ -2448,6 +2631,8 @@ void Node3DEditorViewport::_notification(int p_what) {
}
call_deferred(SNAME("update_transform_gizmo_view"));
rotation_control->set_visible(EDITOR_GET("editors/3d/navigation/show_viewport_rotation_gizmo"));
+ position_control->set_visible(EDITOR_GET("editors/3d/navigation/show_viewport_navigation_gizmo"));
+ look_control->set_visible(EDITOR_GET("editors/3d/navigation/show_viewport_navigation_gizmo"));
} break;
case NOTIFICATION_RESIZED: {
@@ -3370,6 +3555,8 @@ void Node3DEditorViewport::_toggle_camera_preview(bool p_activate) {
ERR_FAIL_COND(!p_activate && !previewing);
rotation_control->set_visible(!p_activate);
+ position_control->set_visible(!p_activate);
+ look_control->set_visible(!p_activate);
if (!p_activate) {
previewing->disconnect("tree_exiting", callable_mp(this, &Node3DEditorViewport::_preview_exited_scene));
@@ -3391,6 +3578,8 @@ void Node3DEditorViewport::_toggle_camera_preview(bool p_activate) {
void Node3DEditorViewport::_toggle_cinema_preview(bool p_activate) {
previewing_cinema = p_activate;
rotation_control->set_visible(!p_activate);
+ position_control->set_visible(!p_activate);
+ look_control->set_visible(!p_activate);
if (!previewing_cinema) {
if (previewing != nullptr) {
@@ -4874,6 +5063,14 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p
preview_node = nullptr;
+ bottom_center_vbox = memnew(VBoxContainer);
+ bottom_center_vbox->set_anchors_preset(LayoutPreset::PRESET_CENTER);
+ bottom_center_vbox->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -20 * EDSCALE);
+ bottom_center_vbox->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -10 * EDSCALE);
+ bottom_center_vbox->set_h_grow_direction(GROW_DIRECTION_BOTH);
+ bottom_center_vbox->set_v_grow_direction(GROW_DIRECTION_BEGIN);
+ surface->add_child(bottom_center_vbox);
+
info_label = memnew(Label);
info_label->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -90 * EDSCALE);
info_label->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -90 * EDSCALE);
@@ -4894,23 +5091,18 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p
previewing_cinema = false;
locked_label = memnew(Label);
- locked_label->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -20 * EDSCALE);
- locked_label->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -10 * EDSCALE);
- locked_label->set_h_grow_direction(GROW_DIRECTION_END);
- locked_label->set_v_grow_direction(GROW_DIRECTION_BEGIN);
locked_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
- surface->add_child(locked_label);
+ locked_label->set_h_size_flags(SIZE_SHRINK_CENTER);
+ bottom_center_vbox->add_child(locked_label);
locked_label->set_text(TTR("View Rotation Locked"));
locked_label->hide();
zoom_limit_label = memnew(Label);
- zoom_limit_label->set_anchors_and_offsets_preset(LayoutPreset::PRESET_BOTTOM_LEFT);
- zoom_limit_label->set_offset(Side::SIDE_TOP, -28 * EDSCALE);
zoom_limit_label->set_text(TTR("To zoom further, change the camera's clipping planes (View -> Settings...)"));
zoom_limit_label->set_name("ZoomLimitMessageLabel");
zoom_limit_label->add_theme_color_override("font_color", Color(1, 1, 1, 1));
zoom_limit_label->hide();
- surface->add_child(zoom_limit_label);
+ bottom_center_vbox->add_child(zoom_limit_label);
preview_material_label = memnew(Label);
preview_material_label->set_anchors_and_offsets_preset(LayoutPreset::PRESET_BOTTOM_LEFT);
@@ -4941,6 +5133,30 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p
// Prevent visible spacing between frame time labels.
top_right_vbox->add_theme_constant_override("separation", 0);
+ const int navigation_control_size = 200;
+
+ position_control = memnew(ViewportNavigationControl);
+ position_control->set_navigation_mode(Node3DEditorViewport::NAVIGATION_MOVE);
+ position_control->set_custom_minimum_size(Size2(navigation_control_size, navigation_control_size) * EDSCALE);
+ position_control->set_h_size_flags(SIZE_SHRINK_END);
+ position_control->set_anchor_and_offset(SIDE_LEFT, ANCHOR_BEGIN, 0 * EDSCALE);
+ position_control->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -navigation_control_size * EDSCALE);
+ position_control->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_BEGIN, navigation_control_size * EDSCALE);
+ position_control->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0 * EDSCALE);
+ position_control->set_viewport(this);
+ surface->add_child(position_control);
+
+ look_control = memnew(ViewportNavigationControl);
+ look_control->set_navigation_mode(Node3DEditorViewport::NAVIGATION_LOOK);
+ look_control->set_custom_minimum_size(Size2(navigation_control_size, navigation_control_size) * EDSCALE);
+ look_control->set_h_size_flags(SIZE_SHRINK_END);
+ look_control->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -navigation_control_size * EDSCALE);
+ look_control->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -navigation_control_size * EDSCALE);
+ look_control->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0 * EDSCALE);
+ look_control->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0 * EDSCALE);
+ look_control->set_viewport(this);
+ surface->add_child(look_control);
+
rotation_control = memnew(ViewportRotationControl);
rotation_control->set_custom_minimum_size(Size2(80, 80) * EDSCALE);
rotation_control->set_h_size_flags(SIZE_SHRINK_END);
@@ -8191,7 +8407,8 @@ Node3DEditor::Node3DEditor() {
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "editors/3d/manipulator_gizmo_size", PROPERTY_HINT_RANGE, "16,160,1"));
EDITOR_DEF("editors/3d/manipulator_gizmo_opacity", 0.9);
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::FLOAT, "editors/3d/manipulator_gizmo_opacity", PROPERTY_HINT_RANGE, "0,1,0.01"));
- EDITOR_DEF("editors/3d/navigation/show_viewport_rotation_gizmo", true);
+ EDITOR_DEF_RST("editors/3d/navigation/show_viewport_rotation_gizmo", true);
+ EDITOR_DEF_RST("editors/3d/navigation/show_viewport_navigation_gizmo", DisplayServer::get_singleton()->screen_is_touchscreen());
current_hover_gizmo_handle = -1;
current_hover_gizmo_handle_secondary = false;
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index b7ac718182..04fc030f2b 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -57,6 +57,7 @@ class SubViewport;
class SubViewportContainer;
class VSplitContainer;
class WorldEnvironment;
+class ViewportNavigationControl;
class ViewportRotationControl : public Control {
GDCLASS(ViewportRotationControl, Control);
@@ -77,7 +78,7 @@ class ViewportRotationControl : public Control {
Vector<Color> axis_colors;
Vector<int> axis_menu_options;
Vector2i orbiting_mouse_start;
- bool orbiting = false;
+ int orbiting_index = -1;
int focused_axis = -2;
const float AXIS_CIRCLE_RADIUS = 8.0f * EDSCALE;
@@ -90,6 +91,8 @@ protected:
void _get_sorted_axis(Vector<Axis2D> &r_axis);
void _update_focus();
void _on_mouse_exited();
+ void _process_click(int p_index, Vector2 p_position, bool p_pressed);
+ void _process_drag(Ref<InputEventWithModifiers> p_event, int p_index, Vector2 p_position, Vector2 p_relative_position);
public:
void set_viewport(Node3DEditorViewport *p_viewport);
@@ -98,6 +101,7 @@ public:
class Node3DEditorViewport : public Control {
GDCLASS(Node3DEditorViewport, Control);
friend class Node3DEditor;
+ friend class ViewportNavigationControl;
friend class ViewportRotationControl;
enum {
VIEW_TOP,
@@ -236,6 +240,9 @@ private:
Label *preview_material_label_desc = nullptr;
VBoxContainer *top_right_vbox = nullptr;
+ VBoxContainer *bottom_center_vbox = nullptr;
+ ViewportNavigationControl *position_control = nullptr;
+ ViewportNavigationControl *look_control = nullptr;
ViewportRotationControl *rotation_control = nullptr;
Gradient *frame_time_gradient = nullptr;
Label *cpu_time_label = nullptr;
@@ -297,7 +304,8 @@ private:
NAVIGATION_PAN,
NAVIGATION_ZOOM,
NAVIGATION_ORBIT,
- NAVIGATION_LOOK
+ NAVIGATION_LOOK,
+ NAVIGATION_MOVE
};
enum TransformMode {
TRANSFORM_NONE,
@@ -916,4 +924,29 @@ public:
~Node3DEditorPlugin();
};
+class ViewportNavigationControl : public Control {
+ GDCLASS(ViewportNavigationControl, Control);
+
+ Node3DEditorViewport *viewport = nullptr;
+ Vector2i focused_mouse_start;
+ Vector2 focused_pos;
+ int focused_index = -1;
+ Node3DEditorViewport::NavigationMode nav_mode = Node3DEditorViewport::NavigationMode::NAVIGATION_NONE;
+
+ const float AXIS_CIRCLE_RADIUS = 30.0f * EDSCALE;
+
+protected:
+ void _notification(int p_what);
+ virtual void gui_input(const Ref<InputEvent> &p_event) override;
+ void _draw();
+ void _on_mouse_exited();
+ void _process_click(int p_index, Vector2 p_position, bool p_pressed);
+ void _process_drag(int p_index, Vector2 p_position, Vector2 p_relative_position);
+ void _update_navigation();
+
+public:
+ void set_navigation_mode(Node3DEditorViewport::NavigationMode p_nav_mode);
+ void set_viewport(Node3DEditorViewport *p_viewport);
+};
+
#endif // NODE_3D_EDITOR_PLUGIN_H
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index fc13393582..ae7570e161 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -152,7 +152,7 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_set(const StringName &p_na
// ID and size related properties.
if (tiles.size() == 1) {
- const Vector2i &coords = tiles.front()->get().tile;
+ const Vector2i coords = tiles.front()->get().tile;
const int &alternative = tiles.front()->get().alternative;
if (alternative == 0) {
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index b7cca72624..249504b6e8 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -1253,7 +1253,6 @@ void ProjectList::migrate_config() {
if (FileAccess::exists(_config_path)) {
return;
}
- print_line("Migrating legacy project list");
List<PropertyInfo> properties;
EditorSettings::get_singleton()->get_property_list(&properties);
@@ -1266,6 +1265,8 @@ void ProjectList::migrate_config() {
}
String path = EDITOR_GET(property_key);
+ print_line("Migrating legacy project '" + path + "'.");
+
String favoriteKey = "favorite_projects/" + property_key.get_slice("/", 1);
bool favorite = EditorSettings::get_singleton()->has_setting(favoriteKey);
add_project(path, favorite);
diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp
index 4a3b0e979f..52f4ae553f 100644
--- a/editor/script_create_dialog.cpp
+++ b/editor/script_create_dialog.cpp
@@ -107,7 +107,15 @@ static Vector<String> _get_hierarchy(String p_class_name) {
void ScriptCreateDialog::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE: {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ Ref<Texture2D> language_icon = get_theme_icon(ScriptServer::get_language(i)->get_type(), SNAME("EditorIcons"));
+ if (language_icon.is_valid()) {
+ language_menu->set_item_icon(i, language_icon);
+ }
+ }
+
String last_language = EditorSettings::get_singleton()->get_project_metadata("script_setup", "last_selected_language", "");
if (!last_language.is_empty()) {
for (int i = 0; i < language_menu->get_item_count(); i++) {
@@ -120,15 +128,9 @@ void ScriptCreateDialog::_notification(int p_what) {
} else {
language_menu->select(default_language);
}
-
- [[fallthrough]];
- }
- case NOTIFICATION_THEME_CHANGED: {
- for (int i = 0; i < ScriptServer::get_language_count(); i++) {
- Ref<Texture2D> language_icon = get_theme_icon(ScriptServer::get_language(i)->get_type(), SNAME("EditorIcons"));
- if (language_icon.is_valid()) {
- language_menu->set_item_icon(i, language_icon);
- }
+ if (EditorSettings::get_singleton()->has_meta("script_setup_use_script_templates")) {
+ is_using_templates = bool(EditorSettings::get_singleton()->get_meta("script_setup_use_script_templates"));
+ use_templates->set_pressed(is_using_templates);
}
path_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons")));
@@ -331,7 +333,12 @@ void ScriptCreateDialog::_template_changed(int p_template) {
EditorSettings::get_singleton()->set_project_metadata("script_setup", "templates_dictionary", dic_templates_project);
} else {
// Save template info to editor dictionary (not a project template).
- templates_dictionary[parent_name->get_text()] = sinfo.get_hash();
+ Dictionary dic_templates;
+ if (EditorSettings::get_singleton()->has_meta("script_setup_templates_dictionary")) {
+ dic_templates = (Dictionary)EditorSettings::get_singleton()->get_meta("script_setup_templates_dictionary");
+ }
+ dic_templates[parent_name->get_text()] = sinfo.get_hash();
+ EditorSettings::get_singleton()->set_meta("script_setup_templates_dictionary", dic_templates);
// Remove template from project dictionary as we last used an editor level template.
Dictionary dic_templates_project = EditorSettings::get_singleton()->get_project_metadata("script_setup", "templates_dictionary", Dictionary());
if (dic_templates_project.has(parent_name->get_text())) {
@@ -480,6 +487,7 @@ void ScriptCreateDialog::_built_in_pressed() {
void ScriptCreateDialog::_use_template_pressed() {
is_using_templates = use_templates->is_pressed();
+ EditorSettings::get_singleton()->set_meta("script_setup_use_script_templates", is_using_templates);
_update_dialog();
}
@@ -597,6 +605,10 @@ void ScriptCreateDialog::_update_template_menu() {
if (is_language_using_templates) {
// Get the latest templates used for each type of node from project settings then global settings.
Dictionary last_local_templates = EditorSettings::get_singleton()->get_project_metadata("script_setup", "templates_dictionary", Dictionary());
+ Dictionary last_global_templates;
+ if (EditorSettings::get_singleton()->has_meta("script_setup_templates_dictionary")) {
+ last_global_templates = (Dictionary)EditorSettings::get_singleton()->get_meta("script_setup_templates_dictionary");
+ }
String inherits_base_type = parent_name->get_text();
// If it inherits from a script, get its parent class first.
@@ -651,7 +663,7 @@ void ScriptCreateDialog::_update_template_menu() {
// Check for last used template for this node in project settings then in global settings.
if (last_local_templates.has(parent_name->get_text()) && t.get_hash() == String(last_local_templates[parent_name->get_text()])) {
last_used_template = id;
- } else if (last_used_template == -1 && templates_dictionary.has(parent_name->get_text()) && t.get_hash() == String(templates_dictionary[parent_name->get_text()])) {
+ } else if (last_used_template == -1 && last_global_templates.has(parent_name->get_text()) && t.get_hash() == String(last_global_templates[parent_name->get_text()])) {
last_used_template = id;
}
t.id = id;
diff --git a/editor/script_create_dialog.h b/editor/script_create_dialog.h
index fb1a49a1ca..25428508da 100644
--- a/editor/script_create_dialog.h
+++ b/editor/script_create_dialog.h
@@ -83,7 +83,6 @@ class ScriptCreateDialog : public ConfirmationDialog {
int current_language;
int default_language;
bool re_check_path = false;
- Dictionary templates_dictionary;
Control *path_controls[2];
Control *name_controls[2];
diff --git a/main/main.cpp b/main/main.cpp
index d857c93b73..2d0843a331 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -1540,6 +1540,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
if (rendering_driver.is_empty() && rendering_method.is_empty() && project_manager) {
rendering_driver = "opengl3";
rendering_method = "gl_compatibility";
+ default_renderer_mobile = "gl_compatibility";
}
#endif
if (renderer_hints.is_empty()) {
diff --git a/modules/etcpak/image_compress_etcpak.cpp b/modules/etcpak/image_compress_etcpak.cpp
index 3d66b27556..e467ed60ee 100644
--- a/modules/etcpak/image_compress_etcpak.cpp
+++ b/modules/etcpak/image_compress_etcpak.cpp
@@ -111,13 +111,16 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_qua
Image::Format target_format = Image::FORMAT_RGBA8;
if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC1) {
target_format = Image::FORMAT_ETC;
+ r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC.
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2) {
target_format = Image::FORMAT_ETC2_RGB8;
+ r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC.
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG) {
target_format = Image::FORMAT_ETC2_RA_AS_RG;
r_img->convert_rg_to_ra_rgba8();
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_ALPHA) {
target_format = Image::FORMAT_ETC2_RGBA8;
+ r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC.
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT1) {
target_format = Image::FORMAT_DXT1;
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5_RA_AS_RG) {
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index c8eda53a2d..4981750b7d 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -547,7 +547,7 @@
<return type="void" />
<param index="0" name="icon_path" type="String" />
<description>
- Add a custom icon to the current script. After loading an icon at [param icon_path], the icon is displayed in the Scene dock for every node that the script is attached to. For named classes, the icon is also displayed in various editor dialogs.
+ Add a custom icon to the current script. The script must be registered as a global class using the [code]class_name[/code] keyword for this to have a visible effect. The icon specified at [param icon_path] is displayed in the Scene dock for every node of that class, as well as in various editor dialogs.
[codeblock]
@icon("res://path/to/class/icon.svg")
[/codeblock]
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 95e577c140..663d72038d 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -196,8 +196,11 @@ Error GDScriptAnalyzer::check_class_member_name_conflict(const GDScriptParser::C
while (current_data_type && current_data_type->kind == GDScriptParser::DataType::Kind::CLASS) {
GDScriptParser::ClassNode *current_class_node = current_data_type->class_type;
if (has_member_name_conflict_in_script_class(p_member_name, current_class_node, p_member_node)) {
- push_error(vformat(R"(The member "%s" already exists in parent class %s.)", p_member_name, current_class_node->identifier->name),
- p_member_node);
+ String parent_class_name = current_class_node->fqcn;
+ if (current_class_node->identifier != nullptr) {
+ parent_class_name = current_class_node->identifier->name;
+ }
+ push_error(vformat(R"(The member "%s" already exists in parent class %s.)", p_member_name, parent_class_name), p_member_node);
return ERR_PARSE_ERROR;
}
current_data_type = &current_class_node->base_type;
@@ -1162,6 +1165,8 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
if (p_function->parameters[i]->default_value->is_constant) {
p_function->default_arg_values.push_back(p_function->parameters[i]->default_value->reduced_value);
+ } else {
+ p_function->default_arg_values.push_back(Variant()); // Prevent shift.
}
}
#endif // TOOLS_ENABLED
@@ -1214,11 +1219,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
if (!valid) {
// Compute parent signature as a string to show in the error message.
- String parent_signature = parent_return_type.is_hard_type() ? parent_return_type.to_string() : "Variant";
- if (parent_signature == "null") {
- parent_signature = "void";
- }
- parent_signature += " " + p_function->identifier->name.operator String() + "(";
+ String parent_signature = p_function->identifier->name.operator String() + "(";
int j = 0;
for (const GDScriptParser::DataType &par_type : parameters_types) {
if (j > 0) {
@@ -1235,7 +1236,15 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
j++;
}
- parent_signature += ")";
+ parent_signature += ") -> ";
+
+ const String return_type = parent_return_type.is_hard_type() ? parent_return_type.to_string() : "Variant";
+ if (return_type == "null") {
+ parent_signature += "void";
+ } else {
+ parent_signature += return_type;
+ }
+
push_error(vformat(R"(The function signature doesn't match the parent. Parent signature is "%s".)", parent_signature), p_function);
}
}
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 103eb60da9..24241b712b 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -1875,6 +1875,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
GDScriptCodeGenerator::Address local = codegen.locals[lv->identifier->name];
GDScriptDataType local_type = _gdtype_from_datatype(lv->get_datatype(), codegen.script);
+ bool initialized = false;
if (lv->initializer != nullptr) {
// For typed arrays we need to make sure this is already initialized correctly so typed assignment work.
if (local_type.has_type && local_type.builtin_type == Variant::ARRAY) {
@@ -1896,15 +1897,23 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary();
}
+ initialized = true;
} else if (local_type.has_type) {
// Initialize with default for type.
if (local_type.has_container_element_type()) {
codegen.generator->write_construct_typed_array(local, local_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>());
+ initialized = true;
} else if (local_type.kind == GDScriptDataType::BUILTIN) {
codegen.generator->write_construct(local, local_type.builtin_type, Vector<GDScriptCodeGenerator::Address>());
+ initialized = true;
}
// The `else` branch is for objects, in such case we leave it as `null`.
}
+
+ // Assigns a null for the unassigned variables in loops.
+ if (!initialized && p_block->is_loop) {
+ codegen.generator->write_construct(local, Variant::NIL, Vector<GDScriptCodeGenerator::Address>());
+ }
} break;
case GDScriptParser::Node::CONSTANT: {
// Local constants.
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 24dd94873b..f2aafe9f0c 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -1835,9 +1835,9 @@ GDScriptParser::ForNode *GDScriptParser::parse_for() {
}
suite->add_local(SuiteNode::Local(n_for->variable, current_function));
}
- suite->parent_for = n_for;
n_for->loop = parse_suite(R"("for" block)", suite);
+ n_for->loop->is_loop = true;
complete_extents(n_for);
// Reset break/continue state.
@@ -2169,6 +2169,7 @@ GDScriptParser::WhileNode *GDScriptParser::parse_while() {
is_continue_match = false;
n_while->loop = parse_suite(R"("while" block)");
+ n_while->loop->is_loop = true;
complete_extents(n_while);
// Reset break/continue state.
@@ -3785,15 +3786,14 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
}
break;
case GDScriptParser::DataType::CLASS:
- // Can assume type is a global GDScript class.
if (ClassDB::is_parent_class(export_type.native_type, SNAME("Resource"))) {
variable->export_info.type = Variant::OBJECT;
variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
- variable->export_info.hint_string = export_type.class_type->identifier->name;
+ variable->export_info.hint_string = export_type.to_string();
} else if (ClassDB::is_parent_class(export_type.native_type, SNAME("Node"))) {
variable->export_info.type = Variant::OBJECT;
variable->export_info.hint = PROPERTY_HINT_NODE_TYPE;
- variable->export_info.hint_string = export_type.class_type->identifier->name;
+ variable->export_info.hint_string = export_type.to_string();
} else {
push_error(R"(Export type can only be built-in, a resource, a node or an enum.)", variable);
return false;
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index f9a1c5a697..d092a2a5e9 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -1055,12 +1055,12 @@ public:
HashMap<StringName, int> locals_indices;
FunctionNode *parent_function = nullptr;
- ForNode *parent_for = nullptr;
IfNode *parent_if = nullptr;
bool has_return = false;
bool has_continue = false;
bool has_unreachable_code = false; // Just so warnings aren't given more than once per block.
+ bool is_loop = false;
bool has_local(const StringName &p_name) const;
const Local &get_local(const StringName &p_name) const;
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_less.out b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_less.out
index 3baeb17066..4ccd2da381 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_less.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_less.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-The function signature doesn't match the parent. Parent signature is "int my_function(int)".
+The function signature doesn't match the parent. Parent signature is "my_function(int) -> int".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_more.out b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_more.out
index 3baeb17066..4ccd2da381 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_more.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_more.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-The function signature doesn't match the parent. Parent signature is "int my_function(int)".
+The function signature doesn't match the parent. Parent signature is "my_function(int) -> int".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_default_values.out b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_default_values.out
index 665c229339..c70a1df10d 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_default_values.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_default_values.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-The function signature doesn't match the parent. Parent signature is "int my_function(int = default)".
+The function signature doesn't match the parent. Parent signature is "my_function(int = default) -> int".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_type.out b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_type.out
index 3baeb17066..4ccd2da381 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_type.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-The function signature doesn't match the parent. Parent signature is "int my_function(int)".
+The function signature doesn't match the parent. Parent signature is "my_function(int) -> int".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_return_type.out b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_return_type.out
index 5b22739a93..61004ff627 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_return_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_return_type.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-The function signature doesn't match the parent. Parent signature is "int my_function()".
+The function signature doesn't match the parent. Parent signature is "my_function() -> int".
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index cfb9a0fbfb..137fd61a25 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -391,10 +391,10 @@ bool CSharpLanguage::supports_builtin_mode() const {
#ifdef TOOLS_ENABLED
static String variant_type_to_managed_name(const String &p_var_type_name) {
if (p_var_type_name.is_empty()) {
- return "object";
+ return "Variant";
}
- if (!ClassDB::class_exists(p_var_type_name)) {
+ if (ClassDB::class_exists(p_var_type_name)) {
return p_var_type_name;
}
@@ -402,12 +402,12 @@ static String variant_type_to_managed_name(const String &p_var_type_name) {
return "Godot.Object";
}
+ if (p_var_type_name == Variant::get_type_name(Variant::INT)) {
+ return "long";
+ }
+
if (p_var_type_name == Variant::get_type_name(Variant::FLOAT)) {
-#ifdef REAL_T_IS_DOUBLE
return "double";
-#else
- return "float";
-#endif
}
if (p_var_type_name == Variant::get_type_name(Variant::STRING)) {
@@ -485,7 +485,7 @@ static String variant_type_to_managed_name(const String &p_var_type_name) {
}
}
- return "object";
+ return "Variant";
}
String CSharpLanguage::make_function(const String &, const String &p_name, const PackedStringArray &p_args) const {
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
index 0459257106..0d0889c491 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
@@ -12,8 +12,7 @@
<Configurations>Debug;ExportDebug;ExportRelease</Configurations>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <GodotProjectDir Condition=" '$(SolutionDir)' != '' ">$(SolutionDir)</GodotProjectDir>
- <GodotProjectDir Condition=" '$(SolutionDir)' == '' ">$(MSBuildProjectDirectory)</GodotProjectDir>
+ <GodotProjectDir Condition=" '$(GodotProjectDir)' == '' ">$(MSBuildProjectDirectory)</GodotProjectDir>
<GodotProjectDir>$([MSBuild]::EnsureTrailingSlash('$(GodotProjectDir)'))</GodotProjectDir>
<!-- Custom output paths for Godot projects. In brief, 'bin\' and 'obj\' are moved to '$(GodotProjectDir)\.godot\mono\temp\'. -->
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 7008fb638f..d67e57edc2 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
@@ -268,8 +268,9 @@ namespace Godot.SourceGenerators
if (parameters.Length > paramTypes.Length)
return null; // Ignore incompatible method
- return new GodotMethodData(method, paramTypes, parameters
- .Select(p => p.Type).ToImmutableArray(), retType, retSymbol);
+ return new GodotMethodData(method, paramTypes,
+ parameters.Select(p => p.Type).ToImmutableArray(),
+ retType != null ? (retType.Value, retSymbol) : null);
}
public static IEnumerable<GodotMethodData> WhereHasGodotCompatibleSignature(
@@ -330,10 +331,10 @@ namespace Godot.SourceGenerators
public static string Path(this Location location)
=> location.SourceTree?.GetLineSpan(location.SourceSpan).Path
- ?? location.GetLineSpan().Path;
+ ?? location.GetLineSpan().Path;
public static int StartLine(this Location location)
=> location.SourceTree?.GetLineSpan(location.SourceSpan).StartLinePosition.Line
- ?? location.GetLineSpan().StartLinePosition.Line;
+ ?? location.GetLineSpan().StartLinePosition.Line;
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs
index abd8079922..0760ea11bb 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs
@@ -6,20 +6,18 @@ namespace Godot.SourceGenerators
public readonly struct GodotMethodData
{
public GodotMethodData(IMethodSymbol method, ImmutableArray<MarshalType> paramTypes,
- ImmutableArray<ITypeSymbol> paramTypeSymbols, MarshalType? retType, ITypeSymbol? retSymbol)
+ ImmutableArray<ITypeSymbol> paramTypeSymbols, (MarshalType MarshalType, ITypeSymbol TypeSymbol)? retType)
{
Method = method;
ParamTypes = paramTypes;
ParamTypeSymbols = paramTypeSymbols;
RetType = retType;
- RetSymbol = retSymbol;
}
public IMethodSymbol Method { get; }
public ImmutableArray<MarshalType> ParamTypes { get; }
public ImmutableArray<ITypeSymbol> ParamTypeSymbols { get; }
- public MarshalType? RetType { get; }
- public ITypeSymbol? RetSymbol { get; }
+ public (MarshalType MarshalType, ITypeSymbol TypeSymbol)? RetType { get; }
}
public readonly struct GodotSignalDelegateData
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs
index 4fdd40f638..5b3f677f87 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs
@@ -304,240 +304,33 @@ namespace Godot.SourceGenerators
{
return marshalType switch
{
- MarshalType.Boolean =>
- source.Append(VariantUtils, ".ConvertToBool(", inputExpr, ")"),
- MarshalType.Char =>
- source.Append("(char)", VariantUtils, ".ConvertToUInt16(", inputExpr, ")"),
- MarshalType.SByte =>
- source.Append(VariantUtils, ".ConvertToInt8(", inputExpr, ")"),
- MarshalType.Int16 =>
- source.Append(VariantUtils, ".ConvertToInt16(", inputExpr, ")"),
- MarshalType.Int32 =>
- source.Append(VariantUtils, ".ConvertToInt32(", inputExpr, ")"),
- MarshalType.Int64 =>
- source.Append(VariantUtils, ".ConvertToInt64(", inputExpr, ")"),
- MarshalType.Byte =>
- source.Append(VariantUtils, ".ConvertToUInt8(", inputExpr, ")"),
- MarshalType.UInt16 =>
- source.Append(VariantUtils, ".ConvertToUInt16(", inputExpr, ")"),
- MarshalType.UInt32 =>
- source.Append(VariantUtils, ".ConvertToUInt32(", inputExpr, ")"),
- MarshalType.UInt64 =>
- source.Append(VariantUtils, ".ConvertToUInt64(", inputExpr, ")"),
- MarshalType.Single =>
- source.Append(VariantUtils, ".ConvertToFloat32(", inputExpr, ")"),
- MarshalType.Double =>
- source.Append(VariantUtils, ".ConvertToFloat64(", inputExpr, ")"),
- MarshalType.String =>
- source.Append(VariantUtils, ".ConvertToStringObject(", inputExpr, ")"),
- MarshalType.Vector2 =>
- source.Append(VariantUtils, ".ConvertToVector2(", inputExpr, ")"),
- MarshalType.Vector2i =>
- source.Append(VariantUtils, ".ConvertToVector2i(", inputExpr, ")"),
- MarshalType.Rect2 =>
- source.Append(VariantUtils, ".ConvertToRect2(", inputExpr, ")"),
- MarshalType.Rect2i =>
- source.Append(VariantUtils, ".ConvertToRect2i(", inputExpr, ")"),
- MarshalType.Transform2D =>
- source.Append(VariantUtils, ".ConvertToTransform2D(", inputExpr, ")"),
- MarshalType.Vector3 =>
- source.Append(VariantUtils, ".ConvertToVector3(", inputExpr, ")"),
- MarshalType.Vector3i =>
- source.Append(VariantUtils, ".ConvertToVector3i(", inputExpr, ")"),
- MarshalType.Basis =>
- source.Append(VariantUtils, ".ConvertToBasis(", inputExpr, ")"),
- MarshalType.Quaternion =>
- source.Append(VariantUtils, ".ConvertToQuaternion(", inputExpr, ")"),
- MarshalType.Transform3D =>
- source.Append(VariantUtils, ".ConvertToTransform3D(", inputExpr, ")"),
- MarshalType.Vector4 =>
- source.Append(VariantUtils, ".ConvertToVector4(", inputExpr, ")"),
- MarshalType.Vector4i =>
- source.Append(VariantUtils, ".ConvertToVector4i(", inputExpr, ")"),
- MarshalType.Projection =>
- source.Append(VariantUtils, ".ConvertToProjection(", inputExpr, ")"),
- MarshalType.AABB =>
- source.Append(VariantUtils, ".ConvertToAABB(", inputExpr, ")"),
- MarshalType.Color =>
- source.Append(VariantUtils, ".ConvertToColor(", inputExpr, ")"),
- MarshalType.Plane =>
- source.Append(VariantUtils, ".ConvertToPlane(", inputExpr, ")"),
- MarshalType.Callable =>
- source.Append(VariantUtils, ".ConvertToCallableManaged(", inputExpr, ")"),
- MarshalType.SignalInfo =>
- source.Append(VariantUtils, ".ConvertToSignalInfo(", inputExpr, ")"),
- MarshalType.Enum =>
- source.Append("(", typeSymbol.FullQualifiedNameIncludeGlobal(),
- ")", VariantUtils, ".ConvertToInt32(", inputExpr, ")"),
- MarshalType.ByteArray =>
- source.Append(VariantUtils, ".ConvertAsPackedByteArrayToSystemArray(", inputExpr, ")"),
- MarshalType.Int32Array =>
- source.Append(VariantUtils, ".ConvertAsPackedInt32ArrayToSystemArray(", inputExpr, ")"),
- MarshalType.Int64Array =>
- source.Append(VariantUtils, ".ConvertAsPackedInt64ArrayToSystemArray(", inputExpr, ")"),
- MarshalType.Float32Array =>
- source.Append(VariantUtils, ".ConvertAsPackedFloat32ArrayToSystemArray(", inputExpr, ")"),
- MarshalType.Float64Array =>
- source.Append(VariantUtils, ".ConvertAsPackedFloat64ArrayToSystemArray(", inputExpr, ")"),
- MarshalType.StringArray =>
- source.Append(VariantUtils, ".ConvertAsPackedStringArrayToSystemArray(", inputExpr, ")"),
- MarshalType.Vector2Array =>
- source.Append(VariantUtils, ".ConvertAsPackedVector2ArrayToSystemArray(", inputExpr, ")"),
- MarshalType.Vector3Array =>
- source.Append(VariantUtils, ".ConvertAsPackedVector3ArrayToSystemArray(", inputExpr, ")"),
- MarshalType.ColorArray =>
- source.Append(VariantUtils, ".ConvertAsPackedColorArrayToSystemArray(", inputExpr, ")"),
- MarshalType.GodotObjectOrDerivedArray =>
- source.Append(VariantUtils, ".ConvertToSystemArrayOfGodotObject<",
- ((IArrayTypeSymbol)typeSymbol).ElementType.FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"),
- MarshalType.SystemArrayOfStringName =>
- source.Append(VariantUtils, ".ConvertToSystemArrayOfStringName(", inputExpr, ")"),
- MarshalType.SystemArrayOfNodePath =>
- source.Append(VariantUtils, ".ConvertToSystemArrayOfNodePath(", inputExpr, ")"),
- MarshalType.SystemArrayOfRID =>
- source.Append(VariantUtils, ".ConvertToSystemArrayOfRID(", inputExpr, ")"),
- MarshalType.Variant =>
- source.Append("global::Godot.Variant.CreateCopyingBorrowed(", inputExpr, ")"),
- MarshalType.GodotObjectOrDerived =>
- source.Append("(", typeSymbol.FullQualifiedNameIncludeGlobal(),
- ")", VariantUtils, ".ConvertToGodotObject(", inputExpr, ")"),
- MarshalType.StringName =>
- source.Append(VariantUtils, ".ConvertToStringNameObject(", inputExpr, ")"),
- MarshalType.NodePath =>
- source.Append(VariantUtils, ".ConvertToNodePathObject(", inputExpr, ")"),
- MarshalType.RID =>
- source.Append(VariantUtils, ".ConvertToRID(", inputExpr, ")"),
- MarshalType.GodotDictionary =>
- source.Append(VariantUtils, ".ConvertToDictionaryObject(", inputExpr, ")"),
- MarshalType.GodotArray =>
- source.Append(VariantUtils, ".ConvertToArrayObject(", inputExpr, ")"),
+ // For generic Godot collections, VariantUtils.ConvertTo<T> is slower, so we need this special case
MarshalType.GodotGenericDictionary =>
source.Append(VariantUtils, ".ConvertToDictionaryObject<",
((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ", ",
- ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"),
+ ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedNameIncludeGlobal(), ">(",
+ inputExpr, ")"),
MarshalType.GodotGenericArray =>
source.Append(VariantUtils, ".ConvertToArrayObject<",
- ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"),
- _ => throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType,
- "Received unexpected marshal type")
+ ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ">(",
+ inputExpr, ")"),
+ _ => source.Append(VariantUtils, ".ConvertTo<",
+ typeSymbol.FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"),
};
}
- public static StringBuilder AppendManagedToNativeVariantExpr(
- this StringBuilder source, string inputExpr, MarshalType marshalType)
+ public static StringBuilder AppendManagedToNativeVariantExpr(this StringBuilder source,
+ string inputExpr, ITypeSymbol typeSymbol, MarshalType marshalType)
{
return marshalType switch
{
- MarshalType.Boolean =>
- source.Append(VariantUtils, ".CreateFromBool(", inputExpr, ")"),
- MarshalType.Char =>
- source.Append(VariantUtils, ".CreateFromInt((ushort)", inputExpr, ")"),
- MarshalType.SByte =>
- source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
- MarshalType.Int16 =>
- source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
- MarshalType.Int32 =>
- source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
- MarshalType.Int64 =>
- source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
- MarshalType.Byte =>
- source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
- MarshalType.UInt16 =>
- source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
- MarshalType.UInt32 =>
- source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
- MarshalType.UInt64 =>
- source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
- MarshalType.Single =>
- source.Append(VariantUtils, ".CreateFromFloat(", inputExpr, ")"),
- MarshalType.Double =>
- source.Append(VariantUtils, ".CreateFromFloat(", inputExpr, ")"),
- MarshalType.String =>
- source.Append(VariantUtils, ".CreateFromString(", inputExpr, ")"),
- MarshalType.Vector2 =>
- source.Append(VariantUtils, ".CreateFromVector2(", inputExpr, ")"),
- MarshalType.Vector2i =>
- source.Append(VariantUtils, ".CreateFromVector2i(", inputExpr, ")"),
- MarshalType.Rect2 =>
- source.Append(VariantUtils, ".CreateFromRect2(", inputExpr, ")"),
- MarshalType.Rect2i =>
- source.Append(VariantUtils, ".CreateFromRect2i(", inputExpr, ")"),
- MarshalType.Transform2D =>
- source.Append(VariantUtils, ".CreateFromTransform2D(", inputExpr, ")"),
- MarshalType.Vector3 =>
- source.Append(VariantUtils, ".CreateFromVector3(", inputExpr, ")"),
- MarshalType.Vector3i =>
- source.Append(VariantUtils, ".CreateFromVector3i(", inputExpr, ")"),
- MarshalType.Basis =>
- source.Append(VariantUtils, ".CreateFromBasis(", inputExpr, ")"),
- MarshalType.Quaternion =>
- source.Append(VariantUtils, ".CreateFromQuaternion(", inputExpr, ")"),
- MarshalType.Transform3D =>
- source.Append(VariantUtils, ".CreateFromTransform3D(", inputExpr, ")"),
- MarshalType.Vector4 =>
- source.Append(VariantUtils, ".CreateFromVector4(", inputExpr, ")"),
- MarshalType.Vector4i =>
- source.Append(VariantUtils, ".CreateFromVector4i(", inputExpr, ")"),
- MarshalType.Projection =>
- source.Append(VariantUtils, ".CreateFromProjection(", inputExpr, ")"),
- MarshalType.AABB =>
- source.Append(VariantUtils, ".CreateFromAABB(", inputExpr, ")"),
- MarshalType.Color =>
- source.Append(VariantUtils, ".CreateFromColor(", inputExpr, ")"),
- MarshalType.Plane =>
- source.Append(VariantUtils, ".CreateFromPlane(", inputExpr, ")"),
- MarshalType.Callable =>
- source.Append(VariantUtils, ".CreateFromCallable(", inputExpr, ")"),
- MarshalType.SignalInfo =>
- source.Append(VariantUtils, ".CreateFromSignalInfo(", inputExpr, ")"),
- MarshalType.Enum =>
- source.Append(VariantUtils, ".CreateFromInt((int)", inputExpr, ")"),
- MarshalType.ByteArray =>
- source.Append(VariantUtils, ".CreateFromPackedByteArray(", inputExpr, ")"),
- MarshalType.Int32Array =>
- source.Append(VariantUtils, ".CreateFromPackedInt32Array(", inputExpr, ")"),
- MarshalType.Int64Array =>
- source.Append(VariantUtils, ".CreateFromPackedInt64Array(", inputExpr, ")"),
- MarshalType.Float32Array =>
- source.Append(VariantUtils, ".CreateFromPackedFloat32Array(", inputExpr, ")"),
- MarshalType.Float64Array =>
- source.Append(VariantUtils, ".CreateFromPackedFloat64Array(", inputExpr, ")"),
- MarshalType.StringArray =>
- source.Append(VariantUtils, ".CreateFromPackedStringArray(", inputExpr, ")"),
- MarshalType.Vector2Array =>
- source.Append(VariantUtils, ".CreateFromPackedVector2Array(", inputExpr, ")"),
- MarshalType.Vector3Array =>
- source.Append(VariantUtils, ".CreateFromPackedVector3Array(", inputExpr, ")"),
- MarshalType.ColorArray =>
- source.Append(VariantUtils, ".CreateFromPackedColorArray(", inputExpr, ")"),
- MarshalType.GodotObjectOrDerivedArray =>
- source.Append(VariantUtils, ".CreateFromSystemArrayOfGodotObject(", inputExpr, ")"),
- MarshalType.SystemArrayOfStringName =>
- source.Append(VariantUtils, ".CreateFromSystemArrayOfStringName(", inputExpr, ")"),
- MarshalType.SystemArrayOfNodePath =>
- source.Append(VariantUtils, ".CreateFromSystemArrayOfNodePath(", inputExpr, ")"),
- MarshalType.SystemArrayOfRID =>
- source.Append(VariantUtils, ".CreateFromSystemArrayOfRID(", inputExpr, ")"),
- MarshalType.Variant =>
- source.Append(inputExpr, ".CopyNativeVariant()"),
- MarshalType.GodotObjectOrDerived =>
- source.Append(VariantUtils, ".CreateFromGodotObject(", inputExpr, ")"),
- MarshalType.StringName =>
- source.Append(VariantUtils, ".CreateFromStringName(", inputExpr, ")"),
- MarshalType.NodePath =>
- source.Append(VariantUtils, ".CreateFromNodePath(", inputExpr, ")"),
- MarshalType.RID =>
- source.Append(VariantUtils, ".CreateFromRID(", inputExpr, ")"),
- MarshalType.GodotDictionary =>
- source.Append(VariantUtils, ".CreateFromDictionary(", inputExpr, ")"),
- MarshalType.GodotArray =>
- source.Append(VariantUtils, ".CreateFromArray(", inputExpr, ")"),
+ // For generic Godot collections, VariantUtils.CreateFrom<T> is slower, so we need this special case
MarshalType.GodotGenericDictionary =>
source.Append(VariantUtils, ".CreateFromDictionary(", inputExpr, ")"),
MarshalType.GodotGenericArray =>
source.Append(VariantUtils, ".CreateFromArray(", inputExpr, ")"),
- _ => throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType,
- "Received unexpected marshal type")
+ _ => source.Append(VariantUtils, ".CreateFrom<",
+ typeSymbol.FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"),
};
}
@@ -546,137 +339,30 @@ namespace Godot.SourceGenerators
{
return marshalType switch
{
- MarshalType.Boolean => source.Append(inputExpr, ".AsBool()"),
- MarshalType.Char => source.Append(inputExpr, ".AsChar()"),
- MarshalType.SByte => source.Append(inputExpr, ".AsSByte()"),
- MarshalType.Int16 => source.Append(inputExpr, ".AsInt16()"),
- MarshalType.Int32 => source.Append(inputExpr, ".AsInt32()"),
- MarshalType.Int64 => source.Append(inputExpr, ".AsInt64()"),
- MarshalType.Byte => source.Append(inputExpr, ".AsByte()"),
- MarshalType.UInt16 => source.Append(inputExpr, ".AsUInt16()"),
- MarshalType.UInt32 => source.Append(inputExpr, ".AsUInt32()"),
- MarshalType.UInt64 => source.Append(inputExpr, ".AsUInt64()"),
- MarshalType.Single => source.Append(inputExpr, ".AsSingle()"),
- MarshalType.Double => source.Append(inputExpr, ".AsDouble()"),
- MarshalType.String => source.Append(inputExpr, ".AsString()"),
- MarshalType.Vector2 => source.Append(inputExpr, ".AsVector2()"),
- MarshalType.Vector2i => source.Append(inputExpr, ".AsVector2i()"),
- MarshalType.Rect2 => source.Append(inputExpr, ".AsRect2()"),
- MarshalType.Rect2i => source.Append(inputExpr, ".AsRect2i()"),
- MarshalType.Transform2D => source.Append(inputExpr, ".AsTransform2D()"),
- MarshalType.Vector3 => source.Append(inputExpr, ".AsVector3()"),
- MarshalType.Vector3i => source.Append(inputExpr, ".AsVector3i()"),
- MarshalType.Basis => source.Append(inputExpr, ".AsBasis()"),
- MarshalType.Quaternion => source.Append(inputExpr, ".AsQuaternion()"),
- MarshalType.Transform3D => source.Append(inputExpr, ".AsTransform3D()"),
- MarshalType.Vector4 => source.Append(inputExpr, ".AsVector4()"),
- MarshalType.Vector4i => source.Append(inputExpr, ".AsVector4i()"),
- MarshalType.Projection => source.Append(inputExpr, ".AsProjection()"),
- MarshalType.AABB => source.Append(inputExpr, ".AsAABB()"),
- MarshalType.Color => source.Append(inputExpr, ".AsColor()"),
- MarshalType.Plane => source.Append(inputExpr, ".AsPlane()"),
- MarshalType.Callable => source.Append(inputExpr, ".AsCallable()"),
- MarshalType.SignalInfo => source.Append(inputExpr, ".AsSignalInfo()"),
- MarshalType.Enum =>
- source.Append("(", typeSymbol.FullQualifiedNameIncludeGlobal(), ")", inputExpr, ".AsInt64()"),
- MarshalType.ByteArray => source.Append(inputExpr, ".AsByteArray()"),
- MarshalType.Int32Array => source.Append(inputExpr, ".AsInt32Array()"),
- MarshalType.Int64Array => source.Append(inputExpr, ".AsInt64Array()"),
- MarshalType.Float32Array => source.Append(inputExpr, ".AsFloat32Array()"),
- MarshalType.Float64Array => source.Append(inputExpr, ".AsFloat64Array()"),
- MarshalType.StringArray => source.Append(inputExpr, ".AsStringArray()"),
- MarshalType.Vector2Array => source.Append(inputExpr, ".AsVector2Array()"),
- MarshalType.Vector3Array => source.Append(inputExpr, ".AsVector3Array()"),
- MarshalType.ColorArray => source.Append(inputExpr, ".AsColorArray()"),
- MarshalType.GodotObjectOrDerivedArray => source.Append(inputExpr, ".AsGodotObjectArray<",
- ((IArrayTypeSymbol)typeSymbol).ElementType.FullQualifiedNameIncludeGlobal(), ">()"),
- MarshalType.SystemArrayOfStringName => source.Append(inputExpr, ".AsSystemArrayOfStringName()"),
- MarshalType.SystemArrayOfNodePath => source.Append(inputExpr, ".AsSystemArrayOfNodePath()"),
- MarshalType.SystemArrayOfRID => source.Append(inputExpr, ".AsSystemArrayOfRID()"),
- MarshalType.Variant => source.Append(inputExpr),
- MarshalType.GodotObjectOrDerived => source.Append("(",
- typeSymbol.FullQualifiedNameIncludeGlobal(), ")", inputExpr, ".AsGodotObject()"),
- MarshalType.StringName => source.Append(inputExpr, ".AsStringName()"),
- MarshalType.NodePath => source.Append(inputExpr, ".AsNodePath()"),
- MarshalType.RID => source.Append(inputExpr, ".AsRID()"),
- MarshalType.GodotDictionary => source.Append(inputExpr, ".AsGodotDictionary()"),
- MarshalType.GodotArray => source.Append(inputExpr, ".AsGodotArray()"),
- MarshalType.GodotGenericDictionary => source.Append(inputExpr, ".AsGodotDictionary<",
- ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ", ",
- ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedNameIncludeGlobal(), ">()"),
- MarshalType.GodotGenericArray => source.Append(inputExpr, ".AsGodotArray<",
- ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ">()"),
- _ => throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType,
- "Received unexpected marshal type")
+ // For generic Godot collections, Variant.As<T> is slower, so we need this special case
+ MarshalType.GodotGenericDictionary =>
+ source.Append(inputExpr, ".AsGodotDictionary<",
+ ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ", ",
+ ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedNameIncludeGlobal(), ">()"),
+ MarshalType.GodotGenericArray =>
+ source.Append(inputExpr, ".AsGodotArray<",
+ ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ">()"),
+ _ => source.Append(inputExpr, ".As<",
+ typeSymbol.FullQualifiedNameIncludeGlobal(), ">()")
};
}
public static StringBuilder AppendManagedToVariantExpr(this StringBuilder source,
- string inputExpr, MarshalType marshalType)
+ string inputExpr, ITypeSymbol typeSymbol, MarshalType marshalType)
{
- switch (marshalType)
+ return marshalType switch
{
- case MarshalType.Boolean:
- case MarshalType.Char:
- case MarshalType.SByte:
- case MarshalType.Int16:
- case MarshalType.Int32:
- case MarshalType.Int64:
- case MarshalType.Byte:
- case MarshalType.UInt16:
- case MarshalType.UInt32:
- case MarshalType.UInt64:
- case MarshalType.Single:
- case MarshalType.Double:
- case MarshalType.String:
- case MarshalType.Vector2:
- case MarshalType.Vector2i:
- case MarshalType.Rect2:
- case MarshalType.Rect2i:
- case MarshalType.Transform2D:
- case MarshalType.Vector3:
- case MarshalType.Vector3i:
- case MarshalType.Basis:
- case MarshalType.Quaternion:
- case MarshalType.Transform3D:
- case MarshalType.Vector4:
- case MarshalType.Vector4i:
- case MarshalType.Projection:
- case MarshalType.AABB:
- case MarshalType.Color:
- case MarshalType.Plane:
- case MarshalType.Callable:
- case MarshalType.SignalInfo:
- case MarshalType.ByteArray:
- case MarshalType.Int32Array:
- case MarshalType.Int64Array:
- case MarshalType.Float32Array:
- case MarshalType.Float64Array:
- case MarshalType.StringArray:
- case MarshalType.Vector2Array:
- case MarshalType.Vector3Array:
- case MarshalType.ColorArray:
- case MarshalType.GodotObjectOrDerivedArray:
- case MarshalType.SystemArrayOfStringName:
- case MarshalType.SystemArrayOfNodePath:
- case MarshalType.SystemArrayOfRID:
- case MarshalType.GodotObjectOrDerived:
- case MarshalType.StringName:
- case MarshalType.NodePath:
- case MarshalType.RID:
- case MarshalType.GodotDictionary:
- case MarshalType.GodotArray:
- case MarshalType.GodotGenericDictionary:
- case MarshalType.GodotGenericArray:
- return source.Append("Variant.CreateFrom(", inputExpr, ")");
- case MarshalType.Enum:
- return source.Append("Variant.CreateFrom((long)", inputExpr, ")");
- case MarshalType.Variant:
- return source.Append(inputExpr);
- default:
- throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType,
- "Received unexpected marshal type");
- }
+ // For generic Godot collections, Variant.From<T> is slower, so we need this special case
+ MarshalType.GodotGenericDictionary or MarshalType.GodotGenericArray =>
+ source.Append("global::Godot.Variant.CreateFrom(", inputExpr, ")"),
+ _ => source.Append("global::Godot.Variant.From<",
+ typeSymbol.FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")")
+ };
}
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
index 2f51018293..f79909589e 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
@@ -135,7 +135,8 @@ namespace Godot.SourceGenerators
source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
- source.Append($" public new class MethodName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.MethodName {{\n");
+ source.Append(
+ $" public new class MethodName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.MethodName {{\n");
// Generate cached StringNames for methods and properties, for fast lookup
@@ -297,7 +298,7 @@ namespace Godot.SourceGenerators
if (method.RetType != null)
{
- returnVal = DeterminePropertyInfo(method.RetType.Value, name: string.Empty);
+ returnVal = DeterminePropertyInfo(method.RetType.Value.MarshalType, name: string.Empty);
}
else
{
@@ -391,7 +392,8 @@ namespace Godot.SourceGenerators
{
source.Append(" ret = ");
- source.AppendManagedToNativeVariantExpr("callRet", method.RetType.Value);
+ source.AppendManagedToNativeVariantExpr("callRet",
+ method.RetType.Value.TypeSymbol, method.RetType.Value.MarshalType);
source.Append(";\n");
source.Append(" return true;\n");
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
index 252f162b0c..6c4206a575 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
@@ -124,7 +124,8 @@ namespace Godot.SourceGenerators
source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
- source.Append($" public new class PropertyName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.PropertyName {{\n");
+ source.Append(
+ $" public new class PropertyName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.PropertyName {{\n");
// Generate cached StringNames for methods and properties, for fast lookup
@@ -199,14 +200,14 @@ namespace Godot.SourceGenerators
foreach (var property in godotClassProperties)
{
GeneratePropertyGetter(property.PropertySymbol.Name,
- property.Type, source, isFirstEntry);
+ property.PropertySymbol.Type, property.Type, source, isFirstEntry);
isFirstEntry = false;
}
foreach (var field in godotClassFields)
{
GeneratePropertyGetter(field.FieldSymbol.Name,
- field.Type, source, isFirstEntry);
+ field.FieldSymbol.Type, field.Type, source, isFirstEntry);
isFirstEntry = false;
}
@@ -303,6 +304,7 @@ namespace Godot.SourceGenerators
private static void GeneratePropertyGetter(
string propertyMemberName,
+ ITypeSymbol propertyTypeSymbol,
MarshalType propertyMarshalType,
StringBuilder source,
bool isFirstEntry
@@ -317,7 +319,8 @@ namespace Godot.SourceGenerators
.Append(propertyMemberName)
.Append(") {\n")
.Append(" value = ")
- .AppendManagedToNativeVariantExpr("this." + propertyMemberName, propertyMarshalType)
+ .AppendManagedToNativeVariantExpr("this." + propertyMemberName,
+ propertyTypeSymbol, propertyMarshalType)
.Append(";\n")
.Append(" return true;\n")
.Append(" }\n");
@@ -376,7 +379,8 @@ namespace Godot.SourceGenerators
if (propertyUsage != PropertyUsageFlags.Category && attr.ConstructorArguments.Length > 1)
hintString = attr.ConstructorArguments[1].Value?.ToString();
- yield return new PropertyInfo(VariantType.Nil, name, PropertyHint.None, hintString, propertyUsage.Value, true);
+ yield return new PropertyInfo(VariantType.Nil, name, PropertyHint.None, hintString,
+ propertyUsage.Value, true);
}
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs
index 3f588a4c90..aa9dd9583e 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs
@@ -78,10 +78,6 @@ namespace Godot.SourceGenerators
var source = new StringBuilder();
- source.Append("using Godot;\n");
- source.Append("using Godot.NativeInterop;\n");
- source.Append("\n");
-
if (hasNamespace)
{
source.Append("namespace ");
@@ -178,7 +174,8 @@ namespace Godot.SourceGenerators
}
else
{
- var propertyGet = propertyDeclarationSyntax.AccessorList?.Accessors.Where(a => a.Keyword.IsKind(SyntaxKind.GetKeyword)).FirstOrDefault();
+ var propertyGet = propertyDeclarationSyntax.AccessorList?.Accessors
+ .Where(a => a.Keyword.IsKind(SyntaxKind.GetKeyword)).FirstOrDefault();
if (propertyGet != null)
{
if (propertyGet.ExpressionBody != null)
@@ -204,7 +201,8 @@ namespace Godot.SourceGenerators
{
var returns = propertyGet.DescendantNodes().OfType<ReturnStatementSyntax>();
if (returns.Count() == 1)
- {// Generate only single return
+ {
+ // Generate only single return
var returnStatementSyntax = returns.Single();
if (returnStatementSyntax.Expression is IdentifierNameSyntax identifierNameSyntax)
{
@@ -281,7 +279,8 @@ namespace Godot.SourceGenerators
{
source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
- string dictionaryType = "System.Collections.Generic.Dictionary<StringName, object>";
+ string dictionaryType =
+ "global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant>";
source.Append("#if TOOLS\n");
source.Append(" internal new static ");
@@ -308,7 +307,8 @@ namespace Godot.SourceGenerators
source.Append(" values.Add(PropertyName.");
source.Append(exportedMember.Name);
source.Append(", ");
- source.Append(defaultValueLocalName);
+ source.AppendManagedToVariantExpr(defaultValueLocalName,
+ exportedMember.TypeSymbol, exportedMember.Type);
source.Append(");\n");
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs
index ed877cbd17..821f3af75f 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs
@@ -162,7 +162,8 @@ namespace Godot.SourceGenerators
source.Append(" info.AddProperty(PropertyName.")
.Append(propertyName)
.Append(", ")
- .AppendManagedToVariantExpr(string.Concat("this.", propertyName), property.Type)
+ .AppendManagedToVariantExpr(string.Concat("this.", propertyName),
+ property.PropertySymbol.Type, property.Type)
.Append(");\n");
}
@@ -175,7 +176,8 @@ namespace Godot.SourceGenerators
source.Append(" info.AddProperty(PropertyName.")
.Append(fieldName)
.Append(", ")
- .AppendManagedToVariantExpr(string.Concat("this.", fieldName), field.Type)
+ .AppendManagedToVariantExpr(string.Concat("this.", fieldName),
+ field.FieldSymbol.Type, field.Type)
.Append(");\n");
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
index 119cc9d4f0..ba6c10aa31 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
@@ -176,7 +176,8 @@ namespace Godot.SourceGenerators
source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
- source.Append($" public new class SignalName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.SignalName {{\n");
+ source.Append(
+ $" public new class SignalName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.SignalName {{\n");
// Generate cached StringNames for methods and properties, for fast lookup
@@ -236,7 +237,8 @@ namespace Godot.SourceGenerators
.Append(signalName)
.Append(";\n");
- source.Append($" /// <inheritdoc cref=\"{signalDelegate.DelegateSymbol.FullQualifiedNameIncludeGlobal()}\"/>\n");
+ source.Append(
+ $" /// <inheritdoc cref=\"{signalDelegate.DelegateSymbol.FullQualifiedNameIncludeGlobal()}\"/>\n");
source.Append(" public event ")
.Append(signalDelegate.DelegateSymbol.FullQualifiedNameIncludeGlobal())
@@ -351,7 +353,7 @@ namespace Godot.SourceGenerators
if (invokeMethodData.RetType != null)
{
- returnVal = DeterminePropertyInfo(invokeMethodData.RetType.Value, name: string.Empty);
+ returnVal = DeterminePropertyInfo(invokeMethodData.RetType.Value.MarshalType, name: string.Empty);
}
else
{
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index 89364d1c02..de10c04e31 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -57,24 +57,22 @@ namespace GodotTools
{
pr.Step("Generating C# project...".TTR());
- string resourceDir = ProjectSettings.GlobalizePath("res://");
-
- string path = resourceDir;
+ string csprojDir = Path.GetDirectoryName(GodotSharpDirs.ProjectCsProjPath);
+ string slnDir = Path.GetDirectoryName(GodotSharpDirs.ProjectSlnPath);
string name = GodotSharpDirs.ProjectAssemblyName;
-
- string guid = CsProjOperations.GenerateGameProject(path, name);
+ string guid = CsProjOperations.GenerateGameProject(csprojDir, name);
if (guid.Length > 0)
{
var solution = new DotNetSolution(name)
{
- DirectoryPath = path
+ DirectoryPath = slnDir
};
var projectInfo = new DotNetSolution.ProjectInfo
{
Guid = guid,
- PathRelativeToSolution = name + ".csproj",
+ PathRelativeToSolution = Path.GetRelativePath(slnDir, GodotSharpDirs.ProjectCsProjPath),
Configs = new List<string> { "Debug", "ExportDebug", "ExportRelease" }
};
@@ -375,6 +373,8 @@ namespace GodotTools
{
base._EnablePlugin();
+ ProjectSettingsChanged += GodotSharpDirs.DetermineProjectLocation;
+
if (Instance != null)
throw new InvalidOperationException();
Instance = this;
@@ -455,7 +455,7 @@ namespace GodotTools
_menuPopup.IdPressed += _MenuOptionPressed;
// External editor settings
- EditorDef("mono/editor/external_editor", ExternalEditorId.None);
+ EditorDef("mono/editor/external_editor", Variant.From(ExternalEditorId.None));
string settingsHintStr = "Disabled";
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs
index acb7cc3ab0..45ae7eb86b 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs
@@ -1,3 +1,4 @@
+using Godot;
using Godot.NativeInterop;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
@@ -8,30 +9,31 @@ namespace GodotTools.Internals
{
public static float EditorScale => Internal.godot_icall_Globals_EditorScale();
- public static unsafe object GlobalDef(string setting, object defaultValue, bool restartIfChanged = false)
+ // ReSharper disable once UnusedMethodReturnValue.Global
+ public static Variant GlobalDef(string setting, Variant defaultValue, bool restartIfChanged = false)
{
using godot_string settingIn = Marshaling.ConvertStringToNative(setting);
- using godot_variant defaultValueIn = Marshaling.ConvertManagedObjectToVariant(defaultValue);
- Internal.godot_icall_Globals_GlobalDef(settingIn, defaultValueIn, restartIfChanged, out godot_variant result);
- using (result)
- return Marshaling.ConvertVariantToManagedObject(result);
+ using godot_variant defaultValueIn = defaultValue.CopyNativeVariant();
+ Internal.godot_icall_Globals_GlobalDef(settingIn, defaultValueIn, restartIfChanged,
+ out godot_variant result);
+ return Variant.CreateTakingOwnershipOfDisposableValue(result);
}
- public static unsafe object EditorDef(string setting, object defaultValue, bool restartIfChanged = false)
+ // ReSharper disable once UnusedMethodReturnValue.Global
+ public static Variant EditorDef(string setting, Variant defaultValue, bool restartIfChanged = false)
{
using godot_string settingIn = Marshaling.ConvertStringToNative(setting);
- using godot_variant defaultValueIn = Marshaling.ConvertManagedObjectToVariant(defaultValue);
- Internal.godot_icall_Globals_EditorDef(settingIn, defaultValueIn, restartIfChanged, out godot_variant result);
- using (result)
- return Marshaling.ConvertVariantToManagedObject(result);
+ using godot_variant defaultValueIn = defaultValue.CopyNativeVariant();
+ Internal.godot_icall_Globals_EditorDef(settingIn, defaultValueIn, restartIfChanged,
+ out godot_variant result);
+ return Variant.CreateTakingOwnershipOfDisposableValue(result);
}
- public static object EditorShortcut(string setting)
+ public static Variant EditorShortcut(string setting)
{
using godot_string settingIn = Marshaling.ConvertStringToNative(setting);
Internal.godot_icall_Globals_EditorShortcut(settingIn, out godot_variant result);
- using (result)
- return Marshaling.ConvertVariantToManagedObject(result);
+ return Variant.CreateTakingOwnershipOfDisposableValue(result);
}
[SuppressMessage("ReSharper", "InconsistentNaming")]
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs
index 14285cc0f1..4e892be55c 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs
@@ -52,10 +52,9 @@ namespace GodotTools.Internals
{
GlobalDef("dotnet/project/assembly_name", "");
GlobalDef("dotnet/project/solution_directory", "");
- GlobalDef("dotnet/project/c#_project_directory", "");
}
- private static void DetermineProjectLocation()
+ public static void DetermineProjectLocation()
{
static string DetermineProjectName()
{
@@ -76,10 +75,11 @@ namespace GodotTools.Internals
string slnParentDir = (string)ProjectSettings.GetSetting("dotnet/project/solution_directory");
if (string.IsNullOrEmpty(slnParentDir))
slnParentDir = "res://";
+ else if (!slnParentDir.StartsWith("res://"))
+ slnParentDir = "res://" + slnParentDir;
- string csprojParentDir = (string)ProjectSettings.GetSetting("dotnet/project/c#_project_directory");
- if (string.IsNullOrEmpty(csprojParentDir))
- csprojParentDir = "res://";
+ // The csproj should be in the same folder as project.godot.
+ string csprojParentDir = "res://";
_projectSlnPath = Path.Combine(ProjectSettings.GlobalizePath(slnParentDir),
string.Concat(_projectAssemblyName, ".sln"));
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 9185506776..9f0bc3fbe3 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -2837,9 +2837,6 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
itype.is_ref_counted = ClassDB::is_parent_class(type_cname, name_cache.type_RefCounted);
itype.memory_own = itype.is_ref_counted;
- itype.cs_variant_to_managed = "(%1)VariantUtils.ConvertToGodotObject(%0)";
- itype.cs_managed_to_variant = "VariantUtils.CreateFromGodotObject(%0)";
-
itype.c_out = "%5return ";
itype.c_out += C_METHOD_UNMANAGED_GET_MANAGED;
itype.c_out += itype.is_ref_counted ? "(%1.Reference);\n" : "(%1);\n";
@@ -3218,8 +3215,6 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
enum_itype.cname = StringName(enum_itype.name);
enum_itype.proxy_name = itype.proxy_name + "." + enum_proxy_name;
TypeInterface::postsetup_enum_type(enum_itype);
- enum_itype.cs_variant_to_managed = "(%1)VariantUtils.ConvertToInt32(%0)";
- enum_itype.cs_managed_to_variant = "VariantUtils.CreateFromInt((int)%0)";
enum_types.insert(enum_itype.cname, enum_itype);
}
@@ -3448,16 +3443,14 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
TypeInterface itype;
-#define INSERT_STRUCT_TYPE(m_type) \
- { \
- itype = TypeInterface::create_value_type(String(#m_type)); \
- itype.c_type_in = #m_type "*"; \
- itype.c_type_out = itype.cs_type; \
- itype.cs_in_expr = "&%0"; \
- itype.cs_in_expr_is_unsafe = true; \
- itype.cs_variant_to_managed = "VariantUtils.ConvertTo%2(%0)"; \
- itype.cs_managed_to_variant = "VariantUtils.CreateFrom%2(%0)"; \
- builtin_types.insert(itype.cname, itype); \
+#define INSERT_STRUCT_TYPE(m_type) \
+ { \
+ itype = TypeInterface::create_value_type(String(#m_type)); \
+ itype.c_type_in = #m_type "*"; \
+ itype.c_type_out = itype.cs_type; \
+ itype.cs_in_expr = "&%0"; \
+ itype.cs_in_expr_is_unsafe = true; \
+ builtin_types.insert(itype.cname, itype); \
}
INSERT_STRUCT_TYPE(Vector2)
@@ -3488,8 +3481,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_out = itype.c_type;
itype.c_arg_in = "&%s";
itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromBool(%1);\n";
- itype.cs_variant_to_managed = "VariantUtils.ConvertToBool(%0)";
- itype.cs_managed_to_variant = "VariantUtils.CreateFromBool(%0)";
builtin_types.insert(itype.cname, itype);
// Integer types
@@ -3510,8 +3501,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_in = itype.name; \
itype.c_type_out = itype.name; \
itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromInt(%1);\n"; \
- itype.cs_variant_to_managed = "VariantUtils.ConvertTo" m_int_struct_name "(%0)"; \
- itype.cs_managed_to_variant = "VariantUtils.CreateFromInt(%0)"; \
builtin_types.insert(itype.cname, itype); \
}
@@ -3547,8 +3536,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_in = itype.proxy_name;
itype.c_type_out = itype.proxy_name;
itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromFloat(%1);\n";
- itype.cs_variant_to_managed = "VariantUtils.ConvertToFloat32(%0)";
- itype.cs_managed_to_variant = "VariantUtils.CreateFromFloat(%0)";
builtin_types.insert(itype.cname, itype);
// double
@@ -3562,8 +3549,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_in = itype.proxy_name;
itype.c_type_out = itype.proxy_name;
itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromFloat(%1);\n";
- itype.cs_variant_to_managed = "VariantUtils.ConvertToFloat64(%0)";
- itype.cs_managed_to_variant = "VariantUtils.CreateFromFloat(%0)";
builtin_types.insert(itype.cname, itype);
}
@@ -3581,8 +3566,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_out = itype.cs_type;
itype.c_type_is_disposable_struct = true;
itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromString(%1);\n";
- itype.cs_variant_to_managed = "VariantUtils.ConvertToStringObject(%0)";
- itype.cs_managed_to_variant = "VariantUtils.CreateFromString(%0)";
builtin_types.insert(itype.cname, itype);
// StringName
@@ -3601,8 +3584,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromStringName(%1);\n";
itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
itype.c_ret_needs_default_initialization = true;
- itype.cs_variant_to_managed = "VariantUtils.ConvertToStringNameObject(%0)";
- itype.cs_managed_to_variant = "VariantUtils.CreateFromStringName(%0)";
builtin_types.insert(itype.cname, itype);
// NodePath
@@ -3620,8 +3601,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_out = itype.cs_type;
itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
itype.c_ret_needs_default_initialization = true;
- itype.cs_variant_to_managed = "VariantUtils.ConvertToNodePathObject(%0)";
- itype.cs_managed_to_variant = "VariantUtils.CreateFromNodePath(%0)";
builtin_types.insert(itype.cname, itype);
// RID
@@ -3634,8 +3613,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type = itype.cs_type;
itype.c_type_in = itype.c_type;
itype.c_type_out = itype.c_type;
- itype.cs_variant_to_managed = "VariantUtils.ConvertToRID(%0)";
- itype.cs_managed_to_variant = "VariantUtils.CreateFromRID(%0)";
builtin_types.insert(itype.cname, itype);
// Variant
@@ -3652,8 +3629,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_out = itype.cs_type;
itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
itype.c_ret_needs_default_initialization = true;
- itype.cs_variant_to_managed = "Variant.CreateCopyingBorrowed(%0)";
- itype.cs_managed_to_variant = "%0.CopyNativeVariant()";
builtin_types.insert(itype.cname, itype);
// Callable
@@ -3666,8 +3641,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_in = "in " + itype.cs_type;
itype.c_type_out = itype.cs_type;
itype.c_type_is_disposable_struct = true;
- itype.cs_variant_to_managed = "VariantUtils.ConvertToCallableManaged(%0)";
- itype.cs_managed_to_variant = "VariantUtils.CreateFromCallable(%0)";
builtin_types.insert(itype.cname, itype);
// Signal
@@ -3684,8 +3657,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_in = "in " + itype.cs_type;
itype.c_type_out = itype.cs_type;
itype.c_type_is_disposable_struct = true;
- itype.cs_variant_to_managed = "VariantUtils.ConvertToSignalInfo(%0)";
- itype.cs_managed_to_variant = "VariantUtils.CreateFromSignalInfo(%0)";
builtin_types.insert(itype.cname, itype);
// VarArg (fictitious type to represent variable arguments)
@@ -3715,8 +3686,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_in = itype.proxy_name; \
itype.c_type_out = itype.proxy_name; \
itype.c_type_is_disposable_struct = true; \
- itype.cs_variant_to_managed = "VariantUtils.ConvertAs%2ToSystemArray(%0)"; \
- itype.cs_managed_to_variant = "VariantUtils.CreateFrom%2(%0)"; \
builtin_types.insert(itype.name, itype); \
}
@@ -3752,8 +3721,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_out = itype.cs_type;
itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
itype.c_ret_needs_default_initialization = true;
- itype.cs_variant_to_managed = "VariantUtils.ConvertToArrayObject(%0)";
- itype.cs_managed_to_variant = "VariantUtils.CreateFromArray(%0)";
builtin_types.insert(itype.cname, itype);
// Array_@generic
@@ -3761,6 +3728,9 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "Array_@generic";
itype.cname = itype.name;
itype.cs_out = "%5return new %2(%0(%1));";
+ // For generic Godot collections, Variant.From<T>/As<T> is slower, so we need this special case
+ itype.cs_variant_to_managed = "VariantUtils.ConvertToArrayObject(%0)";
+ itype.cs_managed_to_variant = "VariantUtils.CreateFromArray(%0)";
builtin_types.insert(itype.cname, itype);
// Dictionary
@@ -3778,8 +3748,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_out = itype.cs_type;
itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
itype.c_ret_needs_default_initialization = true;
- itype.cs_variant_to_managed = "VariantUtils.ConvertToDictionaryObject(%0)";
- itype.cs_managed_to_variant = "VariantUtils.CreateFromDictionary(%0)";
builtin_types.insert(itype.cname, itype);
// Dictionary_@generic
@@ -3787,6 +3755,9 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "Dictionary_@generic";
itype.cname = itype.name;
itype.cs_out = "%5return new %2(%0(%1));";
+ // For generic Godot collections, Variant.From<T>/As<T> is slower, so we need this special case
+ itype.cs_variant_to_managed = "VariantUtils.ConvertToDictionaryObject(%0)";
+ itype.cs_managed_to_variant = "VariantUtils.CreateFromDictionary(%0)";
builtin_types.insert(itype.cname, itype);
// void (fictitious type to represent the return type of methods that do not return anything)
@@ -3852,8 +3823,6 @@ void BindingsGenerator::_populate_global_constants() {
enum_itype.cname = ienum.cname;
enum_itype.proxy_name = enum_itype.name;
TypeInterface::postsetup_enum_type(enum_itype);
- enum_itype.cs_variant_to_managed = "(%1)VariantUtils.ConvertToInt32(%0)";
- enum_itype.cs_managed_to_variant = "VariantUtils.CreateFromInt((int)%0)";
enum_types.insert(enum_itype.cname, enum_itype);
int prefix_length = _determine_enum_prefix(ienum);
@@ -3886,8 +3855,6 @@ void BindingsGenerator::_populate_global_constants() {
enum_itype.cname = enum_cname;
enum_itype.proxy_name = enum_itype.name;
TypeInterface::postsetup_enum_type(enum_itype);
- enum_itype.cs_variant_to_managed = "(%1)VariantUtils.ConvertToInt32(%0)";
- enum_itype.cs_managed_to_variant = "VariantUtils.CreateFromInt((int)%0)";
enum_types.insert(enum_itype.cname, enum_itype);
}
}
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index a479c44368..6d172f4fb8 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -209,7 +209,7 @@ class BindingsGenerator {
String name;
StringName cname;
- int type_parameter_count;
+ int type_parameter_count = 0;
/**
* Identifier name of the base class.
@@ -514,7 +514,12 @@ class BindingsGenerator {
static void postsetup_enum_type(TypeInterface &r_enum_itype);
- TypeInterface() {}
+ TypeInterface() {
+ static String default_cs_variant_to_managed = "VariantUtils.ConvertTo<%1>(%0)";
+ static String default_cs_managed_to_variant = "VariantUtils.CreateFrom<%1>(%0)";
+ cs_variant_to_managed = default_cs_variant_to_managed;
+ cs_managed_to_variant = default_cs_managed_to_variant;
+ }
};
struct InternalCall {
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
index e3b7ac297d..130776499b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
@@ -418,8 +418,8 @@ namespace Godot.Collections
{
for (int i = 0; i < count; i++)
{
- object obj = Marshaling.ConvertVariantToManagedObject(NativeValue.DangerousSelfRef.Elements[i]);
- array.SetValue(obj, index);
+ object boxedVariant = Variant.CreateCopyingBorrowed(NativeValue.DangerousSelfRef.Elements[i]);
+ array.SetValue(boxedVariant, index);
index++;
}
}
@@ -474,6 +474,11 @@ namespace Godot.Collections
}
}
+ internal interface IGenericGodotArray
+ {
+ public Array UnderlyingArray { get; }
+ }
+
/// <summary>
/// Typed wrapper around Godot's Array class, an array of Variant
/// typed elements allocated in the engine in C++. Useful when
@@ -487,7 +492,8 @@ namespace Godot.Collections
IList<T>,
IReadOnlyList<T>,
ICollection<T>,
- IEnumerable<T>
+ IEnumerable<T>,
+ IGenericGodotArray
{
private static godot_variant ToVariantFunc(in Array<T> godotArray) =>
VariantUtils.CreateFromArray(godotArray);
@@ -503,6 +509,8 @@ namespace Godot.Collections
private readonly Array _underlyingArray;
+ Array IGenericGodotArray.UnderlyingArray => _underlyingArray;
+
internal ref godot_array.movable NativeValue
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
index d6fad391b6..e6a8054ae2 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
@@ -159,7 +159,7 @@ namespace Godot.Bridge
for (int i = 0; i < paramCount; i++)
{
- invokeParams[i] = Marshaling.ConvertVariantToManagedObjectOfType(
+ invokeParams[i] = DelegateUtils.RuntimeTypeConversionHelper.ConvertToObjectOfType(
*args[i], parameters[i].ParameterType);
}
@@ -832,7 +832,8 @@ namespace Godot.Bridge
}
else
{
- interopProperties = ((godotsharp_property_info*)NativeMemory.Alloc((nuint)length, (nuint)sizeof(godotsharp_property_info)))!;
+ interopProperties = ((godotsharp_property_info*)NativeMemory.Alloc(
+ (nuint)length, (nuint)sizeof(godotsharp_property_info)))!;
}
try
@@ -858,8 +859,8 @@ namespace Godot.Bridge
addPropInfoFunc(scriptPtr, &currentClassName, interopProperties, length);
- // We're borrowing the StringName's without making an owning copy, so the
- // managed collection needs to be kept alive until `addPropInfoFunc` returns.
+ // We're borrowing the native value of the StringName entries.
+ // The dictionary needs to be kept alive until `addPropInfoFunc` returns.
GC.KeepAlive(properties);
}
finally
@@ -884,12 +885,7 @@ namespace Godot.Bridge
{
// Careful with padding...
public godot_string_name Name; // Not owned
- public godot_variant Value;
-
- public void Dispose()
- {
- Value.Dispose();
- }
+ public godot_variant Value; // Not owned
}
[UnmanagedCallersOnly]
@@ -928,10 +924,35 @@ namespace Godot.Bridge
if (getGodotPropertyDefaultValuesMethod == null)
return;
- var defaultValues = (Dictionary<StringName, object>?)
- getGodotPropertyDefaultValuesMethod.Invoke(null, null);
+ var defaultValuesObj = getGodotPropertyDefaultValuesMethod.Invoke(null, null);
+
+ if (defaultValuesObj == null)
+ return;
+
+ Dictionary<StringName, Variant> defaultValues;
+
+ if (defaultValuesObj is Dictionary<StringName, object> defaultValuesLegacy)
+ {
+ // We have to support this for some time, otherwise this could cause data loss for projects
+ // built with previous releases. Ideally, we should remove this before Godot 4.0 stable.
+
+ if (defaultValuesLegacy.Count <= 0)
+ return;
- if (defaultValues == null || defaultValues.Count <= 0)
+ defaultValues = new();
+
+ foreach (var pair in defaultValuesLegacy)
+ {
+ defaultValues[pair.Key] = Variant.CreateTakingOwnershipOfDisposableValue(
+ DelegateUtils.RuntimeTypeConversionHelper.ConvertToVariant(pair.Value));
+ }
+ }
+ else
+ {
+ defaultValues = (Dictionary<StringName, Variant>)defaultValuesObj;
+ }
+
+ if (defaultValues.Count <= 0)
return;
int length = defaultValues.Count;
@@ -952,7 +973,8 @@ namespace Godot.Bridge
}
else
{
- interopDefaultValues = ((godotsharp_property_def_val_pair*)NativeMemory.Alloc((nuint)length, (nuint)sizeof(godotsharp_property_def_val_pair)))!;
+ interopDefaultValues = ((godotsharp_property_def_val_pair*)NativeMemory.Alloc(
+ (nuint)length, (nuint)sizeof(godotsharp_property_def_val_pair)))!;
}
try
@@ -963,7 +985,7 @@ namespace Godot.Bridge
godotsharp_property_def_val_pair interopProperty = new()
{
Name = (godot_string_name)defaultValuePair.Key.NativeValue, // Not owned
- Value = Marshaling.ConvertManagedObjectToVariant(defaultValuePair.Value)
+ Value = (godot_variant)defaultValuePair.Value.NativeVar // Not owned
};
interopDefaultValues[i] = interopProperty;
@@ -973,15 +995,12 @@ namespace Godot.Bridge
addDefValFunc(scriptPtr, interopDefaultValues, length);
- // We're borrowing the StringName's without making an owning copy, so the
- // managed collection needs to be kept alive until `addDefValFunc` returns.
+ // We're borrowing the native value of the StringName and Variant entries.
+ // The dictionary needs to be kept alive until `addDefValFunc` returns.
GC.KeepAlive(defaultValues);
}
finally
{
- for (int i = 0; i < length; i++)
- interopDefaultValues[i].Dispose();
-
if (!useStack)
NativeMemory.Free(interopDefaultValues);
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
index d19e0c08f2..a3cfecfaa6 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
@@ -186,7 +186,7 @@ namespace Godot
writer.Write(field.Name);
var fieldValue = field.GetValue(target);
- using var fieldValueVariant = Marshaling.ConvertManagedObjectToVariant(fieldValue);
+ using var fieldValueVariant = RuntimeTypeConversionHelper.ConvertToVariant(fieldValue);
byte[] valueBuffer = VarToBytes(fieldValueVariant);
writer.Write(valueBuffer.Length);
writer.Write(valueBuffer);
@@ -443,7 +443,14 @@ namespace Godot
FieldInfo? fieldInfo = targetType.GetField(name,
BindingFlags.Instance | BindingFlags.Public);
- fieldInfo?.SetValue(recreatedTarget, GD.BytesToVar(valueBuffer));
+
+ if (fieldInfo != null)
+ {
+ var variantValue = GD.BytesToVar(valueBuffer);
+ object? managedValue = RuntimeTypeConversionHelper.ConvertToObjectOfType(
+ (godot_variant)variantValue.NativeVar, fieldInfo.FieldType);
+ fieldInfo.SetValue(recreatedTarget, managedValue);
+ }
}
@delegate = Delegate.CreateDelegate(delegateType, recreatedTarget, methodInfo,
@@ -537,5 +544,269 @@ namespace Godot
return type;
}
+
+ internal static class RuntimeTypeConversionHelper
+ {
+ [SuppressMessage("ReSharper", "RedundantNameQualifier")]
+ public static godot_variant ConvertToVariant(object? obj)
+ {
+ if (obj == null)
+ return default;
+
+ switch (obj)
+ {
+ case bool @bool:
+ return VariantUtils.CreateFrom(@bool);
+ case char @char:
+ return VariantUtils.CreateFrom(@char);
+ case sbyte int8:
+ return VariantUtils.CreateFrom(int8);
+ case short int16:
+ return VariantUtils.CreateFrom(int16);
+ case int int32:
+ return VariantUtils.CreateFrom(int32);
+ case long int64:
+ return VariantUtils.CreateFrom(int64);
+ case byte uint8:
+ return VariantUtils.CreateFrom(uint8);
+ case ushort uint16:
+ return VariantUtils.CreateFrom(uint16);
+ case uint uint32:
+ return VariantUtils.CreateFrom(uint32);
+ case ulong uint64:
+ return VariantUtils.CreateFrom(uint64);
+ case float @float:
+ return VariantUtils.CreateFrom(@float);
+ case double @double:
+ return VariantUtils.CreateFrom(@double);
+ case Vector2 vector2:
+ return VariantUtils.CreateFrom(vector2);
+ case Vector2i vector2I:
+ return VariantUtils.CreateFrom(vector2I);
+ case Rect2 rect2:
+ return VariantUtils.CreateFrom(rect2);
+ case Rect2i rect2I:
+ return VariantUtils.CreateFrom(rect2I);
+ case Transform2D transform2D:
+ return VariantUtils.CreateFrom(transform2D);
+ case Vector3 vector3:
+ return VariantUtils.CreateFrom(vector3);
+ case Vector3i vector3I:
+ return VariantUtils.CreateFrom(vector3I);
+ case Vector4 vector4:
+ return VariantUtils.CreateFrom(vector4);
+ case Vector4i vector4I:
+ return VariantUtils.CreateFrom(vector4I);
+ case Basis basis:
+ return VariantUtils.CreateFrom(basis);
+ case Quaternion quaternion:
+ return VariantUtils.CreateFrom(quaternion);
+ case Transform3D transform3d:
+ return VariantUtils.CreateFrom(transform3d);
+ case Projection projection:
+ return VariantUtils.CreateFrom(projection);
+ case AABB aabb:
+ return VariantUtils.CreateFrom(aabb);
+ case Color color:
+ return VariantUtils.CreateFrom(color);
+ case Plane plane:
+ return VariantUtils.CreateFrom(plane);
+ case Callable callable:
+ return VariantUtils.CreateFrom(callable);
+ case SignalInfo signalInfo:
+ return VariantUtils.CreateFrom(signalInfo);
+ case string @string:
+ return VariantUtils.CreateFrom(@string);
+ case byte[] byteArray:
+ return VariantUtils.CreateFrom(byteArray);
+ case int[] int32Array:
+ return VariantUtils.CreateFrom(int32Array);
+ case long[] int64Array:
+ return VariantUtils.CreateFrom(int64Array);
+ case float[] floatArray:
+ return VariantUtils.CreateFrom(floatArray);
+ case double[] doubleArray:
+ return VariantUtils.CreateFrom(doubleArray);
+ case string[] stringArray:
+ return VariantUtils.CreateFrom(stringArray);
+ case Vector2[] vector2Array:
+ return VariantUtils.CreateFrom(vector2Array);
+ case Vector3[] vector3Array:
+ return VariantUtils.CreateFrom(vector3Array);
+ case Color[] colorArray:
+ return VariantUtils.CreateFrom(colorArray);
+ case StringName[] stringNameArray:
+ return VariantUtils.CreateFrom(stringNameArray);
+ case NodePath[] nodePathArray:
+ return VariantUtils.CreateFrom(nodePathArray);
+ case RID[] ridArray:
+ return VariantUtils.CreateFrom(ridArray);
+ case Godot.Object[] godotObjectArray:
+ return VariantUtils.CreateFrom(godotObjectArray);
+ case StringName stringName:
+ return VariantUtils.CreateFrom(stringName);
+ case NodePath nodePath:
+ return VariantUtils.CreateFrom(nodePath);
+ case RID rid:
+ return VariantUtils.CreateFrom(rid);
+ case Collections.Dictionary godotDictionary:
+ return VariantUtils.CreateFrom(godotDictionary);
+ case Collections.Array godotArray:
+ return VariantUtils.CreateFrom(godotArray);
+ case Variant variant:
+ return VariantUtils.CreateFrom(variant);
+ case Godot.Object godotObject:
+ return VariantUtils.CreateFrom(godotObject);
+ case Enum @enum:
+ return VariantUtils.CreateFrom(Convert.ToInt64(@enum));
+ case Collections.IGenericGodotDictionary godotDictionary:
+ return VariantUtils.CreateFrom(godotDictionary.UnderlyingDictionary);
+ case Collections.IGenericGodotArray godotArray:
+ return VariantUtils.CreateFrom(godotArray.UnderlyingArray);
+ }
+
+ GD.PushError("Attempted to convert an unmarshallable managed type to Variant. Name: '" +
+ obj.GetType().FullName + ".");
+ return new godot_variant();
+ }
+
+ private delegate object? ConvertToSystemObjectFunc(in godot_variant managed);
+
+ [SuppressMessage("ReSharper", "RedundantNameQualifier")]
+ // ReSharper disable once RedundantNameQualifier
+ private static readonly System.Collections.Generic.Dictionary<Type, ConvertToSystemObjectFunc>
+ ToSystemObjectFuncByType = new()
+ {
+ [typeof(bool)] = (in godot_variant variant) => VariantUtils.ConvertTo<bool>(variant),
+ [typeof(char)] = (in godot_variant variant) => VariantUtils.ConvertTo<char>(variant),
+ [typeof(sbyte)] = (in godot_variant variant) => VariantUtils.ConvertTo<sbyte>(variant),
+ [typeof(short)] = (in godot_variant variant) => VariantUtils.ConvertTo<short>(variant),
+ [typeof(int)] = (in godot_variant variant) => VariantUtils.ConvertTo<int>(variant),
+ [typeof(long)] = (in godot_variant variant) => VariantUtils.ConvertTo<long>(variant),
+ [typeof(byte)] = (in godot_variant variant) => VariantUtils.ConvertTo<byte>(variant),
+ [typeof(ushort)] = (in godot_variant variant) => VariantUtils.ConvertTo<ushort>(variant),
+ [typeof(uint)] = (in godot_variant variant) => VariantUtils.ConvertTo<uint>(variant),
+ [typeof(ulong)] = (in godot_variant variant) => VariantUtils.ConvertTo<ulong>(variant),
+ [typeof(float)] = (in godot_variant variant) => VariantUtils.ConvertTo<float>(variant),
+ [typeof(double)] = (in godot_variant variant) => VariantUtils.ConvertTo<double>(variant),
+ [typeof(Vector2)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector2>(variant),
+ [typeof(Vector2i)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector2i>(variant),
+ [typeof(Rect2)] = (in godot_variant variant) => VariantUtils.ConvertTo<Rect2>(variant),
+ [typeof(Rect2i)] = (in godot_variant variant) => VariantUtils.ConvertTo<Rect2i>(variant),
+ [typeof(Transform2D)] = (in godot_variant variant) => VariantUtils.ConvertTo<Transform2D>(variant),
+ [typeof(Vector3)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector3>(variant),
+ [typeof(Vector3i)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector3i>(variant),
+ [typeof(Basis)] = (in godot_variant variant) => VariantUtils.ConvertTo<Basis>(variant),
+ [typeof(Quaternion)] = (in godot_variant variant) => VariantUtils.ConvertTo<Quaternion>(variant),
+ [typeof(Transform3D)] = (in godot_variant variant) => VariantUtils.ConvertTo<Transform3D>(variant),
+ [typeof(Vector4)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector4>(variant),
+ [typeof(Vector4i)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector4i>(variant),
+ [typeof(AABB)] = (in godot_variant variant) => VariantUtils.ConvertTo<AABB>(variant),
+ [typeof(Color)] = (in godot_variant variant) => VariantUtils.ConvertTo<Color>(variant),
+ [typeof(Plane)] = (in godot_variant variant) => VariantUtils.ConvertTo<Plane>(variant),
+ [typeof(Callable)] = (in godot_variant variant) => VariantUtils.ConvertTo<Callable>(variant),
+ [typeof(SignalInfo)] = (in godot_variant variant) => VariantUtils.ConvertTo<SignalInfo>(variant),
+ [typeof(string)] = (in godot_variant variant) => VariantUtils.ConvertTo<string>(variant),
+ [typeof(byte[])] = (in godot_variant variant) => VariantUtils.ConvertTo<byte[]>(variant),
+ [typeof(int[])] = (in godot_variant variant) => VariantUtils.ConvertTo<int[]>(variant),
+ [typeof(long[])] = (in godot_variant variant) => VariantUtils.ConvertTo<long[]>(variant),
+ [typeof(float[])] = (in godot_variant variant) => VariantUtils.ConvertTo<float[]>(variant),
+ [typeof(double[])] = (in godot_variant variant) => VariantUtils.ConvertTo<double[]>(variant),
+ [typeof(string[])] = (in godot_variant variant) => VariantUtils.ConvertTo<string[]>(variant),
+ [typeof(Vector2[])] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector2[]>(variant),
+ [typeof(Vector3[])] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector3[]>(variant),
+ [typeof(Color[])] = (in godot_variant variant) => VariantUtils.ConvertTo<Color[]>(variant),
+ [typeof(StringName[])] =
+ (in godot_variant variant) => VariantUtils.ConvertTo<StringName[]>(variant),
+ [typeof(NodePath[])] = (in godot_variant variant) => VariantUtils.ConvertTo<NodePath[]>(variant),
+ [typeof(RID[])] = (in godot_variant variant) => VariantUtils.ConvertTo<RID[]>(variant),
+ [typeof(StringName)] = (in godot_variant variant) => VariantUtils.ConvertTo<StringName>(variant),
+ [typeof(NodePath)] = (in godot_variant variant) => VariantUtils.ConvertTo<NodePath>(variant),
+ [typeof(RID)] = (in godot_variant variant) => VariantUtils.ConvertTo<RID>(variant),
+ [typeof(Godot.Collections.Dictionary)] = (in godot_variant variant) =>
+ VariantUtils.ConvertTo<Godot.Collections.Dictionary>(variant),
+ [typeof(Godot.Collections.Array)] =
+ (in godot_variant variant) => VariantUtils.ConvertTo<Godot.Collections.Array>(variant),
+ [typeof(Variant)] = (in godot_variant variant) => VariantUtils.ConvertTo<Variant>(variant),
+ };
+
+ [SuppressMessage("ReSharper", "RedundantNameQualifier")]
+ public static object? ConvertToObjectOfType(in godot_variant variant, Type type)
+ {
+ if (ToSystemObjectFuncByType.TryGetValue(type, out var func))
+ return func(variant);
+
+ if (typeof(Godot.Object).IsAssignableFrom(type))
+ return Convert.ChangeType(VariantUtils.ConvertTo<Godot.Object>(variant), type);
+
+ if (type.IsEnum)
+ {
+ var enumUnderlyingType = type.GetEnumUnderlyingType();
+
+ switch (Type.GetTypeCode(enumUnderlyingType))
+ {
+ case TypeCode.SByte:
+ return Enum.ToObject(type, VariantUtils.ConvertToInt8(variant));
+ case TypeCode.Int16:
+ return Enum.ToObject(type, VariantUtils.ConvertToInt16(variant));
+ case TypeCode.Int32:
+ return Enum.ToObject(type, VariantUtils.ConvertToInt32(variant));
+ case TypeCode.Int64:
+ return Enum.ToObject(type, VariantUtils.ConvertToInt64(variant));
+ case TypeCode.Byte:
+ return Enum.ToObject(type, VariantUtils.ConvertToUInt8(variant));
+ case TypeCode.UInt16:
+ return Enum.ToObject(type, VariantUtils.ConvertToUInt16(variant));
+ case TypeCode.UInt32:
+ return Enum.ToObject(type, VariantUtils.ConvertToUInt32(variant));
+ case TypeCode.UInt64:
+ return Enum.ToObject(type, VariantUtils.ConvertToUInt64(variant));
+ default:
+ {
+ GD.PushError(
+ "Attempted to convert Variant to enum value of unsupported underlying type. Name: " +
+ type.FullName + " : " + enumUnderlyingType.FullName + ".");
+ return null;
+ }
+ }
+ }
+
+ if (type.IsGenericType)
+ {
+ var genericTypeDef = type.GetGenericTypeDefinition();
+
+ if (genericTypeDef == typeof(Godot.Collections.Dictionary<,>))
+ {
+ var ctor = type.GetConstructor(BindingFlags.Default,
+ new[] { typeof(Godot.Collections.Dictionary) });
+
+ if (ctor == null)
+ throw new InvalidOperationException("Dictionary constructor not found");
+
+ return ctor.Invoke(new object?[]
+ {
+ VariantUtils.ConvertTo<Godot.Collections.Dictionary>(variant)
+ });
+ }
+
+ if (genericTypeDef == typeof(Godot.Collections.Array<>))
+ {
+ var ctor = type.GetConstructor(BindingFlags.Default,
+ new[] { typeof(Godot.Collections.Array) });
+
+ if (ctor == null)
+ throw new InvalidOperationException("Array constructor not found");
+
+ return ctor.Invoke(new object?[]
+ {
+ VariantUtils.ConvertTo<Godot.Collections.Array>(variant)
+ });
+ }
+ }
+
+ GD.PushError($"Attempted to convert Variant to unsupported type. Name: {type.FullName}.");
+ return null;
+ }
+ }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
index f14790a218..cf25e1f0ae 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
@@ -344,6 +344,11 @@ namespace Godot.Collections
}
}
+ internal interface IGenericGodotDictionary
+ {
+ public Dictionary UnderlyingDictionary { get; }
+ }
+
/// <summary>
/// Typed wrapper around Godot's Dictionary class, a dictionary of Variant
/// typed elements allocated in the engine in C++. Useful when
@@ -354,7 +359,8 @@ namespace Godot.Collections
/// <typeparam name="TValue">The type of the dictionary's values.</typeparam>
public class Dictionary<[MustBeVariant] TKey, [MustBeVariant] TValue> :
IDictionary<TKey, TValue>,
- IReadOnlyDictionary<TKey, TValue>
+ IReadOnlyDictionary<TKey, TValue>,
+ IGenericGodotDictionary
{
private static godot_variant ToVariantFunc(in Dictionary<TKey, TValue> godotDictionary) =>
VariantUtils.CreateFromDictionary(godotDictionary);
@@ -370,6 +376,8 @@ namespace Godot.Collections
private readonly Dictionary _underlyingDict;
+ Dictionary IGenericGodotDictionary.UnderlyingDictionary => _underlyingDict;
+
internal ref godot_dictionary.movable NativeValue
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
index 649661ee06..6176093bc1 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
@@ -1,5 +1,7 @@
using System;
using System.Runtime.InteropServices;
+using Godot.Collections;
+using Array = System.Array;
// ReSharper disable InconsistentNaming
@@ -148,6 +150,15 @@ namespace Godot.NativeInterop
{
if (typeof(Godot.Object).IsAssignableFrom(type))
return Variant.Type.Object;
+
+ // We use `IsAssignableFrom` with our helper interfaces to detect generic Godot collections
+ // because `GetGenericTypeDefinition` is not supported in NativeAOT reflection-free mode.
+
+ if (typeof(IGenericGodotDictionary).IsAssignableFrom(type))
+ return Variant.Type.Dictionary;
+
+ if (typeof(IGenericGodotArray).IsAssignableFrom(type))
+ return Variant.Type.Array;
}
else if (type == typeof(Variant))
{
@@ -183,508 +194,6 @@ namespace Godot.NativeInterop
return Variant.Type.Nil;
}
- /* TODO: Reflection and type checking each time is slow. This will be replaced with source generators. */
- public static godot_variant ConvertManagedObjectToVariant(object? p_obj)
- {
- if (p_obj == null)
- return new godot_variant();
-
- switch (p_obj)
- {
- case bool @bool:
- return VariantUtils.CreateFromBool(@bool);
- case char @char:
- return VariantUtils.CreateFromInt(@char);
- case sbyte @int8:
- return VariantUtils.CreateFromInt(@int8);
- case short @int16:
- return VariantUtils.CreateFromInt(@int16);
- case int @int32:
- return VariantUtils.CreateFromInt(@int32);
- case long @int64:
- return VariantUtils.CreateFromInt(@int64);
- case byte @uint8:
- return VariantUtils.CreateFromInt(@uint8);
- case ushort @uint16:
- return VariantUtils.CreateFromInt(@uint16);
- case uint @uint32:
- return VariantUtils.CreateFromInt(@uint32);
- case ulong @uint64:
- return VariantUtils.CreateFromInt(@uint64);
- case float @float:
- return VariantUtils.CreateFromFloat(@float);
- case double @double:
- return VariantUtils.CreateFromFloat(@double);
- case Vector2 @vector2:
- return VariantUtils.CreateFromVector2(@vector2);
- case Vector2i @vector2i:
- return VariantUtils.CreateFromVector2i(@vector2i);
- case Rect2 @rect2:
- return VariantUtils.CreateFromRect2(@rect2);
- case Rect2i @rect2i:
- return VariantUtils.CreateFromRect2i(@rect2i);
- case Transform2D @transform2D:
- return VariantUtils.CreateFromTransform2D(@transform2D);
- case Vector3 @vector3:
- return VariantUtils.CreateFromVector3(@vector3);
- case Vector3i @vector3i:
- return VariantUtils.CreateFromVector3i(@vector3i);
- case Vector4 @vector4:
- return VariantUtils.CreateFromVector4(@vector4);
- case Vector4i @vector4i:
- return VariantUtils.CreateFromVector4i(@vector4i);
- case Basis @basis:
- return VariantUtils.CreateFromBasis(@basis);
- case Quaternion @quaternion:
- return VariantUtils.CreateFromQuaternion(@quaternion);
- case Transform3D @transform3d:
- return VariantUtils.CreateFromTransform3D(@transform3d);
- case Projection @projection:
- return VariantUtils.CreateFromProjection(@projection);
- case AABB @aabb:
- return VariantUtils.CreateFromAABB(@aabb);
- case Color @color:
- return VariantUtils.CreateFromColor(@color);
- case Plane @plane:
- return VariantUtils.CreateFromPlane(@plane);
- case Callable @callable:
- return VariantUtils.CreateFromCallable(@callable);
- case SignalInfo @signalInfo:
- return VariantUtils.CreateFromSignalInfo(@signalInfo);
- case Enum @enum:
- return VariantUtils.CreateFromInt(Convert.ToInt64(@enum));
- case string @string:
- return VariantUtils.CreateFromString(@string);
- case byte[] byteArray:
- return VariantUtils.CreateFromPackedByteArray(byteArray);
- case int[] int32Array:
- return VariantUtils.CreateFromPackedInt32Array(int32Array);
- case long[] int64Array:
- return VariantUtils.CreateFromPackedInt64Array(int64Array);
- case float[] floatArray:
- return VariantUtils.CreateFromPackedFloat32Array(floatArray);
- case double[] doubleArray:
- return VariantUtils.CreateFromPackedFloat64Array(doubleArray);
- case string[] stringArray:
- return VariantUtils.CreateFromPackedStringArray(stringArray);
- case Vector2[] vector2Array:
- return VariantUtils.CreateFromPackedVector2Array(vector2Array);
- case Vector3[] vector3Array:
- return VariantUtils.CreateFromPackedVector3Array(vector3Array);
- case Color[] colorArray:
- return VariantUtils.CreateFromPackedColorArray(colorArray);
- case StringName[] stringNameArray:
- return VariantUtils.CreateFromSystemArrayOfStringName(stringNameArray);
- case NodePath[] nodePathArray:
- return VariantUtils.CreateFromSystemArrayOfNodePath(nodePathArray);
- case RID[] ridArray:
- return VariantUtils.CreateFromSystemArrayOfRID(ridArray);
- case Godot.Object[] godotObjectArray:
- return VariantUtils.CreateFromSystemArrayOfGodotObject(godotObjectArray);
- case Godot.Object godotObject:
- return VariantUtils.CreateFromGodotObject(godotObject);
- case StringName stringName:
- return VariantUtils.CreateFromStringName(stringName);
- case NodePath nodePath:
- return VariantUtils.CreateFromNodePath(nodePath);
- case RID rid:
- return VariantUtils.CreateFromRID(rid);
- case Collections.Dictionary godotDictionary:
- return VariantUtils.CreateFromDictionary(godotDictionary);
- case Collections.Array godotArray:
- return VariantUtils.CreateFromArray(godotArray);
- case Variant variant:
- return NativeFuncs.godotsharp_variant_new_copy((godot_variant)variant.NativeVar);
- }
-
- GD.PushError("Attempted to convert an unmarshallable managed type to Variant. Name: '" +
- p_obj.GetType().FullName + ".");
- return new godot_variant();
- }
-
- public static object? ConvertVariantToManagedObjectOfType(in godot_variant p_var, Type type)
- {
- // This function is only needed to set the value of properties. Fields have their own implementation, set_value_from_variant.
- switch (Type.GetTypeCode(type))
- {
- case TypeCode.Boolean:
- return VariantUtils.ConvertToBool(p_var);
- case TypeCode.Char:
- return VariantUtils.ConvertToChar(p_var);
- case TypeCode.SByte:
- return VariantUtils.ConvertToInt8(p_var);
- case TypeCode.Int16:
- return VariantUtils.ConvertToInt16(p_var);
- case TypeCode.Int32:
- return VariantUtils.ConvertToInt32(p_var);
- case TypeCode.Int64:
- return VariantUtils.ConvertToInt64(p_var);
- case TypeCode.Byte:
- return VariantUtils.ConvertToUInt8(p_var);
- case TypeCode.UInt16:
- return VariantUtils.ConvertToUInt16(p_var);
- case TypeCode.UInt32:
- return VariantUtils.ConvertToUInt32(p_var);
- case TypeCode.UInt64:
- return VariantUtils.ConvertToUInt64(p_var);
- case TypeCode.Single:
- return VariantUtils.ConvertToFloat32(p_var);
- case TypeCode.Double:
- return VariantUtils.ConvertToFloat64(p_var);
- case TypeCode.String:
- return VariantUtils.ConvertToStringObject(p_var);
- default:
- {
- if (type == typeof(Vector2))
- return VariantUtils.ConvertToVector2(p_var);
-
- if (type == typeof(Vector2i))
- return VariantUtils.ConvertToVector2i(p_var);
-
- if (type == typeof(Rect2))
- return VariantUtils.ConvertToRect2(p_var);
-
- if (type == typeof(Rect2i))
- return VariantUtils.ConvertToRect2i(p_var);
-
- if (type == typeof(Transform2D))
- return VariantUtils.ConvertToTransform2D(p_var);
-
- if (type == typeof(Vector3))
- return VariantUtils.ConvertToVector3(p_var);
-
- if (type == typeof(Vector3i))
- return VariantUtils.ConvertToVector3i(p_var);
-
- if (type == typeof(Vector4))
- return VariantUtils.ConvertToVector4(p_var);
-
- if (type == typeof(Vector4i))
- return VariantUtils.ConvertToVector4i(p_var);
-
- if (type == typeof(Basis))
- return VariantUtils.ConvertToBasis(p_var);
-
- if (type == typeof(Quaternion))
- return VariantUtils.ConvertToQuaternion(p_var);
-
- if (type == typeof(Transform3D))
- return VariantUtils.ConvertToTransform3D(p_var);
-
- if (type == typeof(Projection))
- return VariantUtils.ConvertToProjection(p_var);
-
- if (type == typeof(AABB))
- return VariantUtils.ConvertToAABB(p_var);
-
- if (type == typeof(Color))
- return VariantUtils.ConvertToColor(p_var);
-
- if (type == typeof(Plane))
- return VariantUtils.ConvertToPlane(p_var);
-
- if (type == typeof(Callable))
- return VariantUtils.ConvertToCallableManaged(p_var);
-
- if (type == typeof(SignalInfo))
- return VariantUtils.ConvertToSignalInfo(p_var);
-
- if (type.IsEnum)
- {
- var enumUnderlyingType = type.GetEnumUnderlyingType();
- switch (Type.GetTypeCode(enumUnderlyingType))
- {
- case TypeCode.SByte:
- return VariantUtils.ConvertToInt8(p_var);
- case TypeCode.Int16:
- return VariantUtils.ConvertToInt16(p_var);
- case TypeCode.Int32:
- return VariantUtils.ConvertToInt32(p_var);
- case TypeCode.Int64:
- return VariantUtils.ConvertToInt64(p_var);
- case TypeCode.Byte:
- return VariantUtils.ConvertToUInt8(p_var);
- case TypeCode.UInt16:
- return VariantUtils.ConvertToUInt16(p_var);
- case TypeCode.UInt32:
- return VariantUtils.ConvertToUInt32(p_var);
- case TypeCode.UInt64:
- return VariantUtils.ConvertToUInt64(p_var);
- default:
- {
- GD.PushError(
- "Attempted to convert Variant to enum value of unsupported underlying type. Name: " +
- type.FullName + " : " + enumUnderlyingType.FullName + ".");
- return null;
- }
- }
- }
-
- if (type.IsArray || type.IsSZArray)
- {
- return ConvertVariantToSystemArrayOfType(p_var, type);
- }
- else if (type.IsGenericType)
- {
- if (typeof(Godot.Object).IsAssignableFrom(type))
- {
- var godotObject = VariantUtils.ConvertToGodotObject(p_var);
-
- if (!type.IsInstanceOfType(godotObject))
- {
- GD.PushError("Invalid cast when marshaling Godot.Object type." +
- $" `{godotObject.GetType()}` is not assignable to `{type.FullName}`.");
- return null;
- }
-
- return godotObject;
- }
-
- return null;
- }
- else if (type == typeof(Variant))
- {
- return Variant.CreateCopyingBorrowed(p_var);
- }
-
- if (ConvertVariantToManagedObjectOfClass(p_var, type, out object? res))
- return res;
-
- break;
- }
- }
-
- GD.PushError("Attempted to convert Variant to unsupported type. Name: " +
- type.FullName + ".");
- return null;
- }
-
- private static object? ConvertVariantToSystemArrayOfType(in godot_variant p_var, Type type)
- {
- if (type == typeof(byte[]))
- return VariantUtils.ConvertAsPackedByteArrayToSystemArray(p_var);
-
- if (type == typeof(int[]))
- return VariantUtils.ConvertAsPackedInt32ArrayToSystemArray(p_var);
-
- if (type == typeof(long[]))
- return VariantUtils.ConvertAsPackedInt64ArrayToSystemArray(p_var);
-
- if (type == typeof(float[]))
- return VariantUtils.ConvertAsPackedFloat32ArrayToSystemArray(p_var);
-
- if (type == typeof(double[]))
- return VariantUtils.ConvertAsPackedFloat64ArrayToSystemArray(p_var);
-
- if (type == typeof(string[]))
- return VariantUtils.ConvertAsPackedStringArrayToSystemArray(p_var);
-
- if (type == typeof(Vector2[]))
- return VariantUtils.ConvertAsPackedVector2ArrayToSystemArray(p_var);
-
- if (type == typeof(Vector3[]))
- return VariantUtils.ConvertAsPackedVector3ArrayToSystemArray(p_var);
-
- if (type == typeof(Color[]))
- return VariantUtils.ConvertAsPackedColorArrayToSystemArray(p_var);
-
- if (type == typeof(StringName[]))
- return VariantUtils.ConvertToSystemArrayOfStringName(p_var);
-
- if (type == typeof(NodePath[]))
- return VariantUtils.ConvertToSystemArrayOfNodePath(p_var);
-
- if (type == typeof(RID[]))
- return VariantUtils.ConvertToSystemArrayOfRID(p_var);
-
- if (typeof(Godot.Object[]).IsAssignableFrom(type))
- return VariantUtils.ConvertToSystemArrayOfGodotObject(p_var, type);
-
- GD.PushError("Attempted to convert Variant to array of unsupported element type. Name: " +
- type.GetElementType()!.FullName + ".");
- return null;
- }
-
- private static bool ConvertVariantToManagedObjectOfClass(in godot_variant p_var, Type type,
- out object? res)
- {
- if (typeof(Godot.Object).IsAssignableFrom(type))
- {
- if (p_var.Type == Variant.Type.Nil)
- {
- res = null;
- return true;
- }
-
- if (p_var.Type != Variant.Type.Object)
- {
- GD.PushError("Invalid cast when marshaling Godot.Object type." +
- $" Variant type is `{p_var.Type}`; expected `{p_var.Object}`.");
- res = null;
- return true;
- }
-
- var godotObjectPtr = VariantUtils.ConvertToGodotObjectPtr(p_var);
-
- if (godotObjectPtr == IntPtr.Zero)
- {
- res = null;
- return true;
- }
-
- var godotObject = InteropUtils.UnmanagedGetManaged(godotObjectPtr);
-
- if (!type.IsInstanceOfType(godotObject))
- {
- GD.PushError("Invalid cast when marshaling Godot.Object type." +
- $" `{godotObject.GetType()}` is not assignable to `{type.FullName}`.");
- res = null;
- return false;
- }
-
- res = godotObject;
- return true;
- }
-
- if (typeof(StringName) == type)
- {
- res = VariantUtils.ConvertToStringNameObject(p_var);
- return true;
- }
-
- if (typeof(NodePath) == type)
- {
- res = VariantUtils.ConvertToNodePathObject(p_var);
- return true;
- }
-
- if (typeof(RID) == type)
- {
- res = VariantUtils.ConvertToRID(p_var);
- return true;
- }
-
- if (typeof(Collections.Dictionary) == type)
- {
- res = VariantUtils.ConvertToDictionaryObject(p_var);
- return true;
- }
-
- if (typeof(Collections.Array) == type)
- {
- res = VariantUtils.ConvertToArrayObject(p_var);
- return true;
- }
-
- res = null;
- return false;
- }
-
- public static unsafe object? ConvertVariantToManagedObject(in godot_variant p_var)
- {
- switch (p_var.Type)
- {
- case Variant.Type.Bool:
- return p_var.Bool.ToBool();
- case Variant.Type.Int:
- return p_var.Int;
- case Variant.Type.Float:
- {
-#if REAL_T_IS_DOUBLE
- return p_var.Float;
-#else
- return (float)p_var.Float;
-#endif
- }
- case Variant.Type.String:
- return ConvertStringToManaged(p_var.String);
- case Variant.Type.Vector2:
- return p_var.Vector2;
- case Variant.Type.Vector2i:
- return p_var.Vector2i;
- case Variant.Type.Rect2:
- return p_var.Rect2;
- case Variant.Type.Rect2i:
- return p_var.Rect2i;
- case Variant.Type.Vector3:
- return p_var.Vector3;
- case Variant.Type.Vector3i:
- return p_var.Vector3i;
- case Variant.Type.Transform2d:
- return *p_var.Transform2D;
- case Variant.Type.Vector4:
- return p_var.Vector4;
- case Variant.Type.Vector4i:
- return p_var.Vector4i;
- case Variant.Type.Plane:
- return p_var.Plane;
- case Variant.Type.Quaternion:
- return p_var.Quaternion;
- case Variant.Type.Aabb:
- return *p_var.AABB;
- case Variant.Type.Basis:
- return *p_var.Basis;
- case Variant.Type.Transform3d:
- return *p_var.Transform3D;
- case Variant.Type.Projection:
- return *p_var.Projection;
- case Variant.Type.Color:
- return p_var.Color;
- case Variant.Type.StringName:
- {
- // The Variant owns the value, so we need to make a copy
- return StringName.CreateTakingOwnershipOfDisposableValue(
- NativeFuncs.godotsharp_string_name_new_copy(p_var.StringName));
- }
- case Variant.Type.NodePath:
- {
- // The Variant owns the value, so we need to make a copy
- return NodePath.CreateTakingOwnershipOfDisposableValue(
- NativeFuncs.godotsharp_node_path_new_copy(p_var.NodePath));
- }
- case Variant.Type.Rid:
- return p_var.RID;
- case Variant.Type.Object:
- return InteropUtils.UnmanagedGetManaged(p_var.Object);
- case Variant.Type.Callable:
- return ConvertCallableToManaged(p_var.Callable);
- case Variant.Type.Signal:
- return ConvertSignalToManaged(p_var.Signal);
- case Variant.Type.Dictionary:
- {
- // The Variant owns the value, so we need to make a copy
- return Collections.Dictionary.CreateTakingOwnershipOfDisposableValue(
- NativeFuncs.godotsharp_dictionary_new_copy(p_var.Dictionary));
- }
- case Variant.Type.Array:
- {
- // The Variant owns the value, so we need to make a copy
- return Collections.Array.CreateTakingOwnershipOfDisposableValue(
- NativeFuncs.godotsharp_array_new_copy(p_var.Array));
- }
- case Variant.Type.PackedByteArray:
- return VariantUtils.ConvertAsPackedByteArrayToSystemArray(p_var);
- case Variant.Type.PackedInt32Array:
- return VariantUtils.ConvertAsPackedInt32ArrayToSystemArray(p_var);
- case Variant.Type.PackedInt64Array:
- return VariantUtils.ConvertAsPackedInt64ArrayToSystemArray(p_var);
- case Variant.Type.PackedFloat32Array:
- return VariantUtils.ConvertAsPackedFloat32ArrayToSystemArray(p_var);
- case Variant.Type.PackedFloat64Array:
- return VariantUtils.ConvertAsPackedFloat64ArrayToSystemArray(p_var);
- case Variant.Type.PackedStringArray:
- return VariantUtils.ConvertAsPackedStringArrayToSystemArray(p_var);
- case Variant.Type.PackedVector2Array:
- return VariantUtils.ConvertAsPackedVector2ArrayToSystemArray(p_var);
- case Variant.Type.PackedVector3Array:
- return VariantUtils.ConvertAsPackedVector3ArrayToSystemArray(p_var);
- case Variant.Type.PackedColorArray:
- return VariantUtils.ConvertAsPackedColorArrayToSystemArray(p_var);
- default:
- return null;
- }
- }
-
// String
public static unsafe godot_string ConvertStringToNative(string? p_mono_string)
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs
index 694da6db77..80ef2a1ea1 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs
@@ -4,6 +4,8 @@ using System.Runtime.CompilerServices;
namespace Godot.NativeInterop;
+#nullable enable
+
public partial class VariantUtils
{
private static Exception UnsupportedType<T>() => new InvalidOperationException(
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs
index 237a4da364..49a363cef2 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs
@@ -109,16 +109,50 @@ public partial struct Variant : IDisposable
public override string ToString() => AsString();
- public object? Obj
- {
- get
+ public object? Obj =>
+ _obj ??= NativeVar.DangerousSelfRef.Type switch
{
- if (_obj == null)
- _obj = Marshaling.ConvertVariantToManagedObject((godot_variant)NativeVar);
-
- return _obj;
- }
- }
+ Type.Bool => AsBool(),
+ Type.Int => AsInt64(),
+ Type.Float => AsDouble(),
+ Type.String => AsString(),
+ Type.Vector2 => AsVector2(),
+ Type.Vector2i => AsVector2i(),
+ Type.Rect2 => AsRect2(),
+ Type.Rect2i => AsRect2i(),
+ Type.Vector3 => AsVector3(),
+ Type.Vector3i => AsVector3i(),
+ Type.Transform2d => AsTransform2D(),
+ Type.Vector4 => AsVector4(),
+ Type.Vector4i => AsVector4i(),
+ Type.Plane => AsPlane(),
+ Type.Quaternion => AsQuaternion(),
+ Type.Aabb => AsAABB(),
+ Type.Basis => AsBasis(),
+ Type.Transform3d => AsTransform3D(),
+ Type.Projection => AsProjection(),
+ Type.Color => AsColor(),
+ Type.StringName => AsStringName(),
+ Type.NodePath => AsNodePath(),
+ Type.Rid => AsRID(),
+ Type.Object => AsGodotObject(),
+ Type.Callable => AsCallable(),
+ Type.Signal => AsSignalInfo(),
+ Type.Dictionary => AsGodotDictionary(),
+ Type.Array => AsGodotArray(),
+ Type.PackedByteArray => AsByteArray(),
+ Type.PackedInt32Array => AsInt32Array(),
+ Type.PackedInt64Array => AsInt64Array(),
+ Type.PackedFloat32Array => AsFloat32Array(),
+ Type.PackedFloat64Array => AsFloat64Array(),
+ Type.PackedStringArray => AsStringArray(),
+ Type.PackedVector2Array => AsVector2Array(),
+ Type.PackedVector3Array => AsVector3Array(),
+ Type.PackedColorArray => AsColorArray(),
+ Type.Nil => null,
+ Type.Max or _ =>
+ throw new InvalidOperationException($"Invalid Variant type: {NativeVar.DangerousSelfRef.Type}"),
+ };
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant From<[MustBeVariant] T>(in T from) =>
diff --git a/modules/openxr/config.py b/modules/openxr/config.py
index 279168cc59..e503f12739 100644
--- a/modules/openxr/config.py
+++ b/modules/openxr/config.py
@@ -1,6 +1,6 @@
def can_build(env, platform):
if platform in ("linuxbsd", "windows", "android"):
- return env["openxr"]
+ return env["openxr"] and not env["disable_3d"]
else:
# not supported on these platforms
return False
diff --git a/modules/openxr/extensions/openxr_android_extension.cpp b/modules/openxr/extensions/openxr_android_extension.cpp
index ea539f2053..753fc5fa89 100644
--- a/modules/openxr/extensions/openxr_android_extension.cpp
+++ b/modules/openxr/extensions/openxr_android_extension.cpp
@@ -47,11 +47,15 @@ OpenXRAndroidExtension *OpenXRAndroidExtension::get_singleton() {
OpenXRAndroidExtension::OpenXRAndroidExtension(OpenXRAPI *p_openxr_api) :
OpenXRExtensionWrapper(p_openxr_api) {
singleton = this;
- request_extensions[XR_KHR_LOADER_INIT_ANDROID_EXTENSION_NAME] = nullptr; // must be available
+ request_extensions[XR_KHR_LOADER_INIT_ANDROID_EXTENSION_NAME] = &loader_init_extension_available;
request_extensions[XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME] = &create_instance_extension_available;
}
void OpenXRAndroidExtension::on_before_instance_created() {
+ if (!loader_init_extension_available) {
+ print_line("OpenXR: XR_KHR_loader_init_android is not reported as available - trying to initialize anyway...");
+ }
+
EXT_INIT_XR_FUNC(xrInitializeLoaderKHR);
JNIEnv *env = get_jni_env();
diff --git a/modules/openxr/extensions/openxr_android_extension.h b/modules/openxr/extensions/openxr_android_extension.h
index ca6011559a..087b634756 100644
--- a/modules/openxr/extensions/openxr_android_extension.h
+++ b/modules/openxr/extensions/openxr_android_extension.h
@@ -48,6 +48,7 @@ public:
private:
static OpenXRAndroidExtension *singleton;
+ bool loader_init_extension_available = false;
bool create_instance_extension_available = false;
// Initialize the loader
diff --git a/modules/openxr/extensions/openxr_opengl_extension.cpp b/modules/openxr/extensions/openxr_opengl_extension.cpp
index 569030cc11..234c5f8391 100644
--- a/modules/openxr/extensions/openxr_opengl_extension.cpp
+++ b/modules/openxr/extensions/openxr_opengl_extension.cpp
@@ -138,7 +138,7 @@ void *OpenXROpenGLExtension::set_session_create_and_get_next_pointer(void *p_nex
graphics_binding_gl.display = eglGetCurrentDisplay();
graphics_binding_gl.config = (EGLConfig)0; // https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/master/src/tests/hello_xr/graphicsplugin_opengles.cpp#L122
- graphics_binding_gl.context = eglGetCurrentContext();
+ graphics_binding_gl.context = (void *)display_server->window_get_native_handle(DisplayServer::OPENGL_CONTEXT);
#else
graphics_binding_gl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR;
graphics_binding_gl.next = p_next_pointer;
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index d6580ebfa6..59d3865acd 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -51,7 +51,7 @@
#define XR_USE_GRAPHICS_API_VULKAN
#endif
#ifdef GLES3_ENABLED
-#ifdef ANDROID
+#ifdef ANDROID_ENABLED
#define XR_USE_GRAPHICS_API_OPENGL_ES
#include <EGL/egl.h>
#include <EGL/eglext.h>
@@ -59,7 +59,7 @@
#include <GLES3/gl3ext.h>
#else
#define XR_USE_GRAPHICS_API_OPENGL
-#endif // ANDROID
+#endif // ANDROID_ENABLED
#ifdef X11_ENABLED
#include OPENGL_INCLUDE_H
#define GL_GLEXT_PROTOTYPES 1
diff --git a/modules/webxr/doc_classes/WebXRInterface.xml b/modules/webxr/doc_classes/WebXRInterface.xml
index 49dd9f7318..f5964eb4d1 100644
--- a/modules/webxr/doc_classes/WebXRInterface.xml
+++ b/modules/webxr/doc_classes/WebXRInterface.xml
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="WebXRInterface" inherits="XRInterface" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- AR/VR interface using WebXR.
+ XR interface using WebXR.
</brief_description>
<description>
WebXR is an open standard that allows creating VR and AR applications that run in the web browser.
As such, this interface is only available when running in Web exports.
WebXR supports a wide range of devices, from the very capable (like Valve Index, HTC Vive, Oculus Rift and Quest) down to the much less capable (like Google Cardboard, Oculus Go, GearVR, or plain smartphones).
- Since WebXR is based on JavaScript, it makes extensive use of callbacks, which means that [WebXRInterface] is forced to use signals, where other AR/VR interfaces would instead use functions that return a result immediately. This makes [WebXRInterface] quite a bit more complicated to initialize than other AR/VR interfaces.
+ Since WebXR is based on JavaScript, it makes extensive use of callbacks, which means that [WebXRInterface] is forced to use signals, where other XR interfaces would instead use functions that return a result immediately. This makes [WebXRInterface] quite a bit more complicated to initialize than other XR interfaces.
Here's the minimum code required to start an immersive VR session:
[codeblock]
extends Node3D
@@ -69,7 +69,7 @@
func _webxr_session_started():
$Button.visible = false
# This tells Godot to start rendering to the headset.
- get_viewport().xr = true
+ get_viewport().use_xr = true
# This will be the reference space type you ultimately got, out of the
# types that you requested above. This is useful if you want the game to
# work a little differently in 'bounded-floor' versus 'local-floor'.
@@ -79,28 +79,35 @@
$Button.visible = true
# If the user exits immersive mode, then we tell Godot to render to the web
# page again.
- get_viewport().xr = false
+ get_viewport().use_xr = false
func _webxr_session_failed(message):
OS.alert("Failed to initialize: " + message)
[/codeblock]
- There are several ways to handle "controller" input:
- - Using [XRController3D] nodes and their [signal XRController3D.button_pressed] and [signal XRController3D.button_released] signals. This is how controllers are typically handled in AR/VR apps in Godot, however, this will only work with advanced VR controllers like the Oculus Touch or Index controllers, for example. The buttons codes are defined by [url=https://immersive-web.github.io/webxr-gamepads-module/#xr-standard-gamepad-mapping]Section 3.3 of the WebXR Gamepads Module[/url].
- - Using [method Node._unhandled_input] and [InputEventJoypadButton] or [InputEventJoypadMotion]. This works the same as normal joypads, except the [member InputEvent.device] starts at 100, so the left controller is 100 and the right controller is 101, and the button codes are also defined by [url=https://immersive-web.github.io/webxr-gamepads-module/#xr-standard-gamepad-mapping]Section 3.3 of the WebXR Gamepads Module[/url].
- - Using the [signal select], [signal squeeze] and related signals. This method will work for both advanced VR controllers, and non-traditional "controllers" like a tap on the screen, a spoken voice command or a button press on the device itself.
- You can use one or all of these methods to allow your game or app to support a wider or narrower set of devices and input methods, or to allow more advanced interactions with more advanced devices.
+ There are a couple ways to handle "controller" input:
+ - Using [XRController3D] nodes and their [signal XRController3D.button_pressed] and [signal XRController3D.button_released] signals. This is how controllers are typically handled in XR apps in Godot, however, this will only work with advanced VR controllers like the Oculus Touch or Index controllers, for example.
+ - Using the [signal select], [signal squeeze] and related signals. This method will work for both advanced VR controllers, and non-traditional input sources like a tap on the screen, a spoken voice command or a button press on the device itself.
+ You can use both methods to allow your game or app to support a wider or narrower set of devices and input methods, or to allow more advanced interactions with more advanced devices.
</description>
<tutorials>
<link title="How to make a VR game for WebXR with Godot">https://www.snopekgames.com/blog/2020/how-make-vr-game-webxr-godot</link>
</tutorials>
<methods>
- <method name="get_controller" qualifiers="const">
+ <method name="get_input_source_target_ray_mode" qualifiers="const">
+ <return type="int" enum="WebXRInterface.TargetRayMode" />
+ <param index="0" name="input_source_id" type="int" />
+ <description>
+ Returns the target ray mode for the given [code]input_source_id[/code].
+ This can help interpret the input coming from that input source. See [url=https://developer.mozilla.org/en-US/docs/Web/API/XRInputSource/targetRayMode]XRInputSource.targetRayMode[/url] for more information.
+ </description>
+ </method>
+ <method name="get_input_source_tracker" qualifiers="const">
<return type="XRPositionalTracker" />
- <param index="0" name="controller_id" type="int" />
+ <param index="0" name="input_source_id" type="int" />
<description>
- Gets an [XRPositionalTracker] for the given [code]controller_id[/code].
- In the context of WebXR, a "controller" can be an advanced VR controller like the Oculus Touch or Index controllers, or even a tap on the screen, a spoken voice command or a button press on the device itself. When a non-traditional controller is used, interpret the position and orientation of the [XRPositionalTracker] as a ray pointing at the object the user wishes to interact with.
- Use this method to get information about the controller that triggered one of these signals:
+ Gets an [XRPositionalTracker] for the given [code]input_source_id[/code].
+ In the context of WebXR, an input source can be an advanced VR controller like the Oculus Touch or Index controllers, or even a tap on the screen, a spoken voice command or a button press on the device itself. When a non-traditional input source is used, interpret the position and orientation of the [XRPositionalTracker] as a ray pointing at the object the user wishes to interact with.
+ Use this method to get information about the input source that triggered one of these signals:
- [signal selectstart]
- [signal select]
- [signal selectend]
@@ -109,6 +116,13 @@
- [signal squeezestart]
</description>
</method>
+ <method name="is_input_source_active" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="input_source_id" type="int" />
+ <description>
+ Returns [code]true[/code] if there is an active input source with the given [code]input_source_id[/code].
+ </description>
+ </method>
<method name="is_session_supported">
<return type="void" />
<param index="0" name="session_mode" type="String" />
@@ -120,11 +134,6 @@
</method>
</methods>
<members>
- <member name="bounds_geometry" type="PackedVector3Array" setter="" getter="get_bounds_geometry">
- The vertices of a polygon which defines the boundaries of the user's play area.
- This will only be available if [member reference_space_type] is [code]"bounded-floor"[/code] and only on certain browsers and devices that support it.
- The [signal reference_space_reset] signal may indicate when this changes.
- </member>
<member name="optional_features" type="String" setter="set_optional_features" getter="get_optional_features">
A comma-seperated list of optional features used by [method XRInterface.initialize] when setting up the WebXR session.
If a user's browser or device doesn't support one of the given features, initialization will continue, but you won't be able to use the requested feature.
@@ -137,7 +146,7 @@
</member>
<member name="requested_reference_space_types" type="String" setter="set_requested_reference_space_types" getter="get_requested_reference_space_types">
A comma-seperated list of reference space types used by [method XRInterface.initialize] when setting up the WebXR session.
- The reference space types are requested in order, and the first on supported by the users device or browser will be used. The [member reference_space_type] property contains the reference space type that was ultimately used.
+ The reference space types are requested in order, and the first one supported by the users device or browser will be used. The [member reference_space_type] property contains the reference space type that was ultimately selected.
This doesn't have any effect on the interface when already initialized.
Possible values come from [url=https://developer.mozilla.org/en-US/docs/Web/API/XRReferenceSpaceType]WebXR's XRReferenceSpaceType[/url]. If you want to use a particular reference space type, it must be listed in either [member required_features] or [member optional_features].
</member>
@@ -161,35 +170,35 @@
<signal name="reference_space_reset">
<description>
Emitted to indicate that the reference space has been reset or reconfigured.
- When (or whether) this is emitted depends on the user's browser or device, but may include when the user has changed the dimensions of their play space (which you may be able to access via [member bounds_geometry]) or pressed/held a button to recenter their position.
+ When (or whether) this is emitted depends on the user's browser or device, but may include when the user has changed the dimensions of their play space (which you may be able to access via [method XRInterface.get_play_area]) or pressed/held a button to recenter their position.
See [url=https://developer.mozilla.org/en-US/docs/Web/API/XRReferenceSpace/reset_event]WebXR's XRReferenceSpace reset event[/url] for more information.
</description>
</signal>
<signal name="select">
- <param index="0" name="controller_id" type="int" />
+ <param index="0" name="input_source_id" type="int" />
<description>
- Emitted after one of the "controllers" has finished its "primary action".
- Use [method get_controller] to get more information about the controller.
+ Emitted after one of the input sources has finished its "primary action".
+ Use [method get_input_source_tracker] and [method get_input_source_target_ray_mode] to get more information about the input source.
</description>
</signal>
<signal name="selectend">
- <param index="0" name="controller_id" type="int" />
+ <param index="0" name="input_source_id" type="int" />
<description>
- Emitted when one of the "controllers" has finished its "primary action".
- Use [method get_controller] to get more information about the controller.
+ Emitted when one of the input sources has finished its "primary action".
+ Use [method get_input_source_tracker] and [method get_input_source_target_ray_mode] to get more information about the input source.
</description>
</signal>
<signal name="selectstart">
- <param index="0" name="controller_id" type="int" />
+ <param index="0" name="input_source_id" type="int" />
<description>
- Emitted when one of the "controllers" has started its "primary action".
- Use [method get_controller] to get more information about the controller.
+ Emitted when one of the input source has started its "primary action".
+ Use [method get_input_source_tracker] and [method get_input_source_target_ray_mode] to get more information about the input source.
</description>
</signal>
<signal name="session_ended">
<description>
Emitted when the user ends the WebXR session (which can be done using UI from the browser or device).
- At this point, you should do [code]get_viewport().xr = false[/code] to instruct Godot to resume rendering to the screen.
+ At this point, you should do [code]get_viewport().use_xr = false[/code] to instruct Godot to resume rendering to the screen.
</description>
</signal>
<signal name="session_failed">
@@ -202,7 +211,7 @@
<signal name="session_started">
<description>
Emitted by [method XRInterface.initialize] if the session is successfully started.
- At this point, it's safe to do [code]get_viewport().xr = true[/code] to instruct Godot to start rendering to the AR/VR device.
+ At this point, it's safe to do [code]get_viewport().use_xr = true[/code] to instruct Godot to start rendering to the XR device.
</description>
</signal>
<signal name="session_supported">
@@ -213,24 +222,24 @@
</description>
</signal>
<signal name="squeeze">
- <param index="0" name="controller_id" type="int" />
+ <param index="0" name="input_source_id" type="int" />
<description>
- Emitted after one of the "controllers" has finished its "primary squeeze action".
- Use [method get_controller] to get more information about the controller.
+ Emitted after one of the input sources has finished its "primary squeeze action".
+ Use [method get_input_source_tracker] and [method get_input_source_target_ray_mode] to get more information about the input source.
</description>
</signal>
<signal name="squeezeend">
- <param index="0" name="controller_id" type="int" />
+ <param index="0" name="input_source_id" type="int" />
<description>
- Emitted when one of the "controllers" has finished its "primary squeeze action".
- Use [method get_controller] to get more information about the controller.
+ Emitted when one of the input sources has finished its "primary squeeze action".
+ Use [method get_input_source_tracker] and [method get_input_source_target_ray_mode] to get more information about the input source.
</description>
</signal>
<signal name="squeezestart">
- <param index="0" name="controller_id" type="int" />
+ <param index="0" name="input_source_id" type="int" />
<description>
- Emitted when one of the "controllers" has started its "primary squeeze action".
- Use [method get_controller] to get more information about the controller.
+ Emitted when one of the input sources has started its "primary squeeze action".
+ Use [method get_input_source_tracker] and [method get_input_source_target_ray_mode] to get more information about the input source.
</description>
</signal>
<signal name="visibility_state_changed">
@@ -239,4 +248,18 @@
</description>
</signal>
</signals>
+ <constants>
+ <constant name="TARGET_RAY_MODE_UNKNOWN" value="0" enum="TargetRayMode">
+ We don't know the the target ray mode.
+ </constant>
+ <constant name="TARGET_RAY_MODE_GAZE" value="1" enum="TargetRayMode">
+ Target ray originates at the viewer's eyes and points in the direction they are looking.
+ </constant>
+ <constant name="TARGET_RAY_MODE_TRACKED_POINTER" value="2" enum="TargetRayMode">
+ Target ray from a handheld pointer, most likely a VR touch controller.
+ </constant>
+ <constant name="TARGET_RAY_MODE_SCREEN" value="3" enum="TargetRayMode">
+ Target ray from touch screen, mouse or other tactile input device.
+ </constant>
+ </constants>
</class>
diff --git a/modules/webxr/godot_webxr.h b/modules/webxr/godot_webxr.h
index d8d5bd99cc..e31a1d307e 100644
--- a/modules/webxr/godot_webxr.h
+++ b/modules/webxr/godot_webxr.h
@@ -37,12 +37,18 @@ extern "C" {
#include "stddef.h"
+enum WebXRInputEvent {
+ WEBXR_INPUT_EVENT_SELECTSTART,
+ WEBXR_INPUT_EVENT_SELECTEND,
+ WEBXR_INPUT_EVENT_SQUEEZESTART,
+ WEBXR_INPUT_EVENT_SQUEEZEEND,
+};
+
typedef void (*GodotWebXRSupportedCallback)(char *p_session_mode, int p_supported);
typedef void (*GodotWebXRStartedCallback)(char *p_reference_space_type);
typedef void (*GodotWebXREndedCallback)();
typedef void (*GodotWebXRFailedCallback)(char *p_message);
-typedef void (*GodotWebXRControllerCallback)();
-typedef void (*GodotWebXRInputEventCallback)(char *p_signal_name, int p_controller_id);
+typedef void (*GodotWebXRInputEventCallback)(int p_event_type, int p_input_source_id);
typedef void (*GodotWebXRSimpleEventCallback)(char *p_signal_name);
extern int godot_webxr_is_supported();
@@ -56,26 +62,33 @@ extern void godot_webxr_initialize(
GodotWebXRStartedCallback p_on_session_started,
GodotWebXREndedCallback p_on_session_ended,
GodotWebXRFailedCallback p_on_session_failed,
- GodotWebXRControllerCallback p_on_controller_changed,
GodotWebXRInputEventCallback p_on_input_event,
GodotWebXRSimpleEventCallback p_on_simple_event);
extern void godot_webxr_uninitialize();
extern int godot_webxr_get_view_count();
-extern int *godot_webxr_get_render_target_size();
-extern float *godot_webxr_get_transform_for_eye(int p_eye);
-extern float *godot_webxr_get_projection_for_eye(int p_eye);
-extern void godot_webxr_commit(unsigned int p_texture);
+extern bool godot_webxr_get_render_target_size(int *r_size);
+extern bool godot_webxr_get_transform_for_view(int p_view, float *r_transform);
+extern bool godot_webxr_get_projection_for_view(int p_view, float *r_transform);
+extern unsigned int godot_webxr_get_color_texture();
+extern unsigned int godot_webxr_get_depth_texture();
+extern unsigned int godot_webxr_get_velocity_texture();
-extern void godot_webxr_sample_controller_data();
-extern int godot_webxr_get_controller_count();
-extern int godot_webxr_is_controller_connected(int p_controller);
-extern float *godot_webxr_get_controller_transform(int p_controller);
-extern int *godot_webxr_get_controller_buttons(int p_controller);
-extern int *godot_webxr_get_controller_axes(int p_controller);
+extern bool godot_webxr_update_input_source(
+ int p_input_source_id,
+ float *r_target_pose,
+ int *r_target_ray_mode,
+ int *r_touch_index,
+ int *r_has_grip_pose,
+ float *r_grip_pose,
+ int *r_has_standard_mapping,
+ int *r_button_count,
+ float *r_buttons,
+ int *r_axes_count,
+ float *r_axes);
extern char *godot_webxr_get_visibility_state();
-extern int *godot_webxr_get_bounds_geometry();
+extern int godot_webxr_get_bounds_geometry(float **r_points);
#ifdef __cplusplus
}
diff --git a/modules/webxr/native/library_godot_webxr.js b/modules/webxr/native/library_godot_webxr.js
index 714768347c..eaf251d48f 100644
--- a/modules/webxr/native/library_godot_webxr.js
+++ b/modules/webxr/native/library_godot_webxr.js
@@ -33,9 +33,14 @@ const GodotWebXR = {
gl: null,
session: null,
+ gl_binding: null,
+ layer: null,
space: null,
frame: null,
pose: null,
+ view_count: 1,
+ input_sources: new Array(16),
+ touches: new Array(5),
// Monkey-patch the requestAnimationFrame() used by Emscripten for the main
// loop, so that we can swap it out for XRSession.requestAnimationFrame()
@@ -76,34 +81,128 @@ const GodotWebXR = {
}, 0);
},
- // Holds the controllers list between function calls.
- controllers: [],
+ getLayer: () => {
+ const new_view_count = (GodotWebXR.pose) ? GodotWebXR.pose.views.length : 1;
+ let layer = GodotWebXR.layer;
- // Updates controllers array, where the left hand (or sole tracker) is
- // the first element, and the right hand is the second element, and any
- // others placed at the 3rd position and up.
- sampleControllers: () => {
- if (!GodotWebXR.session || !GodotWebXR.frame) {
- return;
+ // If the view count hasn't changed since creating this layer, then
+ // we can simply return it.
+ if (layer && GodotWebXR.view_count === new_view_count) {
+ return layer;
}
- let other_index = 2;
- const controllers = [];
- GodotWebXR.session.inputSources.forEach((input_source) => {
- if (input_source.targetRayMode === 'tracked-pointer') {
- if (input_source.handedness === 'right') {
- controllers[1] = input_source;
- } else if (input_source.handedness === 'left' || !controllers[0]) {
- controllers[0] = input_source;
+ if (!GodotWebXR.session || !GodotWebXR.gl_binding) {
+ return null;
+ }
+
+ const gl = GodotWebXR.gl;
+
+ layer = GodotWebXR.gl_binding.createProjectionLayer({
+ textureType: new_view_count > 1 ? 'texture-array' : 'texture',
+ colorFormat: gl.RGBA8,
+ depthFormat: gl.DEPTH_COMPONENT24,
+ });
+ GodotWebXR.session.updateRenderState({ layers: [layer] });
+
+ GodotWebXR.layer = layer;
+ GodotWebXR.view_count = new_view_count;
+ return layer;
+ },
+
+ getSubImage: () => {
+ if (!GodotWebXR.pose) {
+ return null;
+ }
+ const layer = GodotWebXR.getLayer();
+ if (layer === null) {
+ return null;
+ }
+
+ // Because we always use "texture-array" for multiview and "texture"
+ // when there is only 1 view, it should be safe to only grab the
+ // subimage for the first view.
+ return GodotWebXR.gl_binding.getViewSubImage(layer, GodotWebXR.pose.views[0]);
+ },
+
+ getTextureId: (texture) => {
+ if (texture.name !== undefined) {
+ return texture.name;
+ }
+
+ const id = GL.getNewId(GL.textures);
+ texture.name = id;
+ GL.textures[id] = texture;
+
+ return id;
+ },
+
+ addInputSource: (input_source) => {
+ let name = -1;
+ if (input_source.targetRayMode === 'tracked-pointer' && input_source.handedness === 'left') {
+ name = 0;
+ } else if (input_source.targetRayMode === 'tracked-pointer' && input_source.handedness === 'right') {
+ name = 1;
+ } else {
+ for (let i = 2; i < 16; i++) {
+ if (!GodotWebXR.input_sources[i]) {
+ name = i;
+ break;
}
- } else {
- controllers[other_index++] = input_source;
}
- });
- GodotWebXR.controllers = controllers;
+ }
+ if (name >= 0) {
+ GodotWebXR.input_sources[name] = input_source;
+ input_source.name = name;
+
+ // Find a free touch index for screen sources.
+ if (input_source.targetRayMode === 'screen') {
+ let touch_index = -1;
+ for (let i = 0; i < 5; i++) {
+ if (!GodotWebXR.touches[i]) {
+ touch_index = i;
+ break;
+ }
+ }
+ if (touch_index >= 0) {
+ GodotWebXR.touches[touch_index] = input_source;
+ input_source.touch_index = touch_index;
+ }
+ }
+ }
+ return name;
},
- getControllerId: (input_source) => GodotWebXR.controllers.indexOf(input_source),
+ removeInputSource: (input_source) => {
+ if (input_source.name !== undefined) {
+ const name = input_source.name;
+ if (name >= 0 && name < 16) {
+ GodotWebXR.input_sources[name] = null;
+ }
+
+ if (input_source.touch_index !== undefined) {
+ const touch_index = input_source.touch_index;
+ if (touch_index >= 0 && touch_index < 5) {
+ GodotWebXR.touches[touch_index] = null;
+ }
+ }
+ return name;
+ }
+ return -1;
+ },
+
+ getInputSourceId: (input_source) => {
+ if (input_source !== undefined) {
+ return input_source.name;
+ }
+ return -1;
+ },
+
+ getTouchIndex: (input_source) => {
+ if (input_source.touch_index !== undefined) {
+ return input_source.touch_index;
+ }
+ return -1;
+ },
},
godot_webxr_is_supported__proxy: 'sync',
@@ -132,8 +231,8 @@ const GodotWebXR = {
godot_webxr_initialize__deps: ['emscripten_webgl_get_current_context'],
godot_webxr_initialize__proxy: 'sync',
- godot_webxr_initialize__sig: 'viiiiiiiiii',
- godot_webxr_initialize: function (p_session_mode, p_required_features, p_optional_features, p_requested_reference_spaces, p_on_session_started, p_on_session_ended, p_on_session_failed, p_on_controller_changed, p_on_input_event, p_on_simple_event) {
+ godot_webxr_initialize__sig: 'viiiiiiiii',
+ godot_webxr_initialize: function (p_session_mode, p_required_features, p_optional_features, p_requested_reference_spaces, p_on_session_started, p_on_session_ended, p_on_session_failed, p_on_input_event, p_on_simple_event) {
GodotWebXR.monkeyPatchRequestAnimationFrame(true);
const session_mode = GodotRuntime.parseString(p_session_mode);
@@ -143,7 +242,6 @@ const GodotWebXR = {
const onstarted = GodotRuntime.get_func(p_on_session_started);
const onended = GodotRuntime.get_func(p_on_session_ended);
const onfailed = GodotRuntime.get_func(p_on_session_failed);
- const oncontroller = GodotRuntime.get_func(p_on_controller_changed);
const oninputevent = GodotRuntime.get_func(p_on_input_event);
const onsimpleevent = GodotRuntime.get_func(p_on_simple_event);
@@ -163,24 +261,18 @@ const GodotWebXR = {
});
session.addEventListener('inputsourceschange', function (evt) {
- let controller_changed = false;
- [evt.added, evt.removed].forEach((lst) => {
- lst.forEach((input_source) => {
- if (input_source.targetRayMode === 'tracked-pointer') {
- controller_changed = true;
- }
- });
- });
- if (controller_changed) {
- oncontroller();
- }
+ evt.added.forEach(GodotWebXR.addInputSource);
+ evt.removed.forEach(GodotWebXR.removeInputSource);
});
- ['selectstart', 'select', 'selectend', 'squeezestart', 'squeeze', 'squeezeend'].forEach((input_event) => {
+ ['selectstart', 'selectend', 'squeezestart', 'squeezeend'].forEach((input_event, index) => {
session.addEventListener(input_event, function (evt) {
- const c_str = GodotRuntime.allocString(input_event);
- oninputevent(c_str, GodotWebXR.getControllerId(evt.inputSource));
- GodotRuntime.free(c_str);
+ // Since this happens in-between normal frames, we need to
+ // grab the frame from the event in order to get poses for
+ // the input sources.
+ GodotWebXR.frame = evt.frame;
+ oninputevent(index, GodotWebXR.getInputSourceId(evt.inputSource));
+ GodotWebXR.frame = null;
});
});
@@ -195,9 +287,10 @@ const GodotWebXR = {
GodotWebXR.gl = gl;
gl.makeXRCompatible().then(function () {
- session.updateRenderState({
- baseLayer: new XRWebGLLayer(session, gl),
- });
+ GodotWebXR.gl_binding = new XRWebGLBinding(session, gl); // eslint-disable-line no-undef
+
+ // This will trigger the layer to get created.
+ GodotWebXR.getLayer();
function onReferenceSpaceSuccess(reference_space, reference_space_type) {
GodotWebXR.space = reference_space;
@@ -266,9 +359,14 @@ const GodotWebXR = {
}
GodotWebXR.session = null;
+ GodotWebXR.gl_binding = null;
+ GodotWebXR.layer = null;
GodotWebXR.space = null;
GodotWebXR.frame = null;
GodotWebXR.pose = null;
+ GodotWebXR.view_count = 1;
+ GodotWebXR.input_sources = new Array(16);
+ GodotWebXR.touches = new Array(5);
// Disable the monkey-patched window.requestAnimationFrame() and
// pause/restart the main loop to activate it on all platforms.
@@ -280,215 +378,186 @@ const GodotWebXR = {
godot_webxr_get_view_count__sig: 'i',
godot_webxr_get_view_count: function () {
if (!GodotWebXR.session || !GodotWebXR.pose) {
- return 0;
+ return 1;
}
- return GodotWebXR.pose.views.length;
+ const view_count = GodotWebXR.pose.views.length;
+ return view_count > 0 ? view_count : 1;
},
godot_webxr_get_render_target_size__proxy: 'sync',
- godot_webxr_get_render_target_size__sig: 'i',
- godot_webxr_get_render_target_size: function () {
- if (!GodotWebXR.session || !GodotWebXR.pose) {
- return 0;
+ godot_webxr_get_render_target_size__sig: 'ii',
+ godot_webxr_get_render_target_size: function (r_size) {
+ const subimage = GodotWebXR.getSubImage();
+ if (subimage === null) {
+ return false;
}
- const glLayer = GodotWebXR.session.renderState.baseLayer;
- const view = GodotWebXR.pose.views[0];
- const viewport = glLayer.getViewport(view);
+ GodotRuntime.setHeapValue(r_size + 0, subimage.viewport.width, 'i32');
+ GodotRuntime.setHeapValue(r_size + 4, subimage.viewport.height, 'i32');
- const buf = GodotRuntime.malloc(2 * 4);
- GodotRuntime.setHeapValue(buf + 0, viewport.width, 'i32');
- GodotRuntime.setHeapValue(buf + 4, viewport.height, 'i32');
- return buf;
+ return true;
},
- godot_webxr_get_transform_for_eye__proxy: 'sync',
- godot_webxr_get_transform_for_eye__sig: 'ii',
- godot_webxr_get_transform_for_eye: function (p_eye) {
+ godot_webxr_get_transform_for_view__proxy: 'sync',
+ godot_webxr_get_transform_for_view__sig: 'iii',
+ godot_webxr_get_transform_for_view: function (p_view, r_transform) {
if (!GodotWebXR.session || !GodotWebXR.pose) {
- return 0;
+ return false;
}
const views = GodotWebXR.pose.views;
let matrix;
- if (p_eye === 0) {
- matrix = GodotWebXR.pose.transform.matrix;
+ if (p_view >= 0) {
+ matrix = views[p_view].transform.matrix;
} else {
- matrix = views[p_eye - 1].transform.matrix;
- }
- const buf = GodotRuntime.malloc(16 * 4);
- for (let i = 0; i < 16; i++) {
- GodotRuntime.setHeapValue(buf + (i * 4), matrix[i], 'float');
- }
- return buf;
- },
-
- godot_webxr_get_projection_for_eye__proxy: 'sync',
- godot_webxr_get_projection_for_eye__sig: 'ii',
- godot_webxr_get_projection_for_eye: function (p_eye) {
- if (!GodotWebXR.session || !GodotWebXR.pose) {
- return 0;
+ // For -1 (or any other negative value) return the HMD transform.
+ matrix = GodotWebXR.pose.transform.matrix;
}
- const view_index = (p_eye === 2 /* ARVRInterface::EYE_RIGHT */) ? 1 : 0;
- const matrix = GodotWebXR.pose.views[view_index].projectionMatrix;
- const buf = GodotRuntime.malloc(16 * 4);
for (let i = 0; i < 16; i++) {
- GodotRuntime.setHeapValue(buf + (i * 4), matrix[i], 'float');
+ GodotRuntime.setHeapValue(r_transform + (i * 4), matrix[i], 'float');
}
- return buf;
+
+ return true;
},
- godot_webxr_commit__proxy: 'sync',
- godot_webxr_commit__sig: 'vi',
- godot_webxr_commit: function (p_texture) {
+ godot_webxr_get_projection_for_view__proxy: 'sync',
+ godot_webxr_get_projection_for_view__sig: 'iii',
+ godot_webxr_get_projection_for_view: function (p_view, r_transform) {
if (!GodotWebXR.session || !GodotWebXR.pose) {
- return;
+ return false;
}
- const glLayer = GodotWebXR.session.renderState.baseLayer;
- const views = GodotWebXR.pose.views;
- const gl = GodotWebXR.gl;
-
- const texture = GL.textures[p_texture];
-
- const orig_framebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING);
- const orig_read_framebuffer = gl.getParameter(gl.READ_FRAMEBUFFER_BINDING);
- const orig_read_buffer = gl.getParameter(gl.READ_BUFFER);
- const orig_draw_framebuffer = gl.getParameter(gl.DRAW_FRAMEBUFFER_BINDING);
-
- // Copy from Godot render target into framebuffer from WebXR.
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
- for (let i = 0; i < views.length; i++) {
- const viewport = glLayer.getViewport(views[i]);
-
- const read_fbo = gl.createFramebuffer();
- gl.bindFramebuffer(gl.READ_FRAMEBUFFER, read_fbo);
- if (views.length > 1) {
- gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, texture, 0, i);
- } else {
- gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
- }
- gl.readBuffer(gl.COLOR_ATTACHMENT0);
- gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, glLayer.framebuffer);
-
- // Flip Y upside down on destination.
- gl.blitFramebuffer(0, 0, viewport.width, viewport.height,
- viewport.x, viewport.y + viewport.height, viewport.x + viewport.width, viewport.y,
- gl.COLOR_BUFFER_BIT, gl.NEAREST);
-
- gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
- gl.deleteFramebuffer(read_fbo);
+ const matrix = GodotWebXR.pose.views[p_view].projectionMatrix;
+ for (let i = 0; i < 16; i++) {
+ GodotRuntime.setHeapValue(r_transform + (i * 4), matrix[i], 'float');
}
- // Restore state.
- gl.bindFramebuffer(gl.FRAMEBUFFER, orig_framebuffer);
- gl.bindFramebuffer(gl.READ_FRAMEBUFFER, orig_read_framebuffer);
- gl.readBuffer(orig_read_buffer);
- gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, orig_draw_framebuffer);
+ return true;
},
- godot_webxr_sample_controller_data__proxy: 'sync',
- godot_webxr_sample_controller_data__sig: 'v',
- godot_webxr_sample_controller_data: function () {
- GodotWebXR.sampleControllers();
+ godot_webxr_get_color_texture__proxy: 'sync',
+ godot_webxr_get_color_texture__sig: 'i',
+ godot_webxr_get_color_texture: function () {
+ const subimage = GodotWebXR.getSubImage();
+ if (subimage === null) {
+ return 0;
+ }
+ return GodotWebXR.getTextureId(subimage.colorTexture);
},
- godot_webxr_get_controller_count__proxy: 'sync',
- godot_webxr_get_controller_count__sig: 'i',
- godot_webxr_get_controller_count: function () {
- if (!GodotWebXR.session || !GodotWebXR.frame) {
+ godot_webxr_get_depth_texture__proxy: 'sync',
+ godot_webxr_get_depth_texture__sig: 'i',
+ godot_webxr_get_depth_texture: function () {
+ const subimage = GodotWebXR.getSubImage();
+ if (subimage === null) {
return 0;
}
- return GodotWebXR.controllers.length;
+ if (!subimage.depthStencilTexture) {
+ return 0;
+ }
+ return GodotWebXR.getTextureId(subimage.depthStencilTexture);
},
- godot_webxr_is_controller_connected__proxy: 'sync',
- godot_webxr_is_controller_connected__sig: 'ii',
- godot_webxr_is_controller_connected: function (p_controller) {
- if (!GodotWebXR.session || !GodotWebXR.frame) {
- return false;
+ godot_webxr_get_velocity_texture__proxy: 'sync',
+ godot_webxr_get_velocity_texture__sig: 'i',
+ godot_webxr_get_velocity_texture: function () {
+ const subimage = GodotWebXR.getSubImage();
+ if (subimage === null) {
+ return 0;
+ }
+ if (!subimage.motionVectorTexture) {
+ return 0;
}
- return !!GodotWebXR.controllers[p_controller];
+ return GodotWebXR.getTextureId(subimage.motionVectorTexture);
},
- godot_webxr_get_controller_transform__proxy: 'sync',
- godot_webxr_get_controller_transform__sig: 'ii',
- godot_webxr_get_controller_transform: function (p_controller) {
+ godot_webxr_update_input_source__proxy: 'sync',
+ godot_webxr_update_input_source__sig: 'iiiiiiiiiiii',
+ godot_webxr_update_input_source: function (p_input_source_id, r_target_pose, r_target_ray_mode, r_touch_index, r_has_grip_pose, r_grip_pose, r_has_standard_mapping, r_button_count, r_buttons, r_axes_count, r_axes) {
if (!GodotWebXR.session || !GodotWebXR.frame) {
return 0;
}
- const controller = GodotWebXR.controllers[p_controller];
- if (!controller) {
- return 0;
+ if (p_input_source_id < 0 || p_input_source_id >= GodotWebXR.input_sources.length || !GodotWebXR.input_sources[p_input_source_id]) {
+ return false;
}
+ const input_source = GodotWebXR.input_sources[p_input_source_id];
const frame = GodotWebXR.frame;
const space = GodotWebXR.space;
- const pose = frame.getPose(controller.targetRaySpace, space);
- if (!pose) {
+ // Target pose.
+ const target_pose = frame.getPose(input_source.targetRaySpace, space);
+ if (!target_pose) {
// This can mean that the controller lost tracking.
- return 0;
+ return false;
}
- const matrix = pose.transform.matrix;
-
- const buf = GodotRuntime.malloc(16 * 4);
+ const target_pose_matrix = target_pose.transform.matrix;
for (let i = 0; i < 16; i++) {
- GodotRuntime.setHeapValue(buf + (i * 4), matrix[i], 'float');
+ GodotRuntime.setHeapValue(r_target_pose + (i * 4), target_pose_matrix[i], 'float');
}
- return buf;
- },
- godot_webxr_get_controller_buttons__proxy: 'sync',
- godot_webxr_get_controller_buttons__sig: 'ii',
- godot_webxr_get_controller_buttons: function (p_controller) {
- if (GodotWebXR.controllers.length === 0) {
- return 0;
- }
+ // Target ray mode.
+ let target_ray_mode = 0;
+ switch (input_source.targetRayMode) {
+ case 'gaze':
+ target_ray_mode = 1;
+ break;
- const controller = GodotWebXR.controllers[p_controller];
- if (!controller || !controller.gamepad) {
- return 0;
- }
-
- const button_count = controller.gamepad.buttons.length;
+ case 'tracked-pointer':
+ target_ray_mode = 2;
+ break;
- const buf = GodotRuntime.malloc((button_count + 1) * 4);
- GodotRuntime.setHeapValue(buf, button_count, 'i32');
- for (let i = 0; i < button_count; i++) {
- GodotRuntime.setHeapValue(buf + 4 + (i * 4), controller.gamepad.buttons[i].value, 'float');
- }
- return buf;
- },
+ case 'screen':
+ target_ray_mode = 3;
+ break;
- godot_webxr_get_controller_axes__proxy: 'sync',
- godot_webxr_get_controller_axes__sig: 'ii',
- godot_webxr_get_controller_axes: function (p_controller) {
- if (GodotWebXR.controllers.length === 0) {
- return 0;
+ default:
}
-
- const controller = GodotWebXR.controllers[p_controller];
- if (!controller || !controller.gamepad) {
- return 0;
+ GodotRuntime.setHeapValue(r_target_ray_mode, target_ray_mode, 'i32');
+
+ // Touch index.
+ GodotRuntime.setHeapValue(r_touch_index, GodotWebXR.getTouchIndex(input_source), 'i32');
+
+ // Grip pose.
+ let has_grip_pose = false;
+ if (input_source.gripSpace) {
+ const grip_pose = frame.getPose(input_source.gripSpace, space);
+ if (grip_pose) {
+ const grip_pose_matrix = grip_pose.transform.matrix;
+ for (let i = 0; i < 16; i++) {
+ GodotRuntime.setHeapValue(r_grip_pose + (i * 4), grip_pose_matrix[i], 'float');
+ }
+ has_grip_pose = true;
+ }
}
+ GodotRuntime.setHeapValue(r_has_grip_pose, has_grip_pose ? 1 : 0, 'i32');
+
+ // Gamepad data (mapping, buttons and axes).
+ let has_standard_mapping = false;
+ let button_count = 0;
+ let axes_count = 0;
+ if (input_source.gamepad) {
+ if (input_source.gamepad.mapping === 'xr-standard') {
+ has_standard_mapping = true;
+ }
- const axes_count = controller.gamepad.axes.length;
+ button_count = Math.min(input_source.gamepad.buttons.length, 10);
+ for (let i = 0; i < button_count; i++) {
+ GodotRuntime.setHeapValue(r_buttons + (i * 4), input_source.gamepad.buttons[i].value, 'float');
+ }
- const buf = GodotRuntime.malloc((axes_count + 1) * 4);
- GodotRuntime.setHeapValue(buf, axes_count, 'i32');
- for (let i = 0; i < axes_count; i++) {
- let value = controller.gamepad.axes[i];
- if (i === 1 || i === 3) {
- // Invert the Y-axis on thumbsticks and trackpads, in order to
- // match OpenXR and other XR platform SDKs.
- value *= -1.0;
+ axes_count = Math.min(input_source.gamepad.axes.length, 10);
+ for (let i = 0; i < axes_count; i++) {
+ GodotRuntime.setHeapValue(r_axes + (i * 4), input_source.gamepad.axes[i], 'float');
}
- GodotRuntime.setHeapValue(buf + 4 + (i * 4), value, 'float');
}
- return buf;
+ GodotRuntime.setHeapValue(r_has_standard_mapping, has_standard_mapping ? 1 : 0, 'i32');
+ GodotRuntime.setHeapValue(r_button_count, button_count, 'i32');
+ GodotRuntime.setHeapValue(r_axes_count, axes_count, 'i32');
+
+ return true;
},
godot_webxr_get_visibility_state__proxy: 'sync',
@@ -502,8 +571,8 @@ const GodotWebXR = {
},
godot_webxr_get_bounds_geometry__proxy: 'sync',
- godot_webxr_get_bounds_geometry__sig: 'i',
- godot_webxr_get_bounds_geometry: function () {
+ godot_webxr_get_bounds_geometry__sig: 'ii',
+ godot_webxr_get_bounds_geometry: function (r_points) {
if (!GodotWebXR.space || !GodotWebXR.space.boundsGeometry) {
return 0;
}
@@ -513,7 +582,7 @@ const GodotWebXR = {
return 0;
}
- const buf = GodotRuntime.malloc(((point_count * 3) + 1) * 4);
+ const buf = GodotRuntime.malloc(point_count * 3 * 4);
GodotRuntime.setHeapValue(buf, point_count, 'i32');
for (let i = 0; i < point_count; i++) {
const point = GodotWebXR.space.boundsGeometry[i];
@@ -521,8 +590,9 @@ const GodotWebXR = {
GodotRuntime.setHeapValue(buf + ((i * 3) + 2) * 4, point.y, 'float');
GodotRuntime.setHeapValue(buf + ((i * 3) + 3) * 4, point.z, 'float');
}
+ GodotRuntime.setHeapValue(r_points, buf, 'i32');
- return buf;
+ return point_count;
},
};
diff --git a/modules/webxr/native/webxr.externs.js b/modules/webxr/native/webxr.externs.js
index 9ea105aa93..4b88820b19 100644
--- a/modules/webxr/native/webxr.externs.js
+++ b/modules/webxr/native/webxr.externs.js
@@ -1,3 +1,7 @@
+/*
+ * WebXR Device API
+ */
+
/**
* @type {XR}
*/
@@ -497,3 +501,681 @@ XRPose.prototype.transform;
* @type {boolean}
*/
XRPose.prototype.emulatedPosition;
+
+/*
+ * WebXR Layers API Level 1
+ */
+
+/**
+ * @constructor XRLayer
+ */
+function XRLayer() {}
+
+/**
+ * @constructor XRLayerEventInit
+ */
+function XRLayerEventInit() {}
+
+/**
+ * @type {XRLayer}
+ */
+XRLayerEventInit.prototype.layer;
+
+/**
+ * @constructor XRLayerEvent
+ *
+ * @param {string} type
+ * @param {XRLayerEventInit} init
+ */
+function XRLayerEvent(type, init) {};
+
+/**
+ * @type {XRLayer}
+ */
+XRLayerEvent.prototype.layer;
+
+/**
+ * @constructor XRCompositionLayer
+ * @extends {XRLayer}
+ */
+function XRCompositionLayer() {};
+
+/**
+ * @type {string}
+ */
+XRCompositionLayer.prototype.layout;
+
+/**
+ * @type {boolean}
+ */
+XRCompositionLayer.prototype.blendTextureAberrationCorrection;
+
+/**
+ * @type {?boolean}
+ */
+XRCompositionLayer.prototype.chromaticAberrationCorrection;
+
+/**
+ * @type {boolean}
+ */
+XRCompositionLayer.prototype.forceMonoPresentation;
+
+/**
+ * @type {number}
+ */
+XRCompositionLayer.prototype.opacity;
+
+/**
+ * @type {number}
+ */
+XRCompositionLayer.prototype.mipLevels;
+
+/**
+ * @type {boolean}
+ */
+XRCompositionLayer.prototype.needsRedraw;
+
+/**
+ * @return {void}
+ */
+XRCompositionLayer.prototype.destroy = function () {};
+
+/**
+ * @constructor XRProjectionLayer
+ * @extends {XRCompositionLayer}
+ */
+function XRProjectionLayer() {}
+
+/**
+ * @type {number}
+ */
+XRProjectionLayer.prototype.textureWidth;
+
+/**
+ * @type {number}
+ */
+XRProjectionLayer.prototype.textureHeight;
+
+/**
+ * @type {number}
+ */
+XRProjectionLayer.prototype.textureArrayLength;
+
+/**
+ * @type {boolean}
+ */
+XRProjectionLayer.prototype.ignoreDepthValues;
+
+/**
+ * @type {?number}
+ */
+XRProjectionLayer.prototype.fixedFoveation;
+
+/**
+ * @type {XRRigidTransform}
+ */
+XRProjectionLayer.prototype.deltaPose;
+
+/**
+ * @constructor XRQuadLayer
+ * @extends {XRCompositionLayer}
+ */
+function XRQuadLayer() {}
+
+/**
+ * @type {XRSpace}
+ */
+XRQuadLayer.prototype.space;
+
+/**
+ * @type {XRRigidTransform}
+ */
+XRQuadLayer.prototype.transform;
+
+/**
+ * @type {number}
+ */
+XRQuadLayer.prototype.width;
+
+/**
+ * @type {number}
+ */
+XRQuadLayer.prototype.height;
+
+/**
+ * @type {?function (XRLayerEvent)}
+ */
+XRQuadLayer.prototype.onredraw;
+
+/**
+ * @constructor XRCylinderLayer
+ * @extends {XRCompositionLayer}
+ */
+function XRCylinderLayer() {}
+
+/**
+ * @type {XRSpace}
+ */
+XRCylinderLayer.prototype.space;
+
+/**
+ * @type {XRRigidTransform}
+ */
+XRCylinderLayer.prototype.transform;
+
+/**
+ * @type {number}
+ */
+XRCylinderLayer.prototype.radius;
+
+/**
+ * @type {number}
+ */
+XRCylinderLayer.prototype.centralAngle;
+
+/**
+ * @type {number}
+ */
+XRCylinderLayer.prototype.aspectRatio;
+
+/**
+ * @type {?function (XRLayerEvent)}
+ */
+XRCylinderLayer.prototype.onredraw;
+
+/**
+ * @constructor XREquirectLayer
+ * @extends {XRCompositionLayer}
+ */
+function XREquirectLayer() {}
+
+/**
+ * @type {XRSpace}
+ */
+XREquirectLayer.prototype.space;
+
+/**
+ * @type {XRRigidTransform}
+ */
+XREquirectLayer.prototype.transform;
+
+/**
+ * @type {number}
+ */
+XREquirectLayer.prototype.radius;
+
+/**
+ * @type {number}
+ */
+XREquirectLayer.prototype.centralHorizontalAngle;
+
+/**
+ * @type {number}
+ */
+XREquirectLayer.prototype.upperVerticalAngle;
+
+/**
+ * @type {number}
+ */
+XREquirectLayer.prototype.lowerVerticalAngle;
+
+/**
+ * @type {?function (XRLayerEvent)}
+ */
+XREquirectLayer.prototype.onredraw;
+
+/**
+ * @constructor XRCubeLayer
+ * @extends {XRCompositionLayer}
+ */
+function XRCubeLayer() {}
+
+/**
+ * @type {XRSpace}
+ */
+XRCubeLayer.prototype.space;
+
+/**
+ * @type {DOMPointReadOnly}
+ */
+XRCubeLayer.prototype.orientation;
+
+/**
+ * @type {?function (XRLayerEvent)}
+ */
+XRCubeLayer.prototype.onredraw;
+
+/**
+ * @constructor XRSubImage
+ */
+function XRSubImage() {}
+
+/**
+ * @type {XRViewport}
+ */
+XRSubImage.prototype.viewport;
+
+/**
+ * @constructor XRWebGLSubImage
+ * @extends {XRSubImage}
+ */
+function XRWebGLSubImage () {}
+
+/**
+ * @type {WebGLTexture}
+ */
+XRWebGLSubImage.prototype.colorTexture;
+
+/**
+ * @type {?WebGLTexture}
+ */
+XRWebGLSubImage.prototype.depthStencilTexture;
+
+/**
+ * @type {?WebGLTexture}
+ */
+XRWebGLSubImage.prototype.motionVectorTexture;
+
+/**
+ * @type {?number}
+ */
+XRWebGLSubImage.prototype.imageIndex;
+
+/**
+ * @type {number}
+ */
+XRWebGLSubImage.prototype.colorTextureWidth;
+
+/**
+ * @type {number}
+ */
+XRWebGLSubImage.prototype.colorTextureHeight;
+
+/**
+ * @type {?number}
+ */
+XRWebGLSubImage.prototype.depthStencilTextureWidth;
+
+/**
+ * @type {?number}
+ */
+XRWebGLSubImage.prototype.depthStencilTextureHeight;
+
+/**
+ * @type {?number}
+ */
+
+XRWebGLSubImage.prototype.motionVectorTextureWidth;
+
+/**
+ * @type {?number}
+ */
+XRWebGLSubImage.prototype.motionVectorTextureHeight;
+
+/**
+ * @constructor XRProjectionLayerInit
+ */
+function XRProjectionLayerInit() {}
+
+/**
+ * @type {string}
+ */
+XRProjectionLayerInit.prototype.textureType;
+
+/**
+ * @type {number}
+ */
+XRProjectionLayerInit.prototype.colorFormat;
+
+/**
+ * @type {number}
+ */
+XRProjectionLayerInit.prototype.depthFormat;
+
+/**
+ * @type {number}
+ */
+XRProjectionLayerInit.prototype.scaleFactor;
+
+/**
+ * @constructor XRLayerInit
+ */
+function XRLayerInit() {}
+
+/**
+ * @type {XRSpace}
+ */
+XRLayerInit.prototype.space;
+
+/**
+ * @type {number}
+ */
+XRLayerInit.prototype.colorFormat;
+
+/**
+ * @type {number}
+ */
+XRLayerInit.prototype.depthFormat;
+
+/**
+ * @type {number}
+ */
+XRLayerInit.prototype.mipLevels;
+
+/**
+ * @type {number}
+ */
+XRLayerInit.prototype.viewPixelWidth;
+
+/**
+ * @type {number}
+ */
+XRLayerInit.prototype.viewPixelHeight;
+
+/**
+ * @type {string}
+ */
+XRLayerInit.prototype.layout;
+
+/**
+ * @type {boolean}
+ */
+XRLayerInit.prototype.isStatic;
+
+/**
+ * @constructor XRQuadLayerInit
+ * @extends {XRLayerInit}
+ */
+function XRQuadLayerInit() {}
+
+/**
+ * @type {string}
+ */
+XRQuadLayerInit.prototype.textureType;
+
+/**
+ * @type {?XRRigidTransform}
+ */
+XRQuadLayerInit.prototype.transform;
+
+/**
+ * @type {number}
+ */
+XRQuadLayerInit.prototype.width;
+
+/**
+ * @type {number}
+ */
+XRQuadLayerInit.prototype.height;
+
+/**
+ * @constructor XRCylinderLayerInit
+ * @extends {XRLayerInit}
+ */
+function XRCylinderLayerInit() {}
+
+/**
+ * @type {string}
+ */
+XRCylinderLayerInit.prototype.textureType;
+
+/**
+ * @type {?XRRigidTransform}
+ */
+XRCylinderLayerInit.prototype.transform;
+
+/**
+ * @type {number}
+ */
+XRCylinderLayerInit.prototype.radius;
+
+/**
+ * @type {number}
+ */
+XRCylinderLayerInit.prototype.centralAngle;
+
+/**
+ * @type {number}
+ */
+XRCylinderLayerInit.prototype.aspectRatio;
+
+/**
+ * @constructor XREquirectLayerInit
+ * @extends {XRLayerInit}
+ */
+function XREquirectLayerInit() {}
+
+/**
+ * @type {string}
+ */
+XREquirectLayerInit.prototype.textureType;
+
+/**
+ * @type {?XRRigidTransform}
+ */
+XREquirectLayerInit.prototype.transform;
+
+/**
+ * @type {number}
+ */
+XREquirectLayerInit.prototype.radius;
+
+/**
+ * @type {number}
+ */
+XREquirectLayerInit.prototype.centralHorizontalAngle;
+
+/**
+ * @type {number}
+ */
+XREquirectLayerInit.prototype.upperVerticalAngle;
+
+/**
+ * @type {number}
+ */
+XREquirectLayerInit.prototype.lowerVerticalAngle;
+
+/**
+ * @constructor XRCubeLayerInit
+ * @extends {XRLayerInit}
+ */
+function XRCubeLayerInit() {}
+
+/**
+ * @type {DOMPointReadOnly}
+ */
+XRCubeLayerInit.prototype.orientation;
+
+/**
+ * @constructor XRWebGLBinding
+ *
+ * @param {XRSession} session
+ * @param {WebGLRenderContext|WebGL2RenderingContext} context
+ */
+function XRWebGLBinding(session, context) {}
+
+/**
+ * @type {number}
+ */
+XRWebGLBinding.prototype.nativeProjectionScaleFactor;
+
+/**
+ * @type {number}
+ */
+XRWebGLBinding.prototype.usesDepthValues;
+
+/**
+ * @param {XRProjectionLayerInit} init
+ * @return {XRProjectionLayer}
+ */
+XRWebGLBinding.prototype.createProjectionLayer = function (init) {};
+
+/**
+ * @param {XRQuadLayerInit} init
+ * @return {XRQuadLayer}
+ */
+XRWebGLBinding.prototype.createQuadLayer = function (init) {};
+
+/**
+ * @param {XRCylinderLayerInit} init
+ * @return {XRCylinderLayer}
+ */
+XRWebGLBinding.prototype.createCylinderLayer = function (init) {};
+
+/**
+ * @param {XREquirectLayerInit} init
+ * @return {XREquirectLayer}
+ */
+XRWebGLBinding.prototype.createEquirectLayer = function (init) {};
+
+/**
+ * @param {XRCubeLayerInit} init
+ * @return {XRCubeLayer}
+ */
+XRWebGLBinding.prototype.createCubeLayer = function (init) {};
+
+/**
+ * @param {XRCompositionLayer} layer
+ * @param {XRFrame} frame
+ * @param {string} eye
+ * @return {XRWebGLSubImage}
+ */
+XRWebGLBinding.prototype.getSubImage = function (layer, frame, eye) {};
+
+/**
+ * @param {XRProjectionLayer} layer
+ * @param {XRView} view
+ * @return {XRWebGLSubImage}
+ */
+XRWebGLBinding.prototype.getViewSubImage = function (layer, view) {};
+
+/**
+ * @constructor XRMediaLayerInit
+ */
+function XRMediaLayerInit() {}
+
+/**
+ * @type {XRSpace}
+ */
+XRMediaLayerInit.prototype.space;
+
+/**
+ * @type {string}
+ */
+XRMediaLayerInit.prototype.layout;
+
+/**
+ * @type {boolean}
+ */
+XRMediaLayerInit.prototype.invertStereo;
+
+/**
+ * @constructor XRMediaQuadLayerInit
+ * @extends {XRMediaLayerInit}
+ */
+function XRMediaQuadLayerInit() {}
+
+/**
+ * @type {XRRigidTransform}
+ */
+XRMediaQuadLayerInit.prototype.transform;
+
+/**
+ * @type {number}
+ */
+XRMediaQuadLayerInit.prototype.width;
+
+/**
+ * @type {number}
+ */
+XRMediaQuadLayerInit.prototype.height;
+
+/**
+ * @constructor XRMediaCylinderLayerInit
+ * @extends {XRMediaLayerInit}
+ */
+function XRMediaCylinderLayerInit() {}
+
+/**
+ * @type {XRRigidTransform}
+ */
+XRMediaCylinderLayerInit.prototype.transform;
+
+/**
+ * @type {number}
+ */
+XRMediaCylinderLayerInit.prototype.radius;
+
+/**
+ * @type {number}
+ */
+XRMediaCylinderLayerInit.prototype.centralAngle;
+
+/**
+ * @type {?number}
+ */
+XRMediaCylinderLayerInit.prototype.aspectRatio;
+
+/**
+ * @constructor XRMediaEquirectLayerInit
+ * @extends {XRMediaLayerInit}
+ */
+function XRMediaEquirectLayerInit() {}
+
+/**
+ * @type {XRRigidTransform}
+ */
+XRMediaEquirectLayerInit.prototype.transform;
+
+/**
+ * @type {number}
+ */
+XRMediaEquirectLayerInit.prototype.radius;
+
+/**
+ * @type {number}
+ */
+XRMediaEquirectLayerInit.prototype.centralHorizontalAngle;
+
+/**
+ * @type {number}
+ */
+XRMediaEquirectLayerInit.prototype.upperVerticalAngle;
+
+/**
+ * @type {number}
+ */
+XRMediaEquirectLayerInit.prototype.lowerVerticalAngle;
+
+/**
+ * @constructor XRMediaBinding
+ *
+ * @param {XRSession} session
+ */
+function XRMediaBinding(session) {}
+
+/**
+ * @param {HTMLVideoElement} video
+ * @param {XRMediaQuadLayerInit} init
+ * @return {XRQuadLayer}
+ */
+XRMediaBinding.prototype.createQuadLayer = function(video, init) {};
+
+/**
+ * @param {HTMLVideoElement} video
+ * @param {XRMediaCylinderLayerInit} init
+ * @return {XRCylinderLayer}
+ */
+XRMediaBinding.prototype.createCylinderLayer = function(video, init) {};
+
+/**
+ * @param {HTMLVideoElement} video
+ * @param {XRMediaEquirectLayerInit} init
+ * @return {XREquirectLayer}
+ */
+XRMediaBinding.prototype.createEquirectLayer = function(video, init) {};
+
+/**
+ * @type {Array<XRLayer>}
+ */
+XRRenderState.prototype.layers;
diff --git a/modules/webxr/webxr_interface.cpp b/modules/webxr/webxr_interface.cpp
index b0ad53523a..c0580df172 100644
--- a/modules/webxr/webxr_interface.cpp
+++ b/modules/webxr/webxr_interface.cpp
@@ -42,9 +42,10 @@ void WebXRInterface::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_reference_space_type"), &WebXRInterface::get_reference_space_type);
ClassDB::bind_method(D_METHOD("set_requested_reference_space_types", "requested_reference_space_types"), &WebXRInterface::set_requested_reference_space_types);
ClassDB::bind_method(D_METHOD("get_requested_reference_space_types"), &WebXRInterface::get_requested_reference_space_types);
- ClassDB::bind_method(D_METHOD("get_controller", "controller_id"), &WebXRInterface::get_controller);
+ ClassDB::bind_method(D_METHOD("is_input_source_active", "input_source_id"), &WebXRInterface::is_input_source_active);
+ ClassDB::bind_method(D_METHOD("get_input_source_tracker", "input_source_id"), &WebXRInterface::get_input_source_tracker);
+ ClassDB::bind_method(D_METHOD("get_input_source_target_ray_mode", "input_source_id"), &WebXRInterface::get_input_source_target_ray_mode);
ClassDB::bind_method(D_METHOD("get_visibility_state"), &WebXRInterface::get_visibility_state);
- ClassDB::bind_method(D_METHOD("get_bounds_geometry"), &WebXRInterface::get_bounds_geometry);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "session_mode", PROPERTY_HINT_NONE), "set_session_mode", "get_session_mode");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "required_features", PROPERTY_HINT_NONE), "set_required_features", "get_required_features");
@@ -52,20 +53,24 @@ void WebXRInterface::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "requested_reference_space_types", PROPERTY_HINT_NONE), "set_requested_reference_space_types", "get_requested_reference_space_types");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "reference_space_type", PROPERTY_HINT_NONE), "", "get_reference_space_type");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "visibility_state", PROPERTY_HINT_NONE), "", "get_visibility_state");
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "bounds_geometry", PROPERTY_HINT_NONE), "", "get_bounds_geometry");
ADD_SIGNAL(MethodInfo("session_supported", PropertyInfo(Variant::STRING, "session_mode"), PropertyInfo(Variant::BOOL, "supported")));
ADD_SIGNAL(MethodInfo("session_started"));
ADD_SIGNAL(MethodInfo("session_ended"));
ADD_SIGNAL(MethodInfo("session_failed", PropertyInfo(Variant::STRING, "message")));
- ADD_SIGNAL(MethodInfo("selectstart", PropertyInfo(Variant::INT, "controller_id")));
- ADD_SIGNAL(MethodInfo("select", PropertyInfo(Variant::INT, "controller_id")));
- ADD_SIGNAL(MethodInfo("selectend", PropertyInfo(Variant::INT, "controller_id")));
- ADD_SIGNAL(MethodInfo("squeezestart", PropertyInfo(Variant::INT, "controller_id")));
- ADD_SIGNAL(MethodInfo("squeeze", PropertyInfo(Variant::INT, "controller_id")));
- ADD_SIGNAL(MethodInfo("squeezeend", PropertyInfo(Variant::INT, "controller_id")));
+ ADD_SIGNAL(MethodInfo("selectstart", PropertyInfo(Variant::INT, "input_source_id")));
+ ADD_SIGNAL(MethodInfo("select", PropertyInfo(Variant::INT, "input_source_id")));
+ ADD_SIGNAL(MethodInfo("selectend", PropertyInfo(Variant::INT, "input_source_id")));
+ ADD_SIGNAL(MethodInfo("squeezestart", PropertyInfo(Variant::INT, "input_source_id")));
+ ADD_SIGNAL(MethodInfo("squeeze", PropertyInfo(Variant::INT, "input_source_id")));
+ ADD_SIGNAL(MethodInfo("squeezeend", PropertyInfo(Variant::INT, "input_source_id")));
ADD_SIGNAL(MethodInfo("visibility_state_changed"));
ADD_SIGNAL(MethodInfo("reference_space_reset"));
+
+ BIND_ENUM_CONSTANT(TARGET_RAY_MODE_UNKNOWN);
+ BIND_ENUM_CONSTANT(TARGET_RAY_MODE_GAZE);
+ BIND_ENUM_CONSTANT(TARGET_RAY_MODE_TRACKED_POINTER);
+ BIND_ENUM_CONSTANT(TARGET_RAY_MODE_SCREEN);
}
diff --git a/modules/webxr/webxr_interface.h b/modules/webxr/webxr_interface.h
index 801643bfa6..1afeb5bab0 100644
--- a/modules/webxr/webxr_interface.h
+++ b/modules/webxr/webxr_interface.h
@@ -45,6 +45,13 @@ protected:
static void _bind_methods();
public:
+ enum TargetRayMode {
+ TARGET_RAY_MODE_UNKNOWN,
+ TARGET_RAY_MODE_GAZE,
+ TARGET_RAY_MODE_TRACKED_POINTER,
+ TARGET_RAY_MODE_SCREEN,
+ };
+
virtual void is_session_supported(const String &p_session_mode) = 0;
virtual void set_session_mode(String p_session_mode) = 0;
virtual String get_session_mode() const = 0;
@@ -55,9 +62,12 @@ public:
virtual void set_requested_reference_space_types(String p_requested_reference_space_types) = 0;
virtual String get_requested_reference_space_types() const = 0;
virtual String get_reference_space_type() const = 0;
- virtual Ref<XRPositionalTracker> get_controller(int p_controller_id) const = 0;
+ virtual bool is_input_source_active(int p_input_source_id) const = 0;
+ virtual Ref<XRPositionalTracker> get_input_source_tracker(int p_input_source_id) const = 0;
+ virtual TargetRayMode get_input_source_target_ray_mode(int p_input_source_id) const = 0;
virtual String get_visibility_state() const = 0;
- virtual PackedVector3Array get_bounds_geometry() const = 0;
};
+VARIANT_ENUM_CAST(WebXRInterface::TargetRayMode);
+
#endif // WEBXR_INTERFACE_H
diff --git a/modules/webxr/webxr_interface_js.cpp b/modules/webxr/webxr_interface_js.cpp
index f6ed9f027e..265f6626a7 100644
--- a/modules/webxr/webxr_interface_js.cpp
+++ b/modules/webxr/webxr_interface_js.cpp
@@ -37,6 +37,8 @@
#include "drivers/gles3/storage/texture_storage.h"
#include "emscripten.h"
#include "godot_webxr.h"
+#include "scene/main/scene_tree.h"
+#include "scene/main/window.h"
#include "servers/rendering/renderer_compositor.h"
#include "servers/rendering/rendering_server_globals.h"
@@ -89,25 +91,14 @@ void _emwebxr_on_session_failed(char *p_message) {
interface->emit_signal(SNAME("session_failed"), message);
}
-void _emwebxr_on_controller_changed() {
+extern "C" EMSCRIPTEN_KEEPALIVE void _emwebxr_on_input_event(int p_event_type, int p_input_source_id) {
XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL(xr_server);
Ref<XRInterface> interface = xr_server->find_interface("WebXR");
ERR_FAIL_COND(interface.is_null());
- static_cast<WebXRInterfaceJS *>(interface.ptr())->_on_controller_changed();
-}
-
-extern "C" EMSCRIPTEN_KEEPALIVE void _emwebxr_on_input_event(char *p_signal_name, int p_input_source) {
- XRServer *xr_server = XRServer::get_singleton();
- ERR_FAIL_NULL(xr_server);
-
- Ref<XRInterface> interface = xr_server->find_interface("WebXR");
- ERR_FAIL_COND(interface.is_null());
-
- StringName signal_name = StringName(p_signal_name);
- interface->emit_signal(signal_name, p_input_source + 1);
+ ((WebXRInterfaceJS *)interface.ptr())->_on_input_event(p_event_type, p_input_source_id);
}
extern "C" EMSCRIPTEN_KEEPALIVE void _emwebxr_on_simple_event(char *p_signal_name) {
@@ -165,16 +156,22 @@ String WebXRInterfaceJS::get_reference_space_type() const {
return reference_space_type;
}
-Ref<XRPositionalTracker> WebXRInterfaceJS::get_controller(int p_controller_id) const {
- XRServer *xr_server = XRServer::get_singleton();
- ERR_FAIL_NULL_V(xr_server, Ref<XRPositionalTracker>());
+bool WebXRInterfaceJS::is_input_source_active(int p_input_source_id) const {
+ ERR_FAIL_INDEX_V(p_input_source_id, input_source_count, false);
+ return input_sources[p_input_source_id].active;
+}
- // TODO support more then two controllers
- if (p_controller_id >= 0 && p_controller_id < 2) {
- return controllers[p_controller_id];
- };
+Ref<XRPositionalTracker> WebXRInterfaceJS::get_input_source_tracker(int p_input_source_id) const {
+ ERR_FAIL_INDEX_V(p_input_source_id, input_source_count, Ref<XRPositionalTracker>());
+ return input_sources[p_input_source_id].tracker;
+}
- return Ref<XRPositionalTracker>();
+WebXRInterface::TargetRayMode WebXRInterfaceJS::get_input_source_target_ray_mode(int p_input_source_id) const {
+ ERR_FAIL_INDEX_V(p_input_source_id, input_source_count, WebXRInterface::TARGET_RAY_MODE_UNKNOWN);
+ if (!input_sources[p_input_source_id].active) {
+ return WebXRInterface::TARGET_RAY_MODE_UNKNOWN;
+ }
+ return input_sources[p_input_source_id].target_ray_mode;
}
String WebXRInterfaceJS::get_visibility_state() const {
@@ -188,17 +185,18 @@ String WebXRInterfaceJS::get_visibility_state() const {
return String();
}
-PackedVector3Array WebXRInterfaceJS::get_bounds_geometry() const {
+PackedVector3Array WebXRInterfaceJS::get_play_area() const {
PackedVector3Array ret;
- int *js_bounds = godot_webxr_get_bounds_geometry();
- if (js_bounds) {
- ret.resize(js_bounds[0]);
- for (int i = 0; i < js_bounds[0]; i++) {
- float *js_vector3 = ((float *)js_bounds) + (i * 3) + 1;
+ float *points;
+ int point_count = godot_webxr_get_bounds_geometry(&points);
+ if (point_count > 0) {
+ ret.resize(point_count);
+ for (int i = 0; i < point_count; i++) {
+ float *js_vector3 = points + (i * 3);
ret.set(i, Vector3(js_vector3[0], js_vector3[1], js_vector3[2]));
}
- free(js_bounds);
+ free(points);
}
return ret;
@@ -209,7 +207,7 @@ StringName WebXRInterfaceJS::get_name() const {
};
uint32_t WebXRInterfaceJS::get_capabilities() const {
- return XRInterface::XR_STEREO | XRInterface::XR_MONO;
+ return XRInterface::XR_STEREO | XRInterface::XR_MONO | XRInterface::XR_VR | XRInterface::XR_AR;
};
uint32_t WebXRInterfaceJS::get_view_count() {
@@ -261,7 +259,6 @@ bool WebXRInterfaceJS::initialize() {
&_emwebxr_on_session_started,
&_emwebxr_on_session_ended,
&_emwebxr_on_session_failed,
- &_emwebxr_on_controller_changed,
&_emwebxr_on_input_event,
&_emwebxr_on_simple_event);
};
@@ -287,6 +284,18 @@ void WebXRInterfaceJS::uninitialize() {
godot_webxr_uninitialize();
+ GLES3::TextureStorage *texture_storage = dynamic_cast<GLES3::TextureStorage *>(RSG::texture_storage);
+ if (texture_storage != nullptr) {
+ for (KeyValue<unsigned int, RID> &E : texture_cache) {
+ // Forcibly mark as not part of a render target so we can free it.
+ GLES3::Texture *texture = texture_storage->get_texture(E.value);
+ texture->is_render_target = false;
+
+ texture_storage->texture_free(E.value);
+ }
+ }
+
+ texture_cache.clear();
reference_space_type = "";
initialized = false;
};
@@ -316,27 +325,26 @@ Size2 WebXRInterfaceJS::get_render_target_size() {
return render_targetsize;
}
- int *js_size = godot_webxr_get_render_target_size();
- if (!initialized || js_size == nullptr) {
- // As a temporary default (until WebXR is fully initialized), use half the window size.
- Size2 temp = DisplayServer::get_singleton()->window_get_size();
- temp.width /= 2.0;
- return temp;
- }
+ int js_size[2];
+ bool has_size = godot_webxr_get_render_target_size(js_size);
- render_targetsize.width = js_size[0];
- render_targetsize.height = js_size[1];
+ if (!initialized || !has_size) {
+ // As a temporary default (until WebXR is fully initialized), use the
+ // window size.
+ return DisplayServer::get_singleton()->window_get_size();
+ }
- free(js_size);
+ render_targetsize.width = (float)js_size[0];
+ render_targetsize.height = (float)js_size[1];
return render_targetsize;
};
Transform3D WebXRInterfaceJS::get_camera_transform() {
- Transform3D transform_for_eye;
+ Transform3D camera_transform;
XRServer *xr_server = XRServer::get_singleton();
- ERR_FAIL_NULL_V(xr_server, transform_for_eye);
+ ERR_FAIL_NULL_V(xr_server, camera_transform);
if (initialized) {
float world_scale = xr_server->get_world_scale();
@@ -345,181 +353,382 @@ Transform3D WebXRInterfaceJS::get_camera_transform() {
Transform3D _head_transform = head_transform;
_head_transform.origin *= world_scale;
- transform_for_eye = (xr_server->get_reference_frame()) * _head_transform;
+ camera_transform = (xr_server->get_reference_frame()) * _head_transform;
}
- return transform_for_eye;
+ return camera_transform;
};
Transform3D WebXRInterfaceJS::get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) {
- Transform3D transform_for_eye;
-
XRServer *xr_server = XRServer::get_singleton();
- ERR_FAIL_NULL_V(xr_server, transform_for_eye);
+ ERR_FAIL_NULL_V(xr_server, p_cam_transform);
+ ERR_FAIL_COND_V(!initialized, p_cam_transform);
- float *js_matrix = godot_webxr_get_transform_for_eye(p_view + 1);
- if (!initialized || js_matrix == nullptr) {
- transform_for_eye = p_cam_transform;
- return transform_for_eye;
+ float js_matrix[16];
+ bool has_transform = godot_webxr_get_transform_for_view(p_view, js_matrix);
+ if (!has_transform) {
+ return p_cam_transform;
}
- transform_for_eye = _js_matrix_to_transform(js_matrix);
- free(js_matrix);
+ Transform3D transform_for_view = _js_matrix_to_transform(js_matrix);
float world_scale = xr_server->get_world_scale();
// Scale only the center point of our eye transform, so we don't scale the
// distance between the eyes.
Transform3D _head_transform = head_transform;
- transform_for_eye.origin -= _head_transform.origin;
+ transform_for_view.origin -= _head_transform.origin;
_head_transform.origin *= world_scale;
- transform_for_eye.origin += _head_transform.origin;
+ transform_for_view.origin += _head_transform.origin;
- return p_cam_transform * xr_server->get_reference_frame() * transform_for_eye;
+ return p_cam_transform * xr_server->get_reference_frame() * transform_for_view;
};
Projection WebXRInterfaceJS::get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
- Projection eye;
+ Projection view;
+
+ ERR_FAIL_COND_V(!initialized, view);
- float *js_matrix = godot_webxr_get_projection_for_eye(p_view + 1);
- if (!initialized || js_matrix == nullptr) {
- return eye;
+ float js_matrix[16];
+ bool has_projection = godot_webxr_get_projection_for_view(p_view, js_matrix);
+ if (!has_projection) {
+ return view;
}
int k = 0;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
- eye.columns[i][j] = js_matrix[k++];
+ view.columns[i][j] = js_matrix[k++];
}
}
- free(js_matrix);
-
// Copied from godot_oculus_mobile's ovr_mobile_session.cpp
- eye.columns[2][2] = -(p_z_far + p_z_near) / (p_z_far - p_z_near);
- eye.columns[3][2] = -(2.0f * p_z_far * p_z_near) / (p_z_far - p_z_near);
+ view.columns[2][2] = -(p_z_far + p_z_near) / (p_z_far - p_z_near);
+ view.columns[3][2] = -(2.0f * p_z_far * p_z_near) / (p_z_far - p_z_near);
- return eye;
+ return view;
+}
+
+bool WebXRInterfaceJS::pre_draw_viewport(RID p_render_target) {
+ GLES3::TextureStorage *texture_storage = dynamic_cast<GLES3::TextureStorage *>(RSG::texture_storage);
+ if (texture_storage == nullptr) {
+ return false;
+ }
+
+ GLES3::RenderTarget *rt = texture_storage->get_render_target(p_render_target);
+ if (rt == nullptr) {
+ return false;
+ }
+
+ // Cache the resources so we don't have to get them from JS twice.
+ color_texture = _get_color_texture();
+ depth_texture = _get_depth_texture();
+
+ // Per the WebXR spec, it returns "opaque textures" to us, which may be the
+ // same WebGLTexture object (which would be the same GLuint in C++) but
+ // represent a different underlying resource (probably the next texture in
+ // the XR device's swap chain). In order to render to this texture, we need
+ // to re-attach it to the FBO, otherwise we get an "incomplete FBO" error.
+ //
+ // See: https://immersive-web.github.io/layers/#xropaquetextures
+ //
+ // This is why we're doing this sort of silly check: if the color and depth
+ // textures are the same this frame as last frame, we need to attach them
+ // again, despite the fact that the GLuint for them hasn't changed.
+ if (rt->overridden.is_overridden && rt->overridden.color == color_texture && rt->overridden.depth == depth_texture) {
+ GLES3::Config *config = GLES3::Config::get_singleton();
+ bool use_multiview = rt->view_count > 1 && config->multiview_supported;
+
+ glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo);
+ if (use_multiview) {
+ glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rt->color, 0, 0, rt->view_count);
+ glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, rt->depth, 0, 0, rt->view_count);
+ } else {
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rt->depth, 0);
+ }
+ glBindFramebuffer(GL_FRAMEBUFFER, texture_storage->system_fbo);
+ }
+
+ return true;
}
Vector<BlitToScreen> WebXRInterfaceJS::post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) {
Vector<BlitToScreen> blit_to_screen;
- if (!initialized) {
- return blit_to_screen;
+ // We don't need to do anything here.
+
+ return blit_to_screen;
+};
+
+RID WebXRInterfaceJS::_get_color_texture() {
+ unsigned int texture_id = godot_webxr_get_color_texture();
+ if (texture_id == 0) {
+ return RID();
+ }
+
+ return _get_texture(texture_id);
+}
+
+RID WebXRInterfaceJS::_get_depth_texture() {
+ unsigned int texture_id = godot_webxr_get_depth_texture();
+ if (texture_id == 0) {
+ return RID();
+ }
+
+ return _get_texture(texture_id);
+}
+
+RID WebXRInterfaceJS::_get_texture(unsigned int p_texture_id) {
+ RBMap<unsigned int, RID>::Element *cache = texture_cache.find(p_texture_id);
+ if (cache != nullptr) {
+ return cache->get();
}
GLES3::TextureStorage *texture_storage = dynamic_cast<GLES3::TextureStorage *>(RSG::texture_storage);
- if (!texture_storage) {
- return blit_to_screen;
+ if (texture_storage == nullptr) {
+ return RID();
}
- GLES3::RenderTarget *rt = texture_storage->get_render_target(p_render_target);
+ uint32_t view_count = godot_webxr_get_view_count();
+ Size2 texture_size = get_render_target_size();
- godot_webxr_commit(rt->color);
+ RID texture = texture_storage->texture_create_external(
+ view_count == 1 ? GLES3::Texture::TYPE_2D : GLES3::Texture::TYPE_LAYERED,
+ Image::FORMAT_RGBA8,
+ p_texture_id,
+ (int)texture_size.width,
+ (int)texture_size.height,
+ 1,
+ view_count);
- return blit_to_screen;
-};
+ texture_cache.insert(p_texture_id, texture);
+
+ return texture;
+}
+
+RID WebXRInterfaceJS::get_color_texture() {
+ return color_texture;
+}
+
+RID WebXRInterfaceJS::get_depth_texture() {
+ return depth_texture;
+}
+
+RID WebXRInterfaceJS::get_velocity_texture() {
+ unsigned int texture_id = godot_webxr_get_velocity_texture();
+ if (texture_id == 0) {
+ return RID();
+ }
+
+ return _get_texture(texture_id);
+}
void WebXRInterfaceJS::process() {
if (initialized) {
// Get the "head" position.
- float *js_matrix = godot_webxr_get_transform_for_eye(0);
- if (js_matrix != nullptr) {
+ float js_matrix[16];
+ if (godot_webxr_get_transform_for_view(-1, js_matrix)) {
head_transform = _js_matrix_to_transform(js_matrix);
- free(js_matrix);
}
if (head_tracker.is_valid()) {
head_tracker->set_pose("default", head_transform, Vector3(), Vector3());
}
- godot_webxr_sample_controller_data();
- int controller_count = godot_webxr_get_controller_count();
- for (int i = 0; i < controller_count; i++) {
- _update_tracker(i);
+ // Update all input sources.
+ for (int i = 0; i < input_source_count; i++) {
+ _update_input_source(i);
}
};
};
-void WebXRInterfaceJS::_update_tracker(int p_controller_id) {
+void WebXRInterfaceJS::_update_input_source(int p_input_source_id) {
XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL(xr_server);
- // need to support more then two controllers...
- if (p_controller_id < 0 || p_controller_id > 1) {
+ InputSource &input_source = input_sources[p_input_source_id];
+
+ float target_pose[16];
+ int tmp_target_ray_mode;
+ int touch_index;
+ int has_grip_pose;
+ float grip_pose[16];
+ int has_standard_mapping;
+ int button_count;
+ float buttons[10];
+ int axes_count;
+ float axes[10];
+
+ input_source.active = godot_webxr_update_input_source(
+ p_input_source_id,
+ target_pose,
+ &tmp_target_ray_mode,
+ &touch_index,
+ &has_grip_pose,
+ grip_pose,
+ &has_standard_mapping,
+ &button_count,
+ buttons,
+ &axes_count,
+ axes);
+
+ if (!input_source.active) {
+ if (input_source.tracker.is_valid()) {
+ xr_server->remove_tracker(input_source.tracker);
+ input_source.tracker.unref();
+ }
return;
}
- Ref<XRPositionalTracker> tracker = controllers[p_controller_id];
- if (godot_webxr_is_controller_connected(p_controller_id)) {
- if (tracker.is_null()) {
- tracker.instantiate();
- tracker->set_tracker_type(XRServer::TRACKER_CONTROLLER);
- // Controller id's 0 and 1 are always the left and right hands.
- if (p_controller_id < 2) {
- tracker->set_tracker_name(p_controller_id == 0 ? "left_hand" : "right_hand");
- tracker->set_tracker_desc(p_controller_id == 0 ? "Left hand controller" : "Right hand controller");
- tracker->set_tracker_hand(p_controller_id == 0 ? XRPositionalTracker::TRACKER_HAND_LEFT : XRPositionalTracker::TRACKER_HAND_RIGHT);
- } else {
- char name[1024];
- sprintf(name, "tracker_%i", p_controller_id);
- tracker->set_tracker_name(name);
- tracker->set_tracker_desc(name);
- }
- xr_server->add_tracker(tracker);
+ input_source.target_ray_mode = (WebXRInterface::TargetRayMode)tmp_target_ray_mode;
+ input_source.touch_index = touch_index;
+
+ Ref<XRPositionalTracker> &tracker = input_source.tracker;
+
+ if (tracker.is_null()) {
+ tracker.instantiate();
+
+ StringName tracker_name;
+ if (input_source.target_ray_mode == WebXRInterface::TargetRayMode::TARGET_RAY_MODE_SCREEN) {
+ tracker_name = touch_names[touch_index];
+ } else {
+ tracker_name = tracker_names[p_input_source_id];
}
- float *tracker_matrix = godot_webxr_get_controller_transform(p_controller_id);
- if (tracker_matrix) {
- // Note, poses should NOT have world scale and our reference frame applied!
- Transform3D transform = _js_matrix_to_transform(tracker_matrix);
- tracker->set_pose("default", transform, Vector3(), Vector3());
- free(tracker_matrix);
+ // Input source id's 0 and 1 are always the left and right hands.
+ if (p_input_source_id < 2) {
+ tracker->set_tracker_type(XRServer::TRACKER_CONTROLLER);
+ tracker->set_tracker_name(tracker_name);
+ tracker->set_tracker_desc(p_input_source_id == 0 ? "Left hand controller" : "Right hand controller");
+ tracker->set_tracker_hand(p_input_source_id == 0 ? XRPositionalTracker::TRACKER_HAND_LEFT : XRPositionalTracker::TRACKER_HAND_RIGHT);
+ } else {
+ tracker->set_tracker_name(tracker_name);
+ tracker->set_tracker_desc(tracker_name);
}
+ xr_server->add_tracker(tracker);
+ }
- // TODO implement additional poses such as "aim" and "grip"
+ Transform3D aim_transform = _js_matrix_to_transform(target_pose);
+ tracker->set_pose(SNAME("default"), aim_transform, Vector3(), Vector3());
+ tracker->set_pose(SNAME("aim"), aim_transform, Vector3(), Vector3());
+ if (has_grip_pose) {
+ tracker->set_pose(SNAME("grip"), _js_matrix_to_transform(grip_pose), Vector3(), Vector3());
+ }
- int *buttons = godot_webxr_get_controller_buttons(p_controller_id);
- if (buttons) {
- // TODO buttons should be named properly, this is just a temporary fix
- for (int i = 0; i < buttons[0]; i++) {
- char name[1024];
- sprintf(name, "button_%i", i);
+ for (int i = 0; i < button_count; i++) {
+ StringName button_name = has_standard_mapping ? standard_button_names[i] : unknown_button_names[i];
+ StringName button_pressure_name = has_standard_mapping ? standard_button_pressure_names[i] : unknown_button_pressure_names[i];
+ float value = buttons[i];
+ bool state = value > 0.0;
+ tracker->set_input(button_name, state);
+ tracker->set_input(button_pressure_name, value);
+ }
- float value = *((float *)buttons + (i + 1));
- bool state = value > 0.0;
- tracker->set_input(name, state);
- }
- free(buttons);
+ for (int i = 0; i < axes_count; i++) {
+ StringName axis_name = has_standard_mapping ? standard_axis_names[i] : unknown_axis_names[i];
+ float value = axes[i];
+ if (has_standard_mapping && (i == 1 || i == 3)) {
+ // Invert the Y-axis on thumbsticks and trackpads, in order to
+ // match OpenXR and other XR platform SDKs.
+ value = -value;
}
+ tracker->set_input(axis_name, value);
+ }
- int *axes = godot_webxr_get_controller_axes(p_controller_id);
- if (axes) {
- // TODO again just a temporary fix, split these between proper float and vector2 inputs
- for (int i = 0; i < axes[0]; i++) {
- char name[1024];
- sprintf(name, "axis_%i", i);
+ // Also create Vector2's for the thumbstick and trackpad when we have the
+ // standard mapping.
+ if (has_standard_mapping) {
+ if (axes_count >= 2) {
+ tracker->set_input(standard_vector_names[0], Vector2(axes[0], -axes[1]));
+ }
+ if (axes_count >= 4) {
+ tracker->set_input(standard_vector_names[1], Vector2(axes[2], -axes[3]));
+ }
+ }
- float value = *((float *)axes + (i + 1));
- tracker->set_input(name, value);
+ if (input_source.target_ray_mode == WebXRInterface::TARGET_RAY_MODE_SCREEN) {
+ if (touch_index < 5 && axes_count >= 2) {
+ Vector2 joy_vector = Vector2(axes[0], axes[1]);
+ Vector2 position = _get_screen_position_from_joy_vector(joy_vector);
+
+ if (touches[touch_index].is_touching) {
+ Vector2 delta = position - touches[touch_index].position;
+
+ // If position has changed by at least 1 pixel, generate a drag event.
+ if (abs(delta.x) >= 1.0 || abs(delta.y) >= 1.0) {
+ Ref<InputEventScreenDrag> event;
+ event.instantiate();
+ event->set_index(touch_index);
+ event->set_position(position);
+ event->set_relative(delta);
+ Input::get_singleton()->parse_input_event(event);
+ }
}
- free(axes);
+
+ touches[touch_index].position = position;
}
- } else if (tracker.is_valid()) {
- xr_server->remove_tracker(tracker);
- controllers[p_controller_id].unref();
}
}
-void WebXRInterfaceJS::_on_controller_changed() {
- // Register "virtual" gamepads with Godot for the ones we get from WebXR.
- godot_webxr_sample_controller_data();
- for (int i = 0; i < 2; i++) {
- bool controller_connected = godot_webxr_is_controller_connected(i);
- if (controllers_state[i] != controller_connected) {
- // Input::get_singleton()->joy_connection_changed(i + 100, controller_connected, i == 0 ? "Left" : "Right", "");
- controllers_state[i] = controller_connected;
+void WebXRInterfaceJS::_on_input_event(int p_event_type, int p_input_source_id) {
+ // Get the latest data for this input source. For transient input sources,
+ // we may not have any data at all yet!
+ _update_input_source(p_input_source_id);
+
+ if (p_event_type == WEBXR_INPUT_EVENT_SELECTSTART || p_event_type == WEBXR_INPUT_EVENT_SELECTEND) {
+ const InputSource &input_source = input_sources[p_input_source_id];
+ if (input_source.target_ray_mode == WebXRInterface::TARGET_RAY_MODE_SCREEN) {
+ int touch_index = input_source.touch_index;
+ if (touch_index >= 0 && touch_index < 5) {
+ touches[touch_index].is_touching = (p_event_type == WEBXR_INPUT_EVENT_SELECTSTART);
+
+ Ref<InputEventScreenTouch> event;
+ event.instantiate();
+ event->set_index(touch_index);
+ event->set_position(touches[touch_index].position);
+ event->set_pressed(p_event_type == WEBXR_INPUT_EVENT_SELECTSTART);
+
+ Input::get_singleton()->parse_input_event(event);
+ }
}
}
+
+ switch (p_event_type) {
+ case WEBXR_INPUT_EVENT_SELECTSTART:
+ emit_signal("selectstart", p_input_source_id);
+ break;
+
+ case WEBXR_INPUT_EVENT_SELECTEND:
+ emit_signal("selectend", p_input_source_id);
+ // Emit the 'select' event on our own (rather than intercepting the
+ // one from JavaScript) so that we don't have to needlessly call
+ // _update_input_source() a second time.
+ emit_signal("select", p_input_source_id);
+ break;
+
+ case WEBXR_INPUT_EVENT_SQUEEZESTART:
+ emit_signal("squeezestart", p_input_source_id);
+ break;
+
+ case WEBXR_INPUT_EVENT_SQUEEZEEND:
+ emit_signal("squeezeend", p_input_source_id);
+ // Again, we emit the 'squeeze' event on our own to avoid extra work.
+ emit_signal("squeeze", p_input_source_id);
+ break;
+ }
+}
+
+Vector2 WebXRInterfaceJS::_get_screen_position_from_joy_vector(const Vector2 &p_joy_vector) {
+ SceneTree *scene_tree = Object::cast_to<SceneTree>(OS::get_singleton()->get_main_loop());
+ if (!scene_tree) {
+ return Vector2();
+ }
+
+ Window *viewport = scene_tree->get_root();
+
+ Vector2 position_percentage((p_joy_vector.x + 1.0f) / 2.0f, ((p_joy_vector.y) + 1.0f) / 2.0f);
+ Vector2 position = (Size2)viewport->get_size() * position_percentage;
+
+ return position;
}
WebXRInterfaceJS::WebXRInterfaceJS() {
diff --git a/modules/webxr/webxr_interface_js.h b/modules/webxr/webxr_interface_js.h
index 319adc2ac9..6b484a8872 100644
--- a/modules/webxr/webxr_interface_js.h
+++ b/modules/webxr/webxr_interface_js.h
@@ -39,6 +39,10 @@
The WebXR interface is a VR/AR interface that can be used on the web.
*/
+namespace GLES3 {
+class TextureStorage;
+}
+
class WebXRInterfaceJS : public WebXRInterface {
GDCLASS(WebXRInterfaceJS, WebXRInterface);
@@ -53,13 +57,32 @@ private:
String requested_reference_space_types;
String reference_space_type;
- // TODO maybe turn into a vector to support more then 2 controllers...
- bool controllers_state[2];
- Ref<XRPositionalTracker> controllers[2];
Size2 render_targetsize;
-
+ RBMap<unsigned int, RID> texture_cache;
+ struct Touch {
+ bool is_touching = false;
+ Vector2 position;
+ } touches[5];
+
+ static constexpr uint8_t input_source_count = 16;
+
+ struct InputSource {
+ Ref<XRPositionalTracker> tracker;
+ bool active = false;
+ TargetRayMode target_ray_mode;
+ int touch_index = -1;
+ } input_sources[input_source_count];
+
+ RID color_texture;
+ RID depth_texture;
+
+ RID _get_color_texture();
+ RID _get_depth_texture();
+ RID _get_texture(unsigned int p_texture_id);
Transform3D _js_matrix_to_transform(float *p_js_matrix);
- void _update_tracker(int p_controller_id);
+ void _update_input_source(int p_input_source_id);
+
+ Vector2 _get_screen_position_from_joy_vector(const Vector2 &p_joy_vector);
public:
virtual void is_session_supported(const String &p_session_mode) override;
@@ -73,9 +96,11 @@ public:
virtual String get_requested_reference_space_types() const override;
void _set_reference_space_type(String p_reference_space_type);
virtual String get_reference_space_type() const override;
- virtual Ref<XRPositionalTracker> get_controller(int p_controller_id) const override;
+ virtual bool is_input_source_active(int p_input_source_id) const override;
+ virtual Ref<XRPositionalTracker> get_input_source_tracker(int p_input_source_id) const override;
+ virtual TargetRayMode get_input_source_target_ray_mode(int p_input_source_id) const override;
virtual String get_visibility_state() const override;
- virtual PackedVector3Array get_bounds_geometry() const override;
+ virtual PackedVector3Array get_play_area() const override;
virtual StringName get_name() const override;
virtual uint32_t get_capabilities() const override;
@@ -89,14 +114,129 @@ public:
virtual Transform3D get_camera_transform() override;
virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override;
virtual Projection get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
+ virtual bool pre_draw_viewport(RID p_render_target) override;
virtual Vector<BlitToScreen> post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) override;
+ virtual RID get_color_texture() override;
+ virtual RID get_depth_texture() override;
+ virtual RID get_velocity_texture() override;
virtual void process() override;
- void _on_controller_changed();
+ void _on_input_event(int p_event_type, int p_input_source_id);
WebXRInterfaceJS();
~WebXRInterfaceJS();
+
+private:
+ StringName tracker_names[16] = {
+ StringName("left_hand"),
+ StringName("right_hand"),
+ StringName("tracker_2"),
+ StringName("tracker_3"),
+ StringName("tracker_4"),
+ StringName("tracker_5"),
+ StringName("tracker_6"),
+ StringName("tracker_7"),
+ StringName("tracker_8"),
+ StringName("tracker_9"),
+ StringName("tracker_10"),
+ StringName("tracker_11"),
+ StringName("tracker_12"),
+ StringName("tracker_13"),
+ StringName("tracker_14"),
+ StringName("tracker_15"),
+ };
+
+ StringName touch_names[5] = {
+ StringName("touch_0"),
+ StringName("touch_1"),
+ StringName("touch_2"),
+ StringName("touch_3"),
+ StringName("touch_4"),
+ };
+
+ StringName standard_axis_names[10] = {
+ StringName("touchpad_x"),
+ StringName("touchpad_y"),
+ StringName("thumbstick_x"),
+ StringName("thumbstick_y"),
+ StringName("axis_4"),
+ StringName("axis_5"),
+ StringName("axis_6"),
+ StringName("axis_7"),
+ StringName("axis_8"),
+ StringName("axis_9"),
+ };
+
+ StringName standard_vector_names[2] = {
+ StringName("touchpad"),
+ StringName("thumbstick"),
+ };
+
+ StringName standard_button_names[10] = {
+ StringName("trigger_click"),
+ StringName("grip_click"),
+ StringName("touchpad_click"),
+ StringName("thumbstick_click"),
+ StringName("ax_button"),
+ StringName("by_button"),
+ StringName("button_6"),
+ StringName("button_7"),
+ StringName("button_8"),
+ StringName("button_9"),
+ };
+
+ StringName standard_button_pressure_names[10] = {
+ StringName("trigger"),
+ StringName("grip"),
+ StringName("touchpad_click_pressure"),
+ StringName("thumbstick_click_pressure"),
+ StringName("ax_button_pressure"),
+ StringName("by_button_pressure"),
+ StringName("button_pressure_6"),
+ StringName("button_pressure_7"),
+ StringName("button_pressure_8"),
+ StringName("button_pressure_9"),
+ };
+
+ StringName unknown_button_names[10] = {
+ StringName("button_0"),
+ StringName("button_1"),
+ StringName("button_2"),
+ StringName("button_3"),
+ StringName("button_4"),
+ StringName("button_5"),
+ StringName("button_6"),
+ StringName("button_7"),
+ StringName("button_8"),
+ StringName("button_9"),
+ };
+
+ StringName unknown_axis_names[10] = {
+ StringName("axis_0"),
+ StringName("axis_1"),
+ StringName("axis_2"),
+ StringName("axis_3"),
+ StringName("axis_4"),
+ StringName("axis_5"),
+ StringName("axis_6"),
+ StringName("axis_7"),
+ StringName("axis_8"),
+ StringName("axis_9"),
+ };
+
+ StringName unknown_button_pressure_names[10] = {
+ StringName("button_pressure_0"),
+ StringName("button_pressure_1"),
+ StringName("button_pressure_2"),
+ StringName("button_pressure_3"),
+ StringName("button_pressure_4"),
+ StringName("button_pressure_5"),
+ StringName("button_pressure_6"),
+ StringName("button_pressure_7"),
+ StringName("button_pressure_8"),
+ StringName("button_pressure_9"),
+ };
};
#endif // WEB_ENABLED
diff --git a/platform/android/detect.py b/platform/android/detect.py
index 57a8d34d0e..ec36a40941 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -24,7 +24,11 @@ def can_build():
def get_opts():
return [
("ANDROID_SDK_ROOT", "Path to the Android SDK", get_env_android_sdk_root()),
- ("ndk_platform", 'Target platform (android-<api>, e.g. "android-24")', "android-24"),
+ (
+ "ndk_platform",
+ 'Target platform (android-<api>, e.g. "android-' + str(get_min_target_api()) + '")',
+ "android-" + str(get_min_target_api()),
+ ),
]
@@ -46,6 +50,11 @@ def get_ndk_version():
return "23.2.8568313"
+# This is kept in sync with the value in 'platform/android/java/app/config.gradle'.
+def get_min_target_api():
+ return 21
+
+
def get_flags():
return [
("arch", "arm64"), # Default for convenience.
@@ -87,18 +96,18 @@ def configure(env: "Environment"):
)
sys.exit()
+ if get_min_sdk_version(env["ndk_platform"]) < get_min_target_api():
+ print(
+ "WARNING: minimum supported Android target api is %d. Forcing target api %d."
+ % (get_min_target_api(), get_min_target_api())
+ )
+ env["ndk_platform"] = "android-" + str(get_min_target_api())
+
install_ndk_if_needed(env)
ndk_root = env["ANDROID_NDK_ROOT"]
# Architecture
- if get_min_sdk_version(env["ndk_platform"]) < 21 and env["arch"] in ["x86_64", "arm64"]:
- print(
- 'WARNING: arch="%s" is not supported with "ndk_platform" lower than "android-21". Forcing platform 21.'
- % env["arch"]
- )
- env["ndk_platform"] = "android-21"
-
if env["arch"] == "arm32":
target_triple = "armv7a-linux-androideabi"
elif env["arch"] == "arm64":
@@ -161,7 +170,6 @@ def configure(env: "Environment"):
"-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fvisibility=hidden -fno-strict-aliasing".split()
)
)
- env.Append(CPPDEFINES=["GLES_ENABLED"])
if get_min_sdk_version(env["ndk_platform"]) >= 24:
env.Append(CPPDEFINES=[("_FILE_OFFSET_BITS", 64)])
@@ -184,9 +192,13 @@ def configure(env: "Environment"):
env.Prepend(CPPPATH=["#platform/android"])
env.Append(CPPDEFINES=["ANDROID_ENABLED", "UNIX_ENABLED"])
- env.Append(LIBS=["OpenSLES", "EGL", "GLESv2", "android", "log", "z", "dl"])
+ env.Append(LIBS=["OpenSLES", "EGL", "android", "log", "z", "dl"])
if env["vulkan"]:
env.Append(CPPDEFINES=["VULKAN_ENABLED"])
if not env["use_volk"]:
env.Append(LIBS=["vulkan"])
+
+ if env["opengl3"]:
+ env.Append(CPPDEFINES=["GLES3_ENABLED"])
+ env.Append(LIBS=["GLESv3"])
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp
index 1b261b489e..f76f3844e9 100644
--- a/platform/android/display_server_android.cpp
+++ b/platform/android/display_server_android.cpp
@@ -41,6 +41,10 @@
#include "platform/android/vulkan/vulkan_context_android.h"
#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
#endif
+#ifdef GLES3_ENABLED
+#include "drivers/gles3/rasterizer_gles3.h"
+#include <EGL/egl.h>
+#endif
DisplayServerAndroid *DisplayServerAndroid::get_singleton() {
return static_cast<DisplayServerAndroid *>(DisplayServer::get_singleton());
@@ -323,7 +327,7 @@ int64_t DisplayServerAndroid::window_get_native_handle(HandleType p_handle_type,
}
#ifdef GLES3_ENABLED
case OPENGL_CONTEXT: {
- return eglGetCurrentContext();
+ return reinterpret_cast<int64_t>(eglGetCurrentContext());
}
#endif
default: {
@@ -449,6 +453,14 @@ DisplayServer *DisplayServerAndroid::create_func(const String &p_rendering_drive
DisplayServer *ds = memnew(DisplayServerAndroid(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, r_error));
if (r_error != OK) {
OS::get_singleton()->alert("Your video card driver does not support any of the supported Vulkan versions.", "Unable to initialize Video driver");
+ if (p_rendering_driver == "vulkan") {
+ OS::get_singleton()->alert("Your video card driver does not support the selected Vulkan version.\n"
+ "Please try exporting your game using the gl_compatibility renderer.",
+ "Unable to initialize Video driver");
+ } else {
+ OS::get_singleton()->alert("Your video card driver does not support OpenGL ES 3.0.",
+ "Unable to initialize Video driver");
+ }
}
return ds;
}
@@ -493,28 +505,11 @@ void DisplayServerAndroid::notify_surface_changed(int p_width, int p_height) {
DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) {
rendering_driver = p_rendering_driver;
- // TODO: rendering_driver is broken, change when different drivers are supported again
- rendering_driver = "vulkan";
-
keep_screen_on = GLOBAL_GET("display/window/energy_saving/keep_screen_on");
#if defined(GLES3_ENABLED)
if (rendering_driver == "opengl3") {
- bool gl_initialization_error = false;
-
- if (RasterizerGLES3::is_viable() == OK) {
- RasterizerGLES3::register_config();
- RasterizerGLES3::make_current();
- } else {
- gl_initialization_error = true;
- }
-
- if (gl_initialization_error) {
- OS::get_singleton()->alert("Your device does not support any of the supported OpenGL versions.\n"
- "Please try updating your Android version.",
- "Unable to initialize video driver");
- return;
- }
+ RasterizerGLES3::make_current();
}
#endif
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index c3fba625c6..737e25b270 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -245,7 +245,7 @@ static const int EXPORT_FORMAT_AAB = 1;
static const char *APK_ASSETS_DIRECTORY = "res://android/build/assets";
static const char *AAB_ASSETS_DIRECTORY = "res://android/build/assetPacks/installTime/src/main/assets";
-static const int DEFAULT_MIN_SDK_VERSION = 19; // Should match the value in 'platform/android/java/app/config.gradle#minSdk'
+static const int DEFAULT_MIN_SDK_VERSION = 21; // Should match the value in 'platform/android/java/app/config.gradle#minSdk'
static const int DEFAULT_TARGET_SDK_VERSION = 32; // Should match the value in 'platform/android/java/app/config.gradle#targetSdk'
#ifndef ANDROID_ENABLED
diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml
index 2d4c4763a2..1db135826a 100644
--- a/platform/android/java/app/AndroidManifest.xml
+++ b/platform/android/java/app/AndroidManifest.xml
@@ -13,7 +13,7 @@
android:xlargeScreens="true" />
<uses-feature
- android:glEsVersion="0x00020000"
+ android:glEsVersion="0x00030000"
android:required="true" />
<application
diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle
index e1d9dc4cde..f1b4bfd534 100644
--- a/platform/android/java/app/config.gradle
+++ b/platform/android/java/app/config.gradle
@@ -1,14 +1,17 @@
ext.versions = [
androidGradlePlugin: '7.2.1',
compileSdk : 32,
- minSdk : 19, // Also update 'platform/android/export/export_plugin.cpp#DEFAULT_MIN_SDK_VERSION'
- targetSdk : 32, // Also update 'platform/android/export/export_plugin.cpp#DEFAULT_TARGET_SDK_VERSION'
+ // Also update 'platform/android/export/export_plugin.cpp#DEFAULT_MIN_SDK_VERSION'
+ minSdk : 21,
+ // Also update 'platform/android/export/export_plugin.cpp#DEFAULT_TARGET_SDK_VERSION'
+ targetSdk : 32,
buildTools : '32.0.0',
kotlinVersion : '1.7.0',
fragmentVersion : '1.3.6',
nexusPublishVersion: '1.1.0',
javaVersion : 11,
- ndkVersion : '23.2.8568313' // Also update 'platform/android/detect.py#get_ndk_version()' when this is updated.
+ // Also update 'platform/android/detect.py#get_ndk_version()' when this is updated.
+ ndkVersion : '23.2.8568313'
]
diff --git a/platform/android/java/editor/src/main/AndroidManifest.xml b/platform/android/java/editor/src/main/AndroidManifest.xml
index 6aa5f06f31..f3ddaafd0e 100644
--- a/platform/android/java/editor/src/main/AndroidManifest.xml
+++ b/platform/android/java/editor/src/main/AndroidManifest.xml
@@ -11,7 +11,7 @@
android:xlargeScreens="true" />
<uses-feature
- android:glEsVersion="0x00020000"
+ android:glEsVersion="0x00030000"
android:required="true" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
index 252554126d..f8d937521b 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
@@ -68,7 +68,7 @@ import java.io.InputStream;
* See ContextFactory class definition below.
*
* - The class must use a custom EGLConfigChooser to be able to select
- * an EGLConfig that supports 2.0. This is done by providing a config
+ * an EGLConfig that supports 3.0. This is done by providing a config
* specification to eglChooseConfig() that has the attribute
* EGL10.ELG_RENDERABLE_TYPE containing the EGL_OPENGL_ES2_BIT flag
* set. See ConfigChooser class definition below.
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java
index 445238b1c2..9834fdfb88 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java
@@ -45,20 +45,18 @@ public class RegularConfigChooser implements GLSurfaceView.EGLConfigChooser {
private int[] mValue = new int[1];
- // FIXME: Add support for Vulkan.
-
- /* This EGL config specification is used to specify 2.0 rendering.
+ /* This EGL config specification is used to specify 3.0 rendering.
* We use a minimum size of 4 bits for red/green/blue, but will
* perform actual matching in chooseConfig() below.
*/
private static int EGL_OPENGL_ES2_BIT = 4;
- private static int[] s_configAttribs2 = {
+ private static int[] s_configAttribs = {
EGL10.EGL_RED_SIZE, 4,
EGL10.EGL_GREEN_SIZE, 4,
EGL10.EGL_BLUE_SIZE, 4,
- // EGL10.EGL_DEPTH_SIZE, 16,
+ // EGL10.EGL_DEPTH_SIZE, 16,
// EGL10.EGL_STENCIL_SIZE, EGL10.EGL_DONT_CARE,
- EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, //apparently there is no EGL_OPENGL_ES3_BIT
EGL10.EGL_NONE
};
@@ -75,7 +73,7 @@ public class RegularConfigChooser implements GLSurfaceView.EGLConfigChooser {
/* Get the number of minimally matching EGL configurations
*/
int[] num_config = new int[1];
- egl.eglChooseConfig(display, s_configAttribs2, null, 0, num_config);
+ egl.eglChooseConfig(display, s_configAttribs, null, 0, num_config);
int numConfigs = num_config[0];
@@ -86,7 +84,7 @@ public class RegularConfigChooser implements GLSurfaceView.EGLConfigChooser {
/* Allocate then read the array of minimally matching EGL configs
*/
EGLConfig[] configs = new EGLConfig[numConfigs];
- egl.eglChooseConfig(display, s_configAttribs2, configs, numConfigs, num_config);
+ egl.eglChooseConfig(display, s_configAttribs, configs, numConfigs, num_config);
if (GLUtils.DEBUG) {
GLUtils.printConfigs(egl, display, configs);
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java
index 5d62723170..8fb86bf6d0 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java
@@ -52,17 +52,16 @@ public class RegularContextFactory implements GLSurfaceView.EGLContextFactory {
private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {
- // FIXME: Add support for Vulkan.
- Log.w(TAG, "creating OpenGL ES 2.0 context :");
+ Log.w(TAG, "creating OpenGL ES 3.0 context :");
GLUtils.checkEglError(TAG, "Before eglCreateContext", egl);
EGLContext context;
if (GLUtils.use_debug_opengl) {
- int[] attrib_list2 = { EGL_CONTEXT_CLIENT_VERSION, 2, _EGL_CONTEXT_FLAGS_KHR, _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL10.EGL_NONE };
- context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list2);
+ int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 3, _EGL_CONTEXT_FLAGS_KHR, _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL10.EGL_NONE };
+ context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
} else {
- int[] attrib_list2 = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
- context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list2);
+ int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL10.EGL_NONE };
+ context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
}
GLUtils.checkEglError(TAG, "After eglCreateContext", egl);
return context;
diff --git a/platform/android/java/nativeSrcsConfigs/CMakeLists.txt b/platform/android/java/nativeSrcsConfigs/CMakeLists.txt
index 711f7cd502..e1534c7685 100644
--- a/platform/android/java/nativeSrcsConfigs/CMakeLists.txt
+++ b/platform/android/java/nativeSrcsConfigs/CMakeLists.txt
@@ -17,4 +17,4 @@ target_include_directories(${PROJECT_NAME}
SYSTEM PUBLIC
${GODOT_ROOT_DIR})
-add_definitions(-DUNIX_ENABLED -DVULKAN_ENABLED -DANDROID_ENABLED)
+add_definitions(-DUNIX_ENABLED -DVULKAN_ENABLED -DANDROID_ENABLED -DGLES3_ENABLED -DTOOLS_ENABLED)
diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp
index cb43f26425..4f81e4bccd 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -478,7 +478,6 @@ OS_Android::OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_god
#if defined(GLES3_ENABLED)
gl_extensions = nullptr;
- use_gl2 = false;
#endif
#if defined(VULKAN_ENABLED)
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index a3bee13f69..ad6143e16e 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -2420,7 +2420,7 @@ void DisplayServerMacOS::window_set_position(const Point2i &p_position, WindowID
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd = windows[p_window];
- if ([wd.window_object isZoomed]) {
+ if (NSEqualRects([wd.window_object frame], [[wd.window_object screen] visibleFrame])) {
return;
}
@@ -2543,7 +2543,7 @@ void DisplayServerMacOS::window_set_size(const Size2i p_size, WindowID p_window)
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd = windows[p_window];
- if ([wd.window_object isZoomed]) {
+ if (NSEqualRects([wd.window_object frame], [[wd.window_object screen] visibleFrame])) {
return;
}
@@ -2625,7 +2625,7 @@ void DisplayServerMacOS::window_set_mode(WindowMode p_mode, WindowID p_window) {
wd.exclusive_fullscreen = false;
} break;
case WINDOW_MODE_MAXIMIZED: {
- if ([wd.window_object isZoomed]) {
+ if (NSEqualRects([wd.window_object frame], [[wd.window_object screen] visibleFrame])) {
[wd.window_object zoom:nil];
}
} break;
@@ -2658,7 +2658,7 @@ void DisplayServerMacOS::window_set_mode(WindowMode p_mode, WindowID p_window) {
}
} break;
case WINDOW_MODE_MAXIMIZED: {
- if (![wd.window_object isZoomed]) {
+ if (!NSEqualRects([wd.window_object frame], [[wd.window_object screen] visibleFrame])) {
[wd.window_object zoom:nil];
}
} break;
@@ -2678,7 +2678,7 @@ DisplayServer::WindowMode DisplayServerMacOS::window_get_mode(WindowID p_window)
return WINDOW_MODE_FULLSCREEN;
}
}
- if ([wd.window_object isZoomed] && !wd.resize_disabled) {
+ if (NSEqualRects([wd.window_object frame], [[wd.window_object screen] visibleFrame])) {
return WINDOW_MODE_MAXIMIZED;
}
if ([wd.window_object respondsToSelector:@selector(isMiniaturized)]) {
@@ -2788,8 +2788,10 @@ void DisplayServerMacOS::window_set_flag(WindowFlags p_flag, bool p_enabled, Win
}
if (p_enabled) {
[wd.window_object setStyleMask:[wd.window_object styleMask] & ~NSWindowStyleMaskResizable];
+ [[wd.window_object standardWindowButton:NSWindowZoomButton] setEnabled:NO];
} else {
[wd.window_object setStyleMask:[wd.window_object styleMask] | NSWindowStyleMaskResizable];
+ [[wd.window_object standardWindowButton:NSWindowZoomButton] setEnabled:YES];
}
} break;
case WINDOW_FLAG_EXTEND_TO_TITLE: {
diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp
index 5860a4f0ae..5e71d10a3f 100644
--- a/platform/macos/export/export_plugin.cpp
+++ b/platform/macos/export/export_plugin.cpp
@@ -1834,8 +1834,8 @@ bool EditorExportPlatformMacOS::has_valid_project_configuration(const Ref<Editor
if (p_preset->get("notarization/apple_id_name") != "") {
if (p_preset->get("notarization/apple_id_password") == "") {
err += TTR("Notarization: Apple ID password not specified.") + "\n";
+ valid = false;
}
- valid = false;
}
if (p_preset->get("notarization/api_uuid") != "") {
if (p_preset->get("notarization/api_key") == "") {
diff --git a/platform/web/SCsub b/platform/web/SCsub
index 077024507a..e44e59bfb9 100644
--- a/platform/web/SCsub
+++ b/platform/web/SCsub
@@ -38,15 +38,20 @@ sys_env.AddJSLibraries(
"js/libs/library_godot_webgl2.js",
]
)
+sys_env.AddJSExterns(
+ [
+ "js/libs/library_godot_webgl2.externs.js",
+ ]
+)
if env["javascript_eval"]:
sys_env.AddJSLibraries(["js/libs/library_godot_javascript_singleton.js"])
for lib in sys_env["JS_LIBS"]:
sys_env.Append(LINKFLAGS=["--js-library", lib.abspath])
-for js in env["JS_PRE"]:
+for js in sys_env["JS_PRE"]:
sys_env.Append(LINKFLAGS=["--pre-js", js.abspath])
-for ext in env["JS_EXTERNS"]:
+for ext in sys_env["JS_EXTERNS"]:
sys_env["ENV"]["EMCC_CLOSURE_ARGS"] += " --externs " + ext.abspath
build = []
diff --git a/platform/web/js/libs/library_godot_webgl2.externs.js b/platform/web/js/libs/library_godot_webgl2.externs.js
new file mode 100644
index 0000000000..18d8d0815a
--- /dev/null
+++ b/platform/web/js/libs/library_godot_webgl2.externs.js
@@ -0,0 +1,36 @@
+
+/**
+ * @constructor OVR_multiview2
+ */
+function OVR_multiview2() {}
+
+/**
+ * @type {number}
+ */
+OVR_multiview2.prototype.FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR;
+
+/**
+ * @type {number}
+ */
+OVR_multiview2.prototype.FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR;
+
+/**
+ * @type {number}
+ */
+OVR_multiview2.prototype.MAX_VIEWS_OVR;
+
+/**
+ * @type {number}
+ */
+OVR_multiview2.prototype.FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR;
+
+/**
+ * @param {number} target
+ * @param {number} attachment
+ * @param {WebGLTexture} texture
+ * @param {number} level
+ * @param {number} baseViewIndex
+ * @param {number} numViews
+ * @return {void}
+ */
+OVR_multiview2.prototype.framebufferTextureMultiviewOVR = function(target, attachment, texture, level, baseViewIndex, numViews) {};
diff --git a/platform/web/js/libs/library_godot_webgl2.js b/platform/web/js/libs/library_godot_webgl2.js
index 365f712be7..ba990b3ee0 100644
--- a/platform/web/js/libs/library_godot_webgl2.js
+++ b/platform/web/js/libs/library_godot_webgl2.js
@@ -37,14 +37,15 @@ const GodotWebGL2 = {
godot_webgl2_glFramebufferTextureMultiviewOVR: function (target, attachment, texture, level, base_view_index, num_views) {
const context = GL.currentContext;
if (typeof context.multiviewExt === 'undefined') {
- const ext = context.GLctx.getExtension('OVR_multiview2');
+ const /** OVR_multiview2 */ ext = context.GLctx.getExtension('OVR_multiview2');
if (!ext) {
console.error('Trying to call glFramebufferTextureMultiviewOVR() without the OVR_multiview2 extension');
return;
}
context.multiviewExt = ext;
}
- context.multiviewExt.framebufferTextureMultiviewOVR(target, attachment, GL.textures[texture], level, base_view_index, num_views);
+ const /** OVR_multiview2 */ ext = context.multiviewExt;
+ ext.framebufferTextureMultiviewOVR(target, attachment, GL.textures[texture], level, base_view_index, num_views);
},
};
diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp
index 84bfc48a43..4788a1b813 100644
--- a/scene/2d/node_2d.cpp
+++ b/scene/2d/node_2d.cpp
@@ -328,29 +328,6 @@ void Node2D::set_global_transform(const Transform2D &p_transform) {
}
}
-void Node2D::set_z_index(int p_z) {
- ERR_FAIL_COND(p_z < RS::CANVAS_ITEM_Z_MIN);
- ERR_FAIL_COND(p_z > RS::CANVAS_ITEM_Z_MAX);
- z_index = p_z;
- RS::get_singleton()->canvas_item_set_z_index(get_canvas_item(), z_index);
-}
-
-void Node2D::set_z_as_relative(bool p_enabled) {
- if (z_relative == p_enabled) {
- return;
- }
- z_relative = p_enabled;
- RS::get_singleton()->canvas_item_set_z_as_relative_to_parent(get_canvas_item(), p_enabled);
-}
-
-bool Node2D::is_z_relative() const {
- return z_relative;
-}
-
-int Node2D::get_z_index() const {
- return z_index;
-}
-
Transform2D Node2D::get_relative_transform_to_parent(const Node *p_parent) const {
if (p_parent == this) {
return Transform2D();
@@ -382,15 +359,6 @@ Point2 Node2D::to_global(Point2 p_local) const {
return get_global_transform().xform(p_local);
}
-void Node2D::set_y_sort_enabled(bool p_enabled) {
- y_sort_enabled = p_enabled;
- RS::get_singleton()->canvas_item_set_sort_children_by_y(get_canvas_item(), y_sort_enabled);
-}
-
-bool Node2D::is_y_sort_enabled() const {
- return y_sort_enabled;
-}
-
void Node2D::_notification(int p_notification) {
switch (p_notification) {
case NOTIFICATION_MOVED_IN_PARENT: {
@@ -437,15 +405,6 @@ void Node2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("to_local", "global_point"), &Node2D::to_local);
ClassDB::bind_method(D_METHOD("to_global", "local_point"), &Node2D::to_global);
- ClassDB::bind_method(D_METHOD("set_z_index", "z_index"), &Node2D::set_z_index);
- ClassDB::bind_method(D_METHOD("get_z_index"), &Node2D::get_z_index);
-
- ClassDB::bind_method(D_METHOD("set_z_as_relative", "enable"), &Node2D::set_z_as_relative);
- ClassDB::bind_method(D_METHOD("is_z_relative"), &Node2D::is_z_relative);
-
- ClassDB::bind_method(D_METHOD("set_y_sort_enabled", "enabled"), &Node2D::set_y_sort_enabled);
- ClassDB::bind_method(D_METHOD("is_y_sort_enabled"), &Node2D::is_y_sort_enabled);
-
ClassDB::bind_method(D_METHOD("get_relative_transform_to_parent", "parent"), &Node2D::get_relative_transform_to_parent);
ADD_GROUP("Transform", "");
@@ -460,9 +419,4 @@ void Node2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_scale", "get_global_scale");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "global_skew", PROPERTY_HINT_NONE, "radians", PROPERTY_USAGE_NONE), "set_global_skew", "get_global_skew");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "global_transform", PROPERTY_HINT_NONE, "suffix:px", PROPERTY_USAGE_NONE), "set_global_transform", "get_global_transform");
-
- ADD_GROUP("Ordering", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "z_index", PROPERTY_HINT_RANGE, itos(RS::CANVAS_ITEM_Z_MIN) + "," + itos(RS::CANVAS_ITEM_Z_MAX) + ",1"), "set_z_index", "get_z_index");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "z_as_relative"), "set_z_as_relative", "is_z_relative");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "y_sort_enabled"), "set_y_sort_enabled", "is_y_sort_enabled");
}
diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h
index 04bbdf639d..76707dc422 100644
--- a/scene/2d/node_2d.h
+++ b/scene/2d/node_2d.h
@@ -40,9 +40,6 @@ class Node2D : public CanvasItem {
real_t rotation = 0.0;
Size2 scale = Vector2(1, 1);
real_t skew = 0.0;
- int z_index = 0;
- bool z_relative = true;
- bool y_sort_enabled = false;
Transform2D transform;
@@ -103,21 +100,12 @@ public:
void set_global_skew(const real_t p_radians);
void set_global_scale(const Size2 &p_scale);
- void set_z_index(int p_z);
- int get_z_index() const;
-
void look_at(const Vector2 &p_pos);
real_t get_angle_to(const Vector2 &p_pos) const;
Point2 to_local(Point2 p_global) const;
Point2 to_global(Point2 p_local) const;
- void set_z_as_relative(bool p_enabled);
- bool is_z_relative() const;
-
- virtual void set_y_sort_enabled(bool p_enabled);
- virtual bool is_y_sort_enabled() const;
-
Transform2D get_relative_transform_to_parent(const Node *p_parent) const;
Transform2D get_transform() const override;
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index 015b5b27e3..6200062f60 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -87,36 +87,38 @@ double AnimationNodeAnimation::process(double p_time, bool p_seek, bool p_is_ext
double anim_size = (double)anim->get_length();
double step = 0.0;
double prev_time = cur_time;
- int pingponged = 0;
- bool current_backward = signbit(p_time);
+ Animation::LoopedFlag looped_flag = Animation::LOOPED_FLAG_NONE;
+ bool node_backward = play_mode == PLAY_MODE_BACKWARD;
if (p_seek) {
step = p_time - cur_time;
cur_time = p_time;
} else {
p_time *= backward ? -1.0 : 1.0;
- if (!(cur_time == anim_size && !current_backward) && !(cur_time == 0 && current_backward)) {
- cur_time = cur_time + p_time;
- step = p_time;
- }
+ cur_time = cur_time + p_time;
+ step = p_time;
}
if (anim->get_loop_mode() == Animation::LOOP_PINGPONG) {
if (!Math::is_zero_approx(anim_size)) {
- if ((int)Math::floor(abs(cur_time - prev_time) / anim_size) % 2 == 0) {
- if (prev_time >= 0 && cur_time < 0) {
- backward = !backward;
- pingponged = -1;
- }
- if (prev_time <= anim_size && cur_time > anim_size) {
- backward = !backward;
- pingponged = 1;
- }
+ if (prev_time >= 0 && cur_time < 0) {
+ backward = !backward;
+ looped_flag = node_backward ? Animation::LOOPED_FLAG_END : Animation::LOOPED_FLAG_START;
+ }
+ if (prev_time <= anim_size && cur_time > anim_size) {
+ backward = !backward;
+ looped_flag = node_backward ? Animation::LOOPED_FLAG_START : Animation::LOOPED_FLAG_END;
}
cur_time = Math::pingpong(cur_time, anim_size);
}
} else if (anim->get_loop_mode() == Animation::LOOP_LINEAR) {
if (!Math::is_zero_approx(anim_size)) {
+ if (prev_time >= 0 && cur_time < 0) {
+ looped_flag = node_backward ? Animation::LOOPED_FLAG_END : Animation::LOOPED_FLAG_START;
+ }
+ if (prev_time <= anim_size && cur_time > anim_size) {
+ looped_flag = node_backward ? Animation::LOOPED_FLAG_START : Animation::LOOPED_FLAG_END;
+ }
cur_time = Math::fposmod(cur_time, anim_size);
}
backward = false;
@@ -145,9 +147,9 @@ double AnimationNodeAnimation::process(double p_time, bool p_seek, bool p_is_ext
}
if (play_mode == PLAY_MODE_FORWARD) {
- blend_animation(animation, cur_time, step, p_seek, p_is_external_seeking, 1.0, pingponged);
+ blend_animation(animation, cur_time, step, p_seek, p_is_external_seeking, 1.0, looped_flag);
} else {
- blend_animation(animation, anim_size - cur_time, -step, p_seek, p_is_external_seeking, 1.0, pingponged);
+ blend_animation(animation, anim_size - cur_time, -step, p_seek, p_is_external_seeking, 1.0, looped_flag);
}
set_parameter(time, cur_time);
@@ -309,9 +311,7 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_is_exter
set_parameter(time_to_restart, cur_time_to_restart);
}
- if (!cur_active) {
- return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync);
- }
+ return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync);
}
bool os_seek = p_seek;
@@ -349,10 +349,9 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_is_exter
if (mix == MIX_MODE_ADD) {
main_rem = blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync);
} else {
- main_rem = blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0 - blend, FILTER_BLEND, sync);
+ main_rem = blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0 - blend, FILTER_BLEND, sync); // Unlike below, processing this edge is a corner case.
}
-
- double os_rem = blend_input(1, os_seek ? cur_time : p_time, os_seek, p_is_external_seeking, blend, FILTER_PASS, true);
+ double os_rem = blend_input(1, os_seek ? cur_time : p_time, os_seek, p_is_external_seeking, MAX(CMP_EPSILON, blend), FILTER_PASS, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
if (do_start) {
cur_remaining = os_rem;
@@ -769,17 +768,18 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_ex
blend = xfade_curve->sample(blend);
}
+ // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
if (from_start && !p_seek && switched) { //just switched, seek to start of current
- rem = blend_input(cur_current, 0, true, p_is_external_seeking, 1.0 - blend, FILTER_IGNORE, true);
+ rem = blend_input(cur_current, 0, true, p_is_external_seeking, MAX(CMP_EPSILON, 1.0 - blend), FILTER_IGNORE, true);
} else {
- rem = blend_input(cur_current, p_time, p_seek, p_is_external_seeking, 1.0 - blend, FILTER_IGNORE, true);
+ rem = blend_input(cur_current, p_time, p_seek, p_is_external_seeking, MAX(CMP_EPSILON, 1.0 - blend), FILTER_IGNORE, true);
}
if (p_seek) {
- blend_input(cur_prev, p_time, true, p_is_external_seeking, blend, FILTER_IGNORE, true);
+ blend_input(cur_prev, p_time, true, p_is_external_seeking, MAX(CMP_EPSILON, blend), FILTER_IGNORE, true);
cur_time = p_time;
} else {
- blend_input(cur_prev, p_time, false, p_is_external_seeking, blend, FILTER_IGNORE, true);
+ blend_input(cur_prev, p_time, false, p_is_external_seeking, MAX(CMP_EPSILON, blend), FILTER_IGNORE, true);
cur_time += p_time;
cur_prev_xfading -= p_time;
if (cur_prev_xfading < 0) {
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp
index 360f16de02..d3746c1144 100644
--- a/scene/animation/animation_node_state_machine.cpp
+++ b/scene/animation/animation_node_state_machine.cpp
@@ -433,10 +433,10 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
if (current_curve.is_valid()) {
fade_blend = current_curve->sample(fade_blend);
}
- float rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_is_external_seeking, fade_blend, AnimationNode::FILTER_IGNORE, true);
+ float rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_is_external_seeking, MAX(CMP_EPSILON, fade_blend), AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
if (fading_from != StringName()) {
- p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, p_is_external_seeking, 1.0 - fade_blend, AnimationNode::FILTER_IGNORE, true);
+ p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, p_is_external_seeking, MAX(CMP_EPSILON, 1.0 - fade_blend), AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
}
//guess playback position
@@ -599,13 +599,11 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
current = next;
+ len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, CMP_EPSILON, AnimationNode::FILTER_IGNORE, true); // Process next node's first key in here.
if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_SYNC) {
- len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, 0, AnimationNode::FILTER_IGNORE, true);
pos_current = MIN(pos_current, len_current);
p_state_machine->blend_node(current, p_state_machine->states[current].node, pos_current, true, p_is_external_seeking, 0, AnimationNode::FILTER_IGNORE, true);
-
} else {
- len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, 0, AnimationNode::FILTER_IGNORE, true);
pos_current = 0;
}
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 45eeff71f2..ee6fb58583 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -468,7 +468,7 @@ Variant AnimationPlayer::_post_process_key_value(const Ref<Animation> &p_anim, i
return p_value;
}
-void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double p_time, double p_delta, float p_interp, bool p_is_current, bool p_seeked, bool p_started, int p_pingponged) {
+void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double p_prev_time, double p_time, double p_delta, float p_interp, bool p_is_current, bool p_seeked, bool p_started, Animation::LoopedFlag p_looped_flag) {
_ensure_node_caches(p_anim);
ERR_FAIL_COND(p_anim->node_cache.size() != p_anim->animation->get_track_count());
@@ -683,7 +683,13 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
} else if (p_is_current && p_delta != 0) {
List<int> indices;
- a->track_get_key_indices_in_range(i, p_time, p_delta, &indices, p_pingponged);
+ if (p_started) {
+ int first_key = a->track_find_key(i, p_prev_time, true);
+ if (first_key >= 0) {
+ indices.push_back(first_key);
+ }
+ }
+ a->track_get_key_indices_in_range(i, p_time, p_delta, &indices, p_looped_flag);
for (int &F : indices) {
Variant value = a->track_get_key_value(i, F);
@@ -742,7 +748,13 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
}
List<int> indices;
- a->track_get_key_indices_in_range(i, p_time, p_delta, &indices, p_pingponged);
+ if (p_started) {
+ int first_key = a->track_find_key(i, p_prev_time, true);
+ if (first_key >= 0) {
+ indices.push_back(first_key);
+ }
+ }
+ a->track_get_key_indices_in_range(i, p_time, p_delta, &indices, p_looped_flag);
for (int &E : indices) {
StringName method = a->method_track_get_name(i, E);
@@ -832,7 +844,13 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
} else {
//find stuff to play
List<int> to_play;
- a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play, p_pingponged);
+ if (p_started) {
+ int first_key = a->track_find_key(i, p_prev_time, true);
+ if (first_key >= 0) {
+ to_play.push_back(first_key);
+ }
+ }
+ a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play, p_looped_flag);
if (to_play.size()) {
int idx = to_play.back()->get();
@@ -939,7 +957,13 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
} else {
//find stuff to play
List<int> to_play;
- a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play, p_pingponged);
+ if (p_started) {
+ int first_key = a->track_find_key(i, p_prev_time, true);
+ if (first_key >= 0) {
+ to_play.push_back(first_key);
+ }
+ }
+ a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play, p_looped_flag);
if (to_play.size()) {
int idx = to_play.back()->get();
@@ -969,7 +993,7 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, double p_delta,
double next_pos = cd.pos + delta;
real_t len = cd.from->animation->get_length();
- int pingponged = 0;
+ Animation::LoopedFlag looped_flag = Animation::LOOPED_FLAG_NONE;
switch (cd.from->animation->get_loop_mode()) {
case Animation::LOOP_NONE: {
@@ -998,44 +1022,33 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, double p_delta,
} break;
case Animation::LOOP_LINEAR: {
- double looped_next_pos = Math::fposmod(next_pos, (double)len);
- if (looped_next_pos == 0 && next_pos != 0) {
- // Loop multiples of the length to it, rather than 0
- // so state at time=length is previewable in the editor
- next_pos = len;
- } else {
- next_pos = looped_next_pos;
+ if (next_pos < 0 && cd.pos >= 0) {
+ looped_flag = Animation::LOOPED_FLAG_START;
}
+ if (next_pos > len && cd.pos <= len) {
+ looped_flag = Animation::LOOPED_FLAG_END;
+ }
+ next_pos = Math::fposmod(next_pos, (double)len);
} break;
case Animation::LOOP_PINGPONG: {
- if ((int)Math::floor(abs(next_pos - cd.pos) / len) % 2 == 0) {
- if (next_pos < 0 && cd.pos >= 0) {
- cd.speed_scale *= -1.0;
- pingponged = -1;
- }
- if (next_pos > len && cd.pos <= len) {
- cd.speed_scale *= -1.0;
- pingponged = 1;
- }
+ if (next_pos < 0 && cd.pos >= 0) {
+ cd.speed_scale *= -1.0;
+ looped_flag = Animation::LOOPED_FLAG_START;
}
- double looped_next_pos = Math::pingpong(next_pos, (double)len);
- if (looped_next_pos == 0 && next_pos != 0) {
- // Loop multiples of the length to it, rather than 0
- // so state at time=length is previewable in the editor
- next_pos = len;
- } else {
- next_pos = looped_next_pos;
+ if (next_pos > len && cd.pos <= len) {
+ cd.speed_scale *= -1.0;
+ looped_flag = Animation::LOOPED_FLAG_END;
}
+ next_pos = Math::pingpong(next_pos, (double)len);
} break;
default:
break;
}
+ _animation_process_animation(cd.from, cd.pos, next_pos, delta, p_blend, &cd == &playback.current, p_seeked, p_started, looped_flag);
cd.pos = next_pos;
-
- _animation_process_animation(cd.from, cd.pos, delta, p_blend, &cd == &playback.current, p_seeked, p_started, pingponged);
}
void AnimationPlayer::_animation_process2(double p_delta, bool p_started) {
@@ -1273,23 +1286,6 @@ void AnimationPlayer::_animation_set_cache_update() {
}
void AnimationPlayer::_animation_added(const StringName &p_name, const StringName &p_library) {
- {
- int at_pos = -1;
-
- for (uint32_t i = 0; i < animation_libraries.size(); i++) {
- if (animation_libraries[i].name == p_library) {
- at_pos = i;
- break;
- }
- }
-
- ERR_FAIL_COND(at_pos == -1);
-
- ERR_FAIL_COND(!animation_libraries[at_pos].library->animations.has(p_name));
-
- _ref_anim(animation_libraries[at_pos].library->animations[p_name]);
- }
-
_animation_set_cache_update();
}
@@ -1300,11 +1296,6 @@ void AnimationPlayer::_animation_removed(const StringName &p_name, const StringN
return; // No need to update because not the one from the library being used.
}
- AnimationData animation_data = animation_set[name];
- if (animation_data.animation_library == p_library) {
- _unref_anim(animation_data.animation);
- }
-
_animation_set_cache_update();
// Erase blends if needed
@@ -1400,10 +1391,7 @@ Error AnimationPlayer::add_animation_library(const StringName &p_name, const Ref
ald.library->connect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added).bind(p_name));
ald.library->connect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_removed).bind(p_name));
ald.library->connect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed).bind(p_name));
-
- for (const KeyValue<StringName, Ref<Animation>> &K : ald.library->animations) {
- _ref_anim(K.value);
- }
+ ald.library->connect(SNAME("animation_changed"), callable_mp(this, &AnimationPlayer::_animation_changed));
_animation_set_cache_update();
@@ -1427,27 +1415,16 @@ void AnimationPlayer::remove_animation_library(const StringName &p_name) {
animation_libraries[at_pos].library->disconnect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added));
animation_libraries[at_pos].library->disconnect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_removed));
animation_libraries[at_pos].library->disconnect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed));
+ animation_libraries[at_pos].library->disconnect(SNAME("animation_changed"), callable_mp(this, &AnimationPlayer::_animation_changed));
stop();
- for (const KeyValue<StringName, Ref<Animation>> &K : animation_libraries[at_pos].library->animations) {
- _unref_anim(K.value);
- }
-
animation_libraries.remove_at(at_pos);
_animation_set_cache_update();
notify_property_list_changed();
}
-void AnimationPlayer::_ref_anim(const Ref<Animation> &p_anim) {
- Ref<Animation>(p_anim)->connect("changed", callable_mp(this, &AnimationPlayer::_animation_changed), CONNECT_REFERENCE_COUNTED);
-}
-
-void AnimationPlayer::_unref_anim(const Ref<Animation> &p_anim) {
- Ref<Animation>(p_anim)->disconnect("changed", callable_mp(this, &AnimationPlayer::_animation_changed));
-}
-
void AnimationPlayer::rename_animation_library(const StringName &p_name, const StringName &p_new_name) {
if (p_name == p_new_name) {
return;
@@ -1798,9 +1775,8 @@ double AnimationPlayer::get_current_animation_length() const {
return playback.current.from->animation->get_length();
}
-void AnimationPlayer::_animation_changed() {
+void AnimationPlayer::_animation_changed(const StringName &p_name) {
clear_caches();
- emit_signal(SNAME("caches_cleared"));
if (is_playing()) {
playback.seeked = true; //need to restart stuff, like audio
}
@@ -1839,6 +1815,8 @@ void AnimationPlayer::clear_caches() {
cache_update_size = 0;
cache_update_prop_size = 0;
cache_update_bezier_size = 0;
+
+ emit_signal(SNAME("caches_cleared"));
}
void AnimationPlayer::set_active(bool p_active) {
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index 4f32927d25..0b95ee4e9e 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -268,7 +268,7 @@ private:
NodePath root;
- void _animation_process_animation(AnimationData *p_anim, double p_time, double p_delta, float p_interp, bool p_is_current = true, bool p_seeked = false, bool p_started = false, int p_pingponged = 0);
+ void _animation_process_animation(AnimationData *p_anim, double p_prev_time, double p_time, double p_delta, float p_interp, bool p_is_current = true, bool p_seeked = false, bool p_started = false, Animation::LoopedFlag p_looped_flag = Animation::LOOPED_FLAG_NONE);
void _ensure_node_caches(AnimationData *p_anim, Node *p_root_override = nullptr);
void _animation_process_data(PlaybackData &cd, double p_delta, float p_blend, bool p_seeked, bool p_started);
@@ -291,9 +291,7 @@ private:
return ret;
}
- void _animation_changed();
- void _ref_anim(const Ref<Animation> &p_anim);
- void _unref_anim(const Ref<Animation> &p_anim);
+ void _animation_changed(const StringName &p_name);
void _set_process(bool p_process, bool p_force = false);
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index 50165773d0..16621faa33 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -86,7 +86,7 @@ void AnimationNode::get_child_nodes(List<ChildNode> *r_child_nodes) {
}
}
-void AnimationNode::blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, bool p_is_external_seeking, real_t p_blend, int p_pingponged) {
+void AnimationNode::blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, bool p_is_external_seeking, real_t p_blend, Animation::LoopedFlag p_looped_flag) {
ERR_FAIL_COND(!state);
ERR_FAIL_COND(!state->player->has_animation(p_animation));
@@ -112,7 +112,7 @@ void AnimationNode::blend_animation(const StringName &p_animation, double p_time
anim_state.time = p_time;
anim_state.animation = animation;
anim_state.seeked = p_seeked;
- anim_state.pingponged = p_pingponged;
+ anim_state.looped_flag = p_looped_flag;
anim_state.is_external_seeking = p_is_external_seeking;
state->animation_states.push_back(anim_state);
@@ -413,7 +413,7 @@ void AnimationNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_filters", "filters"), &AnimationNode::_set_filters);
ClassDB::bind_method(D_METHOD("_get_filters"), &AnimationNode::_get_filters);
- ClassDB::bind_method(D_METHOD("blend_animation", "animation", "time", "delta", "seeked", "is_external_seeking", "blend", "pingponged"), &AnimationNode::blend_animation, DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("blend_animation", "animation", "time", "delta", "seeked", "is_external_seeking", "blend", "looped_flag"), &AnimationNode::blend_animation, DEFVAL(Animation::LOOPED_FLAG_NONE));
ClassDB::bind_method(D_METHOD("blend_node", "name", "node", "time", "seek", "is_external_seeking", "blend", "filter", "sync"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true));
ClassDB::bind_method(D_METHOD("blend_input", "input_index", "time", "seek", "is_external_seeking", "blend", "filter", "sync"), &AnimationNode::blend_input, DEFVAL(FILTER_IGNORE), DEFVAL(true));
@@ -807,10 +807,10 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
track_value->is_using_angle |= anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_LINEAR_ANGLE || anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_CUBIC_ANGLE;
if (was_discrete != track_value->is_discrete) {
- WARN_PRINT_ONCE("Tracks with different update modes are blended. Blending prioritizes Discrete/Trigger mode, so other update mode tracks will not be blended.");
+ ERR_PRINT_ED("Value track: " + String(path) + " with different update modes are blended. Blending prioritizes Discrete/Trigger mode, so other update mode tracks will not be blended.");
}
if (was_using_angle != track_value->is_using_angle) {
- WARN_PRINT_ONCE("Tracks for rotation with different interpolation types are blended. Blending prioritizes angle interpolation, so the blending result uses the shortest path referenced to the initial (RESET animation) value.");
+ WARN_PRINT_ED("Value track: " + String(path) + " with different interpolation types for rotation are blended. Blending prioritizes angle interpolation, so the blending result uses the shortest path referenced to the initial (RESET animation) value.");
}
}
@@ -859,7 +859,6 @@ void AnimationTree::_clear_caches() {
memdelete(K.value);
}
playing_caches.clear();
-
track_cache.clear();
cache_valid = false;
}
@@ -1019,7 +1018,7 @@ void AnimationTree::_process_graph(double p_delta) {
double delta = as.delta;
real_t weight = as.blend;
bool seeked = as.seeked;
- int pingponged = as.pingponged;
+ Animation::LoopedFlag looped_flag = as.looped_flag;
bool is_external_seeking = as.is_external_seeking;
#ifndef _3D_DISABLED
bool backward = signbit(delta); // This flag is required only for the root motion since it calculates the difference between the previous and current frames.
@@ -1391,7 +1390,7 @@ void AnimationTree::_process_graph(double p_delta) {
t->object->set_indexed(t->subpath, value);
} else {
List<int> indices;
- a->track_get_key_indices_in_range(i, time, delta, &indices, pingponged);
+ a->track_get_key_indices_in_range(i, time, delta, &indices, looped_flag);
for (int &F : indices) {
Variant value = a->track_get_key_value(i, F);
value = _post_process_key_value(a, i, value, t->object);
@@ -1416,7 +1415,7 @@ void AnimationTree::_process_graph(double p_delta) {
}
} else {
List<int> indices;
- a->track_get_key_indices_in_range(i, time, delta, &indices, pingponged);
+ a->track_get_key_indices_in_range(i, time, delta, &indices, looped_flag);
for (int &F : indices) {
StringName method = a->method_track_get_name(i, F);
Vector<Variant> params = a->method_track_get_params(i, F);
@@ -1479,7 +1478,7 @@ void AnimationTree::_process_graph(double p_delta) {
} else {
//find stuff to play
List<int> to_play;
- a->track_get_key_indices_in_range(i, time, delta, &to_play, pingponged);
+ a->track_get_key_indices_in_range(i, time, delta, &to_play, looped_flag);
if (to_play.size()) {
int idx = to_play.back()->get();
@@ -1594,7 +1593,7 @@ void AnimationTree::_process_graph(double p_delta) {
} else {
//find stuff to play
List<int> to_play;
- a->track_get_key_indices_in_range(i, time, delta, &to_play, pingponged);
+ a->track_get_key_indices_in_range(i, time, delta, &to_play, looped_flag);
if (to_play.size()) {
int idx = to_play.back()->get();
@@ -1630,7 +1629,7 @@ void AnimationTree::_process_graph(double p_delta) {
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
if (t->root_motion) {
- root_motion_position = root_motion_rotation.xform_inv(t->loc);
+ root_motion_position = t->loc;
root_motion_rotation = t->rot;
root_motion_scale = t->scale - Vector3(1, 1, 1);
diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h
index a4b0f992dc..be0dc1af4e 100644
--- a/scene/animation/animation_tree.h
+++ b/scene/animation/animation_tree.h
@@ -69,7 +69,7 @@ public:
real_t blend = 0.0;
bool seeked = false;
bool is_external_seeking = false;
- int pingponged = 0;
+ Animation::LoopedFlag looped_flag = Animation::LOOPED_FLAG_NONE;
};
struct State {
@@ -102,7 +102,7 @@ public:
double _blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_is_external_seeking, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true, real_t *r_max = nullptr);
protected:
- void blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, bool p_is_external_seeking, real_t p_blend, int p_pingponged = 0);
+ void blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, bool p_is_external_seeking, real_t p_blend, Animation::LoopedFlag p_looped_flag = Animation::LOOPED_FLAG_NONE);
double blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_is_external_seeking, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true);
double blend_input(int p_input, double p_time, bool p_seek, bool p_is_external_seeking, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true);
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 92ee21a916..e90a6a69ab 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -220,6 +220,10 @@ PackedStringArray Control::get_configuration_warnings() const {
warnings.push_back(RTR("The Hint Tooltip won't be displayed as the control's Mouse Filter is set to \"Ignore\". To solve this, set the Mouse Filter to \"Stop\" or \"Pass\"."));
}
+ if (get_z_index() != 0) {
+ warnings.push_back(RTR("Changing the Z index of a control only affects the drawing order, not the input event handling order."));
+ }
+
return warnings;
}
@@ -481,10 +485,10 @@ void Control::_validate_property(PropertyInfo &p_property) const {
}
} else if (Object::cast_to<Container>(parent_node)) {
// If the parent is a container, display only container-related properties.
- if (p_property.name.begins_with("anchor_") || p_property.name.begins_with("offset_") || p_property.name.begins_with("grow_") || p_property.name == "anchors_preset" ||
- p_property.name == "position" || p_property.name == "rotation" || p_property.name == "scale" || p_property.name == "size" || p_property.name == "pivot_offset") {
- p_property.usage ^= PROPERTY_USAGE_EDITOR;
-
+ if (p_property.name.begins_with("anchor_") || p_property.name.begins_with("offset_") || p_property.name.begins_with("grow_") || p_property.name == "anchors_preset") {
+ p_property.usage ^= PROPERTY_USAGE_DEFAULT;
+ } else if (p_property.name == "position" || p_property.name == "rotation" || p_property.name == "scale" || p_property.name == "size" || p_property.name == "pivot_offset") {
+ p_property.usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY;
} else if (p_property.name == "layout_mode") {
// Set the layout mode to be disabled with the proper value.
p_property.hint_string = "Position,Anchors,Container,Uncontrolled";
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index 40792dd43f..1c4c8c2574 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -623,7 +623,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
//check disconnect
for (const Connection &E : connections) {
if (E.from == gn->get_name() && E.from_port == j) {
- Node *to = get_node(String(E.to));
+ Node *to = get_node(NodePath(E.to));
if (Object::cast_to<GraphNode>(to)) {
connecting_from = E.to;
connecting_index = E.to_port;
@@ -637,7 +637,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
just_disconnected = true;
emit_signal(SNAME("disconnection_request"), E.from, E.from_port, E.to, E.to_port);
- to = get_node(String(connecting_from)); //maybe it was erased
+ to = get_node(NodePath(connecting_from)); // Maybe it was erased.
if (Object::cast_to<GraphNode>(to)) {
connecting = true;
emit_signal(SNAME("connection_drag_started"), connecting_from, connecting_index, false);
@@ -673,10 +673,10 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
if (is_in_input_hotzone(gn, j, click_pos, port_size)) {
if (right_disconnects || valid_right_disconnect_types.has(gn->get_connection_input_type(j))) {
- //check disconnect
+ // Check disconnect.
for (const Connection &E : connections) {
if (E.to == gn->get_name() && E.to_port == j) {
- Node *fr = get_node(String(E.from));
+ Node *fr = get_node(NodePath(E.from));
if (Object::cast_to<GraphNode>(fr)) {
connecting_from = E.from;
connecting_index = E.from_port;
@@ -689,7 +689,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
if (connecting_type >= 0) {
emit_signal(SNAME("disconnection_request"), E.from, E.from_port, E.to, E.to_port);
- fr = get_node(String(connecting_from)); //maybe it was erased
+ fr = get_node(NodePath(connecting_from)); // Maybe it was erased.
if (Object::cast_to<GraphNode>(fr)) {
connecting = true;
emit_signal(SNAME("connection_drag_started"), connecting_from, connecting_index, true);
@@ -780,26 +780,16 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed()) {
if (connecting_valid) {
if (connecting && connecting_target) {
- String from = connecting_from;
- int from_port = connecting_index;
- String to = connecting_target_to;
- int to_port = connecting_target_index;
-
- if (!connecting_out) {
- SWAP(from, to);
- SWAP(from_port, to_port);
+ if (connecting_out) {
+ emit_signal(SNAME("connection_request"), connecting_from, connecting_index, connecting_target_to, connecting_target_index);
+ } else {
+ emit_signal(SNAME("connection_request"), connecting_target_to, connecting_target_index, connecting_from, connecting_index);
}
- emit_signal(SNAME("connection_request"), from, from_port, to, to_port);
-
} else if (!just_disconnected) {
- String from = connecting_from;
- int from_port = connecting_index;
- Vector2 ofs = mb->get_position();
-
- if (!connecting_out) {
- emit_signal(SNAME("connection_from_empty"), from, from_port, ofs);
+ if (connecting_out) {
+ emit_signal(SNAME("connection_to_empty"), connecting_from, connecting_index, mb->get_position());
} else {
- emit_signal(SNAME("connection_to_empty"), from, from_port, ofs);
+ emit_signal(SNAME("connection_from_empty"), connecting_from, connecting_index, mb->get_position());
}
}
}
@@ -935,17 +925,12 @@ void GraphEdit::_draw_connection_line(CanvasItem *p_where, const Vector2 &p_from
void GraphEdit::_connections_layer_draw() {
Color activity_color = get_theme_color(SNAME("activity"));
- //draw connections
+ // Draw connections.
List<List<Connection>::Element *> to_erase;
for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {
- NodePath fromnp(E->get().from);
-
- Node *from = get_node(fromnp);
- if (!from) {
- to_erase.push_back(E);
- continue;
- }
+ const Connection &c = E->get();
+ Node *from = get_node(NodePath(c.from));
GraphNode *gfrom = Object::cast_to<GraphNode>(from);
if (!gfrom) {
@@ -953,13 +938,7 @@ void GraphEdit::_connections_layer_draw() {
continue;
}
- NodePath tonp(E->get().to);
- Node *to = get_node(tonp);
- if (!to) {
- to_erase.push_back(E);
- continue;
- }
-
+ Node *to = get_node(NodePath(c.to));
GraphNode *gto = Object::cast_to<GraphNode>(to);
if (!gto) {
@@ -967,21 +946,20 @@ void GraphEdit::_connections_layer_draw() {
continue;
}
- Vector2 frompos = gfrom->get_connection_output_position(E->get().from_port) + gfrom->get_position_offset() * zoom;
- Color color = gfrom->get_connection_output_color(E->get().from_port);
- Vector2 topos = gto->get_connection_input_position(E->get().to_port) + gto->get_position_offset() * zoom;
- Color tocolor = gto->get_connection_input_color(E->get().to_port);
+ Vector2 frompos = gfrom->get_connection_output_position(c.from_port) + gfrom->get_position_offset() * zoom;
+ Color color = gfrom->get_connection_output_color(c.from_port);
+ Vector2 topos = gto->get_connection_input_position(c.to_port) + gto->get_position_offset() * zoom;
+ Color tocolor = gto->get_connection_input_color(c.to_port);
- if (E->get().activity > 0) {
- color = color.lerp(activity_color, E->get().activity);
- tocolor = tocolor.lerp(activity_color, E->get().activity);
+ if (c.activity > 0) {
+ color = color.lerp(activity_color, c.activity);
+ tocolor = tocolor.lerp(activity_color, c.activity);
}
_draw_connection_line(connections_layer, frompos, topos, color, tocolor, lines_thickness, zoom);
}
- while (to_erase.size()) {
- connections.erase(to_erase.front()->get());
- to_erase.pop_front();
+ for (List<Connection>::Element *&E : to_erase) {
+ connections.erase(E);
}
}
@@ -989,7 +967,7 @@ void GraphEdit::_top_layer_draw() {
_update_scroll();
if (connecting) {
- Node *fromn = get_node(connecting_from);
+ Node *fromn = get_node(NodePath(connecting_from));
ERR_FAIL_COND(!fromn);
GraphNode *from = Object::cast_to<GraphNode>(fromn);
ERR_FAIL_COND(!from);
@@ -1087,22 +1065,13 @@ void GraphEdit::_minimap_draw() {
// Draw node connections.
Color activity_color = get_theme_color(SNAME("activity"));
for (const Connection &E : connections) {
- NodePath fromnp(E.from);
-
- Node *from = get_node(fromnp);
- if (!from) {
- continue;
- }
+ Node *from = get_node(NodePath(E.from));
GraphNode *gfrom = Object::cast_to<GraphNode>(from);
if (!gfrom) {
continue;
}
- NodePath tonp(E.to);
- Node *to = get_node(tonp);
- if (!to) {
- continue;
- }
+ Node *to = get_node(NodePath(E.to));
GraphNode *gto = Object::cast_to<GraphNode>(to);
if (!gto) {
continue;
@@ -2428,7 +2397,7 @@ void GraphEdit::_bind_methods() {
ADD_SIGNAL(MethodInfo("begin_node_move"));
ADD_SIGNAL(MethodInfo("end_node_move"));
ADD_SIGNAL(MethodInfo("scroll_offset_changed", PropertyInfo(Variant::VECTOR2, "offset")));
- ADD_SIGNAL(MethodInfo("connection_drag_started", PropertyInfo(Variant::STRING, "from_node"), PropertyInfo(Variant::INT, "from_port"), PropertyInfo(Variant::BOOL, "is_output")));
+ ADD_SIGNAL(MethodInfo("connection_drag_started", PropertyInfo(Variant::STRING_NAME, "from_node"), PropertyInfo(Variant::INT, "from_port"), PropertyInfo(Variant::BOOL, "is_output")));
ADD_SIGNAL(MethodInfo("connection_drag_ended"));
BIND_ENUM_CONSTANT(SCROLL_ZOOMS);
diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h
index 101087bdbd..eda7ddd824 100644
--- a/scene/gui/graph_edit.h
+++ b/scene/gui/graph_edit.h
@@ -136,14 +136,14 @@ private:
bool arrange_nodes_button_hidden = false;
bool connecting = false;
- String connecting_from;
+ StringName connecting_from;
bool connecting_out = false;
int connecting_index = 0;
int connecting_type = 0;
Color connecting_color;
bool connecting_target = false;
Vector2 connecting_to;
- String connecting_target_to;
+ StringName connecting_target_to;
int connecting_target_index = 0;
bool just_disconnected = false;
bool connecting_valid = false;
diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp
index 27002fad38..e0e4ead55f 100644
--- a/scene/gui/range.cpp
+++ b/scene/gui/range.cpp
@@ -64,11 +64,6 @@ void Range::_changed_notify(const char *p_what) {
queue_redraw();
}
-void Range::_validate_values() {
- shared->max = MAX(shared->max, shared->min);
- shared->page = CLAMP(shared->page, 0, shared->max - shared->min);
-}
-
void Range::Shared::emit_changed(const char *p_what) {
for (Range *E : owners) {
Range *r = E;
@@ -118,8 +113,9 @@ void Range::set_min(double p_min) {
}
shared->min = p_min;
+ shared->max = MAX(shared->max, shared->min);
+ shared->page = CLAMP(shared->page, 0, shared->max - shared->min);
set_value(shared->val);
- _validate_values();
shared->emit_changed("min");
@@ -127,13 +123,14 @@ void Range::set_min(double p_min) {
}
void Range::set_max(double p_max) {
- if (shared->max == p_max) {
+ double max_validated = MAX(p_max, shared->min);
+ if (shared->max == max_validated) {
return;
}
- shared->max = p_max;
+ shared->max = max_validated;
+ shared->page = CLAMP(shared->page, 0, shared->max - shared->min);
set_value(shared->val);
- _validate_values();
shared->emit_changed("max");
}
@@ -148,13 +145,13 @@ void Range::set_step(double p_step) {
}
void Range::set_page(double p_page) {
- if (shared->page == p_page) {
+ double page_validated = CLAMP(p_page, 0, shared->max - shared->min);
+ if (shared->page == page_validated) {
return;
}
- shared->page = p_page;
+ shared->page = page_validated;
set_value(shared->val);
- _validate_values();
shared->emit_changed("page");
}
diff --git a/scene/gui/range.h b/scene/gui/range.h
index f804155dec..5267216f12 100644
--- a/scene/gui/range.h
+++ b/scene/gui/range.h
@@ -59,7 +59,6 @@ class Range : public Control {
void _value_changed_notify();
void _changed_notify(const char *p_what = "");
- void _validate_values();
protected:
virtual void _value_changed(double p_value);
diff --git a/scene/gui/rich_text_effect.cpp b/scene/gui/rich_text_effect.cpp
index 0dece1c287..20d82095a1 100644
--- a/scene/gui/rich_text_effect.cpp
+++ b/scene/gui/rich_text_effect.cpp
@@ -88,6 +88,9 @@ void CharFXTransform::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_glyph_index"), &CharFXTransform::get_glyph_index);
ClassDB::bind_method(D_METHOD("set_glyph_index", "glyph_index"), &CharFXTransform::set_glyph_index);
+ ClassDB::bind_method(D_METHOD("get_relative_index"), &CharFXTransform::get_relative_index);
+ ClassDB::bind_method(D_METHOD("set_relative_index", "relative_index"), &CharFXTransform::set_relative_index);
+
ClassDB::bind_method(D_METHOD("get_glyph_count"), &CharFXTransform::get_glyph_count);
ClassDB::bind_method(D_METHOD("set_glyph_count", "glyph_count"), &CharFXTransform::set_glyph_count);
@@ -107,5 +110,6 @@ void CharFXTransform::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "glyph_index"), "set_glyph_index", "get_glyph_index");
ADD_PROPERTY(PropertyInfo(Variant::INT, "glyph_count"), "set_glyph_count", "get_glyph_count");
ADD_PROPERTY(PropertyInfo(Variant::INT, "glyph_flags"), "set_glyph_flags", "get_glyph_flags");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "relative_index"), "set_relative_index", "get_relative_index");
ADD_PROPERTY(PropertyInfo(Variant::RID, "font"), "set_font", "get_font");
}
diff --git a/scene/gui/rich_text_effect.h b/scene/gui/rich_text_effect.h
index 4532a812ee..66b8a21760 100644
--- a/scene/gui/rich_text_effect.h
+++ b/scene/gui/rich_text_effect.h
@@ -52,6 +52,7 @@ public:
uint32_t glyph_index = 0;
uint16_t glyph_flags = 0;
uint8_t glyph_count = 0;
+ int32_t relative_index = 0;
RID font;
CharFXTransform();
@@ -84,6 +85,9 @@ public:
uint8_t get_glyph_count() const { return glyph_count; };
void set_glyph_count(uint8_t p_glyph_count) { glyph_count = p_glyph_count; };
+ int32_t get_relative_index() const { return relative_index; };
+ void set_relative_index(int32_t p_relative_index) { relative_index = p_relative_index; };
+
RID get_font() const { return font; };
void set_font(RID p_font) { font = p_font; };
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 642a94b23e..df41863e74 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -890,7 +890,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
Color odd_row_bg = theme_cache.table_odd_row_bg;
Color even_row_bg = theme_cache.table_even_row_bg;
Color border = theme_cache.table_border;
- int hseparation = theme_cache.table_h_separation;
+ int h_separation = theme_cache.table_h_separation;
int col_count = table->columns.size();
int row_count = table->rows.size();
@@ -908,11 +908,11 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
coff.x = rect.size.width - table->columns[col].width - coff.x;
}
if (row % 2 == 0) {
- draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position, Size2(table->columns[col].width + hseparation + frame->padding.position.x + frame->padding.size.x, table->rows[row])), (frame->odd_row_bg != Color(0, 0, 0, 0) ? frame->odd_row_bg : odd_row_bg), true);
+ draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position, Size2(table->columns[col].width + h_separation + frame->padding.position.x + frame->padding.size.x, table->rows[row])), (frame->odd_row_bg != Color(0, 0, 0, 0) ? frame->odd_row_bg : odd_row_bg), true);
} else {
- draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position, Size2(table->columns[col].width + hseparation + frame->padding.position.x + frame->padding.size.x, table->rows[row])), (frame->even_row_bg != Color(0, 0, 0, 0) ? frame->even_row_bg : even_row_bg), true);
+ draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position, Size2(table->columns[col].width + h_separation + frame->padding.position.x + frame->padding.size.x, table->rows[row])), (frame->even_row_bg != Color(0, 0, 0, 0) ? frame->even_row_bg : even_row_bg), true);
}
- draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position, Size2(table->columns[col].width + hseparation + frame->padding.position.x + frame->padding.size.x, table->rows[row])), (frame->border != Color(0, 0, 0, 0) ? frame->border : border), false);
+ draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position, Size2(table->columns[col].width + h_separation + frame->padding.position.x + frame->padding.size.x, table->rows[row])), (frame->border != Color(0, 0, 0, 0) ? frame->border : border), false);
}
for (int j = 0; j < (int)frame->lines.size(); j++) {
@@ -1005,6 +1005,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
if (!custom_effect.is_null()) {
charfx->elapsed_time = item_custom->elapsed_time;
charfx->range = Vector2i(l.char_offset + glyphs[i].start, l.char_offset + glyphs[i].end);
+ charfx->relative_index = l.char_offset + glyphs[i].start - item_fx->char_ofs;
charfx->visibility = txt_visible;
charfx->outline = true;
charfx->font = frid;
@@ -1222,6 +1223,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
if (!custom_effect.is_null()) {
charfx->elapsed_time = item_custom->elapsed_time;
charfx->range = Vector2i(l.char_offset + glyphs[i].start, l.char_offset + glyphs[i].end);
+ charfx->relative_index = l.char_offset + glyphs[i].start - item_fx->char_ofs;
charfx->visibility = txt_visible;
charfx->outline = false;
charfx->font = frid;
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 2da76883b4..93c910a7f0 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -1337,14 +1337,14 @@ Size2 TreeItem::get_minimum_size(int p_column) {
// Icon.
if (cell.mode == CELL_MODE_CHECK) {
- size.width += parent_tree->theme_cache.checked->get_width() + parent_tree->theme_cache.hseparation;
+ size.width += parent_tree->theme_cache.checked->get_width() + parent_tree->theme_cache.h_separation;
}
if (cell.icon.is_valid()) {
Size2i icon_size = cell.get_icon_size();
if (cell.icon_max_w > 0 && icon_size.width > cell.icon_max_w) {
icon_size.width = cell.icon_max_w;
}
- size.width += icon_size.width + parent_tree->theme_cache.hseparation;
+ size.width += icon_size.width + parent_tree->theme_cache.h_separation;
size.height = MAX(size.height, icon_size.height);
}
@@ -1624,8 +1624,8 @@ void Tree::_update_theme_item_cache() {
theme_cache.font_color = get_theme_color(SNAME("font_color"));
theme_cache.font_selected_color = get_theme_color(SNAME("font_selected_color"));
theme_cache.drop_position_color = get_theme_color(SNAME("drop_position_color"));
- theme_cache.hseparation = get_theme_constant(SNAME("h_separation"));
- theme_cache.vseparation = get_theme_constant(SNAME("v_separation"));
+ theme_cache.h_separation = get_theme_constant(SNAME("h_separation"));
+ theme_cache.v_separation = get_theme_constant(SNAME("v_separation"));
theme_cache.item_margin = get_theme_constant(SNAME("item_margin"));
theme_cache.button_margin = get_theme_constant(SNAME("button_margin"));
@@ -1710,7 +1710,7 @@ int Tree::compute_item_height(TreeItem *p_item) const {
height = item_min_height;
}
- height += theme_cache.vseparation;
+ height += theme_cache.v_separation;
return height;
}
@@ -1720,7 +1720,7 @@ int Tree::get_item_height(TreeItem *p_item) const {
return 0;
}
int height = compute_item_height(p_item);
- height += theme_cache.vseparation;
+ height += theme_cache.v_separation;
if (!p_item->collapsed) { /* if not collapsed, check the children */
@@ -1749,7 +1749,7 @@ void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Co
if (p_cell.icon_max_w > 0 && bmsize.width > p_cell.icon_max_w) {
bmsize.width = p_cell.icon_max_w;
}
- w += bmsize.width + theme_cache.hseparation;
+ w += bmsize.width + theme_cache.h_separation;
if (rect.size.width > 0 && (w + ts.width) > rect.size.width) {
ts.width = rect.size.width - w;
}
@@ -1783,8 +1783,8 @@ void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Co
p_cell.text_buf->draw_outline(ci, draw_pos, p_ol_size, p_ol_color);
}
p_cell.text_buf->draw(ci, draw_pos, p_color);
- rect.position.x += ts.width + theme_cache.hseparation;
- rect.size.x -= ts.width + theme_cache.hseparation;
+ rect.position.x += ts.width + theme_cache.h_separation;
+ rect.size.x -= ts.width + theme_cache.h_separation;
}
if (!p_cell.icon.is_null()) {
@@ -1796,8 +1796,8 @@ void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Co
}
p_cell.draw_icon(ci, rect.position + Size2i(0, Math::floor((real_t)(rect.size.y - bmsize.y) / 2)), bmsize, p_icon_color);
- rect.position.x += bmsize.x + theme_cache.hseparation;
- rect.size.x -= bmsize.x + theme_cache.hseparation;
+ rect.position.x += bmsize.x + theme_cache.h_separation;
+ rect.size.x -= bmsize.x + theme_cache.h_separation;
}
if (!rtl) {
@@ -1911,7 +1911,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
bool rtl = cache.rtl;
/* Calculate height of the label part */
- label_h += theme_cache.vseparation;
+ label_h += theme_cache.v_separation;
/* Draw label, if height fits */
@@ -1922,7 +1922,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
ERR_FAIL_COND_V(theme_cache.font.is_null(), -1);
- int ofs = p_pos.x + ((p_item->disable_folding || hide_folding) ? theme_cache.hseparation : theme_cache.item_margin);
+ int ofs = p_pos.x + ((p_item->disable_folding || hide_folding) ? theme_cache.h_separation : theme_cache.item_margin);
int skip2 = 0;
for (int i = 0; i < columns.size(); i++) {
if (skip2) {
@@ -1940,8 +1940,8 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
continue;
}
} else {
- ofs += theme_cache.hseparation;
- w -= theme_cache.hseparation;
+ ofs += theme_cache.h_separation;
+ w -= theme_cache.h_separation;
}
if (p_item->cells[i].expand_right) {
@@ -1998,8 +1998,8 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
Rect2i item_rect = Rect2i(Point2i(ofs, p_pos.y) - theme_cache.offset + p_draw_ofs, Size2i(w, label_h));
Rect2i cell_rect = item_rect;
if (i != 0) {
- cell_rect.position.x -= theme_cache.hseparation;
- cell_rect.size.x += theme_cache.hseparation;
+ cell_rect.position.x -= theme_cache.h_separation;
+ cell_rect.size.x += theme_cache.h_separation;
}
if (theme_cache.draw_guides) {
@@ -2051,8 +2051,8 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
r.position.x = p_draw_ofs.x;
r.size.x = w + ofs;
} else {
- r.position.x -= theme_cache.hseparation;
- r.size.x += theme_cache.hseparation;
+ r.position.x -= theme_cache.h_separation;
+ r.size.x += theme_cache.h_separation;
}
if (rtl) {
r.position.x = get_size().width - r.position.x - r.size.x;
@@ -2136,7 +2136,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
unchecked->draw(ci, check_ofs);
}
- int check_w = checked->get_width() + theme_cache.hseparation;
+ int check_w = checked->get_width() + theme_cache.h_separation;
text_pos.x += check_w;
@@ -2328,7 +2328,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
// Draw relationship lines.
if (theme_cache.draw_relationship_lines > 0 && (!hide_root || c->parent != root) && c->is_visible()) {
- int root_ofs = children_pos.x + ((p_item->disable_folding || hide_folding) ? theme_cache.hseparation : theme_cache.item_margin);
+ int root_ofs = children_pos.x + ((p_item->disable_folding || hide_folding) ? theme_cache.h_separation : theme_cache.item_margin);
int parent_ofs = p_pos.x + theme_cache.item_margin;
Point2i root_pos = Point2i(root_ofs, children_pos.y + label_h / 2) - theme_cache.offset + p_draw_ofs;
@@ -2615,7 +2615,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
return 0;
}
- int item_h = compute_item_height(p_item) + theme_cache.vseparation;
+ int item_h = compute_item_height(p_item) + theme_cache.v_separation;
bool skip = (p_item == root && hide_root);
@@ -2649,7 +2649,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
if (p_item->cells[i].expand_right) {
int plus = 1;
while (i + plus < columns.size() && !p_item->cells[i + plus].editable && p_item->cells[i + plus].mode == TreeItem::CELL_MODE_STRING && p_item->cells[i + plus].text.is_empty() && p_item->cells[i + plus].icon.is_null()) {
- col_width += theme_cache.hseparation;
+ col_width += theme_cache.h_separation;
col_width += get_column_width(i + plus);
plus++;
}
@@ -2669,16 +2669,16 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
if (col == -1) {
return -1;
} else if (col == 0) {
- int margin = x_ofs + theme_cache.item_margin; //-theme_cache.hseparation;
+ int margin = x_ofs + theme_cache.item_margin; //-theme_cache.h_separation;
//int lm = theme_cache.panel_style->get_margin(SIDE_LEFT);
col_width -= margin;
limit_w -= margin;
col_ofs += margin;
x -= margin;
} else {
- col_width -= theme_cache.hseparation;
- limit_w -= theme_cache.hseparation;
- x -= theme_cache.hseparation;
+ col_width -= theme_cache.h_separation;
+ limit_w -= theme_cache.h_separation;
+ x -= theme_cache.h_separation;
}
if (!p_item->disable_folding && !hide_folding && !p_item->cells[col].editable && !p_item->cells[col].selectable && p_item->get_first_child()) {
@@ -4426,7 +4426,7 @@ int Tree::get_column_minimum_width(int p_column) const {
if (p_column == 0) {
item_size.width += theme_cache.item_margin * depth;
} else {
- item_size.width += theme_cache.hseparation;
+ item_size.width += theme_cache.h_separation;
}
// Check if the item is wider.
@@ -4522,7 +4522,7 @@ int Tree::get_item_offset(TreeItem *p_item) const {
ofs += compute_item_height(it);
if (it != root || !hide_root) {
- ofs += theme_cache.vseparation;
+ ofs += theme_cache.v_separation;
}
if (it->first_child && !it->collapsed) {
@@ -4561,7 +4561,7 @@ void Tree::ensure_cursor_is_visible() {
const int tbh = _get_title_button_height();
y_offset -= tbh;
- const int cell_h = compute_item_height(selected_item) + theme_cache.vseparation;
+ const int cell_h = compute_item_height(selected_item) + theme_cache.v_separation;
int screen_h = area_size.height - tbh;
if (h_scroll->is_visible()) {
screen_h -= h_scroll->get_combined_minimum_size().height;
@@ -4727,7 +4727,7 @@ void Tree::scroll_to_item(TreeItem *p_item, bool p_center_on_item) {
const int tbh = _get_title_button_height();
y_offset -= tbh;
- const int cell_h = compute_item_height(p_item) + theme_cache.vseparation;
+ const int cell_h = compute_item_height(p_item) + theme_cache.v_separation;
int screen_h = area_size.height - tbh;
if (h_scroll->is_visible()) {
screen_h -= h_scroll->get_combined_minimum_size().height;
@@ -4855,7 +4855,7 @@ TreeItem *Tree::_find_item_at_pos(TreeItem *p_item, const Point2 &p_pos, int &r_
Point2 pos = p_pos;
if ((root != p_item || !hide_root) && p_item->is_visible()) {
- h = compute_item_height(p_item) + theme_cache.vseparation;
+ h = compute_item_height(p_item) + theme_cache.v_separation;
if (pos.y < h) {
if (drop_mode_flags == DROP_MODE_ON_ITEM) {
section = 0;
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index 77a62e1d6a..cdd90fe4c7 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -531,8 +531,8 @@ private:
float base_scale = 1.0;
- int hseparation = 0;
- int vseparation = 0;
+ int h_separation = 0;
+ int v_separation = 0;
int item_margin = 0;
int button_margin = 0;
Point2 offset;
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index 7bcd4721fc..2563fa5914 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -450,6 +450,39 @@ void CanvasItem::item_rect_changed(bool p_size_changed) {
emit_signal(SceneStringNames::get_singleton()->item_rect_changed);
}
+void CanvasItem::set_z_index(int p_z) {
+ ERR_FAIL_COND(p_z < RS::CANVAS_ITEM_Z_MIN);
+ ERR_FAIL_COND(p_z > RS::CANVAS_ITEM_Z_MAX);
+ z_index = p_z;
+ RS::get_singleton()->canvas_item_set_z_index(canvas_item, z_index);
+ update_configuration_warnings();
+}
+
+void CanvasItem::set_z_as_relative(bool p_enabled) {
+ if (z_relative == p_enabled) {
+ return;
+ }
+ z_relative = p_enabled;
+ RS::get_singleton()->canvas_item_set_z_as_relative_to_parent(canvas_item, p_enabled);
+}
+
+bool CanvasItem::is_z_relative() const {
+ return z_relative;
+}
+
+int CanvasItem::get_z_index() const {
+ return z_index;
+}
+
+void CanvasItem::set_y_sort_enabled(bool p_enabled) {
+ y_sort_enabled = p_enabled;
+ RS::get_singleton()->canvas_item_set_sort_children_by_y(canvas_item, y_sort_enabled);
+}
+
+bool CanvasItem::is_y_sort_enabled() const {
+ return y_sort_enabled;
+}
+
void CanvasItem::draw_dashed_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width, real_t p_dash) {
ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
@@ -913,9 +946,19 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_modulate", "modulate"), &CanvasItem::set_modulate);
ClassDB::bind_method(D_METHOD("get_modulate"), &CanvasItem::get_modulate);
+
ClassDB::bind_method(D_METHOD("set_self_modulate", "self_modulate"), &CanvasItem::set_self_modulate);
ClassDB::bind_method(D_METHOD("get_self_modulate"), &CanvasItem::get_self_modulate);
+ ClassDB::bind_method(D_METHOD("set_z_index", "z_index"), &Node2D::set_z_index);
+ ClassDB::bind_method(D_METHOD("get_z_index"), &Node2D::get_z_index);
+
+ ClassDB::bind_method(D_METHOD("set_z_as_relative", "enable"), &Node2D::set_z_as_relative);
+ ClassDB::bind_method(D_METHOD("is_z_relative"), &Node2D::is_z_relative);
+
+ ClassDB::bind_method(D_METHOD("set_y_sort_enabled", "enabled"), &Node2D::set_y_sort_enabled);
+ ClassDB::bind_method(D_METHOD("is_y_sort_enabled"), &Node2D::is_y_sort_enabled);
+
ClassDB::bind_method(D_METHOD("set_draw_behind_parent", "enable"), &CanvasItem::set_draw_behind_parent);
ClassDB::bind_method(D_METHOD("is_draw_behind_parent_enabled"), &CanvasItem::is_draw_behind_parent_enabled);
@@ -1005,6 +1048,11 @@ void CanvasItem::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_light_mask", "get_light_mask");
ADD_PROPERTY(PropertyInfo(Variant::INT, "visibility_layer", PROPERTY_HINT_LAYERS_2D_RENDER), "set_visibility_layer", "get_visibility_layer");
+ ADD_GROUP("Ordering", "");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "z_index", PROPERTY_HINT_RANGE, itos(RS::CANVAS_ITEM_Z_MIN) + "," + itos(RS::CANVAS_ITEM_Z_MAX) + ",1"), "set_z_index", "get_z_index");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "z_as_relative"), "set_z_as_relative", "is_z_relative");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "y_sort_enabled"), "set_y_sort_enabled", "is_y_sort_enabled");
+
ADD_GROUP("Texture", "texture_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Inherit,Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Anisotropic,Linear Mipmap Anisotropic"), "set_texture_filter", "get_texture_filter");
ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_repeat", PROPERTY_HINT_ENUM, "Inherit,Disabled,Enabled,Mirror"), "set_texture_repeat", "get_texture_repeat");
diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h
index 4e78a175dc..4ace982825 100644
--- a/scene/main/canvas_item.h
+++ b/scene/main/canvas_item.h
@@ -91,6 +91,10 @@ private:
int light_mask = 1;
uint32_t visibility_layer = 1;
+ int z_index = 0;
+ bool z_relative = true;
+ bool y_sort_enabled = false;
+
Window *window = nullptr;
bool visible = true;
bool parent_visible_in_tree = false;
@@ -230,6 +234,17 @@ public:
void set_visibility_layer_bit(uint32_t p_visibility_layer, bool p_enable);
bool get_visibility_layer_bit(uint32_t p_visibility_layer) const;
+ /* ORDERING */
+
+ void set_z_index(int p_z);
+ int get_z_index() const;
+
+ void set_z_as_relative(bool p_enabled);
+ bool is_z_relative() const;
+
+ virtual void set_y_sort_enabled(bool p_enabled);
+ virtual bool is_y_sort_enabled() const;
+
/* DRAWING API */
void draw_dashed_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width = 1.0, real_t p_dash = 2.0);
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 7430a0a835..fdbcb20d30 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1880,9 +1880,11 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
Ref<InputEventScreenTouch> touch_event = p_event;
if (touch_event.is_valid()) {
Size2 pos = touch_event->get_position();
+ const int touch_index = touch_event->get_index();
if (touch_event->is_pressed()) {
Control *over = gui_find_control(pos);
if (over) {
+ gui.touch_focus[touch_index] = over->get_instance_id();
bool stopped = false;
if (over->can_process()) {
touch_event = touch_event->xformed_by(Transform2D()); // Make a copy.
@@ -1899,17 +1901,25 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
return;
}
- } else if (touch_event->get_index() == 0 && gui.last_mouse_focus) {
+ } else {
bool stopped = false;
- if (gui.last_mouse_focus->can_process()) {
+ ObjectID control_id = gui.touch_focus[touch_index];
+ Control *over = control_id.is_valid() ? Object::cast_to<Control>(ObjectDB::get_instance(control_id)) : nullptr;
+ if (over && over->can_process()) {
touch_event = touch_event->xformed_by(Transform2D()); // Make a copy.
- touch_event->set_position(gui.focus_inv_xform.xform(pos));
+ if (over == gui.last_mouse_focus) {
+ pos = gui.focus_inv_xform.xform(pos);
+ } else {
+ pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos);
+ }
+ touch_event->set_position(pos);
- stopped = _gui_call_input(gui.last_mouse_focus, touch_event);
+ stopped = _gui_call_input(over, touch_event);
}
if (stopped) {
set_input_as_handled();
}
+ gui.touch_focus.erase(touch_index);
return;
}
}
@@ -1944,7 +1954,9 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
Ref<InputEventScreenDrag> drag_event = p_event;
if (drag_event.is_valid()) {
- Control *over = gui.mouse_focus;
+ const int drag_event_index = drag_event->get_index();
+ ObjectID control_id = gui.touch_focus[drag_event_index];
+ Control *over = control_id.is_valid() ? Object::cast_to<Control>(ObjectDB::get_instance(control_id)) : nullptr;
if (!over) {
over = gui_find_control(drag_event->get_position());
}
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 5659ee4000..bc8cd54603 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -354,6 +354,7 @@ private:
bool forced_mouse_focus = false; //used for menu buttons
bool mouse_in_viewport = true;
bool key_event_accepted = false;
+ HashMap<int, ObjectID> touch_focus;
Control *mouse_focus = nullptr;
Control *last_mouse_focus = nullptr;
Control *mouse_click_grabber = nullptr;
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index ed9a709382..3967858d47 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -2726,40 +2726,60 @@ Animation::UpdateMode Animation::value_track_get_update_mode(int p_track) const
}
template <class T>
-void Animation::_track_get_key_indices_in_range(const Vector<T> &p_array, double from_time, double to_time, List<int> *p_indices) const {
- if (to_time == length) {
- to_time = length + CMP_EPSILON; //include a little more if at the end
+void Animation::_track_get_key_indices_in_range(const Vector<T> &p_array, double from_time, double to_time, List<int> *p_indices, bool p_is_backward) const {
+ int len = p_array.size();
+ if (len == 0) {
+ return;
}
- int to = _find(p_array, to_time);
-
- // can't really send the events == time, will be sent in the next frame.
- // if event>=len then it will probably never be requested by the anim player.
-
- if (to >= 0 && p_array[to].time >= to_time) {
- to--;
- }
+ int from = 0;
+ int to = len - 1;
- if (to < 0) {
- return; // not bother
+ if (!p_is_backward) {
+ while (p_array[from].time < from_time || Math::is_equal_approx(p_array[from].time, from_time)) {
+ from++;
+ if (to < from) {
+ return;
+ }
+ }
+ while (p_array[to].time > to_time && !Math::is_equal_approx(p_array[to].time, to_time)) {
+ to--;
+ if (to < from) {
+ return;
+ }
+ }
+ } else {
+ while (p_array[from].time < from_time && !Math::is_equal_approx(p_array[from].time, from_time)) {
+ from++;
+ if (to < from) {
+ return;
+ }
+ }
+ while (p_array[to].time > to_time || Math::is_equal_approx(p_array[to].time, to_time)) {
+ to--;
+ if (to < from) {
+ return;
+ }
+ }
}
- int from = _find(p_array, from_time);
-
- // position in the right first event.+
- if (from < 0 || p_array[from].time < from_time) {
- from++;
+ if (from == to) {
+ p_indices->push_back(from);
+ return;
}
- int max = p_array.size();
-
- for (int i = from; i <= to; i++) {
- ERR_CONTINUE(i < 0 || i >= max); // shouldn't happen
- p_indices->push_back(i);
+ if (!p_is_backward) {
+ for (int i = from; i <= to; i++) {
+ p_indices->push_back(i);
+ }
+ } else {
+ for (int i = to; i >= to; i--) {
+ p_indices->push_back(i);
+ }
}
}
-void Animation::track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged) const {
+void Animation::track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices, Animation::LoopedFlag p_looped_flag) const {
ERR_FAIL_INDEX(p_track, tracks.size());
if (p_delta == 0) {
@@ -2771,7 +2791,9 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
double from_time = p_time - p_delta;
double to_time = p_time;
+ bool is_backward = false;
if (from_time > to_time) {
+ is_backward = true;
SWAP(from_time, to_time);
}
@@ -2800,7 +2822,10 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
}
if (from_time > to_time) {
- // handle loop by splitting
+ // Handle loop by splitting.
+ double anim_end = length + CMP_EPSILON;
+ double anim_start = -CMP_EPSILON;
+
switch (t->type) {
case TYPE_POSITION_3D: {
const PositionTrack *tt = static_cast<const PositionTrack *>(t);
@@ -2808,8 +2833,13 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
_get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, length, p_indices);
_get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, to_time, p_indices);
} else {
- _track_get_key_indices_in_range(tt->positions, from_time, length, p_indices);
- _track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices);
+ if (!is_backward) {
+ _track_get_key_indices_in_range(tt->positions, from_time, anim_end, p_indices, is_backward);
+ _track_get_key_indices_in_range(tt->positions, anim_start, to_time, p_indices, is_backward);
+ } else {
+ _track_get_key_indices_in_range(tt->positions, anim_start, to_time, p_indices, is_backward);
+ _track_get_key_indices_in_range(tt->positions, from_time, anim_end, p_indices, is_backward);
+ }
}
} break;
case TYPE_ROTATION_3D: {
@@ -2818,8 +2848,13 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
_get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, length, p_indices);
_get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, to_time, p_indices);
} else {
- _track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices);
- _track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices);
+ if (!is_backward) {
+ _track_get_key_indices_in_range(rt->rotations, from_time, anim_end, p_indices, is_backward);
+ _track_get_key_indices_in_range(rt->rotations, anim_start, to_time, p_indices, is_backward);
+ } else {
+ _track_get_key_indices_in_range(rt->rotations, anim_start, to_time, p_indices, is_backward);
+ _track_get_key_indices_in_range(rt->rotations, from_time, anim_end, p_indices, is_backward);
+ }
}
} break;
case TYPE_SCALE_3D: {
@@ -2828,8 +2863,13 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
_get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, length, p_indices);
_get_compressed_key_indices_in_range<3>(st->compressed_track, 0, to_time, p_indices);
} else {
- _track_get_key_indices_in_range(st->scales, from_time, length, p_indices);
- _track_get_key_indices_in_range(st->scales, 0, to_time, p_indices);
+ if (!is_backward) {
+ _track_get_key_indices_in_range(st->scales, from_time, anim_end, p_indices, is_backward);
+ _track_get_key_indices_in_range(st->scales, anim_start, to_time, p_indices, is_backward);
+ } else {
+ _track_get_key_indices_in_range(st->scales, anim_start, to_time, p_indices, is_backward);
+ _track_get_key_indices_in_range(st->scales, from_time, anim_end, p_indices, is_backward);
+ }
}
} break;
case TYPE_BLEND_SHAPE: {
@@ -2838,38 +2878,83 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
_get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, length, p_indices);
_get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, to_time, p_indices);
} else {
- _track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices);
- _track_get_key_indices_in_range(bst->blend_shapes, 0, to_time, p_indices);
+ if (!is_backward) {
+ _track_get_key_indices_in_range(bst->blend_shapes, from_time, anim_end, p_indices, is_backward);
+ _track_get_key_indices_in_range(bst->blend_shapes, anim_start, to_time, p_indices, is_backward);
+ } else {
+ _track_get_key_indices_in_range(bst->blend_shapes, anim_start, to_time, p_indices, is_backward);
+ _track_get_key_indices_in_range(bst->blend_shapes, from_time, anim_end, p_indices, is_backward);
+ }
}
} break;
case TYPE_VALUE: {
const ValueTrack *vt = static_cast<const ValueTrack *>(t);
- _track_get_key_indices_in_range(vt->values, from_time, length, p_indices);
- _track_get_key_indices_in_range(vt->values, 0, to_time, p_indices);
+ if (!is_backward) {
+ _track_get_key_indices_in_range(vt->values, from_time, anim_end, p_indices, is_backward);
+ _track_get_key_indices_in_range(vt->values, anim_start, to_time, p_indices, is_backward);
+ } else {
+ _track_get_key_indices_in_range(vt->values, anim_start, to_time, p_indices, is_backward);
+ _track_get_key_indices_in_range(vt->values, from_time, anim_end, p_indices, is_backward);
+ }
} break;
case TYPE_METHOD: {
const MethodTrack *mt = static_cast<const MethodTrack *>(t);
- _track_get_key_indices_in_range(mt->methods, from_time, length, p_indices);
- _track_get_key_indices_in_range(mt->methods, 0, to_time, p_indices);
+ if (!is_backward) {
+ _track_get_key_indices_in_range(mt->methods, from_time, anim_end, p_indices, is_backward);
+ _track_get_key_indices_in_range(mt->methods, anim_start, to_time, p_indices, is_backward);
+ } else {
+ _track_get_key_indices_in_range(mt->methods, anim_start, to_time, p_indices, is_backward);
+ _track_get_key_indices_in_range(mt->methods, from_time, anim_end, p_indices, is_backward);
+ }
} break;
case TYPE_BEZIER: {
const BezierTrack *bz = static_cast<const BezierTrack *>(t);
- _track_get_key_indices_in_range(bz->values, from_time, length, p_indices);
- _track_get_key_indices_in_range(bz->values, 0, to_time, p_indices);
+ if (!is_backward) {
+ _track_get_key_indices_in_range(bz->values, from_time, anim_end, p_indices, is_backward);
+ _track_get_key_indices_in_range(bz->values, anim_start, to_time, p_indices, is_backward);
+ } else {
+ _track_get_key_indices_in_range(bz->values, anim_start, to_time, p_indices, is_backward);
+ _track_get_key_indices_in_range(bz->values, from_time, anim_end, p_indices, is_backward);
+ }
} break;
case TYPE_AUDIO: {
const AudioTrack *ad = static_cast<const AudioTrack *>(t);
- _track_get_key_indices_in_range(ad->values, from_time, length, p_indices);
- _track_get_key_indices_in_range(ad->values, 0, to_time, p_indices);
+ if (!is_backward) {
+ _track_get_key_indices_in_range(ad->values, from_time, anim_end, p_indices, is_backward);
+ _track_get_key_indices_in_range(ad->values, anim_start, to_time, p_indices, is_backward);
+ } else {
+ _track_get_key_indices_in_range(ad->values, anim_start, to_time, p_indices, is_backward);
+ _track_get_key_indices_in_range(ad->values, from_time, anim_end, p_indices, is_backward);
+ }
} break;
case TYPE_ANIMATION: {
const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
- _track_get_key_indices_in_range(an->values, from_time, length, p_indices);
- _track_get_key_indices_in_range(an->values, 0, to_time, p_indices);
+ if (!is_backward) {
+ _track_get_key_indices_in_range(an->values, from_time, anim_end, p_indices, is_backward);
+ _track_get_key_indices_in_range(an->values, anim_start, to_time, p_indices, is_backward);
+ } else {
+ _track_get_key_indices_in_range(an->values, anim_start, to_time, p_indices, is_backward);
+ _track_get_key_indices_in_range(an->values, from_time, anim_end, p_indices, is_backward);
+ }
} break;
}
return;
}
+
+ // Not from_time > to_time but most recent of looping...
+ if (p_looped_flag != Animation::LOOPED_FLAG_NONE) {
+ if (!is_backward && Math::is_equal_approx(from_time, 0)) {
+ int edge = track_find_key(p_track, 0, true);
+ if (edge >= 0) {
+ p_indices->push_back(edge);
+ }
+ } else if (is_backward && Math::is_equal_approx(to_time, length)) {
+ int edge = track_find_key(p_track, length, true);
+ if (edge >= 0) {
+ p_indices->push_back(edge);
+ }
+ }
+ }
} break;
case LOOP_PINGPONG: {
if (from_time > length || from_time < 0) {
@@ -2879,162 +2964,164 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
to_time = Math::pingpong(to_time, length);
}
- if ((int)Math::floor(abs(p_delta) / length) % 2 == 0) {
- if (p_pingponged == -1) {
- // handle loop by splitting
- to_time = MAX(CMP_EPSILON, to_time); // To avoid overlapping keys at the turnaround point, one of the point will needs to be shifted slightly.
- switch (t->type) {
- case TYPE_POSITION_3D: {
- const PositionTrack *tt = static_cast<const PositionTrack *>(t);
- if (tt->compressed_track >= 0) {
- _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, from_time, p_indices);
- _get_compressed_key_indices_in_range<3>(tt->compressed_track, CMP_EPSILON, to_time, p_indices);
- } else {
- _track_get_key_indices_in_range(tt->positions, 0, from_time, p_indices);
- _track_get_key_indices_in_range(tt->positions, CMP_EPSILON, to_time, p_indices);
- }
- } break;
- case TYPE_ROTATION_3D: {
- const RotationTrack *rt = static_cast<const RotationTrack *>(t);
- if (rt->compressed_track >= 0) {
- _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, from_time, p_indices);
- _get_compressed_key_indices_in_range<3>(rt->compressed_track, CMP_EPSILON, to_time, p_indices);
- } else {
- _track_get_key_indices_in_range(rt->rotations, 0, from_time, p_indices);
- _track_get_key_indices_in_range(rt->rotations, CMP_EPSILON, to_time, p_indices);
- }
- } break;
- case TYPE_SCALE_3D: {
- const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
- if (st->compressed_track >= 0) {
- _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, from_time, p_indices);
- _get_compressed_key_indices_in_range<3>(st->compressed_track, CMP_EPSILON, to_time, p_indices);
- } else {
- _track_get_key_indices_in_range(st->scales, 0, from_time, p_indices);
- _track_get_key_indices_in_range(st->scales, CMP_EPSILON, to_time, p_indices);
- }
- } break;
- case TYPE_BLEND_SHAPE: {
- const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
- if (bst->compressed_track >= 0) {
- _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, from_time, p_indices);
- _get_compressed_key_indices_in_range<1>(bst->compressed_track, CMP_EPSILON, to_time, p_indices);
- } else {
- _track_get_key_indices_in_range(bst->blend_shapes, 0, from_time, p_indices);
- _track_get_key_indices_in_range(bst->blend_shapes, CMP_EPSILON, to_time, p_indices);
- }
- } break;
- case TYPE_VALUE: {
- const ValueTrack *vt = static_cast<const ValueTrack *>(t);
- _track_get_key_indices_in_range(vt->values, 0, from_time, p_indices);
- _track_get_key_indices_in_range(vt->values, CMP_EPSILON, to_time, p_indices);
- } break;
- case TYPE_METHOD: {
- const MethodTrack *mt = static_cast<const MethodTrack *>(t);
- _track_get_key_indices_in_range(mt->methods, 0, from_time, p_indices);
- _track_get_key_indices_in_range(mt->methods, CMP_EPSILON, to_time, p_indices);
- } break;
- case TYPE_BEZIER: {
- const BezierTrack *bz = static_cast<const BezierTrack *>(t);
- _track_get_key_indices_in_range(bz->values, 0, from_time, p_indices);
- _track_get_key_indices_in_range(bz->values, CMP_EPSILON, to_time, p_indices);
- } break;
- case TYPE_AUDIO: {
- const AudioTrack *ad = static_cast<const AudioTrack *>(t);
- _track_get_key_indices_in_range(ad->values, 0, from_time, p_indices);
- _track_get_key_indices_in_range(ad->values, CMP_EPSILON, to_time, p_indices);
- } break;
- case TYPE_ANIMATION: {
- const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
- _track_get_key_indices_in_range(an->values, 0, from_time, p_indices);
- _track_get_key_indices_in_range(an->values, CMP_EPSILON, to_time, p_indices);
- } break;
- }
- return;
+ if (p_looped_flag == Animation::LOOPED_FLAG_START) {
+ // Handle loop by splitting.
+ switch (t->type) {
+ case TYPE_POSITION_3D: {
+ const PositionTrack *tt = static_cast<const PositionTrack *>(t);
+ if (tt->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, from_time, p_indices);
+ _get_compressed_key_indices_in_range<3>(tt->compressed_track, CMP_EPSILON, to_time, p_indices);
+ } else {
+ _track_get_key_indices_in_range(tt->positions, 0, from_time, p_indices, true);
+ _track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices, false);
+ }
+ } break;
+ case TYPE_ROTATION_3D: {
+ const RotationTrack *rt = static_cast<const RotationTrack *>(t);
+ if (rt->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, from_time, p_indices);
+ _get_compressed_key_indices_in_range<3>(rt->compressed_track, CMP_EPSILON, to_time, p_indices);
+ } else {
+ _track_get_key_indices_in_range(rt->rotations, 0, from_time, p_indices, true);
+ _track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices, false);
+ }
+ } break;
+ case TYPE_SCALE_3D: {
+ const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
+ if (st->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, from_time, p_indices);
+ _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, to_time, p_indices);
+ } else {
+ _track_get_key_indices_in_range(st->scales, 0, from_time, p_indices, true);
+ _track_get_key_indices_in_range(st->scales, 0, to_time, p_indices, false);
+ }
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
+ if (bst->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, from_time, p_indices);
+ _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, to_time, p_indices);
+ } else {
+ _track_get_key_indices_in_range(bst->blend_shapes, 0, from_time, p_indices, true);
+ _track_get_key_indices_in_range(bst->blend_shapes, 0, to_time, p_indices, false);
+ }
+ } break;
+ case TYPE_VALUE: {
+ const ValueTrack *vt = static_cast<const ValueTrack *>(t);
+ _track_get_key_indices_in_range(vt->values, 0, from_time, p_indices, true);
+ _track_get_key_indices_in_range(vt->values, 0, to_time, p_indices, false);
+ } break;
+ case TYPE_METHOD: {
+ const MethodTrack *mt = static_cast<const MethodTrack *>(t);
+ _track_get_key_indices_in_range(mt->methods, 0, from_time, p_indices, true);
+ _track_get_key_indices_in_range(mt->methods, 0, to_time, p_indices, false);
+ } break;
+ case TYPE_BEZIER: {
+ const BezierTrack *bz = static_cast<const BezierTrack *>(t);
+ _track_get_key_indices_in_range(bz->values, 0, from_time, p_indices, true);
+ _track_get_key_indices_in_range(bz->values, 0, to_time, p_indices, false);
+ } break;
+ case TYPE_AUDIO: {
+ const AudioTrack *ad = static_cast<const AudioTrack *>(t);
+ _track_get_key_indices_in_range(ad->values, 0, from_time, p_indices, true);
+ _track_get_key_indices_in_range(ad->values, 0, to_time, p_indices, false);
+ } break;
+ case TYPE_ANIMATION: {
+ const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
+ _track_get_key_indices_in_range(an->values, 0, from_time, p_indices, true);
+ _track_get_key_indices_in_range(an->values, 0, to_time, p_indices, false);
+ } break;
}
- if (p_pingponged == 1) {
- // handle loop by splitting
- to_time = MIN(length - CMP_EPSILON, to_time);
- switch (t->type) {
- case TYPE_POSITION_3D: {
- const PositionTrack *tt = static_cast<const PositionTrack *>(t);
- if (tt->compressed_track >= 0) {
- _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, length, p_indices);
- _get_compressed_key_indices_in_range<3>(tt->compressed_track, to_time, length - CMP_EPSILON, p_indices);
- } else {
- _track_get_key_indices_in_range(tt->positions, from_time, length, p_indices);
- _track_get_key_indices_in_range(tt->positions, to_time, length - CMP_EPSILON, p_indices);
- }
- } break;
- case TYPE_ROTATION_3D: {
- const RotationTrack *rt = static_cast<const RotationTrack *>(t);
- if (rt->compressed_track >= 0) {
- _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, length, p_indices);
- _get_compressed_key_indices_in_range<3>(rt->compressed_track, to_time, length, p_indices);
- } else {
- _track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices);
- _track_get_key_indices_in_range(rt->rotations, to_time, length - CMP_EPSILON, p_indices);
- }
- } break;
- case TYPE_SCALE_3D: {
- const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
- if (st->compressed_track >= 0) {
- _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, length, p_indices);
- _get_compressed_key_indices_in_range<3>(st->compressed_track, to_time, length, p_indices);
- } else {
- _track_get_key_indices_in_range(st->scales, from_time, length, p_indices);
- _track_get_key_indices_in_range(st->scales, to_time, length - CMP_EPSILON, p_indices);
- }
- } break;
- case TYPE_BLEND_SHAPE: {
- const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
- if (bst->compressed_track >= 0) {
- _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, length, p_indices);
- _get_compressed_key_indices_in_range<1>(bst->compressed_track, to_time, length - CMP_EPSILON, p_indices);
- } else {
- _track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices);
- _track_get_key_indices_in_range(bst->blend_shapes, to_time, length - CMP_EPSILON, p_indices);
- }
- } break;
- case TYPE_VALUE: {
- const ValueTrack *vt = static_cast<const ValueTrack *>(t);
- _track_get_key_indices_in_range(vt->values, from_time, length, p_indices);
- _track_get_key_indices_in_range(vt->values, to_time, length - CMP_EPSILON, p_indices);
- } break;
- case TYPE_METHOD: {
- const MethodTrack *mt = static_cast<const MethodTrack *>(t);
- _track_get_key_indices_in_range(mt->methods, from_time, length, p_indices);
- _track_get_key_indices_in_range(mt->methods, to_time, length - CMP_EPSILON, p_indices);
- } break;
- case TYPE_BEZIER: {
- const BezierTrack *bz = static_cast<const BezierTrack *>(t);
- _track_get_key_indices_in_range(bz->values, from_time, length, p_indices);
- _track_get_key_indices_in_range(bz->values, to_time, length - CMP_EPSILON, p_indices);
- } break;
- case TYPE_AUDIO: {
- const AudioTrack *ad = static_cast<const AudioTrack *>(t);
- _track_get_key_indices_in_range(ad->values, from_time, length, p_indices);
- _track_get_key_indices_in_range(ad->values, to_time, length - CMP_EPSILON, p_indices);
- } break;
- case TYPE_ANIMATION: {
- const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
- _track_get_key_indices_in_range(an->values, from_time, length, p_indices);
- _track_get_key_indices_in_range(an->values, to_time, length - CMP_EPSILON, p_indices);
- } break;
- }
- return;
+ return;
+ }
+ if (p_looped_flag == Animation::LOOPED_FLAG_END) {
+ // Handle loop by splitting.
+ switch (t->type) {
+ case TYPE_POSITION_3D: {
+ const PositionTrack *tt = static_cast<const PositionTrack *>(t);
+ if (tt->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<3>(tt->compressed_track, to_time, length, p_indices);
+ } else {
+ _track_get_key_indices_in_range(tt->positions, from_time, length, p_indices, false);
+ _track_get_key_indices_in_range(tt->positions, to_time, length, p_indices, true);
+ }
+ } break;
+ case TYPE_ROTATION_3D: {
+ const RotationTrack *rt = static_cast<const RotationTrack *>(t);
+ if (rt->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<3>(rt->compressed_track, to_time, length, p_indices);
+ } else {
+ _track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices, false);
+ _track_get_key_indices_in_range(rt->rotations, to_time, length, p_indices, true);
+ }
+ } break;
+ case TYPE_SCALE_3D: {
+ const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
+ if (st->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<3>(st->compressed_track, to_time, length, p_indices);
+ } else {
+ _track_get_key_indices_in_range(st->scales, from_time, length, p_indices, false);
+ _track_get_key_indices_in_range(st->scales, to_time, length, p_indices, true);
+ }
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
+ if (bst->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<1>(bst->compressed_track, to_time, length - CMP_EPSILON, p_indices);
+ } else {
+ _track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices, false);
+ _track_get_key_indices_in_range(bst->blend_shapes, to_time, length, p_indices, true);
+ }
+ } break;
+ case TYPE_VALUE: {
+ const ValueTrack *vt = static_cast<const ValueTrack *>(t);
+ _track_get_key_indices_in_range(vt->values, from_time, length, p_indices, false);
+ _track_get_key_indices_in_range(vt->values, to_time, length, p_indices, true);
+ } break;
+ case TYPE_METHOD: {
+ const MethodTrack *mt = static_cast<const MethodTrack *>(t);
+ _track_get_key_indices_in_range(mt->methods, from_time, length, p_indices, false);
+ _track_get_key_indices_in_range(mt->methods, to_time, length, p_indices, true);
+ } break;
+ case TYPE_BEZIER: {
+ const BezierTrack *bz = static_cast<const BezierTrack *>(t);
+ _track_get_key_indices_in_range(bz->values, from_time, length, p_indices, false);
+ _track_get_key_indices_in_range(bz->values, to_time, length, p_indices, true);
+ } break;
+ case TYPE_AUDIO: {
+ const AudioTrack *ad = static_cast<const AudioTrack *>(t);
+ _track_get_key_indices_in_range(ad->values, from_time, length, p_indices, false);
+ _track_get_key_indices_in_range(ad->values, to_time, length, p_indices, true);
+ } break;
+ case TYPE_ANIMATION: {
+ const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
+ _track_get_key_indices_in_range(an->values, from_time, length, p_indices, false);
+ _track_get_key_indices_in_range(an->values, to_time, length, p_indices, true);
+ } break;
}
+ return;
+ }
+
+ // The edge will be pingponged in the next frame and processed there, so let's ignore it now...
+ if (!is_backward && Math::is_equal_approx(to_time, length)) {
+ to_time = length - CMP_EPSILON;
+ } else if (is_backward && Math::is_equal_approx(from_time, 0)) {
+ from_time = CMP_EPSILON;
}
} break;
}
-
switch (t->type) {
case TYPE_POSITION_3D: {
const PositionTrack *tt = static_cast<const PositionTrack *>(t);
if (tt->compressed_track >= 0) {
_get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, to_time - from_time, p_indices);
} else {
- _track_get_key_indices_in_range(tt->positions, from_time, to_time, p_indices);
+ _track_get_key_indices_in_range(tt->positions, from_time, to_time, p_indices, is_backward);
}
} break;
case TYPE_ROTATION_3D: {
@@ -3042,7 +3129,7 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
if (rt->compressed_track >= 0) {
_get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, to_time - from_time, p_indices);
} else {
- _track_get_key_indices_in_range(rt->rotations, from_time, to_time, p_indices);
+ _track_get_key_indices_in_range(rt->rotations, from_time, to_time, p_indices, is_backward);
}
} break;
case TYPE_SCALE_3D: {
@@ -3050,7 +3137,7 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
if (st->compressed_track >= 0) {
_get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, to_time - from_time, p_indices);
} else {
- _track_get_key_indices_in_range(st->scales, from_time, to_time, p_indices);
+ _track_get_key_indices_in_range(st->scales, from_time, to_time, p_indices, is_backward);
}
} break;
case TYPE_BLEND_SHAPE: {
@@ -3058,28 +3145,28 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
if (bst->compressed_track >= 0) {
_get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, to_time - from_time, p_indices);
} else {
- _track_get_key_indices_in_range(bst->blend_shapes, from_time, to_time, p_indices);
+ _track_get_key_indices_in_range(bst->blend_shapes, from_time, to_time, p_indices, is_backward);
}
} break;
case TYPE_VALUE: {
const ValueTrack *vt = static_cast<const ValueTrack *>(t);
- _track_get_key_indices_in_range(vt->values, from_time, to_time, p_indices);
+ _track_get_key_indices_in_range(vt->values, from_time, to_time, p_indices, is_backward);
} break;
case TYPE_METHOD: {
const MethodTrack *mt = static_cast<const MethodTrack *>(t);
- _track_get_key_indices_in_range(mt->methods, from_time, to_time, p_indices);
+ _track_get_key_indices_in_range(mt->methods, from_time, to_time, p_indices, is_backward);
} break;
case TYPE_BEZIER: {
const BezierTrack *bz = static_cast<const BezierTrack *>(t);
- _track_get_key_indices_in_range(bz->values, from_time, to_time, p_indices);
+ _track_get_key_indices_in_range(bz->values, from_time, to_time, p_indices, is_backward);
} break;
case TYPE_AUDIO: {
const AudioTrack *ad = static_cast<const AudioTrack *>(t);
- _track_get_key_indices_in_range(ad->values, from_time, to_time, p_indices);
+ _track_get_key_indices_in_range(ad->values, from_time, to_time, p_indices, is_backward);
} break;
case TYPE_ANIMATION: {
const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
- _track_get_key_indices_in_range(an->values, from_time, to_time, p_indices);
+ _track_get_key_indices_in_range(an->values, from_time, to_time, p_indices, is_backward);
} break;
}
}
@@ -3815,6 +3902,10 @@ void Animation::_bind_methods() {
BIND_ENUM_CONSTANT(LOOP_NONE);
BIND_ENUM_CONSTANT(LOOP_LINEAR);
BIND_ENUM_CONSTANT(LOOP_PINGPONG);
+
+ BIND_ENUM_CONSTANT(LOOPED_FLAG_NONE);
+ BIND_ENUM_CONSTANT(LOOPED_FLAG_END);
+ BIND_ENUM_CONSTANT(LOOPED_FLAG_START);
}
void Animation::clear() {
@@ -5798,7 +5889,8 @@ Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float
}
}
-Animation::Animation() {}
+Animation::Animation() {
+}
Animation::~Animation() {
for (int i = 0; i < tracks.size(); i++) {
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index 6c1ca3cd05..e66af77018 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -74,6 +74,12 @@ public:
LOOP_PINGPONG,
};
+ enum LoopedFlag {
+ LOOPED_FLAG_NONE,
+ LOOPED_FLAG_END,
+ LOOPED_FLAG_START,
+ };
+
#ifdef TOOLS_ENABLED
enum HandleMode {
HANDLE_MODE_FREE,
@@ -250,12 +256,11 @@ private:
_FORCE_INLINE_ T _interpolate(const Vector<TKey<T>> &p_keys, double p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok, bool p_backward = false) const;
template <class T>
- _FORCE_INLINE_ void _track_get_key_indices_in_range(const Vector<T> &p_array, double from_time, double to_time, List<int> *p_indices) const;
+ _FORCE_INLINE_ void _track_get_key_indices_in_range(const Vector<T> &p_array, double from_time, double to_time, List<int> *p_indices, bool p_is_backward) const;
double length = 1.0;
real_t step = 0.1;
LoopMode loop_mode = LOOP_NONE;
- int pingponged = 0;
/* Animation compression page format (version 1):
*
@@ -454,7 +459,7 @@ public:
void copy_track(int p_track, Ref<Animation> p_to_animation);
- void track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged = 0) const;
+ void track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices, Animation::LoopedFlag p_looped_flag = Animation::LOOPED_FLAG_NONE) const;
void set_length(real_t p_length);
real_t get_length() const;
@@ -484,6 +489,7 @@ VARIANT_ENUM_CAST(Animation::TrackType);
VARIANT_ENUM_CAST(Animation::InterpolationType);
VARIANT_ENUM_CAST(Animation::UpdateMode);
VARIANT_ENUM_CAST(Animation::LoopMode);
+VARIANT_ENUM_CAST(Animation::LoopedFlag);
#ifdef TOOLS_ENABLED
VARIANT_ENUM_CAST(Animation::HandleMode);
VARIANT_ENUM_CAST(Animation::HandleSetMode);
diff --git a/scene/resources/animation_library.cpp b/scene/resources/animation_library.cpp
index 427d418551..b37bfbae62 100644
--- a/scene/resources/animation_library.cpp
+++ b/scene/resources/animation_library.cpp
@@ -52,11 +52,13 @@ Error AnimationLibrary::add_animation(const StringName &p_name, const Ref<Animat
ERR_FAIL_COND_V(p_animation.is_null(), ERR_INVALID_PARAMETER);
if (animations.has(p_name)) {
+ animations.get(p_name)->disconnect(SNAME("changed"), callable_mp(this, &AnimationLibrary::_animation_changed));
animations.erase(p_name);
emit_signal(SNAME("animation_removed"), p_name);
}
animations.insert(p_name, p_animation);
+ animations.get(p_name)->connect(SNAME("changed"), callable_mp(this, &AnimationLibrary::_animation_changed).bind(p_name));
emit_signal(SNAME("animation_added"), p_name);
notify_property_list_changed();
return OK;
@@ -65,6 +67,7 @@ Error AnimationLibrary::add_animation(const StringName &p_name, const Ref<Animat
void AnimationLibrary::remove_animation(const StringName &p_name) {
ERR_FAIL_COND_MSG(!animations.has(p_name), vformat("Animation not found: %s.", p_name));
+ animations.get(p_name)->disconnect(SNAME("changed"), callable_mp(this, &AnimationLibrary::_animation_changed));
animations.erase(p_name);
emit_signal(SNAME("animation_removed"), p_name);
notify_property_list_changed();
@@ -75,6 +78,8 @@ void AnimationLibrary::rename_animation(const StringName &p_name, const StringNa
ERR_FAIL_COND_MSG(!is_valid_animation_name(p_new_name), "Invalid animation name: '" + String(p_new_name) + "'.");
ERR_FAIL_COND_MSG(animations.has(p_new_name), vformat("Animation name \"%s\" already exists in library.", p_new_name));
+ animations.get(p_name)->disconnect(SNAME("changed"), callable_mp(this, &AnimationLibrary::_animation_changed));
+ animations.get(p_name)->connect(SNAME("changed"), callable_mp(this, &AnimationLibrary::_animation_changed).bind(p_new_name));
animations.insert(p_new_name, animations[p_name]);
animations.erase(p_name);
emit_signal(SNAME("animation_renamed"), p_name, p_new_name);
@@ -100,6 +105,10 @@ TypedArray<StringName> AnimationLibrary::_get_animation_list() const {
return ret;
}
+void AnimationLibrary::_animation_changed(const StringName &p_name) {
+ emit_signal(SNAME("animation_changed"), p_name);
+}
+
void AnimationLibrary::get_animation_list(List<StringName> *p_animations) const {
List<StringName> anims;
@@ -115,6 +124,9 @@ void AnimationLibrary::get_animation_list(List<StringName> *p_animations) const
}
void AnimationLibrary::_set_data(const Dictionary &p_data) {
+ for (KeyValue<StringName, Ref<Animation>> &K : animations) {
+ K.value->disconnect(SNAME("changed"), callable_mp(this, &AnimationLibrary::_animation_changed));
+ }
animations.clear();
List<Variant> keys;
p_data.get_key_list(&keys);
@@ -146,6 +158,7 @@ void AnimationLibrary::_bind_methods() {
ADD_SIGNAL(MethodInfo("animation_added", PropertyInfo(Variant::STRING_NAME, "name")));
ADD_SIGNAL(MethodInfo("animation_removed", PropertyInfo(Variant::STRING_NAME, "name")));
ADD_SIGNAL(MethodInfo("animation_renamed", PropertyInfo(Variant::STRING_NAME, "name"), PropertyInfo(Variant::STRING_NAME, "to_name")));
+ ADD_SIGNAL(MethodInfo("animation_changed", PropertyInfo(Variant::STRING_NAME, "name")));
}
AnimationLibrary::AnimationLibrary() {
}
diff --git a/scene/resources/animation_library.h b/scene/resources/animation_library.h
index d63807b6d7..54bd641b6d 100644
--- a/scene/resources/animation_library.h
+++ b/scene/resources/animation_library.h
@@ -42,6 +42,8 @@ class AnimationLibrary : public Resource {
TypedArray<StringName> _get_animation_list() const;
+ void _animation_changed(const StringName &p_name);
+
friend class AnimationPlayer; //for faster access
HashMap<StringName, Ref<Animation>> animations;
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index 9289c5da4a..93c3d4f851 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -1407,15 +1407,15 @@ void Curve3D::_bake_segment3d_even_length(RBMap<real_t, Vector3> &r_bake, real_t
Vector3 beg = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, p_begin);
Vector3 end = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, p_end);
- size_t length = beg.distance_to(end);
+ real_t length = beg.distance_to(end);
if (length > p_length && p_depth < p_max_depth) {
real_t mp = (p_begin + p_end) * 0.5;
Vector3 mid = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, mp);
r_bake[mp] = mid;
- _bake_segment3d(r_bake, p_begin, mp, p_a, p_out, p_b, p_in, p_depth + 1, p_max_depth, p_length);
- _bake_segment3d(r_bake, mp, p_end, p_a, p_out, p_b, p_in, p_depth + 1, p_max_depth, p_length);
+ _bake_segment3d_even_length(r_bake, p_begin, mp, p_a, p_out, p_b, p_in, p_depth + 1, p_max_depth, p_length);
+ _bake_segment3d_even_length(r_bake, mp, p_end, p_a, p_out, p_b, p_in, p_depth + 1, p_max_depth, p_length);
}
}
@@ -1839,10 +1839,11 @@ Vector3 Curve3D::get_closest_point(const Vector3 &p_to_point) const {
real_t nearest_dist = -1.0f;
for (int i = 0; i < pc - 1; i++) {
+ const real_t interval = baked_dist_cache[i + 1] - baked_dist_cache[i];
Vector3 origin = r[i];
- Vector3 direction = (r[i + 1] - origin) / bake_interval;
+ Vector3 direction = (r[i + 1] - origin) / interval;
- real_t d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval);
+ real_t d = CLAMP((p_to_point - origin).dot(direction), 0.0f, interval);
Vector3 proj = origin + direction * d;
real_t dist = proj.distance_squared_to(p_to_point);
@@ -1875,13 +1876,16 @@ real_t Curve3D::get_closest_offset(const Vector3 &p_to_point) const {
real_t nearest = 0.0f;
real_t nearest_dist = -1.0f;
- real_t offset = 0.0f;
+ real_t offset;
for (int i = 0; i < pc - 1; i++) {
+ offset = baked_dist_cache[i];
+
+ const real_t interval = baked_dist_cache[i + 1] - baked_dist_cache[i];
Vector3 origin = r[i];
- Vector3 direction = (r[i + 1] - origin) / bake_interval;
+ Vector3 direction = (r[i + 1] - origin) / interval;
- real_t d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval);
+ real_t d = CLAMP((p_to_point - origin).dot(direction), 0.0f, interval);
Vector3 proj = origin + direction * d;
real_t dist = proj.distance_squared_to(p_to_point);
@@ -1890,8 +1894,6 @@ real_t Curve3D::get_closest_offset(const Vector3 &p_to_point) const {
nearest = offset + d;
nearest_dist = dist;
}
-
- offset += bake_interval;
}
return nearest;
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 10d193f950..e457b2d377 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -92,18 +92,13 @@ void Material::inspect_native_shader_code() {
RID Material::get_shader_rid() const {
RID ret;
- if (GDVIRTUAL_REQUIRED_CALL(_get_shader_rid, ret)) {
- return ret;
- }
- return RID();
+ GDVIRTUAL_REQUIRED_CALL(_get_shader_rid, ret);
+ return ret;
}
Shader::Mode Material::get_shader_mode() const {
- Shader::Mode ret;
- if (GDVIRTUAL_REQUIRED_CALL(_get_shader_mode, ret)) {
- return ret;
- }
-
- return Shader::MODE_MAX;
+ Shader::Mode ret = Shader::MODE_MAX;
+ GDVIRTUAL_REQUIRED_CALL(_get_shader_mode, ret);
+ return ret;
}
bool Material::_can_do_next_pass() const {
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 4f68a6f69b..a5e7602c8b 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -40,119 +40,83 @@
Mesh::ConvexDecompositionFunc Mesh::convex_decomposition_function = nullptr;
int Mesh::get_surface_count() const {
- int ret;
- if (GDVIRTUAL_REQUIRED_CALL(_get_surface_count, ret)) {
- return ret;
- }
- return 0;
+ int ret = 0;
+ GDVIRTUAL_REQUIRED_CALL(_get_surface_count, ret);
+ return ret;
}
int Mesh::surface_get_array_len(int p_idx) const {
- int ret;
- if (GDVIRTUAL_REQUIRED_CALL(_surface_get_array_len, p_idx, ret)) {
- return ret;
- }
- return 0;
+ int ret = 0;
+ GDVIRTUAL_REQUIRED_CALL(_surface_get_array_len, p_idx, ret);
+ return ret;
}
int Mesh::surface_get_array_index_len(int p_idx) const {
- int ret;
- if (GDVIRTUAL_REQUIRED_CALL(_surface_get_array_index_len, p_idx, ret)) {
- return ret;
- }
- return 0;
+ int ret = 0;
+ GDVIRTUAL_REQUIRED_CALL(_surface_get_array_index_len, p_idx, ret);
+ return ret;
}
Array Mesh::surface_get_arrays(int p_surface) const {
Array ret;
- if (GDVIRTUAL_REQUIRED_CALL(_surface_get_arrays, p_surface, ret)) {
- return ret;
- }
- return Array();
+ GDVIRTUAL_REQUIRED_CALL(_surface_get_arrays, p_surface, ret);
+ return ret;
}
TypedArray<Array> Mesh::surface_get_blend_shape_arrays(int p_surface) const {
TypedArray<Array> ret;
- if (GDVIRTUAL_REQUIRED_CALL(_surface_get_blend_shape_arrays, p_surface, ret)) {
- return ret;
- }
-
- return TypedArray<Array>();
+ GDVIRTUAL_REQUIRED_CALL(_surface_get_blend_shape_arrays, p_surface, ret);
+ return ret;
}
Dictionary Mesh::surface_get_lods(int p_surface) const {
Dictionary ret;
- if (GDVIRTUAL_REQUIRED_CALL(_surface_get_lods, p_surface, ret)) {
- return ret;
- }
-
- return Dictionary();
+ GDVIRTUAL_REQUIRED_CALL(_surface_get_lods, p_surface, ret);
+ return ret;
}
uint32_t Mesh::surface_get_format(int p_idx) const {
- uint32_t ret;
- if (GDVIRTUAL_REQUIRED_CALL(_surface_get_format, p_idx, ret)) {
- return ret;
- }
-
- return 0;
+ uint32_t ret = 0;
+ GDVIRTUAL_REQUIRED_CALL(_surface_get_format, p_idx, ret);
+ return ret;
}
Mesh::PrimitiveType Mesh::surface_get_primitive_type(int p_idx) const {
- uint32_t ret;
- if (GDVIRTUAL_REQUIRED_CALL(_surface_get_primitive_type, p_idx, ret)) {
- return (Mesh::PrimitiveType)ret;
- }
-
- return PRIMITIVE_MAX;
+ uint32_t ret = PRIMITIVE_MAX;
+ GDVIRTUAL_REQUIRED_CALL(_surface_get_primitive_type, p_idx, ret);
+ return (Mesh::PrimitiveType)ret;
}
void Mesh::surface_set_material(int p_idx, const Ref<Material> &p_material) {
- if (GDVIRTUAL_REQUIRED_CALL(_surface_set_material, p_idx, p_material)) {
- return;
- }
+ GDVIRTUAL_REQUIRED_CALL(_surface_set_material, p_idx, p_material);
}
Ref<Material> Mesh::surface_get_material(int p_idx) const {
Ref<Material> ret;
- if (GDVIRTUAL_REQUIRED_CALL(_surface_get_material, p_idx, ret)) {
- return ret;
- }
-
- return Ref<Material>();
+ GDVIRTUAL_REQUIRED_CALL(_surface_get_material, p_idx, ret);
+ return ret;
}
int Mesh::get_blend_shape_count() const {
- int ret;
- if (GDVIRTUAL_REQUIRED_CALL(_get_blend_shape_count, ret)) {
- return ret;
- }
-
- return 0;
+ int ret = 0;
+ GDVIRTUAL_REQUIRED_CALL(_get_blend_shape_count, ret);
+ return ret;
}
StringName Mesh::get_blend_shape_name(int p_index) const {
StringName ret;
- if (GDVIRTUAL_REQUIRED_CALL(_get_blend_shape_name, p_index, ret)) {
- return ret;
- }
-
- return StringName();
+ GDVIRTUAL_REQUIRED_CALL(_get_blend_shape_name, p_index, ret);
+ return ret;
}
void Mesh::set_blend_shape_name(int p_index, const StringName &p_name) {
- if (GDVIRTUAL_REQUIRED_CALL(_set_blend_shape_name, p_index, p_name)) {
- return;
- }
+ GDVIRTUAL_REQUIRED_CALL(_set_blend_shape_name, p_index, p_name);
}
AABB Mesh::get_aabb() const {
AABB ret;
- if (GDVIRTUAL_REQUIRED_CALL(_get_aabb, ret)) {
- return ret;
- }
-
- return AABB();
+ GDVIRTUAL_REQUIRED_CALL(_get_aabb, ret);
+ return ret;
}
Ref<TriangleMesh> Mesh::generate_triangle_mesh() const {
diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp
index f379f88bed..ea341152e6 100644
--- a/scene/resources/style_box.cpp
+++ b/scene/resources/style_box.cpp
@@ -35,11 +35,9 @@
#include <limits.h>
float StyleBox::get_style_margin(Side p_side) const {
- float ret;
- if (GDVIRTUAL_REQUIRED_CALL(_get_style_margin, p_side, ret)) {
- return ret;
- }
- return 0;
+ float ret = 0;
+ GDVIRTUAL_REQUIRED_CALL(_get_style_margin, p_side, ret);
+ return ret;
}
bool StyleBox::test_mask(const Point2 &p_point, const Rect2 &p_rect) const {
@@ -49,9 +47,7 @@ bool StyleBox::test_mask(const Point2 &p_point, const Rect2 &p_rect) const {
}
void StyleBox::draw(RID p_canvas_item, const Rect2 &p_rect) const {
- if (GDVIRTUAL_REQUIRED_CALL(_draw, p_canvas_item, p_rect)) {
- return;
- }
+ GDVIRTUAL_REQUIRED_CALL(_draw, p_canvas_item, p_rect);
}
void StyleBox::set_default_margin(Side p_side, float p_value) {
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index b5754caa6a..2106619a6b 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -40,19 +40,15 @@
#include "servers/camera/camera_feed.h"
int Texture2D::get_width() const {
- int ret;
- if (GDVIRTUAL_REQUIRED_CALL(_get_width, ret)) {
- return ret;
- }
- return 0;
+ int ret = 0;
+ GDVIRTUAL_REQUIRED_CALL(_get_width, ret);
+ return ret;
}
int Texture2D::get_height() const {
- int ret;
- if (GDVIRTUAL_REQUIRED_CALL(_get_height, ret)) {
- return ret;
- }
- return 0;
+ int ret = 0;
+ GDVIRTUAL_REQUIRED_CALL(_get_height, ret);
+ return ret;
}
Size2 Texture2D::get_size() const {
@@ -1092,57 +1088,44 @@ TypedArray<Image> Texture3D::_get_datai() const {
}
Image::Format Texture3D::get_format() const {
- Image::Format ret;
- if (GDVIRTUAL_REQUIRED_CALL(_get_format, ret)) {
- return ret;
- }
- return Image::FORMAT_MAX;
+ Image::Format ret = Image::FORMAT_MAX;
+ GDVIRTUAL_REQUIRED_CALL(_get_format, ret);
+ return ret;
}
int Texture3D::get_width() const {
- int ret;
- if (GDVIRTUAL_REQUIRED_CALL(_get_width, ret)) {
- return ret;
- }
- return 0;
+ int ret = 0;
+ GDVIRTUAL_REQUIRED_CALL(_get_width, ret);
+ return ret;
}
int Texture3D::get_height() const {
- int ret;
- if (GDVIRTUAL_REQUIRED_CALL(_get_height, ret)) {
- return ret;
- }
- return 0;
+ int ret = 0;
+ GDVIRTUAL_REQUIRED_CALL(_get_height, ret);
+ return ret;
}
int Texture3D::get_depth() const {
- int ret;
- if (GDVIRTUAL_REQUIRED_CALL(_get_depth, ret)) {
- return ret;
- }
-
- return 0;
+ int ret = 0;
+ GDVIRTUAL_REQUIRED_CALL(_get_depth, ret);
+ return ret;
}
bool Texture3D::has_mipmaps() const {
- bool ret;
- if (GDVIRTUAL_REQUIRED_CALL(_has_mipmaps, ret)) {
- return ret;
- }
- return false;
+ bool ret = false;
+ GDVIRTUAL_REQUIRED_CALL(_has_mipmaps, ret);
+ return ret;
}
Vector<Ref<Image>> Texture3D::get_data() const {
TypedArray<Image> ret;
- if (GDVIRTUAL_REQUIRED_CALL(_get_data, ret)) {
- Vector<Ref<Image>> data;
- data.resize(ret.size());
- for (int i = 0; i < data.size(); i++) {
- data.write[i] = ret[i];
- }
- return data;
+ GDVIRTUAL_REQUIRED_CALL(_get_data, ret);
+ Vector<Ref<Image>> data;
+ data.resize(ret.size());
+ for (int i = 0; i < data.size(); i++) {
+ data.write[i] = ret[i];
}
- return Vector<Ref<Image>>();
+ return data;
}
void Texture3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_format"), &Texture3D::get_format);
@@ -2859,60 +2842,45 @@ AnimatedTexture::~AnimatedTexture() {
///////////////////////////////
Image::Format TextureLayered::get_format() const {
- Image::Format ret;
- if (GDVIRTUAL_REQUIRED_CALL(_get_format, ret)) {
- return ret;
- }
- return Image::FORMAT_MAX;
+ Image::Format ret = Image::FORMAT_MAX;
+ GDVIRTUAL_REQUIRED_CALL(_get_format, ret);
+ return ret;
}
TextureLayered::LayeredType TextureLayered::get_layered_type() const {
- uint32_t ret;
- if (GDVIRTUAL_REQUIRED_CALL(_get_layered_type, ret)) {
- return (LayeredType)ret;
- }
- return LAYERED_TYPE_2D_ARRAY;
+ uint32_t ret = LAYERED_TYPE_2D_ARRAY;
+ GDVIRTUAL_REQUIRED_CALL(_get_layered_type, ret);
+ return (LayeredType)ret;
}
int TextureLayered::get_width() const {
- int ret;
- if (GDVIRTUAL_REQUIRED_CALL(_get_width, ret)) {
- return ret;
- }
- return 0;
+ int ret = 0;
+ GDVIRTUAL_REQUIRED_CALL(_get_width, ret);
+ return ret;
}
int TextureLayered::get_height() const {
- int ret;
- if (GDVIRTUAL_REQUIRED_CALL(_get_height, ret)) {
- return ret;
- }
- return 0;
+ int ret = 0;
+ GDVIRTUAL_REQUIRED_CALL(_get_height, ret);
+ return ret;
}
int TextureLayered::get_layers() const {
- int ret;
- if (GDVIRTUAL_REQUIRED_CALL(_get_layers, ret)) {
- return ret;
- }
-
- return 0;
+ int ret = 0;
+ GDVIRTUAL_REQUIRED_CALL(_get_layers, ret);
+ return ret;
}
bool TextureLayered::has_mipmaps() const {
- bool ret;
- if (GDVIRTUAL_REQUIRED_CALL(_has_mipmaps, ret)) {
- return ret;
- }
- return false;
+ bool ret = false;
+ GDVIRTUAL_REQUIRED_CALL(_has_mipmaps, ret);
+ return ret;
}
Ref<Image> TextureLayered::get_layer_data(int p_layer) const {
Ref<Image> ret;
- if (GDVIRTUAL_REQUIRED_CALL(_get_layer_data, p_layer, ret)) {
- return ret;
- }
- return Ref<Image>();
+ GDVIRTUAL_REQUIRED_CALL(_get_layer_data, p_layer, ret);
+ return ret;
}
void TextureLayered::_bind_methods() {
diff --git a/servers/audio/audio_effect.cpp b/servers/audio/audio_effect.cpp
index ef0e3197e1..870a77806b 100644
--- a/servers/audio/audio_effect.cpp
+++ b/servers/audio/audio_effect.cpp
@@ -31,9 +31,7 @@
#include "audio_effect.h"
void AudioEffectInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
- if (GDVIRTUAL_REQUIRED_CALL(_process, p_src_frames, p_dst_frames, p_frame_count)) {
- return;
- }
+ GDVIRTUAL_REQUIRED_CALL(_process, p_src_frames, p_dst_frames, p_frame_count);
}
bool AudioEffectInstance::process_silence() const {
bool ret = false;
@@ -50,10 +48,8 @@ void AudioEffectInstance::_bind_methods() {
Ref<AudioEffectInstance> AudioEffect::instantiate() {
Ref<AudioEffectInstance> ret;
- if (GDVIRTUAL_REQUIRED_CALL(_instantiate, ret)) {
- return ret;
- }
- return Ref<AudioEffectInstance>();
+ GDVIRTUAL_REQUIRED_CALL(_instantiate, ret);
+ return ret;
}
void AudioEffect::_bind_methods() {
GDVIRTUAL_BIND(_instantiate);
diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp
index 33d4c816f2..4b68515e18 100644
--- a/servers/audio/audio_stream.cpp
+++ b/servers/audio/audio_stream.cpp
@@ -71,12 +71,9 @@ void AudioStreamPlayback::seek(double p_time) {
}
int AudioStreamPlayback::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) {
- int ret;
- if (GDVIRTUAL_REQUIRED_CALL(_mix, p_buffer, p_rate_scale, p_frames, ret)) {
- return ret;
- }
-
- return 0;
+ int ret = 0;
+ GDVIRTUAL_REQUIRED_CALL(_mix, p_buffer, p_rate_scale, p_frames, ret);
+ return ret;
}
void AudioStreamPlayback::tag_used_streams() {
@@ -107,20 +104,14 @@ void AudioStreamPlaybackResampled::begin_resample() {
}
int AudioStreamPlaybackResampled::_mix_internal(AudioFrame *p_buffer, int p_frames) {
- int ret;
- if (GDVIRTUAL_REQUIRED_CALL(_mix_resampled, p_buffer, p_frames, ret)) {
- return ret;
- }
-
- return 0;
+ int ret = 0;
+ GDVIRTUAL_REQUIRED_CALL(_mix_resampled, p_buffer, p_frames, ret);
+ return ret;
}
float AudioStreamPlaybackResampled::get_stream_sampling_rate() {
- float ret;
- if (GDVIRTUAL_REQUIRED_CALL(_get_stream_sampling_rate, ret)) {
- return ret;
- }
-
- return 0;
+ float ret = 0;
+ GDVIRTUAL_REQUIRED_CALL(_get_stream_sampling_rate, ret);
+ return ret;
}
void AudioStreamPlaybackResampled::_bind_methods() {
diff --git a/servers/movie_writer/movie_writer.cpp b/servers/movie_writer/movie_writer.cpp
index 0edce4ced6..419943f2d2 100644
--- a/servers/movie_writer/movie_writer.cpp
+++ b/servers/movie_writer/movie_writer.cpp
@@ -52,34 +52,26 @@ MovieWriter *MovieWriter::find_writer_for_file(const String &p_file) {
}
uint32_t MovieWriter::get_audio_mix_rate() const {
- uint32_t ret = 0;
- if (GDVIRTUAL_REQUIRED_CALL(_get_audio_mix_rate, ret)) {
- return ret;
- }
- return 48000;
+ uint32_t ret = 48000;
+ GDVIRTUAL_REQUIRED_CALL(_get_audio_mix_rate, ret);
+ return ret;
}
AudioServer::SpeakerMode MovieWriter::get_audio_speaker_mode() const {
AudioServer::SpeakerMode ret = AudioServer::SPEAKER_MODE_STEREO;
- if (GDVIRTUAL_REQUIRED_CALL(_get_audio_speaker_mode, ret)) {
- return ret;
- }
- return AudioServer::SPEAKER_MODE_STEREO;
+ GDVIRTUAL_REQUIRED_CALL(_get_audio_speaker_mode, ret);
+ return ret;
}
Error MovieWriter::write_begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) {
- Error ret = OK;
- if (GDVIRTUAL_REQUIRED_CALL(_write_begin, p_movie_size, p_fps, p_base_path, ret)) {
- return ret;
- }
- return ERR_UNCONFIGURED;
+ Error ret = ERR_UNCONFIGURED;
+ GDVIRTUAL_REQUIRED_CALL(_write_begin, p_movie_size, p_fps, p_base_path, ret);
+ return ret;
}
Error MovieWriter::write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data) {
- Error ret = OK;
- if (GDVIRTUAL_REQUIRED_CALL(_write_frame, p_image, p_audio_data, ret)) {
- return ret;
- }
- return ERR_UNCONFIGURED;
+ Error ret = ERR_UNCONFIGURED;
+ GDVIRTUAL_REQUIRED_CALL(_write_frame, p_image, p_audio_data, ret);
+ return ret;
}
void MovieWriter::write_end() {
@@ -88,18 +80,15 @@ void MovieWriter::write_end() {
bool MovieWriter::handles_file(const String &p_path) const {
bool ret = false;
- if (GDVIRTUAL_REQUIRED_CALL(_handles_file, p_path, ret)) {
- return ret;
- }
- return false;
+ GDVIRTUAL_REQUIRED_CALL(_handles_file, p_path, ret);
+ return ret;
}
void MovieWriter::get_supported_extensions(List<String> *r_extensions) const {
Vector<String> exts;
- if (GDVIRTUAL_REQUIRED_CALL(_get_supported_extensions, exts)) {
- for (int i = 0; i < exts.size(); i++) {
- r_extensions->push_back(exts[i]);
- }
+ GDVIRTUAL_REQUIRED_CALL(_get_supported_extensions, exts);
+ for (int i = 0; i < exts.size(); i++) {
+ r_extensions->push_back(exts[i]);
}
}
diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp
index 16d382a5f3..075a21b818 100644
--- a/servers/rendering/renderer_canvas_cull.cpp
+++ b/servers/rendering/renderer_canvas_cull.cpp
@@ -275,12 +275,12 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
if (ci->clip) {
if (p_canvas_clip != nullptr) {
ci->final_clip_rect = p_canvas_clip->final_clip_rect.intersection(global_rect);
- if (ci->final_clip_rect == Rect2()) {
- // Clip rects do not intersect, so don't draw this item.
- return;
- }
} else {
- ci->final_clip_rect = global_rect;
+ ci->final_clip_rect = p_clip_rect.intersection(global_rect);
+ }
+ if (ci->final_clip_rect.size.width < 0.5 || ci->final_clip_rect.size.height < 0.5) {
+ // The clip rect area is 0, so don't draw the item.
+ return;
}
ci->final_clip_rect.position = ci->final_clip_rect.position.round();
ci->final_clip_rect.size = ci->final_clip_rect.size.round();
diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
index 4765475804..27c82213e4 100644
--- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
@@ -665,7 +665,6 @@ public:
virtual void skeleton_allocate_data(RID p_skeleton, int p_bones, bool p_2d_skeleton = false) override;
virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) override;
- void skeleton_set_world_transform(RID p_skeleton, bool p_enable, const Transform3D &p_world_transform);
virtual int skeleton_get_bone_count(RID p_skeleton) const override;
virtual void skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform3D &p_transform) override;
virtual Transform3D skeleton_bone_get_transform(RID p_skeleton, int p_bone) const override;
diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp
index 4ee355ee9f..4b68d1f5c0 100644
--- a/servers/rendering/renderer_scene_cull.cpp
+++ b/servers/rendering/renderer_scene_cull.cpp
@@ -2520,7 +2520,7 @@ void RendererSceneCull::render_camera(const Ref<RenderSceneBuffers> &p_render_bu
Projection projections[RendererSceneRender::MAX_RENDER_VIEWS];
uint32_t view_count = p_xr_interface->get_view_count();
- ERR_FAIL_COND_MSG(view_count > RendererSceneRender::MAX_RENDER_VIEWS, "Requested view count is not supported");
+ ERR_FAIL_COND_MSG(view_count == 0 || view_count > RendererSceneRender::MAX_RENDER_VIEWS, "Requested view count is not supported");
float aspect = p_viewport_size.width / (float)p_viewport_size.height;
diff --git a/thirdparty/README.md b/thirdparty/README.md
index a75ceda7d3..937d149747 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -398,7 +398,7 @@ to solve some MSVC warnings. See the patches in the `patches` directory.
## miniupnpc
- Upstream: https://github.com/miniupnp/miniupnp
-- Version: 2.2.3 (2df8120326ed4246e049a7a6de707539604cd514, 2021)
+- Version: 2.2.4 (7d1d8bc3868b08ad003bad235eee57562b95b76d, 2022)
- License: BSD-3-Clause
Files extracted from upstream source:
@@ -668,7 +668,7 @@ instead of `miniz.h` as an external dependency.
## thorvg
- Upstream: https://github.com/Samsung/thorvg
-- Version: 0.8.2 (496796f1e5e85bd5fbba36dae987edb1b3945592, 2022)
+- Version: 0.8.3 (a0fcf51f80a75f63a066df085f60cdaf715188b6, 2022)
- License: MIT
Files extracted from upstream source:
diff --git a/thirdparty/miniupnpc/LICENSE b/thirdparty/miniupnpc/LICENSE
index fe9118c07e..67ff3bb627 100644
--- a/thirdparty/miniupnpc/LICENSE
+++ b/thirdparty/miniupnpc/LICENSE
@@ -1,26 +1,29 @@
-MiniUPnP Project
-Copyright (c) 2005-2020, Thomas BERNARD
+BSD 3-Clause License
+
+Copyright (c) 2005-2022, Thomas BERNARD
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- * The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/thirdparty/miniupnpc/include/miniupnpc.h b/thirdparty/miniupnpc/include/miniupnpc.h
index a10bd950a8..75fb8b702d 100644
--- a/thirdparty/miniupnpc/include/miniupnpc.h
+++ b/thirdparty/miniupnpc/include/miniupnpc.h
@@ -1,9 +1,9 @@
-/* $Id: miniupnpc.h,v 1.59 2021/09/28 21:39:17 nanard Exp $ */
+/* $Id: miniupnpc.h,v 1.61 2022/10/21 21:15:02 nanard Exp $ */
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project: miniupnp
- * http://miniupnp.free.fr/
+ * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* Author: Thomas Bernard
- * Copyright (c) 2005-2021 Thomas Bernard
+ * Copyright (c) 2005-2022 Thomas Bernard
* This software is subjects to the conditions detailed
* in the LICENCE file provided within this distribution */
#ifndef MINIUPNPC_H_INCLUDED
@@ -20,7 +20,7 @@
#define UPNPDISCOVER_MEMORY_ERROR (-102)
/* versions : */
-#define MINIUPNPC_VERSION "2.2.3"
+#define MINIUPNPC_VERSION "2.2.4"
#define MINIUPNPC_API_VERSION 17
/* Source port:
diff --git a/thirdparty/miniupnpc/src/miniupnpcstrings.h b/thirdparty/miniupnpc/src/miniupnpcstrings.h
index eefbc8dbdd..25abc1016e 100644
--- a/thirdparty/miniupnpc/src/miniupnpcstrings.h
+++ b/thirdparty/miniupnpc/src/miniupnpcstrings.h
@@ -4,7 +4,7 @@
#include "core/version.h"
#define OS_STRING VERSION_NAME "/1.0"
-#define MINIUPNPC_VERSION_STRING "2.2.3"
+#define MINIUPNPC_VERSION_STRING "2.2.4"
#if 0
/* according to "UPnP Device Architecture 1.0" */
diff --git a/thirdparty/miniupnpc/src/minixml.c b/thirdparty/miniupnpc/src/minixml.c
index ed2d3c759c..935ec443e7 100644
--- a/thirdparty/miniupnpc/src/minixml.c
+++ b/thirdparty/miniupnpc/src/minixml.c
@@ -1,4 +1,4 @@
-/* $Id: minixml.c,v 1.10 2012/03/05 19:42:47 nanard Exp $ */
+/* $Id: minixml.c,v 1.12 2017/12/12 11:17:40 nanard Exp $ */
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* minixml.c : the minimum size a xml parser can be ! */
/* Project : miniupnp
diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h
index 68935c583b..eda302aec0 100644
--- a/thirdparty/thorvg/inc/config.h
+++ b/thirdparty/thorvg/inc/config.h
@@ -13,5 +13,5 @@
#define THORVG_JPG_LOADER_SUPPORT 1
-#define THORVG_VERSION_STRING "0.8.2"
+#define THORVG_VERSION_STRING "0.8.3"
#endif
diff --git a/thirdparty/thorvg/inc/thorvg.h b/thirdparty/thorvg/inc/thorvg.h
index b08356d9d5..7e8988a65e 100644
--- a/thirdparty/thorvg/inc/thorvg.h
+++ b/thirdparty/thorvg/inc/thorvg.h
@@ -18,7 +18,7 @@
#include <string>
#ifdef TVG_BUILD
- #if defined(_MSC_VER) && !defined(__clang__)
+ #if defined(_WIN32) && !defined(__clang__)
#define TVG_EXPORT __declspec(dllexport)
#define TVG_DEPRECATED __declspec(deprecated)
#else
@@ -74,7 +74,7 @@ class Accessor;
/**
* @brief Enumeration specifying the result from the APIs.
*/
-enum class TVG_EXPORT Result
+enum class Result
{
Success = 0, ///< The value returned in case of a correct request execution.
InvalidArguments, ///< The value returned in the event of a problem with the arguments given to the API - e.g. empty paths or null pointers.
@@ -91,7 +91,7 @@ enum class TVG_EXPORT Result
* Not to be confused with the path commands from the svg path element (like M, L, Q, H and many others).
* TVG interprets all of them and translates to the ones from the PathCommand values.
*/
-enum class TVG_EXPORT PathCommand
+enum class PathCommand
{
Close = 0, ///< Ends the current sub-path and connects it with its initial point. This command doesn't expect any points.
MoveTo, ///< Sets a new initial point of the sub-path and a new current point. This command expects 1 point: the starting position.
@@ -102,7 +102,7 @@ enum class TVG_EXPORT PathCommand
/**
* @brief Enumeration determining the ending type of a stroke in the open sub-paths.
*/
-enum class TVG_EXPORT StrokeCap
+enum class StrokeCap
{
Square = 0, ///< The stroke is extended in both end-points of a sub-path by a rectangle, with the width equal to the stroke width and the length equal to the half of the stroke width. For zero length sub-paths the square is rendered with the size of the stroke width.
Round, ///< The stroke is extended in both end-points of a sub-path by a half circle, with a radius equal to the half of a stroke width. For zero length sub-paths a full circle is rendered.
@@ -112,7 +112,7 @@ enum class TVG_EXPORT StrokeCap
/**
* @brief Enumeration determining the style used at the corners of joined stroked path segments.
*/
-enum class TVG_EXPORT StrokeJoin
+enum class StrokeJoin
{
Bevel = 0, ///< The outer corner of the joined path segments is bevelled at the join point. The triangular region of the corner is enclosed by a straight line between the outer corners of each stroke.
Round, ///< The outer corner of the joined path segments is rounded. The circular region is centered at the join point.
@@ -122,7 +122,7 @@ enum class TVG_EXPORT StrokeJoin
/**
* @brief Enumeration specifying how to fill the area outside the gradient bounds.
*/
-enum class TVG_EXPORT FillSpread
+enum class FillSpread
{
Pad = 0, ///< The remaining area is filled with the closest stop color.
Reflect, ///< The gradient pattern is reflected outside the gradient area until the expected region is filled.
@@ -132,7 +132,7 @@ enum class TVG_EXPORT FillSpread
/**
* @brief Enumeration specifying the algorithm used to establish which parts of the shape are treated as the inside of the shape.
*/
-enum class TVG_EXPORT FillRule
+enum class FillRule
{
Winding = 0, ///< A line from the point to a location outside the shape is drawn. The intersections of the line with the path segment of the shape are counted. Starting from zero, if the path segment of the shape crosses the line clockwise, one is added, otherwise one is subtracted. If the resulting sum is non zero, the point is inside the shape.
EvenOdd ///< A line from the point to a location outside the shape is drawn and its intersections with the path segments of the shape are counted. If the number of intersections is an odd number, the point is inside the shape.
@@ -141,7 +141,7 @@ enum class TVG_EXPORT FillRule
/**
* @brief Enumeration indicating the method used in the composition of two objects - the target and the source.
*/
-enum class TVG_EXPORT CompositeMethod
+enum class CompositeMethod
{
None = 0, ///< No composition is applied.
ClipPath, ///< The intersection of the source and the target is determined and only the resulting pixels from the source are rendered.
@@ -153,7 +153,7 @@ enum class TVG_EXPORT CompositeMethod
/**
* @brief Enumeration specifying the engine type used for the graphics backend. For multiple backends bitwise operation is allowed.
*/
-enum class TVG_EXPORT CanvasEngine
+enum class CanvasEngine
{
Sw = (1 << 1), ///< CPU rasterizer.
Gl = (1 << 2) ///< OpenGL rasterizer.
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp
index bf1c10a0c3..ffd74bdd47 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp
@@ -22,10 +22,10 @@
#ifdef _WIN32
#include <malloc.h>
-#elif __FreeBSD__
- #include<stdlib.h>
-#else
+#elif defined(__linux__)
#include <alloca.h>
+#else
+ #include <stdlib.h>
#endif
#include "tvgMath.h"
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h
index f31ea1eb97..50536299b1 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h
@@ -30,7 +30,7 @@
int32_t dw = surface->stride;
int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
int32_t vv = 0, uu = 0;
- int32_t minx, maxx;
+ int32_t minx = INT32_MAX, maxx = INT32_MIN;
float dx, u, v, iptr;
uint32_t* buf;
SwSpan* span = nullptr; //used only when rle based.
diff --git a/thirdparty/thorvg/src/lib/tvgLoadModule.h b/thirdparty/thorvg/src/lib/tvgLoadModule.h
index bfcc165f31..004983152b 100644
--- a/thirdparty/thorvg/src/lib/tvgLoadModule.h
+++ b/thirdparty/thorvg/src/lib/tvgLoadModule.h
@@ -36,7 +36,6 @@ public:
float vw = 0;
float vh = 0;
float w = 0, h = 0; //default image size
- bool preserveAspect = true; //keep aspect ratio by default.
virtual ~LoadModule() {}
diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp
index ffdef3004c..d11dfc1e7c 100644
--- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp
+++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp
@@ -118,9 +118,9 @@ unique_ptr<Surface> JpgLoader::bitmap()
auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
surface->buffer = (uint32_t*)(image);
- surface->stride = w;
- surface->w = w;
- surface->h = h;
+ surface->stride = static_cast<uint32_t>(w);
+ surface->w = static_cast<uint32_t>(w);
+ surface->h = static_cast<uint32_t>(h);
surface->cs = SwCanvas::ARGB8888;
return unique_ptr<Surface>(surface);
diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp
index dacd45ed03..56b40acf0b 100644
--- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp
+++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp
@@ -808,7 +808,7 @@ inline int jpeg_decoder::huff_decode(huff_tables *pH, int& extra_bits)
// Tables and macro used to fully decode the DPCM differences.
static const int s_extend_test[16] = { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 };
-static const unsigned int s_extend_offset[16] = { 0, ((-1u)<<1) + 1, ((-1u)<<2) + 1, ((-1u)<<3) + 1, ((-1u)<<4) + 1, ((-1u)<<5) + 1, ((-1u)<<6) + 1, ((-1u)<<7) + 1, ((-1u)<<8) + 1, ((-1u)<<9) + 1, ((-1u)<<10) + 1, ((-1u)<<11) + 1, ((-1u)<<12) + 1, ((-1u)<<13) + 1, ((-1u)<<14) + 1, ((-1u)<<15) + 1 };
+static const unsigned int s_extend_offset[16] = { 0, ((~0u)<<1) + 1, ((~0u)<<2) + 1, ((~0u)<<3) + 1, ((~0u)<<4) + 1, ((~0u)<<5) + 1, ((~0u)<<6) + 1, ((~0u)<<7) + 1, ((~0u)<<8) + 1, ((~0u)<<9) + 1, ((~0u)<<10) + 1, ((~0u)<<11) + 1, ((~0u)<<12) + 1, ((~0u)<<13) + 1, ((~0u)<<14) + 1, ((~0u)<<15) + 1 };
// The logical AND's in this macro are to shut up static code analysis (aren't really necessary - couldn't find another way to do this)
#define JPGD_HUFF_EXTEND(x, s) (((x) < s_extend_test[s & 15]) ? ((x) + s_extend_offset[s & 15]) : (x))
diff --git a/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp b/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp
index 2da399d8c3..889f130ce9 100644
--- a/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp
+++ b/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp
@@ -78,9 +78,9 @@ unique_ptr<Surface> RawLoader::bitmap()
auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
surface->buffer = (uint32_t*)(content);
- surface->stride = w;
- surface->w = w;
- surface->h = h;
+ surface->stride = (uint32_t)w;
+ surface->w = (uint32_t)w;
+ surface->h = (uint32_t)h;
surface->cs = SwCanvas::ARGB8888;
return unique_ptr<Surface>(surface);
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp
index 8f46b62ce9..478ba5d3d1 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp
@@ -42,7 +42,10 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from)
to->fill.paint.color = from->fill.paint.color;
to->fill.paint.none = from->fill.paint.none;
to->fill.paint.curColor = from->fill.paint.curColor;
- if (from->fill.paint.url) to->fill.paint.url = strdup(from->fill.paint.url);
+ if (from->fill.paint.url) {
+ if (to->fill.paint.url) free(to->fill.paint.url);
+ to->fill.paint.url = strdup(from->fill.paint.url);
+ }
to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)SvgFillFlags::Paint);
to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Fill);
}
@@ -61,7 +64,10 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from)
to->stroke.paint.color = from->stroke.paint.color;
to->stroke.paint.none = from->stroke.paint.none;
to->stroke.paint.curColor = from->stroke.paint.curColor;
- if (from->stroke.paint.url) to->stroke.paint.url = strdup(from->stroke.paint.url);
+ if (from->stroke.paint.url) {
+ if (to->stroke.paint.url) free(to->stroke.paint.url);
+ to->stroke.paint.url = strdup(from->stroke.paint.url);
+ }
to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Paint);
to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Stroke);
}
@@ -122,8 +128,14 @@ void cssCopyStyleAttr(SvgNode* to, const SvgNode* from)
//Copy style attribute
_copyStyle(to->style, from->style);
- if (from->style->clipPath.url) to->style->clipPath.url = strdup(from->style->clipPath.url);
- if (from->style->mask.url) to->style->mask.url = strdup(from->style->mask.url);
+ if (from->style->clipPath.url) {
+ if (to->style->clipPath.url) free(to->style->clipPath.url);
+ to->style->clipPath.url = strdup(from->style->clipPath.url);
+ }
+ if (from->style->mask.url) {
+ if (to->style->mask.url) free(to->style->mask.url);
+ to->style->mask.url = strdup(from->style->mask.url);
+ }
}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
index 42bfd4de70..737fd96455 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
@@ -115,9 +115,54 @@ static bool _parseNumber(const char** content, float* number)
if ((*content) == end) return false;
//Skip comma if any
*content = _skipComma(end);
+
return true;
}
+
+static constexpr struct
+{
+ AspectRatioAlign align;
+ const char* tag;
+} alignTags[] = {
+ { AspectRatioAlign::XMinYMin, "xMinYMin" },
+ { AspectRatioAlign::XMidYMin, "xMidYMin" },
+ { AspectRatioAlign::XMaxYMin, "xMaxYMin" },
+ { AspectRatioAlign::XMinYMid, "xMinYMid" },
+ { AspectRatioAlign::XMidYMid, "xMidYMid" },
+ { AspectRatioAlign::XMaxYMid, "xMaxYMid" },
+ { AspectRatioAlign::XMinYMax, "xMinYMax" },
+ { AspectRatioAlign::XMidYMax, "xMidYMax" },
+ { AspectRatioAlign::XMaxYMax, "xMaxYMax" },
+};
+
+
+static bool _parseAspectRatio(const char** content, AspectRatioAlign* align, AspectRatioMeetOrSlice* meetOrSlice)
+{
+ if (!strcmp(*content, "none")) {
+ *align = AspectRatioAlign::None;
+ return true;
+ }
+
+ for (unsigned int i = 0; i < sizeof(alignTags) / sizeof(alignTags[0]); i++) {
+ if (!strncmp(*content, alignTags[i].tag, 8)) {
+ *align = alignTags[i].align;
+ *content += 8;
+ *content = _skipSpace(*content, nullptr);
+ break;
+ }
+ }
+
+ if (!strcmp(*content, "meet")) {
+ *meetOrSlice = AspectRatioMeetOrSlice::Meet;
+ } else if (!strcmp(*content, "slice")) {
+ *meetOrSlice = AspectRatioMeetOrSlice::Slice;
+ }
+
+ return true;
+}
+
+
/**
* According to https://www.w3.org/TR/SVG/coords.html#Units
*/
@@ -554,6 +599,7 @@ static void _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char**
}
}
} else if (ref && len >= 3 && !strncmp(str, "url", 3)) {
+ if (*ref) free(*ref);
*ref = _idFromUrl((const char*)(str + 3));
} else {
//Handle named color
@@ -802,7 +848,7 @@ static bool _attrParseSvgNode(void* data, const char* key, const char* value)
}
loader->svgParse->global.x = (int)doc->vx;
} else if (!strcmp(key, "preserveAspectRatio")) {
- if (!strcmp(value, "none")) doc->preserveAspect = false;
+ _parseAspectRatio(&value, &doc->align, &doc->meetOrSlice);
} else if (!strcmp(key, "style")) {
return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
#ifdef THORVG_LOG_ENABLED
@@ -1156,7 +1202,7 @@ static bool _attrParseSymbolNode(void* data, const char* key, const char* value)
symbol->h = _toFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
symbol->hasHeight = true;
} else if (!strcmp(key, "preserveAspectRatio")) {
- if (!strcmp(value, "none")) symbol->preserveAspect = false;
+ _parseAspectRatio(&value, &symbol->align, &symbol->meetOrSlice);
} else if (!strcmp(key, "overflow")) {
if (!strcmp(value, "visible")) symbol->overflowVisible = true;
} else {
@@ -1248,7 +1294,8 @@ static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const cha
loader->svgParse->global.w = 0;
loader->svgParse->global.h = 0;
- doc->preserveAspect = true;
+ doc->align = AspectRatioAlign::XMidYMid;
+ doc->meetOrSlice = AspectRatioMeetOrSlice::Meet;
func(buf, bufLength, _attrParseSvgNode, loader);
if (loader->svgParse->global.w == 0) {
@@ -1309,7 +1356,8 @@ static SvgNode* _createSymbolNode(SvgLoaderData* loader, SvgNode* parent, const
if (!loader->svgParse->node) return nullptr;
loader->svgParse->node->display = false;
- loader->svgParse->node->node.symbol.preserveAspect = true;
+ loader->svgParse->node->node.symbol.align = AspectRatioAlign::XMidYMid;
+ loader->svgParse->node->node.symbol.meetOrSlice = AspectRatioMeetOrSlice::Meet;
loader->svgParse->node->node.symbol.overflowVisible = false;
loader->svgParse->node->node.symbol.hasViewBox = false;
@@ -1331,6 +1379,7 @@ static bool _attrParsePathNode(void* data, const char* key, const char* value)
SvgPathNode* path = &(node->node.path);
if (!strcmp(key, "d")) {
+ if (path->path) free(path->path);
//Temporary: need to copy
path->path = _copyId(value);
} else if (!strcmp(key, "style")) {
@@ -1801,19 +1850,10 @@ static SvgNode* _getDefsNode(SvgNode* node)
}
-static SvgNode* _findChildById(const SvgNode* node, const char* id)
+static SvgNode* _findNodeById(SvgNode *node, const char* id)
{
if (!node) return nullptr;
- auto child = node->child.data;
- for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
- if (((*child)->id) && !strcmp((*child)->id, id)) return (*child);
- }
- return nullptr;
-}
-
-static SvgNode* _findNodeById(SvgNode *node, const char* id)
-{
SvgNode* result = nullptr;
if (node->id && !strcmp(node->id, id)) return node;
@@ -1827,6 +1867,7 @@ static SvgNode* _findNodeById(SvgNode *node, const char* id)
return result;
}
+
static void _cloneGradStops(Array<Fill::ColorStop>& dst, const Array<Fill::ColorStop>& src)
{
for (uint32_t i = 0; i < src.count; ++i) {
@@ -1889,7 +1930,10 @@ static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* paren
child->fill.paint.color = parent->fill.paint.color;
child->fill.paint.none = parent->fill.paint.none;
child->fill.paint.curColor = parent->fill.paint.curColor;
- if (parent->fill.paint.url) child->fill.paint.url = _copyId(parent->fill.paint.url);
+ if (parent->fill.paint.url) {
+ if (child->fill.paint.url) free(child->fill.paint.url);
+ child->fill.paint.url = _copyId(parent->fill.paint.url);
+ }
}
if (!((int)child->fill.flags & (int)SvgFillFlags::Opacity)) {
child->fill.opacity = parent->fill.opacity;
@@ -1902,7 +1946,12 @@ static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* paren
child->stroke.paint.color = parent->stroke.paint.color;
child->stroke.paint.none = parent->stroke.paint.none;
child->stroke.paint.curColor = parent->stroke.paint.curColor;
- child->stroke.paint.url = parent->stroke.paint.url ? _copyId(parent->stroke.paint.url) : nullptr;
+ if (parent->stroke.paint.url) {
+ if (child->stroke.paint.url) free(child->stroke.paint.url);
+ child->stroke.paint.url = _copyId(parent->stroke.paint.url);
+ } else {
+ child->stroke.paint.url = nullptr;
+ }
}
if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Opacity)) {
child->stroke.opacity = parent->stroke.opacity;
@@ -1942,7 +1991,10 @@ static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from)
to->fill.paint.color = from->fill.paint.color;
to->fill.paint.none = from->fill.paint.none;
to->fill.paint.curColor = from->fill.paint.curColor;
- if (from->fill.paint.url) to->fill.paint.url = _copyId(from->fill.paint.url);
+ if (from->fill.paint.url) {
+ if (to->fill.paint.url) free(to->fill.paint.url);
+ to->fill.paint.url = _copyId(from->fill.paint.url);
+ }
}
if (((int)from->fill.flags & (int)SvgFillFlags::Opacity)) {
to->fill.opacity = from->fill.opacity;
@@ -1956,7 +2008,12 @@ static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from)
to->stroke.paint.color = from->stroke.paint.color;
to->stroke.paint.none = from->stroke.paint.none;
to->stroke.paint.curColor = from->stroke.paint.curColor;
- to->stroke.paint.url = from->stroke.paint.url ? _copyId(from->stroke.paint.url) : nullptr;
+ if (from->stroke.paint.url) {
+ if (to->stroke.paint.url) free(to->stroke.paint.url);
+ to->stroke.paint.url = _copyId(from->stroke.paint.url);
+ } else {
+ to->stroke.paint.url = nullptr;
+ }
}
if (((int)from->stroke.flags & (int)SvgStrokeFlags::Opacity)) {
to->stroke.opacity = from->stroke.opacity;
@@ -1992,10 +2049,14 @@ static void _copyAttr(SvgNode* to, const SvgNode* from)
//Copy style attribute
_styleCopy(to->style, from->style);
to->style->flags = (SvgStyleFlags)((int)to->style->flags | (int)from->style->flags);
- if (from->style->fill.paint.url) to->style->fill.paint.url = strdup(from->style->fill.paint.url);
- if (from->style->stroke.paint.url) to->style->stroke.paint.url = strdup(from->style->stroke.paint.url);
- if (from->style->clipPath.url) to->style->clipPath.url = strdup(from->style->clipPath.url);
- if (from->style->mask.url) to->style->mask.url = strdup(from->style->mask.url);
+ if (from->style->clipPath.url) {
+ if (to->style->clipPath.url) free(to->style->clipPath.url);
+ to->style->clipPath.url = strdup(from->style->clipPath.url);
+ }
+ if (from->style->mask.url) {
+ if (to->style->mask.url) free(to->style->mask.url);
+ to->style->mask.url = strdup(from->style->mask.url);
+ }
//Copy node attribute
switch (from->type) {
@@ -2031,7 +2092,10 @@ static void _copyAttr(SvgNode* to, const SvgNode* from)
break;
}
case SvgNodeType::Path: {
- if (from->node.path.path) to->node.path.path = strdup(from->node.path.path);
+ if (from->node.path.path) {
+ if (to->node.path.path) free(to->node.path.path);
+ to->node.path.path = strdup(from->node.path.path);
+ }
break;
}
case SvgNodeType::Polygon: {
@@ -2053,7 +2117,10 @@ static void _copyAttr(SvgNode* to, const SvgNode* from)
to->node.image.y = from->node.image.y;
to->node.image.w = from->node.image.w;
to->node.image.h = from->node.image.h;
- if (from->node.image.href) to->node.image.href = strdup(from->node.image.href);
+ if (from->node.image.href) {
+ if (to->node.image.href) free(to->node.image.href);
+ to->node.image.href = strdup(from->node.image.href);
+ }
break;
}
default: {
@@ -2093,8 +2160,8 @@ static void _clonePostponedNodes(Array<SvgNodeIdPair>* cloneNodes, SvgNode* doc)
for (uint32_t i = 0; i < cloneNodes->count; ++i) {
auto nodeIdPair = cloneNodes->data[i];
auto defs = _getDefsNode(nodeIdPair.node);
- auto nodeFrom = _findChildById(defs, nodeIdPair.id);
- if (!nodeFrom) nodeFrom = _findChildById(doc, nodeIdPair.id);
+ auto nodeFrom = _findNodeById(defs, nodeIdPair.id);
+ if (!nodeFrom) nodeFrom = _findNodeById(doc, nodeIdPair.id);
_cloneNode(nodeFrom, nodeIdPair.node, 0);
if (nodeFrom && nodeFrom->type == SvgNodeType::Symbol && nodeIdPair.node->type == SvgNodeType::Use) {
nodeIdPair.node->node.use.symbol = nodeFrom;
@@ -2141,7 +2208,7 @@ static bool _attrParseUseNode(void* data, const char* key, const char* value)
if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
id = _idFromHref(value);
defs = _getDefsNode(node);
- nodeFrom = _findChildById(defs, id);
+ nodeFrom = _findNodeById(defs, id);
if (nodeFrom) {
_cloneNode(nodeFrom, node, 0);
if (nodeFrom->type == SvgNodeType::Symbol) use->symbol = nodeFrom;
@@ -2695,10 +2762,14 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
if (loader->stack.count > 0) parent = loader->stack.data[loader->stack.count - 1];
else parent = loader->doc;
if (!strcmp(tagName, "style")) {
- node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes);
- loader->cssStyle = node;
- loader->doc->node.doc.style = node;
- loader->style = true;
+ // TODO: For now only the first style node is saved. After the css id selector
+ // is introduced this if condition shouldin't be necessary any more
+ if (!loader->cssStyle) {
+ node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes);
+ loader->cssStyle = node;
+ loader->doc->node.doc.style = node;
+ loader->style = true;
+ }
} else {
node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes);
}
@@ -3127,7 +3198,7 @@ void SvgLoader::run(unsigned tid)
_updateStyle(loaderData.doc, nullptr);
}
- root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, preserveAspect, svgPath);
+ root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, align, meetOrSlice, svgPath);
}
@@ -3160,7 +3231,8 @@ bool SvgLoader::header()
if (vh < FLT_EPSILON) vh = h;
}
- preserveAspect = loaderData.doc->node.doc.preserveAspect;
+ align = loaderData.doc->node.doc.align;
+ meetOrSlice = loaderData.doc->node.doc.meetOrSlice;
} else {
TVGLOG("SVG", "No SVG File. There is no <svg/>");
return false;
@@ -3215,31 +3287,9 @@ bool SvgLoader::resize(Paint* paint, float w, float h)
auto sx = w / this->w;
auto sy = h / this->h;
+ Matrix m = {sx, 0, 0, 0, sy, 0, 0, 0, 1};
+ paint->transform(m);
- if (preserveAspect) {
- //Scale
- auto scale = sx < sy ? sx : sy;
- paint->scale(scale);
- //Align
- auto tx = 0.0f;
- auto ty = 0.0f;
- auto tw = this->w * scale;
- auto th = this->h * scale;
- if (tw > th) ty -= (h - th) * 0.5f;
- else tx -= (w - tw) * 0.5f;
- paint->translate(-tx, -ty);
- } else {
- //Align
- auto tx = 0.0f;
- auto ty = 0.0f;
- auto tw = this->w * sx;
- auto th = this->h * sy;
- if (tw > th) ty -= (h - th) * 0.5f;
- else tx -= (w - tw) * 0.5f;
-
- Matrix m = {sx, 0, -tx, 0, sy, -ty, 0, 0, 1};
- paint->transform(m);
- }
return true;
}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h
index 093fb671b3..f224d1a4ac 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h
@@ -50,6 +50,9 @@ public:
unique_ptr<Paint> paint() override;
private:
+ AspectRatioAlign align = AspectRatioAlign::XMidYMid;
+ AspectRatioMeetOrSlice meetOrSlice = AspectRatioMeetOrSlice::Meet;
+
bool header();
void clear();
void run(unsigned tid) override;
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
index dc9ed558c3..c657c0e21a 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
@@ -145,6 +145,26 @@ enum class SvgParserLengthType
Other
};
+enum class AspectRatioAlign
+{
+ None,
+ XMinYMin,
+ XMidYMin,
+ XMaxYMin,
+ XMinYMid,
+ XMidYMid,
+ XMaxYMid,
+ XMinYMax,
+ XMidYMax,
+ XMaxYMax
+};
+
+enum class AspectRatioMeetOrSlice
+{
+ Meet,
+ Slice
+};
+
struct SvgDocNode
{
float w;
@@ -155,7 +175,8 @@ struct SvgDocNode
float vh;
SvgNode* defs;
SvgNode* style;
- bool preserveAspect;
+ AspectRatioAlign align;
+ AspectRatioMeetOrSlice meetOrSlice;
};
struct SvgGNode
@@ -171,7 +192,8 @@ struct SvgSymbolNode
{
float w, h;
float vx, vy, vw, vh;
- bool preserveAspect;
+ AspectRatioAlign align;
+ AspectRatioMeetOrSlice meetOrSlice;
bool overflowVisible;
bool hasViewBox;
bool hasWidth;
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
index a3f34fd46b..4cb4ffdaeb 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
@@ -49,9 +49,9 @@
*/
+#include "tvgMath.h" /* to include math.h before cstring */
#include <cstring>
#include <string>
-#include "tvgMath.h"
#include "tvgSvgLoaderCommon.h"
#include "tvgSvgSceneBuilder.h"
#include "tvgSvgPath.h"
@@ -68,7 +68,7 @@ struct Box
static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath);
-static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, bool* isMaskWhite = nullptr);
+static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth, bool* isMaskWhite = nullptr);
static inline bool _isGroupType(SvgNodeType type)
@@ -282,7 +282,7 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox
node->style->mask.applying = true;
bool isMaskWhite = true;
- auto comp = _sceneBuildHelper(compNode, vBox, svgPath, true, &isMaskWhite);
+ auto comp = _sceneBuildHelper(compNode, vBox, svgPath, true, 0, &isMaskWhite);
if (comp) {
if (node->transform) comp->transform(*node->transform);
@@ -560,10 +560,84 @@ static unique_ptr<Picture> _imageBuildHelper(SvgNode* node, const Box& vBox, con
}
-static unique_ptr<Scene> _useBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool* isMaskWhite)
+static Matrix _calculateAspectRatioMatrix(AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, float width, float height, const Box& box)
+{
+ auto sx = width / box.w;
+ auto sy = height / box.h;
+ auto tvx = box.x * sx;
+ auto tvy = box.y * sy;
+
+ if (align == AspectRatioAlign::None)
+ return {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1};
+
+ //Scale
+ if (meetOrSlice == AspectRatioMeetOrSlice::Meet) {
+ if (sx < sy) sy = sx;
+ else sx = sy;
+ } else {
+ if (sx < sy) sx = sy;
+ else sy = sx;
+ }
+
+ //Align
+ tvx = box.x * sx;
+ tvy = box.y * sy;
+ auto tvw = box.w * sx;
+ auto tvh = box.h * sy;
+
+ switch (align) {
+ case AspectRatioAlign::XMinYMin: {
+ break;
+ }
+ case AspectRatioAlign::XMidYMin: {
+ tvx -= (width - tvw) * 0.5f;
+ break;
+ }
+ case AspectRatioAlign::XMaxYMin: {
+ tvx -= width - tvw;
+ break;
+ }
+ case AspectRatioAlign::XMinYMid: {
+ tvy -= (height - tvh) * 0.5f;
+ break;
+ }
+ case AspectRatioAlign::XMidYMid: {
+ tvx -= (width - tvw) * 0.5f;
+ tvy -= (height - tvh) * 0.5f;
+ break;
+ }
+ case AspectRatioAlign::XMaxYMid: {
+ tvx -= width - tvw;
+ tvy -= (height - tvh) * 0.5f;
+ break;
+ }
+ case AspectRatioAlign::XMinYMax: {
+ tvy -= height - tvh;
+ break;
+ }
+ case AspectRatioAlign::XMidYMax: {
+ tvx -= (width - tvw) * 0.5f;
+ tvy -= height - tvh;
+ break;
+ }
+ case AspectRatioAlign::XMaxYMax: {
+ tvx -= width - tvw;
+ tvy -= height - tvh;
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+
+ return {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1};
+}
+
+
+static unique_ptr<Scene> _useBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, int depth, bool* isMaskWhite)
{
unique_ptr<Scene> finalScene;
- auto scene = _sceneBuildHelper(node, vBox, svgPath, false, isMaskWhite);
+ auto scene = _sceneBuildHelper(node, vBox, svgPath, false, depth + 1, isMaskWhite);
// mUseTransform = mUseTransform * mTranslate
Matrix mUseTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
@@ -585,20 +659,8 @@ static unique_ptr<Scene> _useBuildHelper(const SvgNode* node, const Box& vBox, c
Matrix mViewBox = {1, 0, 0, 0, 1, 0, 0, 0, 1};
if ((!mathEqual(width, vw) || !mathEqual(height, vh)) && vw > 0 && vh > 0) {
- auto sx = width / vw;
- auto sy = height / vh;
- if (symbol.preserveAspect) {
- if (sx < sy) sy = sx;
- else sx = sy;
- }
-
- auto tvx = symbol.vx * sx;
- auto tvy = symbol.vy * sy;
- auto tvw = vw * sx;
- auto tvh = vh * sy;
- tvy -= (symbol.h - tvh) * 0.5f;
- tvx -= (symbol.w - tvw) * 0.5f;
- mViewBox = {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1};
+ Box box = {symbol.vx, symbol.vy, vw, vh};
+ mViewBox = _calculateAspectRatioMatrix(symbol.align, symbol.meetOrSlice, width, height, box);
} else if (!mathZero(symbol.vx) || !mathZero(symbol.vy)) {
mViewBox = {1, 0, -symbol.vx, 0, 1, -symbol.vy, 0, 0, 1};
}
@@ -642,8 +704,15 @@ static unique_ptr<Scene> _useBuildHelper(const SvgNode* node, const Box& vBox, c
}
-static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, bool* isMaskWhite)
+static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth, bool* isMaskWhite)
{
+ /* Exception handling: Prevent invalid SVG data input.
+ The size is the arbitrary value, we need an experimental size. */
+ if (depth > 2192) {
+ TVGERR("SVG", "Infinite recursive call - stopped after %d calls! Svg file may be incorrectly formatted.", depth);
+ return nullptr;
+ }
+
if (_isGroupType(node->type) || mask) {
auto scene = Scene::gen();
// For a Symbol node, the viewBox transformation has to be applied first - see _useBuildHelper()
@@ -654,12 +723,15 @@ static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox,
for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
if (_isGroupType((*child)->type)) {
if ((*child)->type == SvgNodeType::Use)
- scene->push(_useBuildHelper(*child, vBox, svgPath, isMaskWhite));
+ scene->push(_useBuildHelper(*child, vBox, svgPath, depth + 1, isMaskWhite));
else
- scene->push(_sceneBuildHelper(*child, vBox, svgPath, false, isMaskWhite));
+ scene->push(_sceneBuildHelper(*child, vBox, svgPath, false, depth + 1, isMaskWhite));
} else if ((*child)->type == SvgNodeType::Image) {
auto image = _imageBuildHelper(*child, vBox, svgPath);
- if (image) scene->push(move(image));
+ if (image) {
+ scene->push(move(image));
+ if (isMaskWhite) *isMaskWhite = false;
+ }
} else if ((*child)->type != SvgNodeType::Mask) {
auto shape = _shapeBuildHelper(*child, vBox, svgPath);
if (shape) {
@@ -688,36 +760,18 @@ static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox,
/* External Class Implementation */
/************************************************************************/
-unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, bool preserveAspect, const string& svgPath)
+unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath)
{
+ //TODO: aspect ratio is valid only if viewBox was set
+
if (!node || (node->type != SvgNodeType::Doc)) return nullptr;
Box vBox = {vx, vy, vw, vh};
- auto docNode = _sceneBuildHelper(node, vBox, svgPath, false);
+ auto docNode = _sceneBuildHelper(node, vBox, svgPath, false, 0);
if (!mathEqual(w, vw) || !mathEqual(h, vh)) {
- auto sx = w / vw;
- auto sy = h / vh;
-
- if (preserveAspect) {
- //Scale
- auto scale = sx < sy ? sx : sy;
- docNode->scale(scale);
- //Align
- auto tvx = vx * scale;
- auto tvy = vy * scale;
- auto tvw = vw * scale;
- auto tvh = vh * scale;
- tvx -= (w - tvw) * 0.5f;
- tvy -= (h - tvh) * 0.5f;
- docNode->translate(-tvx, -tvy);
- } else {
- //Align
- auto tvx = vx * sx;
- auto tvy = vy * sy;
- Matrix m = {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1};
- docNode->transform(m);
- }
+ Matrix m = _calculateAspectRatioMatrix(align, meetOrSlice, w, h, vBox);
+ docNode->transform(m);
} else if (!mathZero(vx) || !mathZero(vy)) {
docNode->translate(-vx, -vy);
}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h
index cecbbf02a8..311f3c80e6 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h
@@ -25,6 +25,6 @@
#include "tvgCommon.h"
-unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, bool preserveAspect, const string& svgPath);
+unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath);
#endif //_TVG_SVG_SCENE_BUILDER_H_
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
index c373da2dd5..231badd27d 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
@@ -26,10 +26,10 @@
#ifdef _WIN32
#include <malloc.h>
-#elif __FreeBSD__
- #include<stdlib.h>
-#else
+#elif defined(__linux__)
#include <alloca.h>
+#else
+ #include <stdlib.h>
#endif
#include "tvgXmlParser.h"
diff --git a/thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp b/thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp
index 62a75ecd3d..01a39b6e17 100644
--- a/thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp
+++ b/thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp
@@ -23,10 +23,10 @@
#ifdef _WIN32
#include <malloc.h>
-#elif __FreeBSD__
- #include<stdlib.h>
-#else
+#elif defined(__linux__)
#include <alloca.h>
+#else
+ #include <stdlib.h>
#endif
#include "tvgTvgCommon.h"
diff --git a/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp b/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp
index adf85836c3..57a21dcce1 100644
--- a/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp
+++ b/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp
@@ -28,10 +28,10 @@
#ifdef _WIN32
#include <malloc.h>
-#elif __FreeBSD__
- #include<stdlib.h>
-#else
+#elif defined(__linux__)
#include <alloca.h>
+#else
+ #include <stdlib.h>
#endif
static FILE* _fopen(const char* filename, const char* mode)
diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh
index f2fd2a80e4..8cccc947ce 100755
--- a/thirdparty/thorvg/update-thorvg.sh
+++ b/thirdparty/thorvg/update-thorvg.sh
@@ -1,4 +1,4 @@
-VERSION=0.8.2
+VERSION=0.8.3
rm -rf AUTHORS inc LICENSE src *.zip
curl -L -O https://github.com/Samsung/thorvg/archive/v$VERSION.zip
bsdtar --strip-components=1 -xvf *.zip
@@ -26,3 +26,6 @@ cat << EOF > inc/config.h
#define THORVG_VERSION_STRING "$VERSION"
#endif
EOF
+for source in $(find ./ -type f \( -iname \*.h -o -iname \*.cpp \)); do
+ sed -i -e '$a\' $source
+done