summaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/astcenc/image_compress_astcenc.cpp2
-rw-r--r--modules/basis_universal/image_compress_basisu.cpp72
-rw-r--r--modules/csg/editor/csg_gizmos.h2
-rw-r--r--modules/cvtt/image_compress_cvtt.cpp2
-rw-r--r--modules/etcpak/image_compress_etcpak.cpp2
-rw-r--r--modules/fbx/fbx_document.cpp10
-rw-r--r--modules/fbx/fbx_state.h1
-rw-r--r--modules/gdscript/gdscript.cpp56
-rw-r--r--modules/gdscript/gdscript.h7
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp89
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp60
-rw-r--r--modules/gdscript/gdscript_byte_codegen.h1
-rw-r--r--modules/gdscript/gdscript_codegen.h1
-rw-r--r--modules/gdscript/gdscript_compiler.cpp10
-rw-r--r--modules/gdscript/gdscript_disassembler.cpp46
-rw-r--r--modules/gdscript/gdscript_function.h5
-rw-r--r--modules/gdscript/gdscript_parser.cpp5
-rw-r--r--modules/gdscript/gdscript_parser.h15
-rw-r--r--modules/gdscript/gdscript_utility_functions.cpp4
-rw-r--r--modules/gdscript/gdscript_vm.cpp464
-rw-r--r--modules/gdscript/language_server/gdscript_language_server.h2
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp29
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static.gd10
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static_in_lambda_param.gd15
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static_in_lambda_param.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda_param.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda.gd14
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda_setter.gd15
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda_setter.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_var_init_call_non_static_in_lambda_setter.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_access.gd11
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_access.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.out6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/static_non_static_access.gd75
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/static_non_static_access.out1
-rw-r--r--modules/gdscript/tests/scripts/parser/features/export_arrays.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/export_arrays.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/read_only_dictionary.gd4
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/read_only_dictionary.out6
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/call_native_static_method_validated.gd7
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/call_native_static_method_validated.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/compare_builtin_equals_null.gd4
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/compare_builtin_equals_null.out1
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/compare_builtin_not_equals_null.gd4
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/compare_builtin_not_equals_null.out1
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/compare_null_equals_builtin.gd4
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/compare_null_equals_builtin.out1
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/compare_null_not_equals_builtin.gd4
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/compare_null_not_equals_builtin.out1
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/onready_base_before_subclass.gd18
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/onready_base_before_subclass.out3
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/stringify.gd1
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/stringify.out1
-rw-r--r--modules/gltf/editor/editor_scene_exporter_gltf_plugin.h2
-rw-r--r--modules/gridmap/editor/grid_map_editor_plugin.h2
-rw-r--r--modules/interactive_music/editor/audio_stream_interactive_editor_plugin.h2
-rw-r--r--modules/mobile_vr/doc_classes/MobileVRInterface.xml11
-rw-r--r--modules/mobile_vr/mobile_vr_interface.cpp76
-rw-r--r--modules/mobile_vr/mobile_vr_interface.h16
-rw-r--r--modules/mono/.editorconfig4
-rw-r--r--modules/mono/csharp_script.h2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln11
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/Godot.SourceGenerators.Tests.csproj2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPropertiesGeneratorTests.cs9
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/AbstractGenericNode(Of T)_ScriptProperties.generated.cs49
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/AbstractGenericNode.cs7
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/MustBeVariant.GD0301.cs6
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs3
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs1
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs3
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs14
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs5
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj14
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.sln12
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj2
-rw-r--r--modules/mono/editor/bindings_generator.cpp21
-rw-r--r--modules/mono/editor/bindings_generator.h3
-rw-r--r--modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Godot.SourceGenerators.Internal.csproj2
-rw-r--r--modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs1
-rw-r--r--modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj26
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp.sln15
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/CustomUnsafe.cs16
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs33
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs27
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs11
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs19
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs6
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs14
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2I.cs14
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs61
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs19
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs94
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2I.cs109
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs87
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3I.cs116
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs109
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4I.cs121
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj2
-rw-r--r--modules/mono/glue/runtime_interop.cpp29
-rw-r--r--modules/multiplayer/editor/multiplayer_editor_plugin.h2
-rw-r--r--modules/multiplayer/editor/replication_editor.cpp3
-rw-r--r--modules/multiplayer/editor/replication_editor.h2
-rw-r--r--modules/navigation/2d/nav_mesh_generator_2d.cpp175
-rw-r--r--modules/navigation/2d/nav_mesh_generator_2d.h2
-rw-r--r--modules/navigation/editor/navigation_mesh_editor_plugin.h2
-rw-r--r--modules/noise/editor/noise_editor_plugin.h2
-rw-r--r--modules/noise/register_types.cpp2
-rw-r--r--modules/openxr/action_map/openxr_action_map.cpp164
-rw-r--r--modules/openxr/doc_classes/OpenXRAPIExtension.xml8
-rw-r--r--modules/openxr/doc_classes/OpenXRInterface.xml25
-rw-r--r--modules/openxr/editor/openxr_action_map_editor.h2
-rw-r--r--modules/openxr/editor/openxr_editor_plugin.h2
-rw-r--r--modules/openxr/extensions/openxr_composition_layer_extension.cpp4
-rw-r--r--modules/openxr/extensions/openxr_eye_gaze_interaction.cpp36
-rw-r--r--modules/openxr/extensions/openxr_eye_gaze_interaction.h6
-rw-r--r--modules/openxr/extensions/openxr_hand_interaction_extension.cpp97
-rw-r--r--modules/openxr/extensions/openxr_hand_interaction_extension.h72
-rw-r--r--modules/openxr/extensions/openxr_hand_tracking_extension.cpp2
-rw-r--r--modules/openxr/openxr_api.cpp533
-rw-r--r--modules/openxr/openxr_api.h94
-rw-r--r--modules/openxr/openxr_api_extension.cpp10
-rw-r--r--modules/openxr/openxr_api_extension.h1
-rw-r--r--modules/openxr/openxr_interface.cpp71
-rw-r--r--modules/openxr/openxr_interface.h36
-rw-r--r--modules/openxr/register_types.cpp2
-rw-r--r--modules/raycast/config.py5
-rw-r--r--modules/text_server_adv/gdextension_build/SConstruct27
-rw-r--r--modules/text_server_adv/gdextension_build/methods.py119
-rw-r--r--modules/text_server_adv/text_server_adv.cpp10
-rw-r--r--modules/text_server_adv/text_server_adv.h1
-rw-r--r--modules/text_server_fb/gdextension_build/SConstruct27
-rw-r--r--modules/text_server_fb/gdextension_build/methods.py119
150 files changed, 3111 insertions, 911 deletions
diff --git a/modules/astcenc/image_compress_astcenc.cpp b/modules/astcenc/image_compress_astcenc.cpp
index 31df83efae..941c1f44be 100644
--- a/modules/astcenc/image_compress_astcenc.cpp
+++ b/modules/astcenc/image_compress_astcenc.cpp
@@ -41,7 +41,7 @@ void _compress_astc(Image *r_img, Image::ASTCFormat p_format) {
// TODO: See how to handle lossy quality.
Image::Format img_format = r_img->get_format();
- if (img_format >= Image::FORMAT_DXT1) {
+ if (Image::is_format_compressed(img_format)) {
return; // Do not compress, already compressed.
}
diff --git a/modules/basis_universal/image_compress_basisu.cpp b/modules/basis_universal/image_compress_basisu.cpp
index 72e7977eef..d8ef1c0414 100644
--- a/modules/basis_universal/image_compress_basisu.cpp
+++ b/modules/basis_universal/image_compress_basisu.cpp
@@ -96,17 +96,74 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
} break;
}
+ // Copy the source image data with mipmaps into BasisU.
{
- // Encode the image with mipmaps.
+ const int orig_width = image->get_width();
+ const int orig_height = image->get_height();
+
+ bool is_res_div_4 = (orig_width % 4 == 0) && (orig_height % 4 == 0);
+
+ // Image's resolution rounded up to the nearest values divisible by 4.
+ int next_width = orig_width <= 2 ? orig_width : (orig_width + 3) & ~3;
+ int next_height = orig_height <= 2 ? orig_height : (orig_height + 3) & ~3;
+
Vector<uint8_t> image_data = image->get_data();
basisu::vector<basisu::image> basisu_mipmaps;
+ // Buffer for storing padded mipmap data.
+ Vector<uint32_t> mip_data_padded;
+
for (int32_t i = 0; i <= image->get_mipmap_count(); i++) {
int ofs, size, width, height;
image->get_mipmap_offset_size_and_dimensions(i, ofs, size, width, height);
+ const uint8_t *image_mip_data = image_data.ptr() + ofs;
+
+ // Pad the mipmap's data if its resolution isn't divisible by 4.
+ if (image->has_mipmaps() && !is_res_div_4 && (width > 2 && height > 2) && (width != next_width || height != next_height)) {
+ // Source mip's data interpreted as 32-bit RGBA blocks to help with copying pixel data.
+ const uint32_t *mip_src_data = reinterpret_cast<const uint32_t *>(image_mip_data);
+
+ // Reserve space in the padded buffer.
+ mip_data_padded.resize(next_width * next_height);
+ uint32_t *data_padded_ptr = mip_data_padded.ptrw();
+
+ // Pad mipmap to the nearest block by smearing.
+ int x = 0, y = 0;
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ data_padded_ptr[next_width * y + x] = mip_src_data[width * y + x];
+ }
+
+ // First, smear in x.
+ for (; x < next_width; x++) {
+ data_padded_ptr[next_width * y + x] = data_padded_ptr[next_width * y + x - 1];
+ }
+ }
+
+ // Then, smear in y.
+ for (; y < next_height; y++) {
+ for (x = 0; x < next_width; x++) {
+ data_padded_ptr[next_width * y + x] = data_padded_ptr[next_width * y + x - next_width];
+ }
+ }
+
+ // Override the image_mip_data pointer with our temporary Vector.
+ image_mip_data = reinterpret_cast<const uint8_t *>(mip_data_padded.ptr());
+
+ // Override the mipmap's properties.
+ width = next_width;
+ height = next_height;
+ size = mip_data_padded.size() * 4;
+ }
+
+ // Get the next mipmap's resolution.
+ next_width /= 2;
+ next_height /= 2;
+
+ // Copy the source mipmap's data to a BasisU image.
basisu::image basisu_image(width, height);
- memcpy(basisu_image.get_ptr(), image_data.ptr() + ofs, size);
+ memcpy(basisu_image.get_ptr(), image_mip_data, size);
if (i == 0) {
params.m_source_images.push_back(basisu_image);
@@ -132,10 +189,10 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
// Copy the encoded data to the buffer.
{
- uint8_t *w = basisu_data.ptrw();
- *(uint32_t *)w = decompress_format;
+ uint8_t *wb = basisu_data.ptrw();
+ *(uint32_t *)wb = decompress_format;
- memcpy(w + 4, basisu_out.get_ptr(), basisu_out.size());
+ memcpy(wb + 4, basisu_out.get_ptr(), basisu_out.size());
}
return basisu_data;
@@ -238,12 +295,11 @@ Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) {
uint8_t *dst = out_data.ptrw();
memset(dst, 0, out_data.size());
- uint32_t mip_count = Image::get_image_required_mipmaps(basisu_info.m_orig_width, basisu_info.m_orig_height, image_format);
- for (uint32_t i = 0; i <= mip_count; i++) {
+ for (uint32_t i = 0; i < basisu_info.m_total_levels; i++) {
basist::basisu_image_level_info basisu_level;
transcoder.get_image_level_info(src_ptr, src_size, basisu_level, 0, i);
- uint32_t mip_block_or_pixel_count = image_format >= Image::FORMAT_DXT1 ? basisu_level.m_total_blocks : basisu_level.m_orig_width * basisu_level.m_orig_height;
+ uint32_t mip_block_or_pixel_count = Image::is_format_compressed(image_format) ? basisu_level.m_total_blocks : basisu_level.m_orig_width * basisu_level.m_orig_height;
int ofs = Image::get_image_mipmap_offset(basisu_info.m_width, basisu_info.m_height, image_format, i);
bool result = transcoder.transcode_image_level(src_ptr, src_size, 0, i, dst + ofs, mip_block_or_pixel_count, basisu_format);
diff --git a/modules/csg/editor/csg_gizmos.h b/modules/csg/editor/csg_gizmos.h
index 6281db0a21..de19b33e7d 100644
--- a/modules/csg/editor/csg_gizmos.h
+++ b/modules/csg/editor/csg_gizmos.h
@@ -35,7 +35,7 @@
#include "../csg_shape.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "editor/plugins/node_3d_editor_gizmos.h"
class Gizmo3DHelper;
diff --git a/modules/cvtt/image_compress_cvtt.cpp b/modules/cvtt/image_compress_cvtt.cpp
index e9a7009d7c..7335315c51 100644
--- a/modules/cvtt/image_compress_cvtt.cpp
+++ b/modules/cvtt/image_compress_cvtt.cpp
@@ -142,7 +142,7 @@ static void _digest_job_queue(void *p_job_queue, uint32_t p_index) {
}
void image_compress_cvtt(Image *p_image, Image::UsedChannels p_channels) {
- if (p_image->get_format() >= Image::FORMAT_BPTC_RGBA) {
+ if (p_image->is_compressed()) {
return; //do not compress, already compressed
}
int w = p_image->get_width();
diff --git a/modules/etcpak/image_compress_etcpak.cpp b/modules/etcpak/image_compress_etcpak.cpp
index dcd73101c2..4ce0cf50d9 100644
--- a/modules/etcpak/image_compress_etcpak.cpp
+++ b/modules/etcpak/image_compress_etcpak.cpp
@@ -92,7 +92,7 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img) {
uint64_t start_time = OS::get_singleton()->get_ticks_msec();
Image::Format img_format = r_img->get_format();
- if (img_format >= Image::FORMAT_DXT1) {
+ if (Image::is_format_compressed(img_format)) {
return; // Do not compress, already compressed.
}
if (img_format > Image::FORMAT_RGBA8) {
diff --git a/modules/fbx/fbx_document.cpp b/modules/fbx/fbx_document.cpp
index 5f94a80566..6e9b85dc35 100644
--- a/modules/fbx/fbx_document.cpp
+++ b/modules/fbx/fbx_document.cpp
@@ -693,10 +693,7 @@ Error FBXDocument::_parse_meshes(Ref<FBXState> p_state) {
// Find the first imported skin deformer
for (ufbx_skin_deformer *fbx_skin : fbx_mesh->skin_deformers) {
- if (!p_state->skin_indices.has(fbx_skin->typed_id)) {
- continue;
- }
- GLTFSkinIndex skin_i = p_state->skin_indices[fbx_skin->typed_id];
+ GLTFSkinIndex skin_i = p_state->original_skin_indices[fbx_skin->typed_id];
if (skin_i < 0) {
continue;
}
@@ -2341,7 +2338,7 @@ Error FBXDocument::_parse_skins(Ref<FBXState> p_state) {
HashMap<GLTFNodeIndex, bool> joint_mapping;
for (const ufbx_skin_deformer *fbx_skin : fbx_scene->skin_deformers) {
- if (fbx_skin->clusters.count == 0) {
+ if (fbx_skin->clusters.count == 0 || fbx_skin->weights.count == 0) {
p_state->skin_indices.push_back(-1);
continue;
}
@@ -2387,8 +2384,9 @@ Error FBXDocument::_parse_skins(Ref<FBXState> p_state) {
}
}
}
+ p_state->original_skin_indices = p_state->skin_indices.duplicate();
Error err = SkinTool::_asset_parse_skins(
- p_state->skin_indices.duplicate(),
+ p_state->original_skin_indices,
p_state->skins.duplicate(),
p_state->nodes.duplicate(),
p_state->skin_indices,
diff --git a/modules/fbx/fbx_state.h b/modules/fbx/fbx_state.h
index d10550c228..e52f47e0db 100644
--- a/modules/fbx/fbx_state.h
+++ b/modules/fbx/fbx_state.h
@@ -53,6 +53,7 @@ class FBXState : public GLTFState {
HashMap<Pair<uint64_t, uint64_t>, GLTFTextureIndex, PairHash<uint64_t, uint64_t>> albedo_transparency_textures;
Vector<GLTFSkinIndex> skin_indices;
+ Vector<GLTFSkinIndex> original_skin_indices;
HashMap<ObjectID, GLTFSkeletonIndex> skeleton3d_to_fbx_skeleton;
HashMap<ObjectID, HashMap<ObjectID, GLTFSkinIndex>> skin_and_skeleton3d_to_fbx_skin;
HashSet<String> unique_mesh_names; // Not in GLTFState because GLTFState prefixes mesh names with the scene name (or _)
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 921ed0b416..19b264d764 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -678,6 +678,27 @@ Error GDScript::_static_init() {
#ifdef TOOLS_ENABLED
+void GDScript::_static_default_init() {
+ for (const KeyValue<StringName, MemberInfo> &E : static_variables_indices) {
+ const GDScriptDataType &type = E.value.data_type;
+ // Only initialize builtin types, which are not expected to be `null`.
+ if (!type.has_type || type.kind != GDScriptDataType::BUILTIN) {
+ continue;
+ }
+ if (type.builtin_type == Variant::ARRAY && type.has_container_element_type(0)) {
+ Array default_value;
+ const GDScriptDataType &element_type = type.get_container_element_type(0);
+ default_value.set_typed(element_type.builtin_type, element_type.native_type, element_type.script_type);
+ static_variables.write[E.value.index] = default_value;
+ } else {
+ Variant default_value;
+ Callable::CallError err;
+ Variant::construct(type.builtin_type, default_value, nullptr, 0, err);
+ static_variables.write[E.value.index] = default_value;
+ }
+ }
+}
+
void GDScript::_save_old_static_data() {
old_static_variables_indices = static_variables_indices;
old_static_variables = static_variables;
@@ -841,6 +862,9 @@ Error GDScript::reload(bool p_keep_state) {
#ifdef TOOLS_ENABLED
if (can_run && p_keep_state) {
_restore_old_static_data();
+ } else if (!can_run) {
+ // Initialize static variables with sane default values even if the constructor isn't called.
+ _static_default_init();
}
#endif
@@ -1958,19 +1982,22 @@ int GDScriptInstance::get_method_argument_count(const StringName &p_method, bool
return 0;
}
+void GDScriptInstance::_call_implicit_ready_recursively(GDScript *p_script) {
+ // Call base class first.
+ if (p_script->_base) {
+ _call_implicit_ready_recursively(p_script->_base);
+ }
+ if (p_script->implicit_ready) {
+ Callable::CallError err;
+ p_script->implicit_ready->call(this, nullptr, 0, err);
+ }
+}
+
Variant GDScriptInstance::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
GDScript *sptr = script.ptr();
if (unlikely(p_method == SNAME("_ready"))) {
- // Call implicit ready first, including for the super classes.
- while (sptr) {
- if (sptr->implicit_ready) {
- sptr->implicit_ready->call(this, nullptr, 0, r_error);
- }
- sptr = sptr->_base;
- }
-
- // Reset this back for the regular call.
- sptr = script.ptr();
+ // Call implicit ready first, including for the super classes recursively.
+ _call_implicit_ready_recursively(sptr);
}
while (sptr) {
HashMap<StringName, GDScriptFunction *>::Iterator E = sptr->member_functions.find(p_method);
@@ -2892,7 +2919,7 @@ String ResourceFormatLoaderGDScript::get_resource_type(const String &p_path) con
return "";
}
-void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) {
+void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<String> *r_dependencies, bool p_add_types) {
Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_MSG(file.is_null(), "Cannot open file '" + p_path + "'.");
@@ -2906,8 +2933,13 @@ void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<S
return;
}
+ GDScriptAnalyzer analyzer(&parser);
+ if (OK != analyzer.analyze()) {
+ return;
+ }
+
for (const String &E : parser.get_dependencies()) {
- p_dependencies->push_back(E);
+ r_dependencies->push_back(E);
}
}
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 781e284bfc..f63b0da745 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -169,6 +169,9 @@ private:
GDScriptFunction *static_initializer = nullptr;
Error _static_init();
+#ifdef TOOLS_ENABLED
+ void _static_default_init(); // Initialize static variables with default values based on their types.
+#endif
int subclass_count = 0;
RBSet<Object *> instances;
@@ -365,6 +368,8 @@ class GDScriptInstance : public ScriptInstance {
SelfList<GDScriptFunctionState>::List pending_func_states;
+ void _call_implicit_ready_recursively(GDScript *p_script);
+
public:
virtual Object *get_owner() { return owner; }
@@ -633,7 +638,7 @@ public:
virtual void get_recognized_extensions(List<String> *p_extensions) const override;
virtual bool handles_type(const String &p_type) const override;
virtual String get_resource_type(const String &p_path) const override;
- virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false) override;
+ virtual void get_dependencies(const String &p_path, List<String> *r_dependencies, bool p_add_types = false) override;
};
class ResourceFormatSaverGDScript : public ResourceFormatSaver {
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 0636ac5083..279be65f03 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -562,6 +562,11 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c
class_type.native_type = result.native_type;
p_class->set_datatype(class_type);
+ // Add base class to the list of dependencies.
+ if (result.kind == GDScriptParser::DataType::CLASS) {
+ parser->add_dependency(result.script_path);
+ }
+
// Apply annotations.
for (GDScriptParser::AnnotationNode *&E : p_class->annotations) {
resolve_annotation(E);
@@ -722,13 +727,32 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
}
} else if (ProjectSettings::get_singleton()->has_autoload(first) && ProjectSettings::get_singleton()->get_autoload(first).is_singleton) {
const ProjectSettings::AutoloadInfo &autoload = ProjectSettings::get_singleton()->get_autoload(first);
- Ref<GDScriptParserRef> ref = parser->get_depended_parser_for(autoload.path);
+ String script_path;
+ if (ResourceLoader::get_resource_type(autoload.path) == "PackedScene") {
+ // Try to get script from scene if possible.
+ if (GDScriptLanguage::get_singleton()->has_any_global_constant(autoload.name)) {
+ Variant constant = GDScriptLanguage::get_singleton()->get_any_global_constant(autoload.name);
+ Node *node = Object::cast_to<Node>(constant);
+ if (node != nullptr) {
+ Ref<GDScript> scr = node->get_script();
+ if (scr.is_valid()) {
+ script_path = scr->get_script_path();
+ }
+ }
+ }
+ } else if (ResourceLoader::get_resource_type(autoload.path) == "GDScript") {
+ script_path = autoload.path;
+ }
+ if (script_path.is_empty()) {
+ return bad_type;
+ }
+ Ref<GDScriptParserRef> ref = parser->get_depended_parser_for(script_path);
if (ref.is_null()) {
- push_error(vformat(R"(The referenced autoload "%s" (from "%s") could not be loaded.)", first, autoload.path), p_type);
+ push_error(vformat(R"(The referenced autoload "%s" (from "%s") could not be loaded.)", first, script_path), p_type);
return bad_type;
}
if (ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) {
- push_error(vformat(R"(Could not parse singleton "%s" from "%s".)", first, autoload.path), p_type);
+ push_error(vformat(R"(Could not parse singleton "%s" from "%s".)", first, script_path), p_type);
return bad_type;
}
result = ref->get_parser()->head->get_datatype();
@@ -849,6 +873,11 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
}
p_type->set_datatype(result);
+
+ if (result.kind == GDScriptParser::DataType::CLASS || result.kind == GDScriptParser::DataType::SCRIPT) {
+ parser->add_dependency(result.script_path);
+ }
+
return result;
}
@@ -3001,6 +3030,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
case Variant::PACKED_VECTOR2_ARRAY:
case Variant::PACKED_VECTOR3_ARRAY:
case Variant::PACKED_COLOR_ARRAY:
+ case Variant::PACKED_VECTOR4_ARRAY:
safe_to_fold = false;
break;
default:
@@ -3355,7 +3385,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
}
if (parent_function) {
- push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parent_function->identifier->name), p_call);
+ push_error(vformat(R"*(Cannot call non-static function "%s()" from the static function "%s()".)*", p_call->function_name, parent_function->identifier->name), p_call);
} else {
push_error(vformat(R"*(Cannot call non-static function "%s()" from a static variable initializer.)*", p_call->function_name), p_call);
}
@@ -3769,9 +3799,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
} break;
case GDScriptParser::ClassNode::Member::FUNCTION: {
- if (is_base && (!base.is_meta_type || member.function->is_static)) {
+ if (is_base && (!base.is_meta_type || member.function->is_static || is_constructor)) {
p_identifier->set_datatype(make_callable_type(member.function->info));
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_FUNCTION;
+ p_identifier->function_source = member.function;
+ p_identifier->function_source_is_static = member.function->is_static;
return;
}
} break;
@@ -3820,6 +3852,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
if (method_info.name == p_identifier->name) {
p_identifier->set_datatype(make_callable_type(method_info));
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_FUNCTION;
+ p_identifier->function_source_is_static = method_info.flags & METHOD_FLAG_STATIC;
return;
}
@@ -4000,25 +4033,37 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
}
if (found_source) {
- bool source_is_variable = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
- bool source_is_signal = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_SIGNAL;
- if ((source_is_variable || source_is_signal) && static_context) {
+ const bool source_is_instance_variable = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
+ const bool source_is_instance_function = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_FUNCTION && !p_identifier->function_source_is_static;
+ const bool source_is_signal = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_SIGNAL;
+
+ if (static_context && (source_is_instance_variable || source_is_instance_function || source_is_signal)) {
// Get the parent function above any lambda.
GDScriptParser::FunctionNode *parent_function = parser->current_function;
while (parent_function && parent_function->source_lambda) {
parent_function = parent_function->source_lambda->parent_function;
}
+ String source_type;
+ if (source_is_instance_variable) {
+ source_type = "non-static variable";
+ } else if (source_is_instance_function) {
+ source_type = "non-static function";
+ } else { // source_is_signal
+ source_type = "signal";
+ }
+
if (parent_function) {
- push_error(vformat(R"*(Cannot access %s "%s" from the static function "%s()".)*", source_is_signal ? "signal" : "instance variable", p_identifier->name, parent_function->identifier->name), p_identifier);
+ push_error(vformat(R"*(Cannot access %s "%s" from the static function "%s()".)*", source_type, p_identifier->name, parent_function->identifier->name), p_identifier);
} else {
- push_error(vformat(R"*(Cannot access %s "%s" from a static variable initializer.)*", source_is_signal ? "signal" : "instance variable", p_identifier->name), p_identifier);
+ push_error(vformat(R"*(Cannot access %s "%s" from a static variable initializer.)*", source_type, p_identifier->name), p_identifier);
}
}
if (current_lambda != nullptr) {
- // If the identifier is a member variable (including the native class properties) or a signal, we consider the lambda to be using `self`, so we keep a reference to the current instance.
- if (source_is_variable || source_is_signal) {
+ // If the identifier is a member variable (including the native class properties), member function, or a signal,
+ // we consider the lambda to be using `self`, so we keep a reference to the current instance.
+ if (source_is_instance_variable || source_is_instance_function || source_is_signal) {
mark_lambda_use_self();
return; // No need to capture.
}
@@ -4063,6 +4108,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
if (ScriptServer::is_global_class(name)) {
p_identifier->set_datatype(make_global_class_meta_type(name, p_identifier));
+ parser->add_dependency(p_identifier->get_datatype().script_path);
return;
}
@@ -4105,6 +4151,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
}
result.is_constant = true;
p_identifier->set_datatype(result);
+ parser->add_dependency(autoload.path);
return;
}
}
@@ -4224,7 +4271,6 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) {
push_error("Preloaded path must be a constant string.", p_preload->path);
} else {
p_preload->resolved_path = p_preload->path->reduced_value;
- // TODO: Save this as script dependency.
if (p_preload->resolved_path.is_relative_path()) {
p_preload->resolved_path = parser->script_path.get_base_dir().path_join(p_preload->resolved_path);
}
@@ -4255,6 +4301,8 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) {
push_error(vformat(R"(Could not preload resource file "%s".)", p_preload->resolved_path), p_preload->path);
}
}
+
+ parser->add_dependency(p_preload->resolved_path);
}
}
@@ -4378,7 +4426,6 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
switch (base_type.builtin_type) {
// Expect int or real as index.
case Variant::PACKED_BYTE_ARRAY:
- case Variant::PACKED_COLOR_ARRAY:
case Variant::PACKED_FLOAT32_ARRAY:
case Variant::PACKED_FLOAT64_ARRAY:
case Variant::PACKED_INT32_ARRAY:
@@ -4386,6 +4433,8 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
case Variant::PACKED_STRING_ARRAY:
case Variant::PACKED_VECTOR2_ARRAY:
case Variant::PACKED_VECTOR3_ARRAY:
+ case Variant::PACKED_COLOR_ARRAY:
+ case Variant::PACKED_VECTOR4_ARRAY:
case Variant::ARRAY:
case Variant::STRING:
error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::FLOAT;
@@ -4484,10 +4533,6 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
case Variant::QUATERNION:
result_type.builtin_type = Variant::FLOAT;
break;
- // Return Color.
- case Variant::PACKED_COLOR_ARRAY:
- result_type.builtin_type = Variant::COLOR;
- break;
// Return String.
case Variant::PACKED_STRING_ARRAY:
case Variant::STRING:
@@ -4509,6 +4554,14 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
case Variant::BASIS:
result_type.builtin_type = Variant::VECTOR3;
break;
+ // Return Color.
+ case Variant::PACKED_COLOR_ARRAY:
+ result_type.builtin_type = Variant::COLOR;
+ break;
+ // Return Vector4.
+ case Variant::PACKED_VECTOR4_ARRAY:
+ result_type.builtin_type = Variant::VECTOR4;
+ break;
// Depends on the index.
case Variant::TRANSFORM3D:
case Variant::PROJECTION:
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index bfe090edb0..4cda3d3037 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -109,6 +109,7 @@ uint32_t GDScriptByteCodeGenerator::add_temporary(const GDScriptDataType &p_type
case Variant::PACKED_VECTOR2_ARRAY:
case Variant::PACKED_VECTOR3_ARRAY:
case Variant::PACKED_COLOR_ARRAY:
+ case Variant::PACKED_VECTOR4_ARRAY:
case Variant::VARIANT_MAX:
// Arrays, dictionaries, and objects are reference counted, so we don't use the pool for them.
temp_type = Variant::NIL;
@@ -543,6 +544,9 @@ void GDScriptByteCodeGenerator::write_type_adjust(const Address &p_target, Varia
case Variant::PACKED_COLOR_ARRAY:
append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_COLOR_ARRAY);
break;
+ case Variant::PACKED_VECTOR4_ARRAY:
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_VECTOR4_ARRAY);
+ break;
case Variant::NIL:
case Variant::VARIANT_MAX:
return;
@@ -1196,23 +1200,49 @@ void GDScriptByteCodeGenerator::write_call_builtin_type_static(const Address &p_
}
void GDScriptByteCodeGenerator::write_call_native_static(const Address &p_target, const StringName &p_class, const StringName &p_method, const Vector<Address> &p_arguments) {
- bool is_validated = false;
-
MethodBind *method = ClassDB::get_method(p_class, p_method);
- if (!is_validated) {
- // Perform regular call.
- append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_NATIVE_STATIC, p_arguments.size() + 1);
- for (int i = 0; i < p_arguments.size(); i++) {
- append(p_arguments[i]);
+ // Perform regular call.
+ append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_NATIVE_STATIC, p_arguments.size() + 1);
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ CallTarget ct = get_call_target(p_target);
+ append(ct.target);
+ append(method);
+ append(p_arguments.size());
+ ct.cleanup();
+ return;
+}
+
+void GDScriptByteCodeGenerator::write_call_native_static_validated(const GDScriptCodeGenerator::Address &p_target, MethodBind *p_method, const Vector<GDScriptCodeGenerator::Address> &p_arguments) {
+ Variant::Type return_type = Variant::NIL;
+ bool has_return = p_method->has_return();
+
+ if (has_return) {
+ PropertyInfo return_info = p_method->get_return_info();
+ return_type = return_info.type;
+ }
+
+ CallTarget ct = get_call_target(p_target, return_type);
+
+ if (has_return) {
+ Variant::Type temp_type = temporaries[ct.target.address].type;
+ if (temp_type != return_type) {
+ write_type_adjust(ct.target, return_type);
}
- CallTarget ct = get_call_target(p_target);
- append(ct.target);
- append(method);
- append(p_arguments.size());
- ct.cleanup();
- return;
}
+
+ GDScriptFunction::Opcode code = p_method->has_return() ? GDScriptFunction::OPCODE_CALL_NATIVE_STATIC_VALIDATED_RETURN : GDScriptFunction::OPCODE_CALL_NATIVE_STATIC_VALIDATED_NO_RETURN;
+ append_opcode_and_argcount(code, 1 + p_arguments.size());
+
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(ct.target);
+ append(p_arguments.size());
+ append(p_method);
+ ct.cleanup();
}
void GDScriptByteCodeGenerator::write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) {
@@ -1547,6 +1577,10 @@ void GDScriptByteCodeGenerator::write_for(const Address &p_variable, bool p_use_
begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY;
iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_COLOR_ARRAY;
break;
+ case Variant::PACKED_VECTOR4_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_VECTOR4_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_VECTOR4_ARRAY;
+ break;
default:
break;
}
diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h
index 5a736b2554..34f56a2f5c 100644
--- a/modules/gdscript/gdscript_byte_codegen.h
+++ b/modules/gdscript/gdscript_byte_codegen.h
@@ -518,6 +518,7 @@ public:
virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) override;
virtual void write_call_builtin_type_static(const Address &p_target, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) override;
virtual void write_call_native_static(const Address &p_target, const StringName &p_class, const StringName &p_method, const Vector<Address> &p_arguments) override;
+ virtual void write_call_native_static_validated(const Address &p_target, MethodBind *p_method, const Vector<Address> &p_arguments) override;
virtual void write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) override;
virtual void write_call_method_bind_validated(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) override;
virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h
index 4c33ed499a..c1c0b61395 100644
--- a/modules/gdscript/gdscript_codegen.h
+++ b/modules/gdscript/gdscript_codegen.h
@@ -131,6 +131,7 @@ public:
virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) = 0;
virtual void write_call_builtin_type_static(const Address &p_target, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) = 0;
virtual void write_call_native_static(const Address &p_target, const StringName &p_class, const StringName &p_method, const Vector<Address> &p_arguments) = 0;
+ virtual void write_call_native_static_validated(const Address &p_target, MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
virtual void write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
virtual void write_call_method_bind_validated(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 734e37bc09..a8a7f3d9f7 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -673,7 +673,15 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
} else if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && call->function_name != SNAME("new") &&
ClassDB::class_exists(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name) && !Engine::get_singleton()->has_singleton(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name)) {
// It's a static native method call.
- gen->write_call_native_static(result, static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name, subscript->attribute->name, arguments);
+ StringName class_name = static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name;
+ MethodBind *method = ClassDB::get_method(class_name, subscript->attribute->name);
+ if (_can_use_validate_call(method, arguments)) {
+ // Exact arguments, use validated call.
+ gen->write_call_native_static_validated(result, method, arguments);
+ } else {
+ // Not exact arguments, use regular static call
+ gen->write_call_native_static(result, class_name, subscript->attribute->name, arguments);
+ }
} else {
GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base);
if (r_error) {
diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp
index c7873dcd52..0331045078 100644
--- a/modules/gdscript/gdscript_disassembler.cpp
+++ b/modules/gdscript/gdscript_disassembler.cpp
@@ -678,6 +678,50 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
incr += 4 + argc;
} break;
+ case OPCODE_CALL_NATIVE_STATIC_VALIDATED_RETURN: {
+ int instr_var_args = _code_ptr[++ip];
+ text += "call native static method validated (return) ";
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2 + instr_var_args]];
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+ text += DADDR(1 + argc) + " = ";
+ text += method->get_instance_class();
+ text += ".";
+ text += method->get_name();
+ text += "(";
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(1 + i);
+ }
+ text += ")";
+ incr = 4 + argc;
+ } break;
+
+ case OPCODE_CALL_NATIVE_STATIC_VALIDATED_NO_RETURN: {
+ int instr_var_args = _code_ptr[++ip];
+
+ text += "call native static method validated (no return) ";
+
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2 + instr_var_args]];
+
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+
+ text += method->get_instance_class();
+ text += ".";
+ text += method->get_name();
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0) {
+ text += ", ";
+ }
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr = 4 + argc;
+ } break;
+
case OPCODE_CALL_METHOD_BIND_VALIDATED_RETURN: {
int instr_var_args = _code_ptr[++ip];
text += "call method-bind validated (return) ";
@@ -1002,6 +1046,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
m_macro(PACKED_VECTOR2_ARRAY); \
m_macro(PACKED_VECTOR3_ARRAY); \
m_macro(PACKED_COLOR_ARRAY); \
+ m_macro(PACKED_VECTOR4_ARRAY); \
m_macro(OBJECT)
case OPCODE_ITERATE_BEGIN: {
@@ -1106,6 +1151,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
DISASSEMBLE_TYPE_ADJUST(PACKED_VECTOR2_ARRAY);
DISASSEMBLE_TYPE_ADJUST(PACKED_VECTOR3_ARRAY);
DISASSEMBLE_TYPE_ADJUST(PACKED_COLOR_ARRAY);
+ DISASSEMBLE_TYPE_ADJUST(PACKED_VECTOR4_ARRAY);
case OPCODE_ASSERT: {
text += "assert (";
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index 184d256bcd..759e92d68c 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -264,6 +264,8 @@ public:
OPCODE_CALL_METHOD_BIND_RET,
OPCODE_CALL_BUILTIN_STATIC,
OPCODE_CALL_NATIVE_STATIC,
+ OPCODE_CALL_NATIVE_STATIC_VALIDATED_RETURN,
+ OPCODE_CALL_NATIVE_STATIC_VALIDATED_NO_RETURN,
OPCODE_CALL_METHOD_BIND_VALIDATED_RETURN,
OPCODE_CALL_METHOD_BIND_VALIDATED_NO_RETURN,
OPCODE_AWAIT,
@@ -299,6 +301,7 @@ public:
OPCODE_ITERATE_BEGIN_PACKED_VECTOR2_ARRAY,
OPCODE_ITERATE_BEGIN_PACKED_VECTOR3_ARRAY,
OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_VECTOR4_ARRAY,
OPCODE_ITERATE_BEGIN_OBJECT,
OPCODE_ITERATE,
OPCODE_ITERATE_INT,
@@ -319,6 +322,7 @@ public:
OPCODE_ITERATE_PACKED_VECTOR2_ARRAY,
OPCODE_ITERATE_PACKED_VECTOR3_ARRAY,
OPCODE_ITERATE_PACKED_COLOR_ARRAY,
+ OPCODE_ITERATE_PACKED_VECTOR4_ARRAY,
OPCODE_ITERATE_OBJECT,
OPCODE_STORE_GLOBAL,
OPCODE_STORE_NAMED_GLOBAL,
@@ -359,6 +363,7 @@ public:
OPCODE_TYPE_ADJUST_PACKED_VECTOR2_ARRAY,
OPCODE_TYPE_ADJUST_PACKED_VECTOR3_ARRAY,
OPCODE_TYPE_ADJUST_PACKED_COLOR_ARRAY,
+ OPCODE_TYPE_ADJUST_PACKED_VECTOR4_ARRAY,
OPCODE_ASSERT,
OPCODE_BREAKPOINT,
OPCODE_LINE,
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 9799c6e610..634d4fc867 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -4129,6 +4129,9 @@ static String _get_annotation_error_string(const StringName &p_annotation_name,
case Variant::COLOR:
types.push_back("PackedColorArray");
break;
+ case Variant::VECTOR4:
+ types.push_back("PackedVector4Array");
+ break;
default:
break;
}
@@ -4827,6 +4830,8 @@ static Variant::Type _variant_type_to_typed_array_element_type(Variant::Type p_t
return Variant::VECTOR3;
case Variant::PACKED_COLOR_ARRAY:
return Variant::COLOR;
+ case Variant::PACKED_VECTOR4_ARRAY:
+ return Variant::VECTOR4;
default:
return Variant::NIL;
}
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 77cb8136f8..1e67e2d496 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -902,8 +902,11 @@ public:
VariableNode *variable_source;
ConstantNode *constant_source;
SignalNode *signal_source;
+ FunctionNode *function_source;
};
- FunctionNode *source_function = nullptr;
+ bool function_source_is_static = false; // For non-GDScript scripts.
+
+ FunctionNode *source_function = nullptr; // TODO: Rename to disambiguate `function_source`.
int usages = 0; // Useful for binds/iterator variable.
@@ -1429,6 +1432,8 @@ private:
void reset_extents(Node *p_node, GDScriptTokenizer::Token p_token);
void reset_extents(Node *p_node, Node *p_from);
+ HashSet<String> dependencies;
+
template <typename T>
T *alloc_node() {
T *node = memnew(T);
@@ -1572,9 +1577,11 @@ public:
bool annotation_exists(const String &p_annotation_name) const;
const List<ParserError> &get_errors() const { return errors; }
- const List<String> get_dependencies() const {
- // TODO: Keep track of deps.
- return List<String>();
+ const HashSet<String> &get_dependencies() const {
+ return dependencies;
+ }
+ void add_dependency(const String &p_dependency) {
+ dependencies.insert(p_dependency);
}
#ifdef DEBUG_ENABLED
const List<GDScriptWarning> &get_warnings() const { return warnings; }
diff --git a/modules/gdscript/gdscript_utility_functions.cpp b/modules/gdscript/gdscript_utility_functions.cpp
index e5b0f55df8..f0816650b9 100644
--- a/modules/gdscript/gdscript_utility_functions.cpp
+++ b/modules/gdscript/gdscript_utility_functions.cpp
@@ -519,6 +519,10 @@ struct GDScriptUtilityFunctionsDefinitions {
Vector<Color> d = *p_args[0];
*r_ret = d.size();
} break;
+ case Variant::PACKED_VECTOR4_ARRAY: {
+ Vector<Vector4> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
default: {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index 842975698b..11bd1d224a 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -208,159 +208,165 @@ void (*type_init_function_table[])(Variant *) = {
&VariantInitializer<PackedVector2Array>::init, // PACKED_VECTOR2_ARRAY.
&VariantInitializer<PackedVector3Array>::init, // PACKED_VECTOR3_ARRAY.
&VariantInitializer<PackedColorArray>::init, // PACKED_COLOR_ARRAY.
+ &VariantInitializer<PackedVector4Array>::init, // PACKED_VECTOR4_ARRAY.
};
#if defined(__GNUC__)
-#define OPCODES_TABLE \
- static const void *switch_table_ops[] = { \
- &&OPCODE_OPERATOR, \
- &&OPCODE_OPERATOR_VALIDATED, \
- &&OPCODE_TYPE_TEST_BUILTIN, \
- &&OPCODE_TYPE_TEST_ARRAY, \
- &&OPCODE_TYPE_TEST_NATIVE, \
- &&OPCODE_TYPE_TEST_SCRIPT, \
- &&OPCODE_SET_KEYED, \
- &&OPCODE_SET_KEYED_VALIDATED, \
- &&OPCODE_SET_INDEXED_VALIDATED, \
- &&OPCODE_GET_KEYED, \
- &&OPCODE_GET_KEYED_VALIDATED, \
- &&OPCODE_GET_INDEXED_VALIDATED, \
- &&OPCODE_SET_NAMED, \
- &&OPCODE_SET_NAMED_VALIDATED, \
- &&OPCODE_GET_NAMED, \
- &&OPCODE_GET_NAMED_VALIDATED, \
- &&OPCODE_SET_MEMBER, \
- &&OPCODE_GET_MEMBER, \
- &&OPCODE_SET_STATIC_VARIABLE, \
- &&OPCODE_GET_STATIC_VARIABLE, \
- &&OPCODE_ASSIGN, \
- &&OPCODE_ASSIGN_NULL, \
- &&OPCODE_ASSIGN_TRUE, \
- &&OPCODE_ASSIGN_FALSE, \
- &&OPCODE_ASSIGN_TYPED_BUILTIN, \
- &&OPCODE_ASSIGN_TYPED_ARRAY, \
- &&OPCODE_ASSIGN_TYPED_NATIVE, \
- &&OPCODE_ASSIGN_TYPED_SCRIPT, \
- &&OPCODE_CAST_TO_BUILTIN, \
- &&OPCODE_CAST_TO_NATIVE, \
- &&OPCODE_CAST_TO_SCRIPT, \
- &&OPCODE_CONSTRUCT, \
- &&OPCODE_CONSTRUCT_VALIDATED, \
- &&OPCODE_CONSTRUCT_ARRAY, \
- &&OPCODE_CONSTRUCT_TYPED_ARRAY, \
- &&OPCODE_CONSTRUCT_DICTIONARY, \
- &&OPCODE_CALL, \
- &&OPCODE_CALL_RETURN, \
- &&OPCODE_CALL_ASYNC, \
- &&OPCODE_CALL_UTILITY, \
- &&OPCODE_CALL_UTILITY_VALIDATED, \
- &&OPCODE_CALL_GDSCRIPT_UTILITY, \
- &&OPCODE_CALL_BUILTIN_TYPE_VALIDATED, \
- &&OPCODE_CALL_SELF_BASE, \
- &&OPCODE_CALL_METHOD_BIND, \
- &&OPCODE_CALL_METHOD_BIND_RET, \
- &&OPCODE_CALL_BUILTIN_STATIC, \
- &&OPCODE_CALL_NATIVE_STATIC, \
- &&OPCODE_CALL_METHOD_BIND_VALIDATED_RETURN, \
- &&OPCODE_CALL_METHOD_BIND_VALIDATED_NO_RETURN, \
- &&OPCODE_AWAIT, \
- &&OPCODE_AWAIT_RESUME, \
- &&OPCODE_CREATE_LAMBDA, \
- &&OPCODE_CREATE_SELF_LAMBDA, \
- &&OPCODE_JUMP, \
- &&OPCODE_JUMP_IF, \
- &&OPCODE_JUMP_IF_NOT, \
- &&OPCODE_JUMP_TO_DEF_ARGUMENT, \
- &&OPCODE_JUMP_IF_SHARED, \
- &&OPCODE_RETURN, \
- &&OPCODE_RETURN_TYPED_BUILTIN, \
- &&OPCODE_RETURN_TYPED_ARRAY, \
- &&OPCODE_RETURN_TYPED_NATIVE, \
- &&OPCODE_RETURN_TYPED_SCRIPT, \
- &&OPCODE_ITERATE_BEGIN, \
- &&OPCODE_ITERATE_BEGIN_INT, \
- &&OPCODE_ITERATE_BEGIN_FLOAT, \
- &&OPCODE_ITERATE_BEGIN_VECTOR2, \
- &&OPCODE_ITERATE_BEGIN_VECTOR2I, \
- &&OPCODE_ITERATE_BEGIN_VECTOR3, \
- &&OPCODE_ITERATE_BEGIN_VECTOR3I, \
- &&OPCODE_ITERATE_BEGIN_STRING, \
- &&OPCODE_ITERATE_BEGIN_DICTIONARY, \
- &&OPCODE_ITERATE_BEGIN_ARRAY, \
- &&OPCODE_ITERATE_BEGIN_PACKED_BYTE_ARRAY, \
- &&OPCODE_ITERATE_BEGIN_PACKED_INT32_ARRAY, \
- &&OPCODE_ITERATE_BEGIN_PACKED_INT64_ARRAY, \
- &&OPCODE_ITERATE_BEGIN_PACKED_FLOAT32_ARRAY, \
- &&OPCODE_ITERATE_BEGIN_PACKED_FLOAT64_ARRAY, \
- &&OPCODE_ITERATE_BEGIN_PACKED_STRING_ARRAY, \
- &&OPCODE_ITERATE_BEGIN_PACKED_VECTOR2_ARRAY, \
- &&OPCODE_ITERATE_BEGIN_PACKED_VECTOR3_ARRAY, \
- &&OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY, \
- &&OPCODE_ITERATE_BEGIN_OBJECT, \
- &&OPCODE_ITERATE, \
- &&OPCODE_ITERATE_INT, \
- &&OPCODE_ITERATE_FLOAT, \
- &&OPCODE_ITERATE_VECTOR2, \
- &&OPCODE_ITERATE_VECTOR2I, \
- &&OPCODE_ITERATE_VECTOR3, \
- &&OPCODE_ITERATE_VECTOR3I, \
- &&OPCODE_ITERATE_STRING, \
- &&OPCODE_ITERATE_DICTIONARY, \
- &&OPCODE_ITERATE_ARRAY, \
- &&OPCODE_ITERATE_PACKED_BYTE_ARRAY, \
- &&OPCODE_ITERATE_PACKED_INT32_ARRAY, \
- &&OPCODE_ITERATE_PACKED_INT64_ARRAY, \
- &&OPCODE_ITERATE_PACKED_FLOAT32_ARRAY, \
- &&OPCODE_ITERATE_PACKED_FLOAT64_ARRAY, \
- &&OPCODE_ITERATE_PACKED_STRING_ARRAY, \
- &&OPCODE_ITERATE_PACKED_VECTOR2_ARRAY, \
- &&OPCODE_ITERATE_PACKED_VECTOR3_ARRAY, \
- &&OPCODE_ITERATE_PACKED_COLOR_ARRAY, \
- &&OPCODE_ITERATE_OBJECT, \
- &&OPCODE_STORE_GLOBAL, \
- &&OPCODE_STORE_NAMED_GLOBAL, \
- &&OPCODE_TYPE_ADJUST_BOOL, \
- &&OPCODE_TYPE_ADJUST_INT, \
- &&OPCODE_TYPE_ADJUST_FLOAT, \
- &&OPCODE_TYPE_ADJUST_STRING, \
- &&OPCODE_TYPE_ADJUST_VECTOR2, \
- &&OPCODE_TYPE_ADJUST_VECTOR2I, \
- &&OPCODE_TYPE_ADJUST_RECT2, \
- &&OPCODE_TYPE_ADJUST_RECT2I, \
- &&OPCODE_TYPE_ADJUST_VECTOR3, \
- &&OPCODE_TYPE_ADJUST_VECTOR3I, \
- &&OPCODE_TYPE_ADJUST_TRANSFORM2D, \
- &&OPCODE_TYPE_ADJUST_VECTOR4, \
- &&OPCODE_TYPE_ADJUST_VECTOR4I, \
- &&OPCODE_TYPE_ADJUST_PLANE, \
- &&OPCODE_TYPE_ADJUST_QUATERNION, \
- &&OPCODE_TYPE_ADJUST_AABB, \
- &&OPCODE_TYPE_ADJUST_BASIS, \
- &&OPCODE_TYPE_ADJUST_TRANSFORM3D, \
- &&OPCODE_TYPE_ADJUST_PROJECTION, \
- &&OPCODE_TYPE_ADJUST_COLOR, \
- &&OPCODE_TYPE_ADJUST_STRING_NAME, \
- &&OPCODE_TYPE_ADJUST_NODE_PATH, \
- &&OPCODE_TYPE_ADJUST_RID, \
- &&OPCODE_TYPE_ADJUST_OBJECT, \
- &&OPCODE_TYPE_ADJUST_CALLABLE, \
- &&OPCODE_TYPE_ADJUST_SIGNAL, \
- &&OPCODE_TYPE_ADJUST_DICTIONARY, \
- &&OPCODE_TYPE_ADJUST_ARRAY, \
- &&OPCODE_TYPE_ADJUST_PACKED_BYTE_ARRAY, \
- &&OPCODE_TYPE_ADJUST_PACKED_INT32_ARRAY, \
- &&OPCODE_TYPE_ADJUST_PACKED_INT64_ARRAY, \
- &&OPCODE_TYPE_ADJUST_PACKED_FLOAT32_ARRAY, \
- &&OPCODE_TYPE_ADJUST_PACKED_FLOAT64_ARRAY, \
- &&OPCODE_TYPE_ADJUST_PACKED_STRING_ARRAY, \
- &&OPCODE_TYPE_ADJUST_PACKED_VECTOR2_ARRAY, \
- &&OPCODE_TYPE_ADJUST_PACKED_VECTOR3_ARRAY, \
- &&OPCODE_TYPE_ADJUST_PACKED_COLOR_ARRAY, \
- &&OPCODE_ASSERT, \
- &&OPCODE_BREAKPOINT, \
- &&OPCODE_LINE, \
- &&OPCODE_END \
- }; \
+#define OPCODES_TABLE \
+ static const void *switch_table_ops[] = { \
+ &&OPCODE_OPERATOR, \
+ &&OPCODE_OPERATOR_VALIDATED, \
+ &&OPCODE_TYPE_TEST_BUILTIN, \
+ &&OPCODE_TYPE_TEST_ARRAY, \
+ &&OPCODE_TYPE_TEST_NATIVE, \
+ &&OPCODE_TYPE_TEST_SCRIPT, \
+ &&OPCODE_SET_KEYED, \
+ &&OPCODE_SET_KEYED_VALIDATED, \
+ &&OPCODE_SET_INDEXED_VALIDATED, \
+ &&OPCODE_GET_KEYED, \
+ &&OPCODE_GET_KEYED_VALIDATED, \
+ &&OPCODE_GET_INDEXED_VALIDATED, \
+ &&OPCODE_SET_NAMED, \
+ &&OPCODE_SET_NAMED_VALIDATED, \
+ &&OPCODE_GET_NAMED, \
+ &&OPCODE_GET_NAMED_VALIDATED, \
+ &&OPCODE_SET_MEMBER, \
+ &&OPCODE_GET_MEMBER, \
+ &&OPCODE_SET_STATIC_VARIABLE, \
+ &&OPCODE_GET_STATIC_VARIABLE, \
+ &&OPCODE_ASSIGN, \
+ &&OPCODE_ASSIGN_NULL, \
+ &&OPCODE_ASSIGN_TRUE, \
+ &&OPCODE_ASSIGN_FALSE, \
+ &&OPCODE_ASSIGN_TYPED_BUILTIN, \
+ &&OPCODE_ASSIGN_TYPED_ARRAY, \
+ &&OPCODE_ASSIGN_TYPED_NATIVE, \
+ &&OPCODE_ASSIGN_TYPED_SCRIPT, \
+ &&OPCODE_CAST_TO_BUILTIN, \
+ &&OPCODE_CAST_TO_NATIVE, \
+ &&OPCODE_CAST_TO_SCRIPT, \
+ &&OPCODE_CONSTRUCT, \
+ &&OPCODE_CONSTRUCT_VALIDATED, \
+ &&OPCODE_CONSTRUCT_ARRAY, \
+ &&OPCODE_CONSTRUCT_TYPED_ARRAY, \
+ &&OPCODE_CONSTRUCT_DICTIONARY, \
+ &&OPCODE_CALL, \
+ &&OPCODE_CALL_RETURN, \
+ &&OPCODE_CALL_ASYNC, \
+ &&OPCODE_CALL_UTILITY, \
+ &&OPCODE_CALL_UTILITY_VALIDATED, \
+ &&OPCODE_CALL_GDSCRIPT_UTILITY, \
+ &&OPCODE_CALL_BUILTIN_TYPE_VALIDATED, \
+ &&OPCODE_CALL_SELF_BASE, \
+ &&OPCODE_CALL_METHOD_BIND, \
+ &&OPCODE_CALL_METHOD_BIND_RET, \
+ &&OPCODE_CALL_BUILTIN_STATIC, \
+ &&OPCODE_CALL_NATIVE_STATIC, \
+ &&OPCODE_CALL_NATIVE_STATIC_VALIDATED_RETURN, \
+ &&OPCODE_CALL_NATIVE_STATIC_VALIDATED_NO_RETURN, \
+ &&OPCODE_CALL_METHOD_BIND_VALIDATED_RETURN, \
+ &&OPCODE_CALL_METHOD_BIND_VALIDATED_NO_RETURN, \
+ &&OPCODE_AWAIT, \
+ &&OPCODE_AWAIT_RESUME, \
+ &&OPCODE_CREATE_LAMBDA, \
+ &&OPCODE_CREATE_SELF_LAMBDA, \
+ &&OPCODE_JUMP, \
+ &&OPCODE_JUMP_IF, \
+ &&OPCODE_JUMP_IF_NOT, \
+ &&OPCODE_JUMP_TO_DEF_ARGUMENT, \
+ &&OPCODE_JUMP_IF_SHARED, \
+ &&OPCODE_RETURN, \
+ &&OPCODE_RETURN_TYPED_BUILTIN, \
+ &&OPCODE_RETURN_TYPED_ARRAY, \
+ &&OPCODE_RETURN_TYPED_NATIVE, \
+ &&OPCODE_RETURN_TYPED_SCRIPT, \
+ &&OPCODE_ITERATE_BEGIN, \
+ &&OPCODE_ITERATE_BEGIN_INT, \
+ &&OPCODE_ITERATE_BEGIN_FLOAT, \
+ &&OPCODE_ITERATE_BEGIN_VECTOR2, \
+ &&OPCODE_ITERATE_BEGIN_VECTOR2I, \
+ &&OPCODE_ITERATE_BEGIN_VECTOR3, \
+ &&OPCODE_ITERATE_BEGIN_VECTOR3I, \
+ &&OPCODE_ITERATE_BEGIN_STRING, \
+ &&OPCODE_ITERATE_BEGIN_DICTIONARY, \
+ &&OPCODE_ITERATE_BEGIN_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_BYTE_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_INT32_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_INT64_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_FLOAT32_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_FLOAT64_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_STRING_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_VECTOR2_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_VECTOR3_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_VECTOR4_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_OBJECT, \
+ &&OPCODE_ITERATE, \
+ &&OPCODE_ITERATE_INT, \
+ &&OPCODE_ITERATE_FLOAT, \
+ &&OPCODE_ITERATE_VECTOR2, \
+ &&OPCODE_ITERATE_VECTOR2I, \
+ &&OPCODE_ITERATE_VECTOR3, \
+ &&OPCODE_ITERATE_VECTOR3I, \
+ &&OPCODE_ITERATE_STRING, \
+ &&OPCODE_ITERATE_DICTIONARY, \
+ &&OPCODE_ITERATE_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_BYTE_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_INT32_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_INT64_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_FLOAT32_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_FLOAT64_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_STRING_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_VECTOR2_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_VECTOR3_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_COLOR_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_VECTOR4_ARRAY, \
+ &&OPCODE_ITERATE_OBJECT, \
+ &&OPCODE_STORE_GLOBAL, \
+ &&OPCODE_STORE_NAMED_GLOBAL, \
+ &&OPCODE_TYPE_ADJUST_BOOL, \
+ &&OPCODE_TYPE_ADJUST_INT, \
+ &&OPCODE_TYPE_ADJUST_FLOAT, \
+ &&OPCODE_TYPE_ADJUST_STRING, \
+ &&OPCODE_TYPE_ADJUST_VECTOR2, \
+ &&OPCODE_TYPE_ADJUST_VECTOR2I, \
+ &&OPCODE_TYPE_ADJUST_RECT2, \
+ &&OPCODE_TYPE_ADJUST_RECT2I, \
+ &&OPCODE_TYPE_ADJUST_VECTOR3, \
+ &&OPCODE_TYPE_ADJUST_VECTOR3I, \
+ &&OPCODE_TYPE_ADJUST_TRANSFORM2D, \
+ &&OPCODE_TYPE_ADJUST_VECTOR4, \
+ &&OPCODE_TYPE_ADJUST_VECTOR4I, \
+ &&OPCODE_TYPE_ADJUST_PLANE, \
+ &&OPCODE_TYPE_ADJUST_QUATERNION, \
+ &&OPCODE_TYPE_ADJUST_AABB, \
+ &&OPCODE_TYPE_ADJUST_BASIS, \
+ &&OPCODE_TYPE_ADJUST_TRANSFORM3D, \
+ &&OPCODE_TYPE_ADJUST_PROJECTION, \
+ &&OPCODE_TYPE_ADJUST_COLOR, \
+ &&OPCODE_TYPE_ADJUST_STRING_NAME, \
+ &&OPCODE_TYPE_ADJUST_NODE_PATH, \
+ &&OPCODE_TYPE_ADJUST_RID, \
+ &&OPCODE_TYPE_ADJUST_OBJECT, \
+ &&OPCODE_TYPE_ADJUST_CALLABLE, \
+ &&OPCODE_TYPE_ADJUST_SIGNAL, \
+ &&OPCODE_TYPE_ADJUST_DICTIONARY, \
+ &&OPCODE_TYPE_ADJUST_ARRAY, \
+ &&OPCODE_TYPE_ADJUST_PACKED_BYTE_ARRAY, \
+ &&OPCODE_TYPE_ADJUST_PACKED_INT32_ARRAY, \
+ &&OPCODE_TYPE_ADJUST_PACKED_INT64_ARRAY, \
+ &&OPCODE_TYPE_ADJUST_PACKED_FLOAT32_ARRAY, \
+ &&OPCODE_TYPE_ADJUST_PACKED_FLOAT64_ARRAY, \
+ &&OPCODE_TYPE_ADJUST_PACKED_STRING_ARRAY, \
+ &&OPCODE_TYPE_ADJUST_PACKED_VECTOR2_ARRAY, \
+ &&OPCODE_TYPE_ADJUST_PACKED_VECTOR3_ARRAY, \
+ &&OPCODE_TYPE_ADJUST_PACKED_COLOR_ARRAY, \
+ &&OPCODE_TYPE_ADJUST_PACKED_VECTOR4_ARRAY, \
+ &&OPCODE_ASSERT, \
+ &&OPCODE_BREAKPOINT, \
+ &&OPCODE_LINE, \
+ &&OPCODE_END \
+ }; \
static_assert((sizeof(switch_table_ops) / sizeof(switch_table_ops[0]) == (OPCODE_END + 1)), "Opcodes in jump table aren't the same as opcodes in enum.");
#define OPCODE(m_op) \
@@ -428,6 +434,7 @@ void (*type_init_function_table[])(Variant *) = {
#define OP_GET_PACKED_VECTOR2_ARRAY get_vector2_array
#define OP_GET_PACKED_VECTOR3_ARRAY get_vector3_array
#define OP_GET_PACKED_COLOR_ARRAY get_color_array
+#define OP_GET_PACKED_VECTOR4_ARRAY get_vector4_array
#define OP_GET_TRANSFORM3D get_transform
#define OP_GET_TRANSFORM2D get_transform2d
#define OP_GET_PROJECTION get_projection
@@ -882,23 +889,27 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#endif
#ifdef DEBUG_ENABLED
if (!valid) {
- Object *obj = dst->get_validated_object();
- String v = index->operator String();
- bool read_only_property = false;
- if (obj) {
- read_only_property = ClassDB::has_property(obj->get_class_name(), v) && (ClassDB::get_property_setter(obj->get_class_name(), v) == StringName());
- }
- if (read_only_property) {
- err_text = vformat(R"(Cannot set value into property "%s" (on base "%s") because it is read-only.)", v, _get_var_type(dst));
+ if (dst->is_read_only()) {
+ err_text = "Invalid assignment on read-only value (on base: '" + _get_var_type(dst) + "').";
} else {
- if (!v.is_empty()) {
- v = "'" + v + "'";
- } else {
- v = "of type '" + _get_var_type(index) + "'";
+ Object *obj = dst->get_validated_object();
+ String v = index->operator String();
+ bool read_only_property = false;
+ if (obj) {
+ read_only_property = ClassDB::has_property(obj->get_class_name(), v) && (ClassDB::get_property_setter(obj->get_class_name(), v) == StringName());
}
- err_text = "Invalid assignment of property or key " + v + " with value of type '" + _get_var_type(value) + "' on a base object of type '" + _get_var_type(dst) + "'.";
- if (err_code == Variant::VariantSetError::SET_INDEXED_ERR) {
- err_text = "Invalid assignment of index " + v + " (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'.";
+ if (read_only_property) {
+ err_text = vformat(R"(Cannot set value into property "%s" (on base "%s") because it is read-only.)", v, _get_var_type(dst));
+ } else {
+ if (!v.is_empty()) {
+ v = "'" + v + "'";
+ } else {
+ v = "of type '" + _get_var_type(index) + "'";
+ }
+ err_text = "Invalid assignment of property or key " + v + " with value of type '" + _get_var_type(value) + "' on a base object of type '" + _get_var_type(dst) + "'.";
+ if (err_code == Variant::VariantSetError::SET_INDEXED_ERR) {
+ err_text = "Invalid assignment of index " + v + " (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'.";
+ }
}
}
OPCODE_BREAK;
@@ -924,13 +935,17 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#ifdef DEBUG_ENABLED
if (!valid) {
- String v = index->operator String();
- if (!v.is_empty()) {
- v = "'" + v + "'";
+ if (dst->is_read_only()) {
+ err_text = "Invalid assignment on read-only value (on base: '" + _get_var_type(dst) + "').";
} else {
- v = "of type '" + _get_var_type(index) + "'";
+ String v = index->operator String();
+ if (!v.is_empty()) {
+ v = "'" + v + "'";
+ } else {
+ v = "of type '" + _get_var_type(index) + "'";
+ }
+ err_text = "Invalid assignment of property or key " + v + " with value of type '" + _get_var_type(value) + "' on a base object of type '" + _get_var_type(dst) + "'.";
}
- err_text = "Invalid assignment of property or key " + v + " with value of type '" + _get_var_type(value) + "' on a base object of type '" + _get_var_type(dst) + "'.";
OPCODE_BREAK;
}
#endif
@@ -956,13 +971,17 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#ifdef DEBUG_ENABLED
if (oob) {
- String v = index->operator String();
- if (!v.is_empty()) {
- v = "'" + v + "'";
+ if (dst->is_read_only()) {
+ err_text = "Invalid assignment on read-only value (on base: '" + _get_var_type(dst) + "').";
} else {
- v = "of type '" + _get_var_type(index) + "'";
+ String v = index->operator String();
+ if (!v.is_empty()) {
+ v = "'" + v + "'";
+ } else {
+ v = "of type '" + _get_var_type(index) + "'";
+ }
+ err_text = "Out of bounds set index " + v + " (on base: '" + _get_var_type(dst) + "')";
}
- err_text = "Out of bounds set index " + v + " (on base: '" + _get_var_type(dst) + "')";
OPCODE_BREAK;
}
#endif
@@ -1090,15 +1109,19 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#ifdef DEBUG_ENABLED
if (!valid) {
- Object *obj = dst->get_validated_object();
- bool read_only_property = false;
- if (obj) {
- read_only_property = ClassDB::has_property(obj->get_class_name(), *index) && (ClassDB::get_property_setter(obj->get_class_name(), *index) == StringName());
- }
- if (read_only_property) {
- err_text = vformat(R"(Cannot set value into property "%s" (on base "%s") because it is read-only.)", String(*index), _get_var_type(dst));
+ if (dst->is_read_only()) {
+ err_text = "Invalid assignment on read-only value (on base: '" + _get_var_type(dst) + "').";
} else {
- err_text = "Invalid assignment of property or key '" + String(*index) + "' with value of type '" + _get_var_type(value) + "' on a base object of type '" + _get_var_type(dst) + "'.";
+ Object *obj = dst->get_validated_object();
+ bool read_only_property = false;
+ if (obj) {
+ read_only_property = ClassDB::has_property(obj->get_class_name(), *index) && (ClassDB::get_property_setter(obj->get_class_name(), *index) == StringName());
+ }
+ if (read_only_property) {
+ err_text = vformat(R"(Cannot set value into property "%s" (on base "%s") because it is read-only.)", String(*index), _get_var_type(dst));
+ } else {
+ err_text = "Invalid assignment of property or key '" + String(*index) + "' with value of type '" + _get_var_type(value) + "' on a base object of type '" + _get_var_type(dst) + "'.";
+ }
}
OPCODE_BREAK;
}
@@ -1956,6 +1979,78 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
DISPATCH_OPCODE;
+ OPCODE(OPCODE_CALL_NATIVE_STATIC_VALIDATED_RETURN) {
+ LOAD_INSTRUCTION_ARGS
+ CHECK_SPACE(3 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+
+ GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _methods_count);
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2]];
+
+ Variant **argptrs = instruction_args;
+
+#ifdef DEBUG_ENABLED
+ uint64_t call_time = 0;
+ if (GDScriptLanguage::get_singleton()->profiling && GDScriptLanguage::get_singleton()->profile_native_calls) {
+ call_time = OS::get_singleton()->get_ticks_usec();
+ }
+#endif
+
+ GET_INSTRUCTION_ARG(ret, argc);
+ method->validated_call(nullptr, (const Variant **)argptrs, ret);
+
+#ifdef DEBUG_ENABLED
+ if (GDScriptLanguage::get_singleton()->profiling && GDScriptLanguage::get_singleton()->profile_native_calls) {
+ uint64_t t_taken = OS::get_singleton()->get_ticks_usec() - call_time;
+ _profile_native_call(t_taken, method->get_name(), method->get_instance_class());
+ function_call_time += t_taken;
+ }
+#endif
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CALL_NATIVE_STATIC_VALIDATED_NO_RETURN) {
+ LOAD_INSTRUCTION_ARGS
+ CHECK_SPACE(3 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+
+ GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _methods_count);
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2]];
+
+ Variant **argptrs = instruction_args;
+#ifdef DEBUG_ENABLED
+ uint64_t call_time = 0;
+ if (GDScriptLanguage::get_singleton()->profiling && GDScriptLanguage::get_singleton()->profile_native_calls) {
+ call_time = OS::get_singleton()->get_ticks_usec();
+ }
+#endif
+
+ GET_INSTRUCTION_ARG(ret, argc);
+ VariantInternal::initialize(ret, Variant::NIL);
+ method->validated_call(nullptr, (const Variant **)argptrs, nullptr);
+
+#ifdef DEBUG_ENABLED
+ if (GDScriptLanguage::get_singleton()->profiling && GDScriptLanguage::get_singleton()->profile_native_calls) {
+ uint64_t t_taken = OS::get_singleton()->get_ticks_usec() - call_time;
+ _profile_native_call(t_taken, method->get_name(), method->get_instance_class());
+ function_call_time += t_taken;
+ }
+#endif
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
OPCODE(OPCODE_CALL_METHOD_BIND_VALIDATED_RETURN) {
LOAD_INSTRUCTION_ARGS
CHECK_SPACE(3 + instr_arg_count);
@@ -2969,6 +3064,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE_ITERATE_BEGIN_PACKED_ARRAY(VECTOR2, Vector2, get_vector2_array, VECTOR2, Vector2, get_vector2);
OPCODE_ITERATE_BEGIN_PACKED_ARRAY(VECTOR3, Vector3, get_vector3_array, VECTOR3, Vector3, get_vector3);
OPCODE_ITERATE_BEGIN_PACKED_ARRAY(COLOR, Color, get_color_array, COLOR, Color, get_color);
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(VECTOR4, Vector4, get_vector4_array, VECTOR4, Vector4, get_vector4);
OPCODE(OPCODE_ITERATE_BEGIN_OBJECT) {
CHECK_SPACE(4);
@@ -3304,6 +3400,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE_ITERATE_PACKED_ARRAY(VECTOR2, Vector2, get_vector2_array, get_vector2);
OPCODE_ITERATE_PACKED_ARRAY(VECTOR3, Vector3, get_vector3_array, get_vector3);
OPCODE_ITERATE_PACKED_ARRAY(COLOR, Color, get_color_array, get_color);
+ OPCODE_ITERATE_PACKED_ARRAY(VECTOR4, Vector4, get_vector4_array, get_vector4);
OPCODE(OPCODE_ITERATE_OBJECT) {
CHECK_SPACE(4);
@@ -3435,6 +3532,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE_TYPE_ADJUST(PACKED_VECTOR2_ARRAY, PackedVector2Array);
OPCODE_TYPE_ADJUST(PACKED_VECTOR3_ARRAY, PackedVector3Array);
OPCODE_TYPE_ADJUST(PACKED_COLOR_ARRAY, PackedColorArray);
+ OPCODE_TYPE_ADJUST(PACKED_VECTOR4_ARRAY, PackedVector4Array);
OPCODE(OPCODE_ASSERT) {
CHECK_SPACE(3);
diff --git a/modules/gdscript/language_server/gdscript_language_server.h b/modules/gdscript/language_server/gdscript_language_server.h
index 2ace5ca446..4ae5ab6cbf 100644
--- a/modules/gdscript/language_server/gdscript_language_server.h
+++ b/modules/gdscript/language_server/gdscript_language_server.h
@@ -34,7 +34,7 @@
#include "../gdscript_parser.h"
#include "gdscript_language_protocol.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class GDScriptLanguageServer : public EditorPlugin {
GDCLASS(GDScriptLanguageServer, EditorPlugin);
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index 853a8e0f19..a63d32ef30 100644
--- a/modules/gdscript/language_server/gdscript_workspace.cpp
+++ b/modules/gdscript/language_server/gdscript_workspace.cpp
@@ -233,18 +233,25 @@ void GDScriptWorkspace::reload_all_workspace_scripts() {
void GDScriptWorkspace::list_script_files(const String &p_root_dir, List<String> &r_files) {
Error err;
Ref<DirAccess> dir = DirAccess::open(p_root_dir, &err);
- if (OK == err) {
- dir->list_dir_begin();
- String file_name = dir->get_next();
- while (file_name.length()) {
- if (dir->current_is_dir() && file_name != "." && file_name != ".." && file_name != "./") {
- list_script_files(p_root_dir.path_join(file_name), r_files);
- } else if (file_name.ends_with(".gd")) {
- String script_file = p_root_dir.path_join(file_name);
- r_files.push_back(script_file);
- }
- file_name = dir->get_next();
+ if (OK != err) {
+ return;
+ }
+
+ // Ignore scripts in directories with a .gdignore file.
+ if (dir->file_exists(".gdignore")) {
+ return;
+ }
+
+ dir->list_dir_begin();
+ String file_name = dir->get_next();
+ while (file_name.length()) {
+ if (dir->current_is_dir() && file_name != "." && file_name != ".." && file_name != "./") {
+ list_script_files(p_root_dir.path_join(file_name), r_files);
+ } else if (file_name.ends_with(".gd")) {
+ String script_file = p_root_dir.path_join(file_name);
+ r_files.push_back(script_file);
}
+ file_name = dir->get_next();
}
}
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static.gd b/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static.gd
new file mode 100644
index 0000000000..e041aeb914
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static.gd
@@ -0,0 +1,10 @@
+# GH-91403
+
+static func static_func():
+ print(non_static_func)
+
+func non_static_func():
+ pass
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static.out b/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static.out
new file mode 100644
index 0000000000..d8d6c8bc1b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot access non-static function "non_static_func" from the static function "static_func()".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static_in_lambda_param.gd b/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static_in_lambda_param.gd
new file mode 100644
index 0000000000..36bc9dbf15
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static_in_lambda_param.gd
@@ -0,0 +1,15 @@
+# GH-91403
+
+func non_static_func():
+ pass
+
+static func static_func(
+ f := func ():
+ var g := func ():
+ print(non_static_func)
+ g.call()
+):
+ f.call()
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static_in_lambda_param.out b/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static_in_lambda_param.out
new file mode 100644
index 0000000000..d8d6c8bc1b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static_in_lambda_param.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot access non-static function "non_static_func" from the static function "static_func()".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static.out b/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static.out
index b78f131345..c094c08cd8 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Cannot call non-static function "non_static_func()" from static function "static_func()".
+Cannot call non-static function "non_static_func()" from the static function "static_func()".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda.out b/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda.out
index b78f131345..c094c08cd8 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Cannot call non-static function "non_static_func()" from static function "static_func()".
+Cannot call non-static function "non_static_func()" from the static function "static_func()".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda_param.out b/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda_param.out
index b78f131345..c094c08cd8 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda_param.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda_param.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Cannot call non-static function "non_static_func()" from static function "static_func()".
+Cannot call non-static function "non_static_func()" from the static function "static_func()".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda.gd b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda.gd
new file mode 100644
index 0000000000..7ae5bea7d7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda.gd
@@ -0,0 +1,14 @@
+# GH-91403
+
+func non_static_func():
+ pass
+
+static var static_var = func ():
+ var f := func ():
+ var g := func ():
+ print(non_static_func)
+ g.call()
+ f.call()
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda.out b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda.out
new file mode 100644
index 0000000000..153e81b405
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot access non-static function "non_static_func" from a static variable initializer.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda_setter.gd b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda_setter.gd
new file mode 100644
index 0000000000..7479afc532
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda_setter.gd
@@ -0,0 +1,15 @@
+# GH-91403
+
+func non_static_func():
+ pass
+
+static var static_var:
+ set(_value):
+ var f := func ():
+ var g := func ():
+ print(non_static_func)
+ g.call()
+ f.call()
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda_setter.out b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda_setter.out
new file mode 100644
index 0000000000..de43f2d3c4
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda_setter.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot access non-static function "non_static_func" from the static function "@static_var_setter()".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_call_non_static_in_lambda_setter.out b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_call_non_static_in_lambda_setter.out
index cdf3ab2aeb..a285b80025 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_call_non_static_in_lambda_setter.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_call_non_static_in_lambda_setter.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Cannot call non-static function "non_static_func()" from static function "@static_var_setter()".
+Cannot call non-static function "non_static_func()" from the static function "@static_var_setter()".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_access.gd b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_access.gd
new file mode 100644
index 0000000000..a21b2c4470
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_access.gd
@@ -0,0 +1,11 @@
+# GH-91403
+
+@static_unload
+
+func non_static():
+ return "non static"
+
+static var static_var = Callable(non_static)
+
+func test():
+ print("does not run")
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_access.out b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_access.out
new file mode 100644
index 0000000000..a95069dc4f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_access.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot access non-static function "non_static" from a static variable initializer.
diff --git a/modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.gd b/modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.gd
index 73d0f9096c..18675e5725 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.gd
@@ -345,3 +345,12 @@ func test():
prints(x and true)
prints(x or false)
prints(x or true)
+
+ # TYPE_PACKED_VECTOR4_ARRAY
+ x = PackedVector4Array([Vector4.ONE])
+ prints("TYPE_PACKED_VECTOR4_ARRAY")
+ prints(not x)
+ prints(x and false)
+ prints(x and true)
+ prints(x or false)
+ prints(x or true)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.out b/modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.out
index e2945c910a..47f9d7548b 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.out
+++ b/modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.out
@@ -227,3 +227,9 @@ false
true
true
true
+TYPE_PACKED_VECTOR4_ARRAY
+false
+false
+true
+true
+true
diff --git a/modules/gdscript/tests/scripts/analyzer/features/static_non_static_access.gd b/modules/gdscript/tests/scripts/analyzer/features/static_non_static_access.gd
new file mode 100644
index 0000000000..80ceb6d1a9
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/static_non_static_access.gd
@@ -0,0 +1,75 @@
+@static_unload
+
+static var static_var
+var non_static_var
+
+signal my_signal()
+
+static func static_func():
+ pass
+
+func non_static_func():
+ pass
+
+static var test_static_var_lambda = func ():
+ static_func()
+ print(static_func)
+ static_var = 1
+ print(static_var)
+
+var test_non_static_var_lambda = func ():
+ static_func()
+ print(static_func)
+ static_var = 1
+ print(static_var)
+
+ non_static_func()
+ print(non_static_func)
+ non_static_var = 1
+ print(non_static_var)
+ my_signal.emit()
+ print(my_signal)
+
+static var test_static_var_setter:
+ set(_value):
+ static_func()
+ print(static_func)
+ static_var = 1
+ print(static_var)
+
+var test_non_static_var_setter:
+ set(_value):
+ static_func()
+ print(static_func)
+ static_var = 1
+ print(static_var)
+
+ non_static_func()
+ print(non_static_func)
+ non_static_var = 1
+ print(non_static_var)
+ my_signal.emit()
+ print(my_signal)
+
+static func test_static_func():
+ static_func()
+ print(static_func)
+ static_var = 1
+ print(static_var)
+
+func test_non_static_func():
+ static_func()
+ print(static_func)
+ static_var = 1
+ print(static_var)
+
+ non_static_func()
+ print(non_static_func)
+ non_static_var = 1
+ print(non_static_var)
+ my_signal.emit()
+ print(my_signal)
+
+func test():
+ test_static_var_lambda = null
+ test_non_static_var_lambda = null
diff --git a/modules/gdscript/tests/scripts/analyzer/features/static_non_static_access.out b/modules/gdscript/tests/scripts/analyzer/features/static_non_static_access.out
new file mode 100644
index 0000000000..d73c5eb7cd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/static_non_static_access.out
@@ -0,0 +1 @@
+GDTEST_OK
diff --git a/modules/gdscript/tests/scripts/parser/features/export_arrays.gd b/modules/gdscript/tests/scripts/parser/features/export_arrays.gd
index ddfb186aa4..0d97135a7b 100644
--- a/modules/gdscript/tests/scripts/parser/features/export_arrays.gd
+++ b/modules/gdscript/tests/scripts/parser/features/export_arrays.gd
@@ -63,6 +63,7 @@ var temp_packed_float64_array: PackedFloat64Array
var temp_packed_color_array: PackedColorArray
var temp_packed_vector2_array: PackedVector2Array
var temp_packed_vector3_array: PackedVector3Array
+var temp_packed_vector4_array: PackedVector4Array
@export var test_weak_packed_byte_array = temp_packed_byte_array
@export var test_weak_packed_int32_array = temp_packed_int32_array
@@ -72,6 +73,7 @@ var temp_packed_vector3_array: PackedVector3Array
@export var test_weak_packed_color_array = temp_packed_color_array
@export var test_weak_packed_vector2_array = temp_packed_vector2_array
@export var test_weak_packed_vector3_array = temp_packed_vector3_array
+@export var test_weak_packed_vector4_array = temp_packed_vector4_array
@export_range(1, 10) var test_range_weak_packed_byte_array = temp_packed_byte_array
@export_range(1, 10) var test_range_weak_packed_int32_array = temp_packed_int32_array
diff --git a/modules/gdscript/tests/scripts/parser/features/export_arrays.out b/modules/gdscript/tests/scripts/parser/features/export_arrays.out
index 00e75fcc43..acbf389645 100644
--- a/modules/gdscript/tests/scripts/parser/features/export_arrays.out
+++ b/modules/gdscript/tests/scripts/parser/features/export_arrays.out
@@ -123,6 +123,8 @@ var test_weak_packed_vector2_array: PackedVector2Array
hint=TYPE_STRING hint_string="Vector2:Vector2" usage=DEFAULT|SCRIPT_VARIABLE
var test_weak_packed_vector3_array: PackedVector3Array
hint=TYPE_STRING hint_string="Vector3:Vector3" usage=DEFAULT|SCRIPT_VARIABLE
+var test_weak_packed_vector4_array: PackedVector4Array
+ hint=TYPE_STRING hint_string="Vector4:Vector4" usage=DEFAULT|SCRIPT_VARIABLE
var test_range_weak_packed_byte_array: PackedByteArray
hint=TYPE_STRING hint_string="int/RANGE:1,10" usage=DEFAULT|SCRIPT_VARIABLE
var test_range_weak_packed_int32_array: PackedInt32Array
diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.out b/modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.out
index c524a1ae6b..350d5d1d45 100644
--- a/modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.out
+++ b/modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.out
@@ -3,4 +3,4 @@ GDTEST_RUNTIME_ERROR
>> on function: test()
>> runtime/errors/constant_array_is_deep.gd
>> 6
->> Invalid assignment of property or key '0' with value of type 'int' on a base object of type 'Dictionary'.
+>> Invalid assignment on read-only value (on base: 'Dictionary').
diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.out b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.out
index cf51b0262d..5f1f372b0a 100644
--- a/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.out
+++ b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.out
@@ -3,4 +3,4 @@ GDTEST_RUNTIME_ERROR
>> on function: test()
>> runtime/errors/constant_dictionary_is_deep.gd
>> 6
->> Invalid assignment of index '0' (on base: 'Array') with value of type 'int'.
+>> Invalid assignment on read-only value (on base: 'Array').
diff --git a/modules/gdscript/tests/scripts/runtime/errors/read_only_dictionary.gd b/modules/gdscript/tests/scripts/runtime/errors/read_only_dictionary.gd
new file mode 100644
index 0000000000..2f31ecc52f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/read_only_dictionary.gd
@@ -0,0 +1,4 @@
+func test():
+ var dictionary := { "a": 0 }
+ dictionary.make_read_only()
+ dictionary.a = 1
diff --git a/modules/gdscript/tests/scripts/runtime/errors/read_only_dictionary.out b/modules/gdscript/tests/scripts/runtime/errors/read_only_dictionary.out
new file mode 100644
index 0000000000..f7d531e119
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/read_only_dictionary.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> runtime/errors/read_only_dictionary.gd
+>> 4
+>> Invalid assignment on read-only value (on base: 'Dictionary').
diff --git a/modules/gdscript/tests/scripts/runtime/features/call_native_static_method_validated.gd b/modules/gdscript/tests/scripts/runtime/features/call_native_static_method_validated.gd
new file mode 100644
index 0000000000..35e4dbd6a0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/call_native_static_method_validated.gd
@@ -0,0 +1,7 @@
+func test():
+ # Validated native static call with return value.
+ print(FileAccess.file_exists("some_file"))
+
+ # Validated native static call without return value.
+ Node.print_orphan_nodes()
+
diff --git a/modules/gdscript/tests/scripts/runtime/features/call_native_static_method_validated.out b/modules/gdscript/tests/scripts/runtime/features/call_native_static_method_validated.out
new file mode 100644
index 0000000000..44302c8137
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/call_native_static_method_validated.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+false
diff --git a/modules/gdscript/tests/scripts/runtime/features/compare_builtin_equals_null.gd b/modules/gdscript/tests/scripts/runtime/features/compare_builtin_equals_null.gd
index 809d0d28a9..5d8dafc4a1 100644
--- a/modules/gdscript/tests/scripts/runtime/features/compare_builtin_equals_null.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/compare_builtin_equals_null.gd
@@ -140,3 +140,7 @@ func test():
# PackedColorArray
value = PackedColorArray()
print(value == null)
+
+ # PackedVector4Array
+ value = PackedVector4Array()
+ print(value == null)
diff --git a/modules/gdscript/tests/scripts/runtime/features/compare_builtin_equals_null.out b/modules/gdscript/tests/scripts/runtime/features/compare_builtin_equals_null.out
index 27423ab8e7..e0e222eccc 100644
--- a/modules/gdscript/tests/scripts/runtime/features/compare_builtin_equals_null.out
+++ b/modules/gdscript/tests/scripts/runtime/features/compare_builtin_equals_null.out
@@ -34,3 +34,4 @@ false
false
false
false
+false
diff --git a/modules/gdscript/tests/scripts/runtime/features/compare_builtin_not_equals_null.gd b/modules/gdscript/tests/scripts/runtime/features/compare_builtin_not_equals_null.gd
index f46afb0f18..88286ede03 100644
--- a/modules/gdscript/tests/scripts/runtime/features/compare_builtin_not_equals_null.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/compare_builtin_not_equals_null.gd
@@ -140,3 +140,7 @@ func test():
# PackedColorArray
value = PackedColorArray()
print(value != null)
+
+ # PackedVector4Array
+ value = PackedVector4Array()
+ print(value != null)
diff --git a/modules/gdscript/tests/scripts/runtime/features/compare_builtin_not_equals_null.out b/modules/gdscript/tests/scripts/runtime/features/compare_builtin_not_equals_null.out
index a11c47854a..f6e72aedd5 100644
--- a/modules/gdscript/tests/scripts/runtime/features/compare_builtin_not_equals_null.out
+++ b/modules/gdscript/tests/scripts/runtime/features/compare_builtin_not_equals_null.out
@@ -34,3 +34,4 @@ true
true
true
true
+true
diff --git a/modules/gdscript/tests/scripts/runtime/features/compare_null_equals_builtin.gd b/modules/gdscript/tests/scripts/runtime/features/compare_null_equals_builtin.gd
index 7649062fda..6ca1b3e490 100644
--- a/modules/gdscript/tests/scripts/runtime/features/compare_null_equals_builtin.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/compare_null_equals_builtin.gd
@@ -136,3 +136,7 @@ func test():
# PackedColorArray
value = PackedColorArray()
print(null == value)
+
+ # PackedVector4Array
+ value = PackedVector4Array()
+ print(null == value)
diff --git a/modules/gdscript/tests/scripts/runtime/features/compare_null_equals_builtin.out b/modules/gdscript/tests/scripts/runtime/features/compare_null_equals_builtin.out
index 639f6027b9..27423ab8e7 100644
--- a/modules/gdscript/tests/scripts/runtime/features/compare_null_equals_builtin.out
+++ b/modules/gdscript/tests/scripts/runtime/features/compare_null_equals_builtin.out
@@ -33,3 +33,4 @@ false
false
false
false
+false
diff --git a/modules/gdscript/tests/scripts/runtime/features/compare_null_not_equals_builtin.gd b/modules/gdscript/tests/scripts/runtime/features/compare_null_not_equals_builtin.gd
index 8d5f9df1b8..d7addfa390 100644
--- a/modules/gdscript/tests/scripts/runtime/features/compare_null_not_equals_builtin.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/compare_null_not_equals_builtin.gd
@@ -136,3 +136,7 @@ func test():
# PackedColorArray
value = PackedColorArray()
print(null != value)
+
+ # PackedVector4Array
+ value = PackedVector4Array()
+ print(null != value)
diff --git a/modules/gdscript/tests/scripts/runtime/features/compare_null_not_equals_builtin.out b/modules/gdscript/tests/scripts/runtime/features/compare_null_not_equals_builtin.out
index d1e332afba..a11c47854a 100644
--- a/modules/gdscript/tests/scripts/runtime/features/compare_null_not_equals_builtin.out
+++ b/modules/gdscript/tests/scripts/runtime/features/compare_null_not_equals_builtin.out
@@ -33,3 +33,4 @@ true
true
true
true
+true
diff --git a/modules/gdscript/tests/scripts/runtime/features/onready_base_before_subclass.gd b/modules/gdscript/tests/scripts/runtime/features/onready_base_before_subclass.gd
new file mode 100644
index 0000000000..99156adb28
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/onready_base_before_subclass.gd
@@ -0,0 +1,18 @@
+#GH-63329
+class A extends Node:
+ @onready var a := get_value("a")
+
+ func get_value(var_name: String) -> String:
+ print(var_name)
+ return var_name
+
+class B extends A:
+ @onready var b := get_value("b")
+
+ func _ready():
+ pass
+
+func test():
+ var node := B.new()
+ node._ready()
+ node.free()
diff --git a/modules/gdscript/tests/scripts/runtime/features/onready_base_before_subclass.out b/modules/gdscript/tests/scripts/runtime/features/onready_base_before_subclass.out
new file mode 100644
index 0000000000..b417ce67ca
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/onready_base_before_subclass.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+a
+b
diff --git a/modules/gdscript/tests/scripts/runtime/features/stringify.gd b/modules/gdscript/tests/scripts/runtime/features/stringify.gd
index 0dbb252b0e..8579baf876 100644
--- a/modules/gdscript/tests/scripts/runtime/features/stringify.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/stringify.gd
@@ -40,3 +40,4 @@ func test():
print(PackedVector2Array([Vector2.ONE, Vector2.ZERO]))
print(PackedVector3Array([Vector3.ONE, Vector3.ZERO]))
print(PackedColorArray([Color.RED, Color.BLUE, Color.GREEN]))
+ print(PackedVector4Array([Vector4.ONE, Vector4.ZERO]))
diff --git a/modules/gdscript/tests/scripts/runtime/features/stringify.out b/modules/gdscript/tests/scripts/runtime/features/stringify.out
index 1f33de00cc..7833b6e213 100644
--- a/modules/gdscript/tests/scripts/runtime/features/stringify.out
+++ b/modules/gdscript/tests/scripts/runtime/features/stringify.out
@@ -32,3 +32,4 @@ Node::[signal]property_list_changed
[(1, 1), (0, 0)]
[(1, 1, 1), (0, 0, 0)]
[(1, 0, 0, 1), (0, 0, 1, 1), (0, 1, 0, 1)]
+[(1, 1, 1, 1), (0, 0, 0, 0)]
diff --git a/modules/gltf/editor/editor_scene_exporter_gltf_plugin.h b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.h
index 683ff6d4f6..6bc6160571 100644
--- a/modules/gltf/editor/editor_scene_exporter_gltf_plugin.h
+++ b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.h
@@ -36,7 +36,7 @@
#include "../gltf_document.h"
#include "editor_scene_exporter_gltf_settings.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class EditorFileDialog;
class EditorInspector;
diff --git a/modules/gridmap/editor/grid_map_editor_plugin.h b/modules/gridmap/editor/grid_map_editor_plugin.h
index 924e21aef5..cfa0f0c35c 100644
--- a/modules/gridmap/editor/grid_map_editor_plugin.h
+++ b/modules/gridmap/editor/grid_map_editor_plugin.h
@@ -35,7 +35,7 @@
#include "../grid_map.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/box_container.h"
#include "scene/gui/item_list.h"
#include "scene/gui/slider.h"
diff --git a/modules/interactive_music/editor/audio_stream_interactive_editor_plugin.h b/modules/interactive_music/editor/audio_stream_interactive_editor_plugin.h
index 730d1ca83b..3c50b0d5cc 100644
--- a/modules/interactive_music/editor/audio_stream_interactive_editor_plugin.h
+++ b/modules/interactive_music/editor/audio_stream_interactive_editor_plugin.h
@@ -32,7 +32,7 @@
#define AUDIO_STREAM_INTERACTIVE_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/dialogs.h"
class CheckBox;
diff --git a/modules/mobile_vr/doc_classes/MobileVRInterface.xml b/modules/mobile_vr/doc_classes/MobileVRInterface.xml
index 1be8cc828d..0dbe06d220 100644
--- a/modules/mobile_vr/doc_classes/MobileVRInterface.xml
+++ b/modules/mobile_vr/doc_classes/MobileVRInterface.xml
@@ -34,9 +34,20 @@
<member name="k2" type="float" setter="set_k2" getter="get_k2" default="0.215">
The k2 lens factor, see k1.
</member>
+ <member name="offset_rect" type="Rect2" setter="set_offset_rect" getter="get_offset_rect" default="Rect2(0, 0, 1, 1)">
+ Set the offset rect relative to the area being rendered. A length of 1 represents the whole rendering area on that axis.
+ </member>
<member name="oversample" type="float" setter="set_oversample" getter="get_oversample" default="1.5">
The oversample setting. Because of the lens distortion we have to render our buffers at a higher resolution then the screen can natively handle. A value between 1.5 and 2.0 often provides good results but at the cost of performance.
</member>
+ <member name="vrs_min_radius" type="float" setter="set_vrs_min_radius" getter="get_vrs_min_radius" default="20.0">
+ The minimum radius around the focal point where full quality is guaranteed if VRS is used as a percentage of screen size.
+ [b]Note:[/b] Mobile and Forward+ renderers only. Requires [member Viewport.vrs_mode] to be set to [constant Viewport.VRS_XR].
+ </member>
+ <member name="vrs_strength" type="float" setter="set_vrs_strength" getter="get_vrs_strength" default="1.0">
+ The strength used to calculate the VRS density map. The greater this value, the more noticeable VRS is. This improves performance at the cost of quality.
+ [b]Note:[/b] Mobile and Forward+ renderers only. Requires [member Viewport.vrs_mode] to be set to [constant Viewport.VRS_XR].
+ </member>
<member name="xr_play_area_mode" type="int" setter="set_play_area_mode" getter="get_play_area_mode" overrides="XRInterface" enum="XRInterface.PlayAreaMode" default="1" />
</members>
</class>
diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp
index 94a3f0777e..f27281866a 100644
--- a/modules/mobile_vr/mobile_vr_interface.cpp
+++ b/modules/mobile_vr/mobile_vr_interface.cpp
@@ -126,7 +126,7 @@ void MobileVRInterface::set_position_from_sensors() {
// 9dof is a misleading marketing term coming from 3 accelerometer axis + 3 gyro axis + 3 magnetometer axis = 9 axis
// but in reality this only offers 3 dof (yaw, pitch, roll) orientation
- Basis orientation;
+ Basis orientation = head_transform.basis;
uint64_t ticks = OS::get_singleton()->get_ticks_usec();
uint64_t ticks_elapsed = ticks - last_ticks;
@@ -230,6 +230,9 @@ void MobileVRInterface::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_display_to_lens", "display_to_lens"), &MobileVRInterface::set_display_to_lens);
ClassDB::bind_method(D_METHOD("get_display_to_lens"), &MobileVRInterface::get_display_to_lens);
+ ClassDB::bind_method(D_METHOD("set_offset_rect", "offset_rect"), &MobileVRInterface::set_offset_rect);
+ ClassDB::bind_method(D_METHOD("get_offset_rect"), &MobileVRInterface::get_offset_rect);
+
ClassDB::bind_method(D_METHOD("set_oversample", "oversample"), &MobileVRInterface::set_oversample);
ClassDB::bind_method(D_METHOD("get_oversample"), &MobileVRInterface::get_oversample);
@@ -243,9 +246,20 @@ void MobileVRInterface::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "iod", PROPERTY_HINT_RANGE, "4.0,10.0,0.1"), "set_iod", "get_iod");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "display_width", PROPERTY_HINT_RANGE, "5.0,25.0,0.1"), "set_display_width", "get_display_width");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "display_to_lens", PROPERTY_HINT_RANGE, "5.0,25.0,0.1"), "set_display_to_lens", "get_display_to_lens");
+ ADD_PROPERTY(PropertyInfo(Variant::RECT2, "offset_rect"), "set_offset_rect", "get_offset_rect");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversample", PROPERTY_HINT_RANGE, "1.0,2.0,0.1"), "set_oversample", "get_oversample");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "k1", PROPERTY_HINT_RANGE, "0.1,10.0,0.0001"), "set_k1", "get_k1");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "k2", PROPERTY_HINT_RANGE, "0.1,10.0,0.0001"), "set_k2", "get_k2");
+
+ ClassDB::bind_method(D_METHOD("get_vrs_min_radius"), &MobileVRInterface::get_vrs_min_radius);
+ ClassDB::bind_method(D_METHOD("set_vrs_min_radius", "radius"), &MobileVRInterface::set_vrs_min_radius);
+
+ ClassDB::bind_method(D_METHOD("get_vrs_strength"), &MobileVRInterface::get_vrs_strength);
+ ClassDB::bind_method(D_METHOD("set_vrs_strength", "strength"), &MobileVRInterface::set_vrs_strength);
+
+ ADD_GROUP("Vulkan VRS", "vrs_");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "vrs_min_radius", PROPERTY_HINT_RANGE, "1.0,100.0,1.0"), "set_vrs_min_radius", "get_vrs_min_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "vrs_strength", PROPERTY_HINT_RANGE, "0.1,10.0,0.1"), "set_vrs_strength", "get_vrs_strength");
}
void MobileVRInterface::set_eye_height(const double p_eye_height) {
@@ -256,6 +270,14 @@ double MobileVRInterface::get_eye_height() const {
return eye_height;
}
+void MobileVRInterface::set_offset_rect(const Rect2 &p_offset_rect) {
+ offset_rect = p_offset_rect;
+}
+
+Rect2 MobileVRInterface::get_offset_rect() const {
+ return offset_rect;
+}
+
void MobileVRInterface::set_iod(const double p_iod) {
intraocular_dist = p_iod;
};
@@ -304,6 +326,22 @@ double MobileVRInterface::get_k2() const {
return k2;
};
+float MobileVRInterface::get_vrs_min_radius() const {
+ return xr_vrs.get_vrs_min_radius();
+}
+
+void MobileVRInterface::set_vrs_min_radius(float p_vrs_min_radius) {
+ xr_vrs.set_vrs_min_radius(p_vrs_min_radius);
+}
+
+float MobileVRInterface::get_vrs_strength() const {
+ return xr_vrs.get_vrs_strength();
+}
+
+void MobileVRInterface::set_vrs_strength(float p_vrs_strength) {
+ xr_vrs.set_vrs_strength(p_vrs_strength);
+}
+
uint32_t MobileVRInterface::get_view_count() {
// needs stereo...
return 2;
@@ -477,11 +515,18 @@ Vector<BlitToScreen> MobileVRInterface::post_draw_viewport(RID p_render_target,
Vector<BlitToScreen> blit_to_screen;
- // We must have a valid render target
+ // We must have a valid render target.
ERR_FAIL_COND_V(!p_render_target.is_valid(), blit_to_screen);
- // Because we are rendering to our device we must use our main viewport!
- ERR_FAIL_COND_V(p_screen_rect == Rect2(), blit_to_screen);
+ // We will only output to screen if this is our main viewport.
+ if (p_screen_rect == Rect2()) {
+ // Warn the developer once, it's up to the developer to output to screen.
+ WARN_PRINT_ONCE("SubViewport used with MobileVRInterface, no output to screen");
+
+ return blit_to_screen;
+ }
+
+ Rect2 modified_screen_rect = Rect2(p_screen_rect.position + offset_rect.position * p_screen_rect.size, p_screen_rect.size * offset_rect.size);
// and add our blits
BlitToScreen blit;
@@ -494,16 +539,16 @@ Vector<BlitToScreen> MobileVRInterface::post_draw_viewport(RID p_render_target,
blit.lens_distortion.aspect_ratio = aspect;
// left eye
- blit.dst_rect = p_screen_rect;
+ blit.dst_rect = modified_screen_rect;
blit.dst_rect.size.width *= 0.5;
blit.multi_view.layer = 0;
blit.lens_distortion.eye_center.x = ((-intraocular_dist / 2.0) + (display_width / 4.0)) / (display_width / 2.0);
blit_to_screen.push_back(blit);
// right eye
- blit.dst_rect = p_screen_rect;
+ blit.dst_rect = modified_screen_rect;
blit.dst_rect.size.width *= 0.5;
- blit.dst_rect.position.x = blit.dst_rect.size.width;
+ blit.dst_rect.position.x += blit.dst_rect.size.width;
blit.multi_view.layer = 1;
blit.lens_distortion.eye_center.x = ((intraocular_dist / 2.0) - (display_width / 4.0)) / (display_width / 2.0);
blit_to_screen.push_back(blit);
@@ -528,6 +573,23 @@ void MobileVRInterface::process() {
};
};
+RID MobileVRInterface::get_vrs_texture() {
+ PackedVector2Array eye_foci;
+
+ Size2 target_size = get_render_target_size();
+ real_t aspect_ratio = target_size.x / target_size.y;
+ uint32_t view_count = get_view_count();
+
+ for (uint32_t v = 0; v < view_count; v++) {
+ Projection cm = get_projection_for_view(v, aspect_ratio, 0.1, 1000.0);
+ Vector3 center = cm.xform(Vector3(0.0, 0.0, 999.0));
+
+ eye_foci.push_back(Vector2(center.x, center.y));
+ }
+
+ return xr_vrs.make_vrs_texture(target_size, eye_foci);
+}
+
MobileVRInterface::MobileVRInterface() {}
MobileVRInterface::~MobileVRInterface() {
diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h
index f680d8aa11..490b1c393c 100644
--- a/modules/mobile_vr/mobile_vr_interface.h
+++ b/modules/mobile_vr/mobile_vr_interface.h
@@ -33,6 +33,7 @@
#include "servers/xr/xr_interface.h"
#include "servers/xr/xr_positional_tracker.h"
+#include "servers/xr/xr_vrs.h"
/**
The mobile interface is a native VR interface that can be used on Android and iOS phones.
@@ -62,6 +63,8 @@ private:
double display_to_lens = 4.0;
double oversample = 1.5;
+ Rect2 offset_rect = Rect2(0, 0, 1, 1); // Full screen rect.
+
double k1 = 0.215;
double k2 = 0.215;
double aspect = 1.0;
@@ -70,6 +73,8 @@ private:
Ref<XRPositionalTracker> head;
Transform3D head_transform;
+ XRVRS xr_vrs;
+
/*
logic for processing our sensor data, this was originally in our positional tracker logic but I think
that doesn't make sense in hindsight. It only makes marginally more sense to park it here for now,
@@ -121,6 +126,9 @@ public:
void set_display_width(const double p_display_width);
double get_display_width() const;
+ void set_offset_rect(const Rect2 &p_offset_rect);
+ Rect2 get_offset_rect() const;
+
void set_display_to_lens(const double p_display_to_lens);
double get_display_to_lens() const;
@@ -133,6 +141,12 @@ public:
void set_k2(const double p_k2);
double get_k2() const;
+ float get_vrs_min_radius() const;
+ void set_vrs_min_radius(float p_vrs_min_radius);
+
+ float get_vrs_strength() const;
+ void set_vrs_strength(float p_vrs_strength);
+
virtual StringName get_name() const override;
virtual uint32_t get_capabilities() const override;
@@ -156,6 +170,8 @@ public:
virtual void process() override;
+ virtual RID get_vrs_texture() override;
+
MobileVRInterface();
~MobileVRInterface();
};
diff --git a/modules/mono/.editorconfig b/modules/mono/.editorconfig
index 1e5ae28396..fcd10461ad 100644
--- a/modules/mono/.editorconfig
+++ b/modules/mono/.editorconfig
@@ -1,3 +1,7 @@
+[*.{sln,csproj}]
+end_of_line = crlf
+charset = utf-8-bom
+
[*.sln]
indent_style = tab
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index e3f39c50f4..17df3988ee 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -41,7 +41,7 @@
#include "core/templates/self_list.h"
#ifdef TOOLS_ENABLED
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#endif
class CSharpScript;
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln
index 9674626183..2c78645493 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln
@@ -1,5 +1,8 @@
-
+
Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.9.34728.123
+MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.NET.Sdk", "Godot.NET.Sdk\Godot.NET.Sdk.csproj", "{31B00BFA-DEA1-42FA-A472-9E54A92A8A5F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.SourceGenerators", "Godot.SourceGenerators\Godot.SourceGenerators.csproj", "{32D31B23-2A45-4099-B4F5-95B4C8FF7D9F}"
@@ -37,4 +40,10 @@ Global
{AEBF0036-DA76-4341-B651-A3F2856AB2FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AEBF0036-DA76-4341-B651-A3F2856AB2FA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {49831022-16BD-41E0-A5F3-EDE1279F4176}
+ EndGlobalSection
EndGlobal
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj
index b396a5b0c7..74623a60ba 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.Build.NoTargets/2.0.1">
+<Project Sdk="Microsoft.Build.NoTargets/2.0.1">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj
index d0907c1cd4..fc887313d5 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/Godot.SourceGenerators.Tests.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/Godot.SourceGenerators.Tests.csproj
index e5a81c0e1c..31a255dcdf 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/Godot.SourceGenerators.Tests.csproj
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/Godot.SourceGenerators.Tests.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPropertiesGeneratorTests.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPropertiesGeneratorTests.cs
index 3cc5841097..724fb164e0 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPropertiesGeneratorTests.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPropertiesGeneratorTests.cs
@@ -57,4 +57,13 @@ public class ScriptPropertiesGeneratorTests
"ScriptBoilerplate_ScriptProperties.generated.cs", "OuterClass.NestedClass_ScriptProperties.generated.cs"
);
}
+
+ [Fact]
+ public async void AbstractGenericNode()
+ {
+ await CSharpSourceGeneratorVerifier<ScriptPropertiesGenerator>.Verify(
+ "AbstractGenericNode.cs",
+ "AbstractGenericNode(Of T)_ScriptProperties.generated.cs"
+ );
+ }
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/AbstractGenericNode(Of T)_ScriptProperties.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/AbstractGenericNode(Of T)_ScriptProperties.generated.cs
new file mode 100644
index 0000000000..a561c5fc0d
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/AbstractGenericNode(Of T)_ScriptProperties.generated.cs
@@ -0,0 +1,49 @@
+using Godot;
+using Godot.NativeInterop;
+
+partial class AbstractGenericNode<T>
+{
+#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword
+ /// <summary>
+ /// Cached StringNames for the properties and fields contained in this class, for fast lookup.
+ /// </summary>
+ public new class PropertyName : global::Godot.Node.PropertyName {
+ /// <summary>
+ /// Cached name for the 'MyArray' property.
+ /// </summary>
+ public new static readonly global::Godot.StringName MyArray = "MyArray";
+ }
+ /// <inheritdoc/>
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ protected override bool SetGodotClassPropertyValue(in godot_string_name name, in godot_variant value)
+ {
+ if (name == PropertyName.MyArray) {
+ this.MyArray = global::Godot.NativeInterop.VariantUtils.ConvertToArray<T>(value);
+ return true;
+ }
+ return base.SetGodotClassPropertyValue(name, value);
+ }
+ /// <inheritdoc/>
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ protected override bool GetGodotClassPropertyValue(in godot_string_name name, out godot_variant value)
+ {
+ if (name == PropertyName.MyArray) {
+ value = global::Godot.NativeInterop.VariantUtils.CreateFromArray(this.MyArray);
+ return true;
+ }
+ return base.GetGodotClassPropertyValue(name, out value);
+ }
+ /// <summary>
+ /// Get the property information for all the properties declared in this class.
+ /// This method is used by Godot to register the available properties in the editor.
+ /// Do not call this method.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ internal new static global::System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo> GetGodotPropertyList()
+ {
+ var properties = new global::System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo>();
+ properties.Add(new(type: (global::Godot.Variant.Type)28, name: PropertyName.MyArray, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
+ return properties;
+ }
+#pragma warning restore CS0109
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/AbstractGenericNode.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/AbstractGenericNode.cs
new file mode 100644
index 0000000000..cee4f67921
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/AbstractGenericNode.cs
@@ -0,0 +1,7 @@
+using Godot;
+
+public abstract partial class AbstractGenericNode<[MustBeVariant] T> : Node
+{
+ [Export] // This should be included, but without type hints.
+ public Godot.Collections.Array<T> MyArray { get; set; } = new();
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/MustBeVariant.GD0301.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/MustBeVariant.GD0301.cs
index 462da31d66..2b5eecab8a 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/MustBeVariant.GD0301.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/MustBeVariant.GD0301.cs
@@ -66,6 +66,12 @@ public class MustBeVariantGD0301
Method<Rid[]>();
}
+ public void MethodCallDynamic()
+ {
+ dynamic self = this;
+ self.Method<object>();
+ }
+
public void Method<[MustBeVariant] T>()
{
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj
index fbabed50d0..1aa2979e76 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>10</LangVersion>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs
index 834beaa131..4cf6a9f431 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs
@@ -44,7 +44,8 @@ namespace Godot.SourceGenerators
PackedVector2Array = 35,
PackedVector3Array = 36,
PackedColorArray = 37,
- Max = 38
+ PackedVector4Array = 38,
+ Max = 39
}
internal enum PropertyHint
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs
index be6af117eb..bfb735e72f 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs
@@ -51,6 +51,7 @@ namespace Godot.SourceGenerators
StringArray,
Vector2Array,
Vector3Array,
+ Vector4Array,
ColorArray,
GodotObjectOrDerivedArray,
SystemArrayOfStringName,
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 0258f53062..d272832950 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs
@@ -66,6 +66,7 @@ namespace Godot.SourceGenerators
MarshalType.StringArray => VariantType.PackedStringArray,
MarshalType.Vector2Array => VariantType.PackedVector2Array,
MarshalType.Vector3Array => VariantType.PackedVector3Array,
+ MarshalType.Vector4Array => VariantType.PackedVector4Array,
MarshalType.ColorArray => VariantType.PackedColorArray,
MarshalType.GodotObjectOrDerivedArray => VariantType.Array,
MarshalType.SystemArrayOfStringName => VariantType.Array,
@@ -190,6 +191,8 @@ namespace Godot.SourceGenerators
return MarshalType.Vector2Array;
case { Name: "Vector3" }:
return MarshalType.Vector3Array;
+ case { Name: "Vector4" }:
+ return MarshalType.Vector4Array;
case { Name: "Color" }:
return MarshalType.ColorArray;
case { Name: "StringName" }:
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs
index 95eaca4d3d..e894e7a86c 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs
@@ -50,8 +50,18 @@ namespace Godot.SourceGenerators
var typeSymbol = sm.GetSymbolInfo(typeSyntax).Symbol as ITypeSymbol;
Helper.ThrowIfNull(typeSymbol);
- var parentSymbol = sm.GetSymbolInfo(parentSyntax).Symbol;
- Helper.ThrowIfNull(parentSymbol);
+ var parentSymbolInfo = sm.GetSymbolInfo(parentSyntax);
+ var parentSymbol = parentSymbolInfo.Symbol;
+ if (parentSymbol == null)
+ {
+ if (parentSymbolInfo.CandidateReason == CandidateReason.LateBound)
+ {
+ // Invocations on dynamic are late bound so we can't retrieve the symbol.
+ continue;
+ }
+
+ Helper.ThrowIfNull(parentSymbol);
+ }
if (!ShouldCheckTypeArgument(context, parentSyntax, parentSymbol, typeSyntax, typeSymbol, i))
{
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 a0e410e31a..21223654f3 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
@@ -658,7 +658,10 @@ namespace Godot.SourceGenerators
var elementType = MarshalUtils.GetArrayElementType(type);
if (elementType == null)
- return false; // Non-generic Array, so there's no hint to add
+ return false; // Non-generic Array, so there's no hint to add.
+
+ if (elementType.TypeKind == TypeKind.TypeParameter)
+ return false; // The generic is not constructed, we can't really hint anything.
var elementMarshalType = MarshalUtils.ConvertManagedTypeToMarshalType(elementType, typeCache)!.Value;
var elementVariantType = MarshalUtils.ConvertMarshalTypeToVariantType(elementMarshalType)!.Value;
diff --git a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj
index fd836f9ad2..f807132509 100644
--- a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{6CE9A984-37B1-4F8A-8FE9-609F05F071B3}</ProjectGuid>
<TargetFramework>net6.0</TargetFramework>
diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj b/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj
index 0755484465..f692f26a8b 100644
--- a/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{639E48BD-44E5-4091-8EDD-22D36DC0768D}</ProjectGuid>
<TargetFramework>net6.0</TargetFramework>
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj
index 3bf678e9f9..0174b25b3f 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{B06C2951-C8E3-4F28-80B2-717CF327EB19}</ProjectGuid>
<OutputType>Exe</OutputType>
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj
index f681228892..2ee28715bf 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{92600954-25F0-4291-8E11-1FEE9FC4BE20}</ProjectGuid>
<TargetFramework>netstandard2.0</TargetFramework>
diff --git a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj
index 09908c85a1..7360118ee4 100644
--- a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{EAFFF236-FA96-4A4D-BD23-0E51EF988277}</ProjectGuid>
<OutputType>Exe</OutputType>
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
index 623475e11a..5547ffcf19 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}</ProjectGuid>
<TargetFramework>net6.0</TargetFramework>
diff --git a/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj b/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj
index d60e6343ea..7c19fabaf6 100644
--- a/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj
@@ -1,8 +1,8 @@
-<Project Sdk="Microsoft.NET.Sdk">
- <PropertyGroup>
- <TargetFramework>net6.0</TargetFramework>
- <!-- Specify compile items manually to avoid including dangling generated items. -->
- <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
- </PropertyGroup>
- <Import Project="GenerateGodotNupkgsVersions.targets" />
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <TargetFramework>net6.0</TargetFramework>
+ <!-- Specify compile items manually to avoid including dangling generated items. -->
+ <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
+ </PropertyGroup>
+ <Import Project="GenerateGodotNupkgsVersions.targets" />
</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.sln b/modules/mono/editor/GodotTools/GodotTools.sln
index 564775635d..1182e2467b 100644
--- a/modules/mono/editor/GodotTools/GodotTools.sln
+++ b/modules/mono/editor/GodotTools/GodotTools.sln
@@ -1,6 +1,8 @@
-
+
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2012
+# Visual Studio Version 17
+VisualStudioVersion = 17.9.34728.123
+MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.ProjectEditor", "GodotTools.ProjectEditor\GodotTools.ProjectEditor.csproj", "{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools", "GodotTools\GodotTools.csproj", "{27B00618-A6F2-4828-B922-05CAEB08C286}"
@@ -62,4 +64,10 @@ Global
{55666071-BEC1-4A52-8A98-9A4A7A947DBF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{55666071-BEC1-4A52-8A98-9A4A7A947DBF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {521EC35A-F7F0-46A9-92CE-680D2F5B02B8}
+ EndGlobalSection
EndGlobal
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
index 35b3f5a710..96d2c5c75f 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{27B00618-A6F2-4828-B922-05CAEB08C286}</ProjectGuid>
<TargetFramework>net6.0</TargetFramework>
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index e1ce41edd5..d46160127d 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -330,6 +330,8 @@ String BindingsGenerator::bbcode_to_text(const String &p_bbcode, const TypeInter
output.append("'" BINDINGS_NAMESPACE ".Vector3[]'");
} else if (tag == "PackedColorArray") {
output.append("'" BINDINGS_NAMESPACE ".Color[]'");
+ } else if (tag == "PackedVector4Array") {
+ output.append("'" BINDINGS_NAMESPACE ".Vector4[]'");
} else {
const TypeInterface *target_itype = _get_type_or_null(TypeReference(tag));
@@ -646,6 +648,8 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Vector3\"/>[]");
} else if (tag == "PackedColorArray") {
xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Color\"/>[]");
+ } else if (tag == "PackedVector4Array") {
+ xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Vector4\"/>[]");
} else {
const TypeInterface *target_itype = _get_type_or_null(TypeReference(tag));
@@ -3516,6 +3520,7 @@ bool BindingsGenerator::_arg_default_value_is_assignable_to_type(const Variant &
case Variant::PACKED_STRING_ARRAY:
case Variant::PACKED_VECTOR2_ARRAY:
case Variant::PACKED_VECTOR3_ARRAY:
+ case Variant::PACKED_VECTOR4_ARRAY:
case Variant::PACKED_COLOR_ARRAY:
case Variant::CALLABLE:
case Variant::SIGNAL:
@@ -4246,6 +4251,7 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
case Variant::PACKED_STRING_ARRAY:
case Variant::PACKED_VECTOR2_ARRAY:
case Variant::PACKED_VECTOR3_ARRAY:
+ case Variant::PACKED_VECTOR4_ARRAY:
case Variant::PACKED_COLOR_ARRAY:
r_iarg.default_argument = "Array.Empty<%s>()";
r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
@@ -4585,6 +4591,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
INSERT_ARRAY(PackedColorArray, godot_packed_color_array, Color);
INSERT_ARRAY(PackedVector2Array, godot_packed_vector2_array, Vector2);
INSERT_ARRAY(PackedVector3Array, godot_packed_vector3_array, Vector3);
+ INSERT_ARRAY(PackedVector4Array, godot_packed_vector4_array, Vector4);
#undef INSERT_ARRAY
@@ -4852,7 +4859,7 @@ static void handle_cmdline_options(String glue_dir_path) {
}
static void cleanup_and_exit_godot() {
- // Exit once done
+ // Exit once done.
Main::cleanup(true);
::exit(0);
}
@@ -4871,7 +4878,7 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
elem = elem->next();
} else {
ERR_PRINT(generate_all_glue_option + ": No output directory specified (expected path to '{GODOT_ROOT}/modules/mono/glue').");
- // Exit once done with invalid command line arguments
+ // Exit once done with invalid command line arguments.
cleanup_and_exit_godot();
}
@@ -4882,8 +4889,14 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
}
if (glue_dir_path.length()) {
- handle_cmdline_options(glue_dir_path);
- // Exit once done
+ if (Engine::get_singleton()->is_editor_hint() ||
+ Engine::get_singleton()->is_project_manager_hint()) {
+ handle_cmdline_options(glue_dir_path);
+ } else {
+ // Running from a project folder, which doesn't make sense and crashes.
+ ERR_PRINT(generate_all_glue_option + ": Cannot generate Mono glue while running a game project. Change current directory or enable --editor.");
+ }
+ // Exit once done.
cleanup_and_exit_godot();
}
}
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index a397dcb026..556d287af4 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -705,7 +705,7 @@ class BindingsGenerator {
StringName type_Vector4i = StaticCString::create("Vector4i");
// Object not included as it must be checked for all derived classes
- static constexpr int nullable_types_count = 18;
+ static constexpr int nullable_types_count = 19;
StringName nullable_types[nullable_types_count] = {
type_String,
type_StringName,
@@ -727,6 +727,7 @@ class BindingsGenerator {
StaticCString::create(_STR(PackedVector2Array)),
StaticCString::create(_STR(PackedVector3Array)),
StaticCString::create(_STR(PackedColorArray)),
+ StaticCString::create(_STR(PackedVector4Array)),
};
bool is_nullable_type(const StringName &p_type) const {
diff --git a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Godot.SourceGenerators.Internal.csproj b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Godot.SourceGenerators.Internal.csproj
index 4d1a5bb76c..81add0e44f 100644
--- a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Godot.SourceGenerators.Internal.csproj
+++ b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Godot.SourceGenerators.Internal.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>10</LangVersion>
diff --git a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs
index f3f6759e1d..08e293afcc 100644
--- a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs
+++ b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs
@@ -468,6 +468,7 @@ using Godot.NativeInterop;
"Godot.NativeInterop.godot_packed_string_array",
"Godot.NativeInterop.godot_packed_vector2_array",
"Godot.NativeInterop.godot_packed_vector3_array",
+ "Godot.NativeInterop.godot_packed_vector4_array",
"Godot.NativeInterop.godot_packed_color_array",
};
}
diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj b/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj
index e58d730ee3..1e60743fb1 100644
--- a/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj
+++ b/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj
@@ -1,18 +1,18 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
- <PropertyGroup>
- <TargetFramework>net6.0</TargetFramework>
- <LangVersion>10</LangVersion>
- <Nullable>enable</Nullable>
- <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <PropertyGroup>
+ <TargetFramework>net6.0</TargetFramework>
+ <LangVersion>10</LangVersion>
+ <Nullable>enable</Nullable>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
- <!-- To generate the .runtimeconfig.json file-->
- <EnableDynamicLoading>true</EnableDynamicLoading>
- <RollForward>LatestMajor</RollForward>
- </PropertyGroup>
+ <!-- To generate the .runtimeconfig.json file-->
+ <EnableDynamicLoading>true</EnableDynamicLoading>
+ <RollForward>LatestMajor</RollForward>
+ </PropertyGroup>
- <ItemGroup>
- <ProjectReference Include="..\GodotSharp\GodotSharp.csproj" />
- </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\GodotSharp\GodotSharp.csproj" />
+ </ItemGroup>
</Project>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp.sln b/modules/mono/glue/GodotSharp/GodotSharp.sln
index 8db42c2d1a..81ff7d7550 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp.sln
+++ b/modules/mono/glue/GodotSharp/GodotSharp.sln
@@ -1,5 +1,8 @@
+
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2012
+# Visual Studio Version 17
+VisualStudioVersion = 17.9.34728.123
+MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharp", "GodotSharp\GodotSharp.csproj", "{AEBF0036-DA76-4341-B651-A3F2856AB2FA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharpEditor", "GodotSharpEditor\GodotSharpEditor.csproj", "{8FBEC238-D944-4074-8548-B3B524305905}"
@@ -10,8 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.SourceGenerators.Inte
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AEBF0036-DA76-4341-B651-A3F2856AB2FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
@@ -31,4 +34,10 @@ Global
{7749662B-E30C-419A-B745-13852573360A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7749662B-E30C-419A-B745-13852573360A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {371B0F03-042D-45FD-A270-F3141F2480CD}
+ EndGlobalSection
EndGlobal
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs
index feaa1d07da..ab7f8ede44 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs
@@ -69,7 +69,7 @@ namespace Godot
public readonly Aabb Abs()
{
Vector3 end = End;
- Vector3 topLeft = new Vector3(Mathf.Min(_position.X, end.X), Mathf.Min(_position.Y, end.Y), Mathf.Min(_position.Z, end.Z));
+ Vector3 topLeft = end.Min(_position);
return new Aabb(topLeft, _size.Abs());
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/CustomUnsafe.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/CustomUnsafe.cs
index afef20a7f2..171cf86edb 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/CustomUnsafe.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/CustomUnsafe.cs
@@ -296,6 +296,22 @@ public static class CustomUnsafe
=> ref *ReadOnlyRefAsPointer(in source);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_vector4_array* AsPointer(ref godot_packed_vector4_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_vector4_array* ReadOnlyRefAsPointer(in godot_packed_vector4_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_vector4_array AsRef(godot_packed_vector4_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_vector4_array AsRef(in godot_packed_vector4_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe godot_packed_color_array* AsPointer(ref godot_packed_color_array value)
=> value.GetUnsafeAddress();
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
index a019dd3513..7e5c01d0f8 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
@@ -1134,6 +1134,39 @@ namespace Godot.NativeInterop
}
[StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_vector4_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_vector4_array* GetUnsafeAddress()
+ => (godot_packed_vector4_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe Vector4* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_vector4_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe Vector4* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? (int)(*((ulong*)_ptr - 1)) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
public ref struct godot_packed_color_array
{
[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 9f7fa53e24..15b7ce7c73 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
@@ -133,6 +133,9 @@ namespace Godot.NativeInterop
if (type == typeof(Vector3[]))
return Variant.Type.PackedVector3Array;
+ if (type == typeof(Vector4[]))
+ return Variant.Type.PackedVector4Array;
+
if (type == typeof(Color[]))
return Variant.Type.PackedColorArray;
@@ -574,6 +577,30 @@ namespace Godot.NativeInterop
return NativeFuncs.godotsharp_packed_vector3_array_new_mem_copy(src, p_array.Length);
}
+ // PackedVector4Array
+
+ public static unsafe Vector4[] ConvertNativePackedVector4ArrayToSystemArray(godot_packed_vector4_array p_array)
+ {
+ Vector4* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<Vector4>();
+ int sizeInBytes = size * sizeof(Vector4);
+ var array = new Vector4[size];
+ fixed (Vector4* dest = array)
+ Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes);
+ return array;
+ }
+
+ public static unsafe godot_packed_vector4_array ConvertSystemArrayToNativePackedVector4Array(
+ Span<Vector4> p_array)
+ {
+ if (p_array.IsEmpty)
+ return new godot_packed_vector4_array();
+ fixed (Vector4* src = p_array)
+ return NativeFuncs.godotsharp_packed_vector4_array_new_mem_copy(src, p_array.Length);
+ }
+
// PackedColorArray
public static unsafe Color[] ConvertNativePackedColorArrayToSystemArray(godot_packed_color_array p_array)
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
index fef21fae46..c4fd639cce 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
@@ -144,6 +144,9 @@ namespace Godot.NativeInterop
public static partial godot_packed_vector3_array godotsharp_packed_vector3_array_new_mem_copy(Vector3* p_src,
int p_length);
+ public static partial godot_packed_vector4_array godotsharp_packed_vector4_array_new_mem_copy(Vector4* p_src,
+ int p_length);
+
public static partial godot_packed_color_array godotsharp_packed_color_array_new_mem_copy(Color* p_src,
int p_length);
@@ -224,6 +227,9 @@ namespace Godot.NativeInterop
public static partial void godotsharp_variant_new_packed_vector3_array(out godot_variant r_dest,
in godot_packed_vector3_array p_pv3a);
+ public static partial void godotsharp_variant_new_packed_vector4_array(out godot_variant r_dest,
+ in godot_packed_vector4_array p_pv4a);
+
public static partial void godotsharp_variant_new_packed_color_array(out godot_variant r_dest,
in godot_packed_color_array p_pca);
@@ -302,6 +308,9 @@ namespace Godot.NativeInterop
public static partial godot_packed_vector3_array godotsharp_variant_as_packed_vector3_array(
in godot_variant p_self);
+ public static partial godot_packed_vector4_array godotsharp_variant_as_packed_vector4_array(
+ in godot_variant p_self);
+
public static partial godot_packed_color_array godotsharp_variant_as_packed_color_array(in godot_variant p_self);
public static partial godot_bool godotsharp_variant_equals(in godot_variant p_a, in godot_variant p_b);
@@ -352,6 +361,8 @@ namespace Godot.NativeInterop
public static partial void godotsharp_packed_vector3_array_destroy(ref godot_packed_vector3_array p_self);
+ public static partial void godotsharp_packed_vector4_array_destroy(ref godot_packed_vector4_array p_self);
+
public static partial void godotsharp_packed_color_array_destroy(ref godot_packed_color_array p_self);
public static partial void godotsharp_variant_destroy(ref godot_variant p_self);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs
index 94609984ac..dc151e2c3e 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs
@@ -165,6 +165,12 @@ namespace Godot.NativeInterop
return ret;
}
+ public static godot_variant CreateFromPackedVector4Array(in godot_packed_vector4_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_vector4_array(out godot_variant ret, from);
+ return ret;
+ }
+
public static godot_variant CreateFromPackedColorArray(in godot_packed_color_array from)
{
NativeFuncs.godotsharp_variant_new_packed_color_array(out godot_variant ret, from);
@@ -228,6 +234,13 @@ namespace Godot.NativeInterop
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedVector4Array(Span<Vector4> from)
+ {
+ using var nativePackedArray = Marshaling.ConvertSystemArrayToNativePackedVector4Array(from);
+ return CreateFromPackedVector4Array(nativePackedArray);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static godot_variant CreateFromPackedColorArray(Span<Color> from)
{
using var nativePackedArray = Marshaling.ConvertSystemArrayToNativePackedColorArray(from);
@@ -605,6 +618,12 @@ namespace Godot.NativeInterop
return Marshaling.ConvertNativePackedVector3ArrayToSystemArray(packedArray);
}
+ public static Vector4[] ConvertAsPackedVector4ArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector4_array(p_var);
+ return Marshaling.ConvertNativePackedVector4ArrayToSystemArray(packedArray);
+ }
+
public static Color[] ConvertAsPackedColorArrayToSystemArray(in godot_variant p_var)
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_color_array(p_var);
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 d8f7214c2f..2897cc4199 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs
@@ -155,6 +155,9 @@ public partial class VariantUtils
if (typeof(T) == typeof(Vector3[]))
return CreateFromPackedVector3Array(UnsafeAs<Vector3[]>(from));
+ if (typeof(T) == typeof(Vector4[]))
+ return CreateFromPackedVector4Array(UnsafeAs<Vector4[]>(from));
+
if (typeof(T) == typeof(Color[]))
return CreateFromPackedColorArray(UnsafeAs<Color[]>(from));
@@ -343,6 +346,9 @@ public partial class VariantUtils
if (typeof(T) == typeof(Vector3[]))
return UnsafeAsT(ConvertAsPackedVector3ArrayToSystemArray(variant));
+ if (typeof(T) == typeof(Vector4[]))
+ return UnsafeAsT(ConvertAsPackedVector4ArrayToSystemArray(variant));
+
if (typeof(T) == typeof(Color[]))
return UnsafeAsT(ConvertAsPackedColorArrayToSystemArray(variant));
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
index 9d9065911e..19721b6cca 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
@@ -69,7 +69,7 @@ namespace Godot
public readonly Rect2 Abs()
{
Vector2 end = End;
- Vector2 topLeft = new Vector2(Mathf.Min(_position.X, end.X), Mathf.Min(_position.Y, end.Y));
+ Vector2 topLeft = end.Min(_position);
return new Rect2(topLeft, _size.Abs());
}
@@ -91,14 +91,12 @@ namespace Godot
return new Rect2();
}
- newRect._position.X = Mathf.Max(b._position.X, _position.X);
- newRect._position.Y = Mathf.Max(b._position.Y, _position.Y);
+ newRect._position = b._position.Max(_position);
Vector2 bEnd = b._position + b._size;
Vector2 end = _position + _size;
- newRect._size.X = Mathf.Min(bEnd.X, end.X) - newRect._position.X;
- newRect._size.Y = Mathf.Min(bEnd.Y, end.Y) - newRect._position.Y;
+ newRect._size = bEnd.Min(end) - newRect._position;
return newRect;
}
@@ -338,11 +336,9 @@ namespace Godot
{
Rect2 newRect;
- newRect._position.X = Mathf.Min(b._position.X, _position.X);
- newRect._position.Y = Mathf.Min(b._position.Y, _position.Y);
+ newRect._position = b._position.Min(_position);
- newRect._size.X = Mathf.Max(b._position.X + b._size.X, _position.X + _size.X);
- newRect._size.Y = Mathf.Max(b._position.Y + b._size.Y, _position.Y + _size.Y);
+ newRect._size = (b._position + b._size).Max(_position + _size);
newRect._size -= newRect._position; // Make relative again
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2I.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2I.cs
index 65704b3da7..7ee9ff8552 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2I.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2I.cs
@@ -69,7 +69,7 @@ namespace Godot
public readonly Rect2I Abs()
{
Vector2I end = End;
- Vector2I topLeft = new Vector2I(Mathf.Min(_position.X, end.X), Mathf.Min(_position.Y, end.Y));
+ Vector2I topLeft = end.Min(_position);
return new Rect2I(topLeft, _size.Abs());
}
@@ -91,14 +91,12 @@ namespace Godot
return new Rect2I();
}
- newRect._position.X = Mathf.Max(b._position.X, _position.X);
- newRect._position.Y = Mathf.Max(b._position.Y, _position.Y);
+ newRect._position = b._position.Max(_position);
Vector2I bEnd = b._position + b._size;
Vector2I end = _position + _size;
- newRect._size.X = Mathf.Min(bEnd.X, end.X) - newRect._position.X;
- newRect._size.Y = Mathf.Min(bEnd.Y, end.Y) - newRect._position.Y;
+ newRect._size = bEnd.Min(end) - newRect._position;
return newRect;
}
@@ -295,11 +293,9 @@ namespace Godot
{
Rect2I newRect;
- newRect._position.X = Mathf.Min(b._position.X, _position.X);
- newRect._position.Y = Mathf.Min(b._position.Y, _position.Y);
+ newRect._position = b._position.Min(_position);
- newRect._size.X = Mathf.Max(b._position.X + b._size.X, _position.X + _size.X);
- newRect._size.Y = Mathf.Max(b._position.Y + b._size.Y, _position.Y + _size.Y);
+ newRect._size = (b._position + b._size).Max(_position + _size);
newRect._size -= newRect._position; // Make relative again
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
index 37f319b697..d8a3e91699 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
@@ -417,76 +417,31 @@ namespace Godot
}
/// <summary>
- /// Performs a case-sensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater.
+ /// Performs a case-sensitive comparison to another string and returns an integer that indicates their relative position in the sort order.
/// </summary>
/// <seealso cref="NocasecmpTo(string, string)"/>
/// <seealso cref="CompareTo(string, string, bool)"/>
/// <param name="instance">The string to compare.</param>
/// <param name="to">The other string to compare.</param>
- /// <returns>-1 if less, 0 if equal and +1 if greater.</returns>
+ /// <returns>An integer that indicates the lexical relationship between the two comparands.</returns>
public static int CasecmpTo(this string instance, string to)
{
return instance.CompareTo(to, caseSensitive: true);
}
/// <summary>
- /// Performs a comparison to another string, return -1 if less, 0 if equal and +1 if greater.
+ /// Performs a comparison to another string and returns an integer that indicates their relative position in the sort order.
/// </summary>
/// <param name="instance">The string to compare.</param>
/// <param name="to">The other string to compare.</param>
/// <param name="caseSensitive">
/// If <see langword="true"/>, the comparison will be case sensitive.
/// </param>
- /// <returns>-1 if less, 0 if equal and +1 if greater.</returns>
+ /// <returns>An integer that indicates the lexical relationship between the two comparands.</returns>
+ [Obsolete("Use string.Compare instead.")]
public static int CompareTo(this string instance, string to, bool caseSensitive = true)
{
- if (string.IsNullOrEmpty(instance))
- return string.IsNullOrEmpty(to) ? 0 : -1;
-
- if (string.IsNullOrEmpty(to))
- return 1;
-
- int instanceIndex = 0;
- int toIndex = 0;
-
- if (caseSensitive) // Outside while loop to avoid checking multiple times, despite some code duplication.
- {
- while (true)
- {
- if (to[toIndex] == 0 && instance[instanceIndex] == 0)
- return 0; // We're equal
- if (instance[instanceIndex] == 0)
- return -1; // If this is empty, and the other one is not, then we're less... I think?
- if (to[toIndex] == 0)
- return 1; // Otherwise the other one is smaller...
- if (instance[instanceIndex] < to[toIndex]) // More than
- return -1;
- if (instance[instanceIndex] > to[toIndex]) // Less than
- return 1;
-
- instanceIndex++;
- toIndex++;
- }
- }
- else
- {
- while (true)
- {
- if (to[toIndex] == 0 && instance[instanceIndex] == 0)
- return 0; // We're equal
- if (instance[instanceIndex] == 0)
- return -1; // If this is empty, and the other one is not, then we're less... I think?
- if (to[toIndex] == 0)
- return 1; // Otherwise the other one is smaller..
- if (char.ToUpperInvariant(instance[instanceIndex]) < char.ToUpperInvariant(to[toIndex])) // More than
- return -1;
- if (char.ToUpperInvariant(instance[instanceIndex]) > char.ToUpperInvariant(to[toIndex])) // Less than
- return 1;
-
- instanceIndex++;
- toIndex++;
- }
- }
+ return string.Compare(instance, to, !caseSensitive);
}
/// <summary>
@@ -1297,13 +1252,13 @@ namespace Godot
}
/// <summary>
- /// Perform a case-insensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater.
+ /// Performs a case-insensitive comparison to another string and returns an integer that indicates their relative position in the sort order.
/// </summary>
/// <seealso cref="CasecmpTo(string, string)"/>
/// <seealso cref="CompareTo(string, string, bool)"/>
/// <param name="instance">The string to compare.</param>
/// <param name="to">The other string to compare.</param>
- /// <returns>-1 if less, 0 if equal and +1 if greater.</returns>
+ /// <returns>An integer that indicates the lexical relationship between the two comparands.</returns>
public static int NocasecmpTo(this string instance, string to)
{
return instance.CompareTo(to, caseSensitive: false);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs
index c2d3050adc..b40f524859 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs
@@ -149,6 +149,7 @@ public partial struct Variant : IDisposable
Type.PackedStringArray => AsStringArray(),
Type.PackedVector2Array => AsVector2Array(),
Type.PackedVector3Array => AsVector3Array(),
+ Type.PackedVector4Array => AsVector4Array(),
Type.PackedColorArray => AsColorArray(),
Type.Nil => null,
Type.Max or _ =>
@@ -320,6 +321,10 @@ public partial struct Variant : IDisposable
VariantUtils.ConvertAsPackedVector3ArrayToSystemArray((godot_variant)NativeVar);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector4[] AsVector4Array() =>
+ VariantUtils.ConvertAsPackedVector4ArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public Color[] AsColorArray() =>
VariantUtils.ConvertAsPackedColorArrayToSystemArray((godot_variant)NativeVar);
@@ -492,6 +497,9 @@ public partial struct Variant : IDisposable
public static explicit operator Vector3[](Variant from) => from.AsVector3Array();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Vector4[](Variant from) => from.AsVector4Array();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Color[](Variant from) => from.AsColorArray();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -642,6 +650,9 @@ public partial struct Variant : IDisposable
public static Variant CreateFrom(Span<Vector3> from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<Vector4> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Span<Color> from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -841,6 +852,10 @@ public partial struct Variant : IDisposable
(Variant)from.AsSpan();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Vector4[] from) =>
+ (Variant)from.AsSpan();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Variant(Color[] from) =>
(Variant)from.AsSpan();
@@ -893,6 +908,10 @@ public partial struct Variant : IDisposable
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedVector3Array(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<Vector4> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedVector4Array(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Variant(Span<Color> from) =>
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedColorArray(from));
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
index 856fd54352..50bf56d832 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
@@ -192,6 +192,23 @@ namespace Godot
}
/// <summary>
+ /// Returns a new vector with all components clamped between the
+ /// <paramref name="min"/> and <paramref name="max"/> using
+ /// <see cref="Mathf.Clamp(real_t, real_t, real_t)"/>.
+ /// </summary>
+ /// <param name="min">The minimum allowed value.</param>
+ /// <param name="max">The maximum allowed value.</param>
+ /// <returns>The vector with all components clamped.</returns>
+ public readonly Vector2 Clamp(real_t min, real_t max)
+ {
+ return new Vector2
+ (
+ Mathf.Clamp(X, min, max),
+ Mathf.Clamp(Y, min, max)
+ );
+ }
+
+ /// <summary>
/// Returns the cross product of this vector and <paramref name="with"/>.
/// </summary>
/// <param name="with">The other vector.</param>
@@ -413,6 +430,70 @@ namespace Godot
}
/// <summary>
+ /// Returns the result of the component-wise maximum between
+ /// this vector and <paramref name="with"/>.
+ /// Equivalent to <c>new Vector2(Mathf.Max(X, with.X), Mathf.Max(Y, with.Y))</c>.
+ /// </summary>
+ /// <param name="with">The other vector to use.</param>
+ /// <returns>The resulting maximum vector.</returns>
+ public readonly Vector2 Max(Vector2 with)
+ {
+ return new Vector2
+ (
+ Mathf.Max(X, with.X),
+ Mathf.Max(Y, with.Y)
+ );
+ }
+
+ /// <summary>
+ /// Returns the result of the component-wise maximum between
+ /// this vector and <paramref name="with"/>.
+ /// Equivalent to <c>new Vector2(Mathf.Max(X, with), Mathf.Max(Y, with))</c>.
+ /// </summary>
+ /// <param name="with">The other value to use.</param>
+ /// <returns>The resulting maximum vector.</returns>
+ public readonly Vector2 Max(real_t with)
+ {
+ return new Vector2
+ (
+ Mathf.Max(X, with),
+ Mathf.Max(Y, with)
+ );
+ }
+
+ /// <summary>
+ /// Returns the result of the component-wise minimum between
+ /// this vector and <paramref name="with"/>.
+ /// Equivalent to <c>new Vector2(Mathf.Min(X, with.X), Mathf.Min(Y, with.Y))</c>.
+ /// </summary>
+ /// <param name="with">The other vector to use.</param>
+ /// <returns>The resulting minimum vector.</returns>
+ public readonly Vector2 Min(Vector2 with)
+ {
+ return new Vector2
+ (
+ Mathf.Min(X, with.X),
+ Mathf.Min(Y, with.Y)
+ );
+ }
+
+ /// <summary>
+ /// Returns the result of the component-wise minimum between
+ /// this vector and <paramref name="with"/>.
+ /// Equivalent to <c>new Vector2(Mathf.Min(X, with), Mathf.Min(Y, with))</c>.
+ /// </summary>
+ /// <param name="with">The other value to use.</param>
+ /// <returns>The resulting minimum vector.</returns>
+ public readonly Vector2 Min(real_t with)
+ {
+ return new Vector2
+ (
+ Mathf.Min(X, with),
+ Mathf.Min(Y, with)
+ );
+ }
+
+ /// <summary>
/// Returns the axis of the vector's highest value. See <see cref="Axis"/>.
/// If both components are equal, this method returns <see cref="Axis.X"/>.
/// </summary>
@@ -600,7 +681,7 @@ namespace Godot
}
/// <summary>
- /// Returns this vector with each component snapped to the nearest multiple of <paramref name="step"/>.
+ /// Returns a new vector with each component snapped to the nearest multiple of the corresponding component in <paramref name="step"/>.
/// This can also be used to round to an arbitrary number of decimals.
/// </summary>
/// <param name="step">A vector value representing the step size to snap to.</param>
@@ -611,6 +692,17 @@ namespace Godot
}
/// <summary>
+ /// Returns a new vector with each component snapped to the nearest multiple of <paramref name="step"/>.
+ /// This can also be used to round to an arbitrary number of decimals.
+ /// </summary>
+ /// <param name="step">The step size to snap to.</param>
+ /// <returns>The snapped vector.</returns>
+ public readonly Vector2 Snapped(real_t step)
+ {
+ return new Vector2(Mathf.Snapped(X, step), Mathf.Snapped(Y, step));
+ }
+
+ /// <summary>
/// Returns a perpendicular vector rotated 90 degrees counter-clockwise
/// compared to the original, with the same length.
/// </summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2I.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2I.cs
index 511cc7971c..9442db4d86 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2I.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2I.cs
@@ -125,6 +125,23 @@ namespace Godot
}
/// <summary>
+ /// Returns a new vector with all components clamped between the
+ /// <paramref name="min"/> and <paramref name="max"/> using
+ /// <see cref="Mathf.Clamp(int, int, int)"/>.
+ /// </summary>
+ /// <param name="min">The minimum allowed value.</param>
+ /// <param name="max">The maximum allowed value.</param>
+ /// <returns>The vector with all components clamped.</returns>
+ public readonly Vector2I Clamp(int min, int max)
+ {
+ return new Vector2I
+ (
+ Mathf.Clamp(X, min, max),
+ Mathf.Clamp(Y, min, max)
+ );
+ }
+
+ /// <summary>
/// Returns the squared distance between this vector and <paramref name="to"/>.
/// This method runs faster than <see cref="DistanceTo"/>, so prefer it if
/// you need to compare vectors or need the squared distance for some formula.
@@ -175,6 +192,70 @@ namespace Godot
}
/// <summary>
+ /// Returns the result of the component-wise maximum between
+ /// this vector and <paramref name="with"/>.
+ /// Equivalent to <c>new Vector2I(Mathf.Max(X, with.X), Mathf.Max(Y, with.Y))</c>.
+ /// </summary>
+ /// <param name="with">The other vector to use.</param>
+ /// <returns>The resulting maximum vector.</returns>
+ public readonly Vector2I Max(Vector2I with)
+ {
+ return new Vector2I
+ (
+ Mathf.Max(X, with.X),
+ Mathf.Max(Y, with.Y)
+ );
+ }
+
+ /// <summary>
+ /// Returns the result of the component-wise maximum between
+ /// this vector and <paramref name="with"/>.
+ /// Equivalent to <c>new Vector2I(Mathf.Max(X, with), Mathf.Max(Y, with))</c>.
+ /// </summary>
+ /// <param name="with">The other value to use.</param>
+ /// <returns>The resulting maximum vector.</returns>
+ public readonly Vector2I Max(int with)
+ {
+ return new Vector2I
+ (
+ Mathf.Max(X, with),
+ Mathf.Max(Y, with)
+ );
+ }
+
+ /// <summary>
+ /// Returns the result of the component-wise minimum between
+ /// this vector and <paramref name="with"/>.
+ /// Equivalent to <c>new Vector2I(Mathf.Min(X, with.X), Mathf.Min(Y, with.Y))</c>.
+ /// </summary>
+ /// <param name="with">The other vector to use.</param>
+ /// <returns>The resulting minimum vector.</returns>
+ public readonly Vector2I Min(Vector2I with)
+ {
+ return new Vector2I
+ (
+ Mathf.Min(X, with.X),
+ Mathf.Min(Y, with.Y)
+ );
+ }
+
+ /// <summary>
+ /// Returns the result of the component-wise minimum between
+ /// this vector and <paramref name="with"/>.
+ /// Equivalent to <c>new Vector2I(Mathf.Min(X, with), Mathf.Min(Y, with))</c>.
+ /// </summary>
+ /// <param name="with">The other value to use.</param>
+ /// <returns>The resulting minimum vector.</returns>
+ public readonly Vector2I Min(int with)
+ {
+ return new Vector2I
+ (
+ Mathf.Min(X, with),
+ Mathf.Min(Y, with)
+ );
+ }
+
+ /// <summary>
/// Returns the axis of the vector's highest value. See <see cref="Axis"/>.
/// If both components are equal, this method returns <see cref="Axis.X"/>.
/// </summary>
@@ -208,6 +289,34 @@ namespace Godot
return v;
}
+ /// <summary>
+ /// Returns a new vector with each component snapped to the closest multiple of the corresponding component in <paramref name="step"/>.
+ /// </summary>
+ /// <param name="step">A vector value representing the step size to snap to.</param>
+ /// <returns>The snapped vector.</returns>
+ public readonly Vector2I Snapped(Vector2I step)
+ {
+ return new Vector2I
+ (
+ (int)Mathf.Snapped((double)X, (double)step.X),
+ (int)Mathf.Snapped((double)Y, (double)step.Y)
+ );
+ }
+
+ /// <summary>
+ /// Returns a new vector with each component snapped to the closest multiple of <paramref name="step"/>.
+ /// </summary>
+ /// <param name="step">The step size to snap to.</param>
+ /// <returns>The snapped vector.</returns>
+ public readonly Vector2I Snapped(int step)
+ {
+ return new Vector2I
+ (
+ (int)Mathf.Snapped((double)X, (double)step),
+ (int)Mathf.Snapped((double)Y, (double)step)
+ );
+ }
+
// Constants
private static readonly Vector2I _minValue = new Vector2I(int.MinValue, int.MinValue);
private static readonly Vector2I _maxValue = new Vector2I(int.MaxValue, int.MaxValue);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
index 6300705107..27f2713efa 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
@@ -179,6 +179,24 @@ namespace Godot
}
/// <summary>
+ /// Returns a new vector with all components clamped between the
+ /// <paramref name="min"/> and <paramref name="max"/> using
+ /// <see cref="Mathf.Clamp(real_t, real_t, real_t)"/>.
+ /// </summary>
+ /// <param name="min">The minimum allowed value.</param>
+ /// <param name="max">The maximum allowed value.</param>
+ /// <returns>The vector with all components clamped.</returns>
+ public readonly Vector3 Clamp(real_t min, real_t max)
+ {
+ return new Vector3
+ (
+ Mathf.Clamp(X, min, max),
+ Mathf.Clamp(Y, min, max),
+ Mathf.Clamp(Z, min, max)
+ );
+ }
+
+ /// <summary>
/// Returns the cross product of this vector and <paramref name="with"/>.
/// </summary>
/// <param name="with">The other vector.</param>
@@ -419,6 +437,57 @@ namespace Godot
}
/// <summary>
+ /// Returns the result of the component-wise maximum between
+ /// this vector and <paramref name="with"/>.
+ /// Equivalent to <c>new Vector3(Mathf.Max(X, with.X), Mathf.Max(Y, with.Y), Mathf.Max(Z, with.Z))</c>.
+ /// </summary>
+ /// <param name="with">The other vector to use.</param>
+ /// <returns>The resulting maximum vector.</returns>
+ public readonly Vector3 Max(Vector3 with)
+ {
+ return new Vector3
+ (
+ Mathf.Max(X, with.X),
+ Mathf.Max(Y, with.Y),
+ Mathf.Max(Z, with.Z)
+ );
+ }
+
+ /// <summary>
+ /// Returns the result of the component-wise maximum between
+ /// this vector and <paramref name="with"/>.
+ /// Equivalent to <c>new Vector3(Mathf.Max(X, with), Mathf.Max(Y, with), Mathf.Max(Z, with))</c>.
+ /// </summary>
+ /// <param name="with">The other value to use.</param>
+ /// <returns>The resulting maximum vector.</returns>
+ public readonly Vector3 Max(real_t with)
+ {
+ return new Vector3
+ (
+ Mathf.Max(X, with),
+ Mathf.Max(Y, with),
+ Mathf.Max(Z, with)
+ );
+ }
+
+ /// <summary>
+ /// Returns the result of the component-wise minimum between
+ /// this vector and <paramref name="with"/>.
+ /// Equivalent to <c>new Vector3(Mathf.Min(X, with.X), Mathf.Min(Y, with.Y), Mathf.Min(Z, with.Z))</c>.
+ /// </summary>
+ /// <param name="with">The other vector to use.</param>
+ /// <returns>The resulting minimum vector.</returns>
+ public readonly Vector3 Min(Vector3 with)
+ {
+ return new Vector3
+ (
+ Mathf.Min(X, with.X),
+ Mathf.Min(Y, with.Y),
+ Mathf.Min(Z, with.Z)
+ );
+ }
+
+ /// <summary>
/// Returns the axis of the vector's highest value. See <see cref="Axis"/>.
/// If all components are equal, this method returns <see cref="Axis.X"/>.
/// </summary>
@@ -643,7 +712,7 @@ namespace Godot
}
/// <summary>
- /// Returns this vector with each component snapped to the nearest multiple of <paramref name="step"/>.
+ /// Returns a new vector with each component snapped to the nearest multiple of the corresponding component in <paramref name="step"/>.
/// This can also be used to round to an arbitrary number of decimals.
/// </summary>
/// <param name="step">A vector value representing the step size to snap to.</param>
@@ -658,6 +727,22 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns a new vector with each component snapped to the nearest multiple of <paramref name="step"/>.
+ /// This can also be used to round to an arbitrary number of decimals.
+ /// </summary>
+ /// <param name="step">The step size to snap to.</param>
+ /// <returns>The snapped vector.</returns>
+ public readonly Vector3 Snapped(real_t step)
+ {
+ return new Vector3
+ (
+ Mathf.Snapped(X, step),
+ Mathf.Snapped(Y, step),
+ Mathf.Snapped(Z, step)
+ );
+ }
+
// Constants
private static readonly Vector3 _zero = new Vector3(0, 0, 0);
private static readonly Vector3 _one = new Vector3(1, 1, 1);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3I.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3I.cs
index aea46efc5b..8312e2c231 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3I.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3I.cs
@@ -133,6 +133,24 @@ namespace Godot
}
/// <summary>
+ /// Returns a new vector with all components clamped between the
+ /// <paramref name="min"/> and <paramref name="max"/> using
+ /// <see cref="Mathf.Clamp(int, int, int)"/>.
+ /// </summary>
+ /// <param name="min">The minimum allowed value.</param>
+ /// <param name="max">The maximum allowed value.</param>
+ /// <returns>The vector with all components clamped.</returns>
+ public readonly Vector3I Clamp(int min, int max)
+ {
+ return new Vector3I
+ (
+ Mathf.Clamp(X, min, max),
+ Mathf.Clamp(Y, min, max),
+ Mathf.Clamp(Z, min, max)
+ );
+ }
+
+ /// <summary>
/// Returns the squared distance between this vector and <paramref name="to"/>.
/// This method runs faster than <see cref="DistanceTo"/>, so prefer it if
/// you need to compare vectors or need the squared distance for some formula.
@@ -185,6 +203,74 @@ namespace Godot
}
/// <summary>
+ /// Returns the result of the component-wise maximum between
+ /// this vector and <paramref name="with"/>.
+ /// Equivalent to <c>new Vector3I(Mathf.Max(X, with.X), Mathf.Max(Y, with.Y), Mathf.Max(Z, with.Z))</c>.
+ /// </summary>
+ /// <param name="with">The other vector to use.</param>
+ /// <returns>The resulting maximum vector.</returns>
+ public readonly Vector3I Max(Vector3I with)
+ {
+ return new Vector3I
+ (
+ Mathf.Max(X, with.X),
+ Mathf.Max(Y, with.Y),
+ Mathf.Max(Z, with.Z)
+ );
+ }
+
+ /// <summary>
+ /// Returns the result of the component-wise maximum between
+ /// this vector and <paramref name="with"/>.
+ /// Equivalent to <c>new Vector3I(Mathf.Max(X, with), Mathf.Max(Y, with), Mathf.Max(Z, with))</c>.
+ /// </summary>
+ /// <param name="with">The other value to use.</param>
+ /// <returns>The resulting maximum vector.</returns>
+ public readonly Vector3I Max(int with)
+ {
+ return new Vector3I
+ (
+ Mathf.Max(X, with),
+ Mathf.Max(Y, with),
+ Mathf.Max(Z, with)
+ );
+ }
+
+ /// <summary>
+ /// Returns the result of the component-wise minimum between
+ /// this vector and <paramref name="with"/>.
+ /// Equivalent to <c>new Vector3I(Mathf.Min(X, with.X), Mathf.Min(Y, with.Y), Mathf.Min(Z, with.Z))</c>.
+ /// </summary>
+ /// <param name="with">The other vector to use.</param>
+ /// <returns>The resulting minimum vector.</returns>
+ public readonly Vector3I Min(Vector3I with)
+ {
+ return new Vector3I
+ (
+ Mathf.Min(X, with.X),
+ Mathf.Min(Y, with.Y),
+ Mathf.Min(Z, with.Z)
+ );
+ }
+
+ /// <summary>
+ /// Returns the result of the component-wise minimum between
+ /// this vector and <paramref name="with"/>.
+ /// Equivalent to <c>new Vector3I(Mathf.Min(X, with), Mathf.Min(Y, with), Mathf.Min(Z, with))</c>.
+ /// </summary>
+ /// <param name="with">The other value to use.</param>
+ /// <returns>The resulting minimum vector.</returns>
+ public readonly Vector3I Min(int with)
+ {
+ return new Vector3I
+ (
+ Mathf.Min(X, with),
+ Mathf.Min(Y, with),
+ Mathf.Min(Z, with)
+ );
+ }
+
+ /// <summary>
/// Returns the axis of the vector's highest value. See <see cref="Axis"/>.
/// If all components are equal, this method returns <see cref="Axis.X"/>.
/// </summary>
@@ -219,6 +305,36 @@ namespace Godot
return v;
}
+ /// <summary>
+ /// Returns a new vector with each component snapped to the closest multiple of the corresponding component in <paramref name="step"/>.
+ /// </summary>
+ /// <param name="step">A vector value representing the step size to snap to.</param>
+ /// <returns>The snapped vector.</returns>
+ public readonly Vector3I Snapped(Vector3I step)
+ {
+ return new Vector3I
+ (
+ (int)Mathf.Snapped((double)X, (double)step.X),
+ (int)Mathf.Snapped((double)Y, (double)step.Y),
+ (int)Mathf.Snapped((double)Z, (double)step.Z)
+ );
+ }
+
+ /// <summary>
+ /// Returns a new vector with each component snapped to the closest multiple of <paramref name="step"/>.
+ /// </summary>
+ /// <param name="step">The step size to snap to.</param>
+ /// <returns>The snapped vector.</returns>
+ public readonly Vector3I Snapped(int step)
+ {
+ return new Vector3I
+ (
+ (int)Mathf.Snapped((double)X, (double)step),
+ (int)Mathf.Snapped((double)Y, (double)step),
+ (int)Mathf.Snapped((double)Z, (double)step)
+ );
+ }
+
// Constants
private static readonly Vector3I _minValue = new Vector3I(int.MinValue, int.MinValue, int.MinValue);
private static readonly Vector3I _maxValue = new Vector3I(int.MaxValue, int.MaxValue, int.MaxValue);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs
index 7c4832943c..ec59197fa3 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs
@@ -177,6 +177,25 @@ namespace Godot
}
/// <summary>
+ /// Returns a new vector with all components clamped between the
+ /// <paramref name="min"/> and <paramref name="max"/> using
+ /// <see cref="Mathf.Clamp(real_t, real_t, real_t)"/>.
+ /// </summary>
+ /// <param name="min">The minimum allowed value.</param>
+ /// <param name="max">The maximum allowed value.</param>
+ /// <returns>The vector with all components clamped.</returns>
+ public readonly Vector4 Clamp(real_t min, real_t max)
+ {
+ return new Vector4
+ (
+ Mathf.Clamp(X, min, max),
+ Mathf.Clamp(Y, min, max),
+ Mathf.Clamp(Z, min, max),
+ Mathf.Clamp(W, min, max)
+ );
+ }
+
+ /// <summary>
/// Performs a cubic interpolation between vectors <paramref name="preA"/>, this vector,
/// <paramref name="b"/>, and <paramref name="postB"/>, by the given amount <paramref name="weight"/>.
/// </summary>
@@ -352,6 +371,78 @@ namespace Godot
}
/// <summary>
+ /// Returns the result of the component-wise maximum between
+ /// this vector and <paramref name="with"/>.
+ /// Equivalent to <c>new Vector4(Mathf.Max(X, with.X), Mathf.Max(Y, with.Y), Mathf.Max(Z, with.Z), Mathf.Max(W, with.W))</c>.
+ /// </summary>
+ /// <param name="with">The other vector to use.</param>
+ /// <returns>The resulting maximum vector.</returns>
+ public readonly Vector4 Max(Vector4 with)
+ {
+ return new Vector4
+ (
+ Mathf.Max(X, with.X),
+ Mathf.Max(Y, with.Y),
+ Mathf.Max(Z, with.Z),
+ Mathf.Max(W, with.W)
+ );
+ }
+
+ /// <summary>
+ /// Returns the result of the component-wise maximum between
+ /// this vector and <paramref name="with"/>.
+ /// Equivalent to <c>new Vector4(Mathf.Max(X, with), Mathf.Max(Y, with), Mathf.Max(Z, with), Mathf.Max(W, with))</c>.
+ /// </summary>
+ /// <param name="with">The other value to use.</param>
+ /// <returns>The resulting maximum vector.</returns>
+ public readonly Vector4 Max(real_t with)
+ {
+ return new Vector4
+ (
+ Mathf.Max(X, with),
+ Mathf.Max(Y, with),
+ Mathf.Max(Z, with),
+ Mathf.Max(W, with)
+ );
+ }
+
+ /// <summary>
+ /// Returns the result of the component-wise minimum between
+ /// this vector and <paramref name="with"/>.
+ /// Equivalent to <c>new Vector4(Mathf.Min(X, with.X), Mathf.Min(Y, with.Y), Mathf.Min(Z, with.Z), Mathf.Min(W, with.W))</c>.
+ /// </summary>
+ /// <param name="with">The other vector to use.</param>
+ /// <returns>The resulting minimum vector.</returns>
+ public readonly Vector4 Min(Vector4 with)
+ {
+ return new Vector4
+ (
+ Mathf.Min(X, with.X),
+ Mathf.Min(Y, with.Y),
+ Mathf.Min(Z, with.Z),
+ Mathf.Min(W, with.W)
+ );
+ }
+
+ /// <summary>
+ /// Returns the result of the component-wise minimum between
+ /// this vector and <paramref name="with"/>.
+ /// Equivalent to <c>new Vector4(Mathf.Min(X, with), Mathf.Min(Y, with), Mathf.Min(Z, with), Mathf.Min(W, with))</c>.
+ /// </summary>
+ /// <param name="with">The other value to use.</param>
+ /// <returns>The resulting minimum vector.</returns>
+ public readonly Vector4 Min(real_t with)
+ {
+ return new Vector4
+ (
+ Mathf.Min(X, with),
+ Mathf.Min(Y, with),
+ Mathf.Min(Z, with),
+ Mathf.Min(W, with)
+ );
+ }
+
+ /// <summary>
/// Returns the axis of the vector's highest value. See <see cref="Axis"/>.
/// If all components are equal, this method returns <see cref="Axis.X"/>.
/// </summary>
@@ -465,7 +556,7 @@ namespace Godot
}
/// <summary>
- /// Returns this vector with each component snapped to the nearest multiple of <paramref name="step"/>.
+ /// Returns a new vector with each component snapped to the nearest multiple of the corresponding component in <paramref name="step"/>.
/// This can also be used to round to an arbitrary number of decimals.
/// </summary>
/// <param name="step">A vector value representing the step size to snap to.</param>
@@ -480,6 +571,22 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns a new vector with each component snapped to the nearest multiple of <paramref name="step"/>.
+ /// This can also be used to round to an arbitrary number of decimals.
+ /// </summary>
+ /// <param name="step">The step size to snap to.</param>
+ /// <returns>The snapped vector.</returns>
+ public readonly Vector4 Snapped(real_t step)
+ {
+ return new Vector4(
+ Mathf.Snapped(X, step),
+ Mathf.Snapped(Y, step),
+ Mathf.Snapped(Z, step),
+ Mathf.Snapped(W, step)
+ );
+ }
+
// Constants
private static readonly Vector4 _zero = new Vector4(0, 0, 0, 0);
private static readonly Vector4 _one = new Vector4(1, 1, 1, 1);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4I.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4I.cs
index 27aa86b7e4..ba8e54b88b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4I.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4I.cs
@@ -150,6 +150,25 @@ namespace Godot
}
/// <summary>
+ /// Returns a new vector with all components clamped between
+ /// <paramref name="min"/> and <paramref name="max"/> using
+ /// <see cref="Mathf.Clamp(int, int, int)"/>.
+ /// </summary>
+ /// <param name="min">The minimum allowed value.</param>
+ /// <param name="max">The maximum allowed value.</param>
+ /// <returns>The vector with all components clamped.</returns>
+ public readonly Vector4I Clamp(int min, int max)
+ {
+ return new Vector4I
+ (
+ Mathf.Clamp(X, min, max),
+ Mathf.Clamp(Y, min, max),
+ Mathf.Clamp(Z, min, max),
+ Mathf.Clamp(W, min, max)
+ );
+ }
+
+ /// <summary>
/// Returns the squared distance between this vector and <paramref name="to"/>.
/// This method runs faster than <see cref="DistanceTo"/>, so prefer it if
/// you need to compare vectors or need the squared distance for some formula.
@@ -204,6 +223,78 @@ namespace Godot
}
/// <summary>
+ /// Returns the result of the component-wise maximum between
+ /// this vector and <paramref name="with"/>.
+ /// Equivalent to <c>new Vector4I(Mathf.Max(X, with.X), Mathf.Max(Y, with.Y), Mathf.Max(Z, with.Z), Mathf.Max(W, with.W))</c>.
+ /// </summary>
+ /// <param name="with">The other vector to use.</param>
+ /// <returns>The resulting maximum vector.</returns>
+ public readonly Vector4I Max(Vector4I with)
+ {
+ return new Vector4I
+ (
+ Mathf.Max(X, with.X),
+ Mathf.Max(Y, with.Y),
+ Mathf.Max(Z, with.Z),
+ Mathf.Max(W, with.W)
+ );
+ }
+
+ /// <summary>
+ /// Returns the result of the component-wise maximum between
+ /// this vector and <paramref name="with"/>.
+ /// Equivalent to <c>new Vector4I(Mathf.Max(X, with), Mathf.Max(Y, with), Mathf.Max(Z, with), Mathf.Max(W, with))</c>.
+ /// </summary>
+ /// <param name="with">The other value to use.</param>
+ /// <returns>The resulting maximum vector.</returns>
+ public readonly Vector4I Max(int with)
+ {
+ return new Vector4I
+ (
+ Mathf.Max(X, with),
+ Mathf.Max(Y, with),
+ Mathf.Max(Z, with),
+ Mathf.Max(W, with)
+ );
+ }
+
+ /// <summary>
+ /// Returns the result of the component-wise minimum between
+ /// this vector and <paramref name="with"/>.
+ /// Equivalent to <c>new Vector4I(Mathf.Min(X, with.X), Mathf.Min(Y, with.Y), Mathf.Min(Z, with.Z), Mathf.Min(W, with.W))</c>.
+ /// </summary>
+ /// <param name="with">The other vector to use.</param>
+ /// <returns>The resulting minimum vector.</returns>
+ public readonly Vector4I Min(Vector4I with)
+ {
+ return new Vector4I
+ (
+ Mathf.Min(X, with.X),
+ Mathf.Min(Y, with.Y),
+ Mathf.Min(Z, with.Z),
+ Mathf.Min(W, with.W)
+ );
+ }
+
+ /// <summary>
+ /// Returns the result of the component-wise minimum between
+ /// this vector and <paramref name="with"/>.
+ /// Equivalent to <c>new Vector4I(Mathf.Min(X, with), Mathf.Min(Y, with), Mathf.Min(Z, with), Mathf.Min(W, with))</c>.
+ /// </summary>
+ /// <param name="with">The other value to use.</param>
+ /// <returns>The resulting minimum vector.</returns>
+ public readonly Vector4I Min(int with)
+ {
+ return new Vector4I
+ (
+ Mathf.Min(X, with),
+ Mathf.Min(Y, with),
+ Mathf.Min(Z, with),
+ Mathf.Min(W, with)
+ );
+ }
+
+ /// <summary>
/// Returns the axis of the vector's highest value. See <see cref="Axis"/>.
/// If all components are equal, this method returns <see cref="Axis.X"/>.
/// </summary>
@@ -254,6 +345,36 @@ namespace Godot
return new Vector4I(Mathf.Sign(X), Mathf.Sign(Y), Mathf.Sign(Z), Mathf.Sign(W));
}
+ /// <summary>
+ /// Returns a new vector with each component snapped to the closest multiple of the corresponding component in <paramref name="step"/>.
+ /// </summary>
+ /// <param name="step">A vector value representing the step size to snap to.</param>
+ /// <returns>The snapped vector.</returns>
+ public readonly Vector4I Snapped(Vector4I step)
+ {
+ return new Vector4I(
+ (int)Mathf.Snapped((double)X, (double)step.X),
+ (int)Mathf.Snapped((double)Y, (double)step.Y),
+ (int)Mathf.Snapped((double)Z, (double)step.Z),
+ (int)Mathf.Snapped((double)W, (double)step.W)
+ );
+ }
+
+ /// <summary>
+ /// Returns a new vector with each component snapped to the closest multiple of <paramref name="step"/>.
+ /// </summary>
+ /// <param name="step">The step size to snap to.</param>
+ /// <returns>The snapped vector.</returns>
+ public readonly Vector4I Snapped(int step)
+ {
+ return new Vector4I(
+ (int)Mathf.Snapped((double)X, (double)step),
+ (int)Mathf.Snapped((double)Y, (double)step),
+ (int)Mathf.Snapped((double)Z, (double)step),
+ (int)Mathf.Snapped((double)W, (double)step)
+ );
+ }
+
// Constants
private static readonly Vector4I _minValue = new Vector4I(int.MinValue, int.MinValue, int.MinValue, int.MinValue);
private static readonly Vector4I _maxValue = new Vector4I(int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
index 67282416ed..6b25087c93 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
+++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{AEBF0036-DA76-4341-B651-A3F2856AB2FA}</ProjectGuid>
<OutputPath>bin/$(Configuration)</OutputPath>
diff --git a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj
index 8373edb9bf..4561fdaf2b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj
+++ b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{8FBEC238-D944-4074-8548-B3B524305905}</ProjectGuid>
<OutputPath>bin/$(Configuration)</OutputPath>
diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp
index 4bb324c0ee..1af462dafd 100644
--- a/modules/mono/glue/runtime_interop.cpp
+++ b/modules/mono/glue/runtime_interop.cpp
@@ -461,6 +461,16 @@ godot_packed_array godotsharp_packed_vector3_array_new_mem_copy(const Vector3 *p
return ret;
}
+godot_packed_array godotsharp_packed_vector4_array_new_mem_copy(const Vector4 *p_src, int32_t p_length) {
+ godot_packed_array ret;
+ memnew_placement(&ret, PackedVector4Array);
+ PackedVector4Array *array = reinterpret_cast<PackedVector4Array *>(&ret);
+ array->resize(p_length);
+ Vector4 *dst = array->ptrw();
+ memcpy(dst, p_src, p_length * sizeof(Vector4));
+ return ret;
+}
+
godot_packed_array godotsharp_packed_color_array_new_mem_copy(const Color *p_src, int32_t p_length) {
godot_packed_array ret;
memnew_placement(&ret, PackedColorArray);
@@ -646,6 +656,10 @@ void godotsharp_variant_new_packed_vector3_array(godot_variant *r_dest, const Pa
memnew_placement(r_dest, Variant(*p_pv3a));
}
+void godotsharp_variant_new_packed_vector4_array(godot_variant *r_dest, const PackedVector4Array *p_pv4a) {
+ memnew_placement(r_dest, Variant(*p_pv4a));
+}
+
void godotsharp_variant_new_packed_color_array(godot_variant *r_dest, const PackedColorArray *p_pca) {
memnew_placement(r_dest, Variant(*p_pca));
}
@@ -886,6 +900,13 @@ godot_packed_array godotsharp_variant_as_packed_vector3_array(const Variant *p_s
return raw_dest;
}
+godot_packed_array godotsharp_variant_as_packed_vector4_array(const Variant *p_self) {
+ godot_packed_array raw_dest;
+ PackedVector4Array *dest = (PackedVector4Array *)&raw_dest;
+ memnew_placement(dest, PackedVector4Array(p_self->operator PackedVector4Array()));
+ return raw_dest;
+}
+
godot_packed_array godotsharp_variant_as_packed_color_array(const Variant *p_self) {
godot_packed_array raw_dest;
PackedColorArray *dest = (PackedColorArray *)&raw_dest;
@@ -974,6 +995,10 @@ void godotsharp_packed_vector3_array_destroy(PackedVector3Array *p_self) {
p_self->~PackedVector3Array();
}
+void godotsharp_packed_vector4_array_destroy(PackedVector4Array *p_self) {
+ p_self->~PackedVector4Array();
+}
+
void godotsharp_packed_color_array_destroy(PackedColorArray *p_self) {
p_self->~PackedColorArray();
}
@@ -1456,6 +1481,7 @@ static const void *unmanaged_callbacks[]{
(void *)godotsharp_packed_float64_array_new_mem_copy,
(void *)godotsharp_packed_vector2_array_new_mem_copy,
(void *)godotsharp_packed_vector3_array_new_mem_copy,
+ (void *)godotsharp_packed_vector4_array_new_mem_copy,
(void *)godotsharp_packed_color_array_new_mem_copy,
(void *)godotsharp_packed_string_array_add,
(void *)godotsharp_callable_new_with_delegate,
@@ -1484,6 +1510,7 @@ static const void *unmanaged_callbacks[]{
(void *)godotsharp_variant_new_packed_string_array,
(void *)godotsharp_variant_new_packed_vector2_array,
(void *)godotsharp_variant_new_packed_vector3_array,
+ (void *)godotsharp_variant_new_packed_vector4_array,
(void *)godotsharp_variant_new_packed_color_array,
(void *)godotsharp_variant_as_bool,
(void *)godotsharp_variant_as_int,
@@ -1520,6 +1547,7 @@ static const void *unmanaged_callbacks[]{
(void *)godotsharp_variant_as_packed_string_array,
(void *)godotsharp_variant_as_packed_vector2_array,
(void *)godotsharp_variant_as_packed_vector3_array,
+ (void *)godotsharp_variant_as_packed_vector4_array,
(void *)godotsharp_variant_as_packed_color_array,
(void *)godotsharp_variant_equals,
(void *)godotsharp_string_new_with_utf16_chars,
@@ -1538,6 +1566,7 @@ static const void *unmanaged_callbacks[]{
(void *)godotsharp_packed_string_array_destroy,
(void *)godotsharp_packed_vector2_array_destroy,
(void *)godotsharp_packed_vector3_array_destroy,
+ (void *)godotsharp_packed_vector4_array_destroy,
(void *)godotsharp_packed_color_array_destroy,
(void *)godotsharp_variant_destroy,
(void *)godotsharp_string_destroy,
diff --git a/modules/multiplayer/editor/multiplayer_editor_plugin.h b/modules/multiplayer/editor/multiplayer_editor_plugin.h
index a22144cdcf..e8ade539a7 100644
--- a/modules/multiplayer/editor/multiplayer_editor_plugin.h
+++ b/modules/multiplayer/editor/multiplayer_editor_plugin.h
@@ -31,8 +31,8 @@
#ifndef MULTIPLAYER_EDITOR_PLUGIN_H
#define MULTIPLAYER_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
#include "editor/plugins/editor_debugger_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class EditorNetworkProfiler;
class MultiplayerEditorDebugger : public EditorDebuggerPlugin {
diff --git a/modules/multiplayer/editor/replication_editor.cpp b/modules/multiplayer/editor/replication_editor.cpp
index 8453a41473..73e53a6a07 100644
--- a/modules/multiplayer/editor/replication_editor.cpp
+++ b/modules/multiplayer/editor/replication_editor.cpp
@@ -234,7 +234,8 @@ ReplicationEditor::ReplicationEditor() {
Variant::PACKED_STRING_ARRAY,
Variant::PACKED_VECTOR2_ARRAY,
Variant::PACKED_VECTOR3_ARRAY,
- Variant::PACKED_COLOR_ARRAY
+ Variant::PACKED_COLOR_ARRAY,
+ Variant::PACKED_VECTOR4_ARRAY,
};
prop_selector->set_type_filter(types);
prop_selector->connect("selected", callable_mp(this, &ReplicationEditor::_pick_node_property_selected));
diff --git a/modules/multiplayer/editor/replication_editor.h b/modules/multiplayer/editor/replication_editor.h
index 80c1892ec3..8f11774292 100644
--- a/modules/multiplayer/editor/replication_editor.h
+++ b/modules/multiplayer/editor/replication_editor.h
@@ -33,7 +33,7 @@
#include "../scene_replication_config.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/box_container.h"
class ConfirmationDialog;
diff --git a/modules/navigation/2d/nav_mesh_generator_2d.cpp b/modules/navigation/2d/nav_mesh_generator_2d.cpp
index 15a645816c..2198158f9c 100644
--- a/modules/navigation/2d/nav_mesh_generator_2d.cpp
+++ b/modules/navigation/2d/nav_mesh_generator_2d.cpp
@@ -243,7 +243,7 @@ void NavMeshGenerator2D::generator_parse_geometry_node(Ref<NavigationPolygon> p_
generator_parse_multimeshinstance2d_node(p_navigation_mesh, p_source_geometry_data, p_node);
generator_parse_polygon2d_node(p_navigation_mesh, p_source_geometry_data, p_node);
generator_parse_staticbody2d_node(p_navigation_mesh, p_source_geometry_data, p_node);
- generator_parse_tilemap_node(p_navigation_mesh, p_source_geometry_data, p_node);
+ generator_parse_tile_map_layer_node(p_navigation_mesh, p_source_geometry_data, p_node);
generator_parse_navigationobstacle_node(p_navigation_mesh, p_source_geometry_data, p_node);
generator_rid_rwlock.read_lock();
@@ -259,6 +259,14 @@ void NavMeshGenerator2D::generator_parse_geometry_node(Ref<NavigationPolygon> p_
for (int i = 0; i < p_node->get_child_count(); i++) {
generator_parse_geometry_node(p_navigation_mesh, p_source_geometry_data, p_node->get_child(i), p_recurse_children);
}
+ } else if (Object::cast_to<TileMap>(p_node)) {
+ // Special case for TileMap, so that internal layer get parsed even if p_recurse_children is false.
+ for (int i = 0; i < p_node->get_child_count(); i++) {
+ TileMapLayer *tile_map_layer = Object::cast_to<TileMapLayer>(p_node->get_child(i));
+ if (tile_map_layer->get_index_in_tile_map() >= 0) {
+ generator_parse_tile_map_layer_node(p_navigation_mesh, p_source_geometry_data, tile_map_layer);
+ }
+ }
}
}
@@ -580,141 +588,102 @@ void NavMeshGenerator2D::generator_parse_staticbody2d_node(const Ref<NavigationP
}
}
-void NavMeshGenerator2D::generator_parse_tilemap_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node) {
- TileMap *tilemap = Object::cast_to<TileMap>(p_node);
-
- if (tilemap == nullptr) {
+void NavMeshGenerator2D::generator_parse_tile_map_layer_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node) {
+ TileMapLayer *tile_map_layer = Object::cast_to<TileMapLayer>(p_node);
+ if (tile_map_layer == nullptr) {
return;
}
- NavigationPolygon::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type();
- uint32_t parsed_collision_mask = p_navigation_mesh->get_parsed_collision_mask();
-
- if (tilemap->get_layers_count() <= 0) {
- return;
- }
-
- Ref<TileSet> tile_set = tilemap->get_tileset();
+ Ref<TileSet> tile_set = tile_map_layer->get_tile_set();
if (!tile_set.is_valid()) {
return;
}
int physics_layers_count = tile_set->get_physics_layers_count();
int navigation_layers_count = tile_set->get_navigation_layers_count();
-
if (physics_layers_count <= 0 && navigation_layers_count <= 0) {
return;
}
- HashSet<Vector2i> cells_with_navigation_polygon;
- HashSet<Vector2i> cells_with_collision_polygon;
+ NavigationPolygon::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type();
+ uint32_t parsed_collision_mask = p_navigation_mesh->get_parsed_collision_mask();
+
+ const Transform2D tilemap_xform = p_source_geometry_data->root_node_transform * tile_map_layer->get_global_transform();
- const Transform2D tilemap_xform = p_source_geometry_data->root_node_transform * tilemap->get_global_transform();
+ TypedArray<Vector2i> used_cells = tile_map_layer->get_used_cells();
+ for (int used_cell_index = 0; used_cell_index < used_cells.size(); used_cell_index++) {
+ const Vector2i &cell = used_cells[used_cell_index];
-#ifdef DEBUG_ENABLED
- int error_print_counter = 0;
- int error_print_max = 10;
-#endif // DEBUG_ENABLED
+ const TileData *tile_data = tile_map_layer->get_cell_tile_data(cell);
+ if (tile_data == nullptr) {
+ continue;
+ }
- for (int tilemap_layer = 0; tilemap_layer < tilemap->get_layers_count(); tilemap_layer++) {
- TypedArray<Vector2i> used_cells = tilemap->get_used_cells(tilemap_layer);
+ // Transform flags.
+ const int alternative_id = tile_map_layer->get_cell_alternative_tile(cell);
+ bool flip_h = (alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_H);
+ bool flip_v = (alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_V);
+ bool transpose = (alternative_id & TileSetAtlasSource::TRANSFORM_TRANSPOSE);
+
+ Transform2D tile_transform;
+ tile_transform.set_origin(tile_map_layer->map_to_local(cell));
+
+ const Transform2D tile_transform_offset = tilemap_xform * tile_transform;
+
+ // Parse traversable polygons.
+ for (int navigation_layer = 0; navigation_layer < navigation_layers_count; navigation_layer++) {
+ Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(navigation_layer, flip_h, flip_v, transpose);
+ if (navigation_polygon.is_valid()) {
+ for (int outline_index = 0; outline_index < navigation_polygon->get_outline_count(); outline_index++) {
+ const Vector<Vector2> &navigation_polygon_outline = navigation_polygon->get_outline(outline_index);
+ if (navigation_polygon_outline.is_empty()) {
+ continue;
+ }
- for (int used_cell_index = 0; used_cell_index < used_cells.size(); used_cell_index++) {
- const Vector2i &cell = used_cells[used_cell_index];
+ Vector<Vector2> traversable_outline;
+ traversable_outline.resize(navigation_polygon_outline.size());
- const TileData *tile_data = tilemap->get_cell_tile_data(tilemap_layer, cell, false);
- if (tile_data == nullptr) {
- continue;
- }
+ const Vector2 *navigation_polygon_outline_ptr = navigation_polygon_outline.ptr();
+ Vector2 *traversable_outline_ptrw = traversable_outline.ptrw();
- // Transform flags.
- const int alternative_id = tilemap->get_cell_alternative_tile(tilemap_layer, cell, false);
- bool flip_h = (alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_H);
- bool flip_v = (alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_V);
- bool transpose = (alternative_id & TileSetAtlasSource::TRANSFORM_TRANSPOSE);
-
- Transform2D tile_transform;
- tile_transform.set_origin(tilemap->map_to_local(cell));
-
- const Transform2D tile_transform_offset = tilemap_xform * tile_transform;
-
- if (navigation_layers_count > 0) {
- Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(tilemap_layer, flip_h, flip_v, transpose);
- if (navigation_polygon.is_valid()) {
- if (cells_with_navigation_polygon.has(cell)) {
-#ifdef DEBUG_ENABLED
- error_print_counter++;
- if (error_print_counter <= error_print_max) {
- WARN_PRINT(vformat("TileMap navigation mesh baking error. The TileMap cell key Vector2i(%s, %s) has navigation mesh from 2 or more different TileMap layers assigned. This can cause unexpected navigation mesh baking results. The duplicated cell data was ignored.", cell.x, cell.y));
- }
-#endif // DEBUG_ENABLED
- } else {
- cells_with_navigation_polygon.insert(cell);
-
- for (int outline_index = 0; outline_index < navigation_polygon->get_outline_count(); outline_index++) {
- const Vector<Vector2> &navigation_polygon_outline = navigation_polygon->get_outline(outline_index);
- if (navigation_polygon_outline.size() == 0) {
- continue;
- }
-
- Vector<Vector2> traversable_outline;
- traversable_outline.resize(navigation_polygon_outline.size());
-
- const Vector2 *navigation_polygon_outline_ptr = navigation_polygon_outline.ptr();
- Vector2 *traversable_outline_ptrw = traversable_outline.ptrw();
-
- for (int traversable_outline_index = 0; traversable_outline_index < traversable_outline.size(); traversable_outline_index++) {
- traversable_outline_ptrw[traversable_outline_index] = tile_transform_offset.xform(navigation_polygon_outline_ptr[traversable_outline_index]);
- }
-
- p_source_geometry_data->_add_traversable_outline(traversable_outline);
- }
+ for (int traversable_outline_index = 0; traversable_outline_index < traversable_outline.size(); traversable_outline_index++) {
+ traversable_outline_ptrw[traversable_outline_index] = tile_transform_offset.xform(navigation_polygon_outline_ptr[traversable_outline_index]);
}
+
+ p_source_geometry_data->_add_traversable_outline(traversable_outline);
}
}
+ }
- if (physics_layers_count > 0 && (parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_STATIC_COLLIDERS || parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_BOTH) && (tile_set->get_physics_layer_collision_layer(tilemap_layer) & parsed_collision_mask)) {
- if (cells_with_collision_polygon.has(cell)) {
-#ifdef DEBUG_ENABLED
- error_print_counter++;
- if (error_print_counter <= error_print_max) {
- WARN_PRINT(vformat("TileMap navigation mesh baking error. The cell key Vector2i(%s, %s) has collision polygons from 2 or more different TileMap layers assigned that all match the parsed collision mask. This can cause unexpected navigation mesh baking results. The duplicated cell data was ignored.", cell.x, cell.y));
+ // Parse obstacles.
+ for (int physics_layer = 0; physics_layer < physics_layers_count; physics_layer++) {
+ if ((parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_STATIC_COLLIDERS || parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_BOTH) &&
+ (tile_set->get_physics_layer_collision_layer(physics_layer) & parsed_collision_mask)) {
+ for (int collision_polygon_index = 0; collision_polygon_index < tile_data->get_collision_polygons_count(physics_layer); collision_polygon_index++) {
+ PackedVector2Array collision_polygon_points = tile_data->get_collision_polygon_points(physics_layer, collision_polygon_index);
+ if (collision_polygon_points.is_empty()) {
+ continue;
}
-#endif // DEBUG_ENABLED
- } else {
- cells_with_collision_polygon.insert(cell);
-
- for (int collision_polygon_index = 0; collision_polygon_index < tile_data->get_collision_polygons_count(tilemap_layer); collision_polygon_index++) {
- PackedVector2Array collision_polygon_points = tile_data->get_collision_polygon_points(tilemap_layer, collision_polygon_index);
- if (collision_polygon_points.size() == 0) {
- continue;
- }
-
- if (flip_h || flip_v || transpose) {
- collision_polygon_points = TileData::get_transformed_vertices(collision_polygon_points, flip_h, flip_v, transpose);
- }
- Vector<Vector2> obstruction_outline;
- obstruction_outline.resize(collision_polygon_points.size());
+ if (flip_h || flip_v || transpose) {
+ collision_polygon_points = TileData::get_transformed_vertices(collision_polygon_points, flip_h, flip_v, transpose);
+ }
- const Vector2 *collision_polygon_points_ptr = collision_polygon_points.ptr();
- Vector2 *obstruction_outline_ptrw = obstruction_outline.ptrw();
+ Vector<Vector2> obstruction_outline;
+ obstruction_outline.resize(collision_polygon_points.size());
- for (int obstruction_outline_index = 0; obstruction_outline_index < obstruction_outline.size(); obstruction_outline_index++) {
- obstruction_outline_ptrw[obstruction_outline_index] = tile_transform_offset.xform(collision_polygon_points_ptr[obstruction_outline_index]);
- }
+ const Vector2 *collision_polygon_points_ptr = collision_polygon_points.ptr();
+ Vector2 *obstruction_outline_ptrw = obstruction_outline.ptrw();
- p_source_geometry_data->_add_obstruction_outline(obstruction_outline);
+ for (int obstruction_outline_index = 0; obstruction_outline_index < obstruction_outline.size(); obstruction_outline_index++) {
+ obstruction_outline_ptrw[obstruction_outline_index] = tile_transform_offset.xform(collision_polygon_points_ptr[obstruction_outline_index]);
}
+
+ p_source_geometry_data->_add_obstruction_outline(obstruction_outline);
}
}
}
}
-#ifdef DEBUG_ENABLED
- if (error_print_counter > error_print_max) {
- ERR_PRINT(vformat("TileMap navigation mesh baking error. A total of %s cells with navigation or collision polygons from 2 or more different TileMap layers overlap. This can cause unexpected navigation mesh baking results. The duplicated cell data was ignored.", error_print_counter));
- }
-#endif // DEBUG_ENABLED
}
void NavMeshGenerator2D::generator_parse_navigationobstacle_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node) {
diff --git a/modules/navigation/2d/nav_mesh_generator_2d.h b/modules/navigation/2d/nav_mesh_generator_2d.h
index 235a84d548..d5f9694242 100644
--- a/modules/navigation/2d/nav_mesh_generator_2d.h
+++ b/modules/navigation/2d/nav_mesh_generator_2d.h
@@ -89,7 +89,7 @@ class NavMeshGenerator2D : public Object {
static void generator_parse_multimeshinstance2d_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
static void generator_parse_polygon2d_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
static void generator_parse_staticbody2d_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
- static void generator_parse_tilemap_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
+ static void generator_parse_tile_map_layer_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
static void generator_parse_navigationobstacle_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
static bool generator_emit_callback(const Callable &p_callback);
diff --git a/modules/navigation/editor/navigation_mesh_editor_plugin.h b/modules/navigation/editor/navigation_mesh_editor_plugin.h
index b73d8d2e69..6114c62ebf 100644
--- a/modules/navigation/editor/navigation_mesh_editor_plugin.h
+++ b/modules/navigation/editor/navigation_mesh_editor_plugin.h
@@ -33,7 +33,7 @@
#ifdef TOOLS_ENABLED
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class AcceptDialog;
class Button;
diff --git a/modules/noise/editor/noise_editor_plugin.h b/modules/noise/editor/noise_editor_plugin.h
index 948ccba29b..aa94cf4d23 100644
--- a/modules/noise/editor/noise_editor_plugin.h
+++ b/modules/noise/editor/noise_editor_plugin.h
@@ -33,7 +33,7 @@
#ifdef TOOLS_ENABLED
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class NoiseEditorPlugin : public EditorPlugin {
GDCLASS(NoiseEditorPlugin, EditorPlugin)
diff --git a/modules/noise/register_types.cpp b/modules/noise/register_types.cpp
index 29eb42522f..363b7bdc31 100644
--- a/modules/noise/register_types.cpp
+++ b/modules/noise/register_types.cpp
@@ -40,7 +40,7 @@
#endif
#ifdef TOOLS_ENABLED
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#endif
void initialize_noise_module(ModuleInitializationLevel p_level) {
diff --git a/modules/openxr/action_map/openxr_action_map.cpp b/modules/openxr/action_map/openxr_action_map.cpp
index bbcb63a7e6..ba0e4f6cdd 100644
--- a/modules/openxr/action_map/openxr_action_map.cpp
+++ b/modules/openxr/action_map/openxr_action_map.cpp
@@ -167,11 +167,11 @@ void OpenXRActionMap::create_default_action_sets() {
// we still want it to be part of our action map as we may deploy the same game to platforms that do and don't support it.
// - the same applies for interaction profiles that are only supported if the relevant extension is supported.
- // Create our Godot action set
+ // Create our Godot action set.
Ref<OpenXRActionSet> action_set = OpenXRActionSet::new_action_set("godot", "Godot action set");
add_action_set(action_set);
- // Create our actions
+ // Create our actions.
Ref<OpenXRAction> trigger = action_set->add_new_action("trigger", "Trigger", OpenXRAction::OPENXR_ACTION_FLOAT, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> trigger_click = action_set->add_new_action("trigger_click", "Trigger click", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> trigger_touch = action_set->add_new_action("trigger_touch", "Trigger touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
@@ -193,7 +193,7 @@ void OpenXRActionMap::create_default_action_sets() {
Ref<OpenXRAction> default_pose = action_set->add_new_action("default_pose", "Default pose", OpenXRAction::OPENXR_ACTION_POSE,
"/user/hand/left,"
"/user/hand/right,"
- // "/user/vive_tracker_htcx/role/handheld_object," <-- getting errors on this one
+ // "/user/vive_tracker_htcx/role/handheld_object," <-- getting errors on this one.
"/user/vive_tracker_htcx/role/left_foot,"
"/user/vive_tracker_htcx/role/right_foot,"
"/user/vive_tracker_htcx/role/left_shoulder,"
@@ -213,7 +213,7 @@ void OpenXRActionMap::create_default_action_sets() {
Ref<OpenXRAction> haptic = action_set->add_new_action("haptic", "Haptic", OpenXRAction::OPENXR_ACTION_HAPTIC,
"/user/hand/left,"
"/user/hand/right,"
- // "/user/vive_tracker_htcx/role/handheld_object," <-- getting errors on this one
+ // "/user/vive_tracker_htcx/role/handheld_object," <-- getting errors on this one.
"/user/vive_tracker_htcx/role/left_foot,"
"/user/vive_tracker_htcx/role/right_foot,"
"/user/vive_tracker_htcx/role/left_shoulder,"
@@ -227,7 +227,7 @@ void OpenXRActionMap::create_default_action_sets() {
"/user/vive_tracker_htcx/role/camera,"
"/user/vive_tracker_htcx/role/keyboard");
- // Create our interaction profiles
+ // Create our interaction profiles.
Ref<OpenXRInteractionProfile> profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/khr/simple_controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
@@ -235,11 +235,11 @@ void OpenXRActionMap::create_default_action_sets() {
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click");
profile->add_new_binding(select_button, "/user/hand/left/input/select/click,/user/hand/right/input/select/click");
- // generic has no support for triggers, grip, A/B buttons, nor joystick/trackpad inputs
+ // generic has no support for triggers, grip, A/B buttons, nor joystick/trackpad inputs.
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
- // Create our Vive controller profile
+ // Create our Vive controller profile.
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
@@ -247,64 +247,64 @@ void OpenXRActionMap::create_default_action_sets() {
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click");
profile->add_new_binding(select_button, "/user/hand/left/input/system/click,/user/hand/right/input/system/click");
- // wmr controller has no a/b/x/y buttons
+ // wmr controller has no a/b/x/y buttons.
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/click,/user/hand/right/input/trigger/click");
- profile->add_new_binding(grip, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click"); // OpenXR will convert bool to float
+ profile->add_new_binding(grip, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click"); // OpenXR will convert bool to float.
profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click");
- // primary on our vive controller is our trackpad
+ // primary on our vive controller is our trackpad.
profile->add_new_binding(primary, "/user/hand/left/input/trackpad,/user/hand/right/input/trackpad");
profile->add_new_binding(primary_click, "/user/hand/left/input/trackpad/click,/user/hand/right/input/trackpad/click");
profile->add_new_binding(primary_touch, "/user/hand/left/input/trackpad/touch,/user/hand/right/input/trackpad/touch");
- // vive controllers have no secondary input
+ // vive controllers have no secondary input.
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
- // Create our WMR controller profile
+ // Create our WMR controller profile.
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/microsoft/motion_controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
- // wmr controllers have no select button we can use
+ // wmr controllers have no select button we can use.
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click");
- // wmr controller has no a/b/x/y buttons
+ // wmr controller has no a/b/x/y buttons.
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
- profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value"); // OpenXR will convert float to bool
- profile->add_new_binding(grip, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click"); // OpenXR will convert bool to float
+ profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value"); // OpenXR will convert float to bool.
+ profile->add_new_binding(grip, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click"); // OpenXR will convert bool to float.
profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click");
- // primary on our wmr controller is our thumbstick, no touch
+ // primary on our wmr controller is our thumbstick, no touch.
profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
- // secondary on our wmr controller is our trackpad
+ // secondary on our wmr controller is our trackpad.
profile->add_new_binding(secondary, "/user/hand/left/input/trackpad,/user/hand/right/input/trackpad");
profile->add_new_binding(secondary_click, "/user/hand/left/input/trackpad/click,/user/hand/right/input/trackpad/click");
profile->add_new_binding(secondary_touch, "/user/hand/left/input/trackpad/touch,/user/hand/right/input/trackpad/touch");
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
- // Create our Meta touch controller profile
+ // Create our Meta touch controller profile.
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/oculus/touch_controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
- // touch controllers have no select button we can use
- profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/system/click"); // right hand system click may not be available
- profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand
+ // touch controllers have no select button we can use.
+ profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/system/click"); // right hand system click may not be available.
+ profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand.
profile->add_new_binding(ax_touch, "/user/hand/left/input/x/touch,/user/hand/right/input/a/touch");
- profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand
+ profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand.
profile->add_new_binding(by_touch, "/user/hand/left/input/y/touch,/user/hand/right/input/b/touch");
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
- profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value"); // should be converted to boolean
+ profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value"); // should be converted to boolean.
profile->add_new_binding(trigger_touch, "/user/hand/left/input/trigger/touch,/user/hand/right/input/trigger/touch");
- profile->add_new_binding(grip, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value"); // should be converted to boolean
+ profile->add_new_binding(grip, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value"); // should be converted to boolean.
profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value");
- // primary on our touch controller is our thumbstick
+ // primary on our touch controller is our thumbstick.
profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
profile->add_new_binding(primary_touch, "/user/hand/left/input/thumbstick/touch,/user/hand/right/input/thumbstick/touch");
- // touch controller has no secondary input
+ // touch controller has no secondary input.
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
@@ -314,73 +314,73 @@ void OpenXRActionMap::create_default_action_sets() {
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
- profile->add_new_binding(select_button, "/user/hand/left/input/system/click,/user/hand/right/input/system/click"); // system click may not be available
+ profile->add_new_binding(select_button, "/user/hand/left/input/system/click,/user/hand/right/input/system/click"); // system click may not be available.
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click");
- profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand
+ profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand.
profile->add_new_binding(ax_touch, "/user/hand/left/input/x/touch,/user/hand/right/input/a/touch");
- profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand
+ profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand.
profile->add_new_binding(by_touch, "/user/hand/left/input/y/touch,/user/hand/right/input/b/touch");
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
- profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value"); // should be converted to boolean
+ profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value"); // should be converted to boolean.
profile->add_new_binding(trigger_touch, "/user/hand/left/input/trigger/touch,/user/hand/right/input/trigger/touch");
- profile->add_new_binding(grip, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value"); // should be converted to boolean
+ profile->add_new_binding(grip, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value"); // should be converted to boolean.
profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value");
- // primary on our pico controller is our thumbstick
+ // primary on our pico controller is our thumbstick.
profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
profile->add_new_binding(primary_touch, "/user/hand/left/input/thumbstick/touch,/user/hand/right/input/thumbstick/touch");
- // pico controller has no secondary input
+ // pico controller has no secondary input.
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
- // Create our Valve index controller profile
+ // Create our Valve index controller profile.
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/valve/index_controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
- // index controllers have no select button we can use
+ // index controllers have no select button we can use.
profile->add_new_binding(menu_button, "/user/hand/left/input/system/click,/user/hand/right/input/system/click");
- profile->add_new_binding(ax_button, "/user/hand/left/input/a/click,/user/hand/right/input/a/click"); // a on both controllers
+ profile->add_new_binding(ax_button, "/user/hand/left/input/a/click,/user/hand/right/input/a/click"); // a on both controllers.
profile->add_new_binding(ax_touch, "/user/hand/left/input/a/touch,/user/hand/right/input/a/touch");
- profile->add_new_binding(by_button, "/user/hand/left/input/b/click,/user/hand/right/input/b/click"); // b on both controllers
+ profile->add_new_binding(by_button, "/user/hand/left/input/b/click,/user/hand/right/input/b/click"); // b on both controllers.
profile->add_new_binding(by_touch, "/user/hand/left/input/b/touch,/user/hand/right/input/b/touch");
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/click,/user/hand/right/input/trigger/click");
profile->add_new_binding(trigger_touch, "/user/hand/left/input/trigger/touch,/user/hand/right/input/trigger/touch");
profile->add_new_binding(grip, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value");
- profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value"); // this should do a float to bool conversion
- profile->add_new_binding(grip_force, "/user/hand/left/input/squeeze/force,/user/hand/right/input/squeeze/force"); // grip force seems to be unique to the Valve Index
- // primary on our index controller is our thumbstick
+ profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value"); // this should do a float to bool conversion.
+ profile->add_new_binding(grip_force, "/user/hand/left/input/squeeze/force,/user/hand/right/input/squeeze/force"); // grip force seems to be unique to the Valve Index.
+ // primary on our index controller is our thumbstick.
profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
profile->add_new_binding(primary_touch, "/user/hand/left/input/thumbstick/touch,/user/hand/right/input/thumbstick/touch");
- // secondary on our index controller is our trackpad
+ // secondary on our index controller is our trackpad.
profile->add_new_binding(secondary, "/user/hand/left/input/trackpad,/user/hand/right/input/trackpad");
profile->add_new_binding(secondary_click, "/user/hand/left/input/trackpad/force,/user/hand/right/input/trackpad/force"); // not sure if this will work but doesn't seem to support click...
profile->add_new_binding(secondary_touch, "/user/hand/left/input/trackpad/touch,/user/hand/right/input/trackpad/touch");
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
- // Create our HP MR controller profile
+ // Create our HP MR controller profile.
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/hp/mixed_reality_controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
- // hpmr controllers have no select button we can use
+ // hpmr controllers have no select button we can use.
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click");
- // hpmr controllers only register click, not touch, on our a/b/x/y buttons
- profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand
- profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand
+ // hpmr controllers only register click, not touch, on our a/b/x/y buttons.
+ profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand.
+ profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand.
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(grip, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value");
profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value");
- // primary on our hpmr controller is our thumbstick
+ // primary on our hpmr controller is our thumbstick.
profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
- // No secondary on our hpmr controller
+ // No secondary on our hpmr controller.
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
@@ -391,72 +391,72 @@ void OpenXRActionMap::create_default_action_sets() {
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
- // Odyssey controllers have no select button we can use
+ // Odyssey controllers have no select button we can use.
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click");
- // Odyssey controller has no a/b/x/y buttons
+ // Odyssey controller has no a/b/x/y buttons.
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(grip, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click");
profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click");
- // primary on our Odyssey controller is our thumbstick, no touch
+ // primary on our Odyssey controller is our thumbstick, no touch.
profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
- // secondary on our Odyssey controller is our trackpad
+ // secondary on our Odyssey controller is our trackpad.
profile->add_new_binding(secondary, "/user/hand/left/input/trackpad,/user/hand/right/input/trackpad");
profile->add_new_binding(secondary_click, "/user/hand/left/input/trackpad/click,/user/hand/right/input/trackpad/click");
profile->add_new_binding(secondary_touch, "/user/hand/left/input/trackpad/touch,/user/hand/right/input/trackpad/touch");
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
- // Create our Vive Cosmos controller
+ // Create our Vive Cosmos controller.
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_cosmos_controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click");
- profile->add_new_binding(select_button, "/user/hand/right/input/system/click"); // we'll map system to select
- profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand
- profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand
+ profile->add_new_binding(select_button, "/user/hand/right/input/system/click"); // we'll map system to select.
+ profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand.
+ profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand.
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/click,/user/hand/right/input/trigger/click");
profile->add_new_binding(grip, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click");
profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click");
- // primary on our Cosmos controller is our thumbstick
+ // primary on our Cosmos controller is our thumbstick.
profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
profile->add_new_binding(primary_touch, "/user/hand/left/input/thumbstick/touch,/user/hand/right/input/thumbstick/touch");
- // No secondary on our cosmos controller
+ // No secondary on our cosmos controller.
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
- // Create our Vive Focus 3 controller
+ // Create our Vive Focus 3 controller.
// Note, Vive Focus 3 currently is not yet supported as a stand alone device
- // however HTC currently has a beta OpenXR runtime in testing we may support in the near future
+ // however HTC currently has a beta OpenXR runtime in testing we may support in the near future.
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_focus3_controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click");
- profile->add_new_binding(select_button, "/user/hand/right/input/system/click"); // we'll map system to select
- profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand
- profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand
+ profile->add_new_binding(select_button, "/user/hand/right/input/system/click"); // we'll map system to select.
+ profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand.
+ profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand.
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/click,/user/hand/right/input/trigger/click");
profile->add_new_binding(trigger_touch, "/user/hand/left/input/trigger/touch,/user/hand/right/input/trigger/touch");
profile->add_new_binding(grip, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click");
profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click");
- // primary on our Focus 3 controller is our thumbstick
+ // primary on our Focus 3 controller is our thumbstick.
profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
profile->add_new_binding(primary_touch, "/user/hand/left/input/thumbstick/touch,/user/hand/right/input/thumbstick/touch");
- // We only have a thumb rest
+ // We only have a thumb rest.
profile->add_new_binding(secondary_touch, "/user/hand/left/input/thumbrest/touch,/user/hand/right/input/thumbrest/touch");
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
- // Create our Huawei controller
+ // Create our Huawei controller.
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/huawei/controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
@@ -465,17 +465,17 @@ void OpenXRActionMap::create_default_action_sets() {
profile->add_new_binding(menu_button, "/user/hand/left/input/home/click,/user/hand/right/input/home/click");
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/click,/user/hand/right/input/trigger/click");
- // primary on our Huawei controller is our trackpad
+ // primary on our Huawei controller is our trackpad.
profile->add_new_binding(primary, "/user/hand/left/input/trackpad,/user/hand/right/input/trackpad");
profile->add_new_binding(primary_click, "/user/hand/left/input/trackpad/click,/user/hand/right/input/trackpad/click");
profile->add_new_binding(primary_touch, "/user/hand/left/input/trackpad/touch,/user/hand/right/input/trackpad/touch");
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
- // Create our HTC Vive tracker profile
+ // Create our HTC Vive tracker profile.
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_tracker_htcx");
profile->add_new_binding(default_pose,
- // "/user/vive_tracker_htcx/role/handheld_object/input/grip/pose," <-- getting errors on this one
+ // "/user/vive_tracker_htcx/role/handheld_object/input/grip/pose," <-- getting errors on this one.
"/user/vive_tracker_htcx/role/left_foot/input/grip/pose,"
"/user/vive_tracker_htcx/role/right_foot/input/grip/pose,"
"/user/vive_tracker_htcx/role/left_shoulder/input/grip/pose,"
@@ -489,7 +489,7 @@ void OpenXRActionMap::create_default_action_sets() {
"/user/vive_tracker_htcx/role/camera/input/grip/pose,"
"/user/vive_tracker_htcx/role/keyboard/input/grip/pose");
profile->add_new_binding(haptic,
- // "/user/vive_tracker_htcx/role/handheld_object/output/haptic," <-- getting errors on this one
+ // "/user/vive_tracker_htcx/role/handheld_object/output/haptic," <-- getting errors on this one.
"/user/vive_tracker_htcx/role/left_foot/output/haptic,"
"/user/vive_tracker_htcx/role/right_foot/output/haptic,"
"/user/vive_tracker_htcx/role/left_shoulder/output/haptic,"
@@ -504,10 +504,30 @@ void OpenXRActionMap::create_default_action_sets() {
"/user/vive_tracker_htcx/role/keyboard/output/haptic");
add_interaction_profile(profile);
- // Create our eye gaze interaction profile
+ // Create our eye gaze interaction profile.
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/ext/eye_gaze_interaction");
profile->add_new_binding(default_pose, "/user/eyes_ext/input/gaze_ext/pose");
add_interaction_profile(profile);
+
+ // Create our hand interaction profile.
+ profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/ext/hand_interaction_ext");
+ profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
+ profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
+ profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
+
+ // Use pinch as primary.
+ profile->add_new_binding(primary, "/user/hand/left/input/pinch_ext/value,/user/hand/right/input/pinch_ext/value");
+ profile->add_new_binding(primary_click, "/user/hand/left/input/pinch_ext/ready_ext,/user/hand/right/input/pinch_ext/ready_ext");
+
+ // Use activation as secondary.
+ profile->add_new_binding(secondary, "/user/hand/left/input/aim_activate_ext/value,/user/hand/right/input/aim_activate_ext/value");
+ profile->add_new_binding(secondary_click, "/user/hand/left/input/aim_activate_ext/ready_ext,/user/hand/right/input/aim_activate_ext/ready_ext");
+
+ // We link grasp to our grip.
+ profile->add_new_binding(grip, "/user/hand/left/input/grasp_ext/value,/user/hand/right/input/grasp_ext/value");
+ profile->add_new_binding(grip_click, "/user/hand/left/input/grasp_ext/ready_ext,/user/hand/right/input/grasp_ext/ready_ext");
+ add_interaction_profile(profile);
}
void OpenXRActionMap::create_editor_action_sets() {
diff --git a/modules/openxr/doc_classes/OpenXRAPIExtension.xml b/modules/openxr/doc_classes/OpenXRAPIExtension.xml
index f737f3b642..4419d24dd3 100644
--- a/modules/openxr/doc_classes/OpenXRAPIExtension.xml
+++ b/modules/openxr/doc_classes/OpenXRAPIExtension.xml
@@ -54,7 +54,7 @@
<method name="get_next_frame_time">
<return type="int" />
<description>
- Returns the timing for the next frame.
+ Returns the predicted display timing for the next frame.
</description>
</method>
<method name="get_play_space">
@@ -63,6 +63,12 @@
Returns the play space, which is an [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrSpace.html]XrSpace[/url] cast to an integer.
</description>
</method>
+ <method name="get_predicted_display_time">
+ <return type="int" />
+ <description>
+ Returns the predicted display timing for the current frame.
+ </description>
+ </method>
<method name="get_session">
<return type="int" />
<description>
diff --git a/modules/openxr/doc_classes/OpenXRInterface.xml b/modules/openxr/doc_classes/OpenXRInterface.xml
index 05dff7d6ae..309cbe0d72 100644
--- a/modules/openxr/doc_classes/OpenXRInterface.xml
+++ b/modules/openxr/doc_classes/OpenXRInterface.xml
@@ -106,6 +106,13 @@
[b]Note:[/b] This feature is only available on the compatibility renderer and currently only available on some stand alone headsets. For Vulkan set [member Viewport.vrs_mode] to [code]VRS_XR[/code] on desktop.
</description>
</method>
+ <method name="is_hand_interaction_supported" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if OpenXR's hand interaction profile is supported and enabled.
+ [b]Note:[/b] This only returns a valid value after OpenXR has been initialized.
+ </description>
+ </method>
<method name="is_hand_tracking_supported">
<return type="bool" />
<description>
@@ -145,8 +152,21 @@
<member name="render_target_size_multiplier" type="float" setter="set_render_target_size_multiplier" getter="get_render_target_size_multiplier" default="1.0">
The render size multiplier for the current HMD. Must be set before the interface has been initialized.
</member>
+ <member name="vrs_min_radius" type="float" setter="set_vrs_min_radius" getter="get_vrs_min_radius" default="20.0">
+ The minimum radius around the focal point where full quality is guaranteed if VRS is used as a percentage of screen size.
+ [b]Note:[/b] Mobile and Forward+ renderers only. Requires [member Viewport.vrs_mode] to be set to [constant Viewport.VRS_XR].
+ </member>
+ <member name="vrs_strength" type="float" setter="set_vrs_strength" getter="get_vrs_strength" default="1.0">
+ The strength used to calculate the VRS density map. The greater this value, the more noticeable VRS is. This improves performance at the cost of quality.
+ [b]Note:[/b] Mobile and Forward+ renderers only. Requires [member Viewport.vrs_mode] to be set to [constant Viewport.VRS_XR].
+ </member>
</members>
<signals>
+ <signal name="instance_exiting">
+ <description>
+ Informs our OpenXR instance is exiting.
+ </description>
+ </signal>
<signal name="pose_recentered">
<description>
Informs the user queued a recenter of the player position.
@@ -169,6 +189,11 @@
Informs our OpenXR session now has focus.
</description>
</signal>
+ <signal name="session_loss_pending">
+ <description>
+ Informs our OpenXR session is in the process of being lost.
+ </description>
+ </signal>
<signal name="session_stopping">
<description>
Informs our OpenXR session is stopping.
diff --git a/modules/openxr/editor/openxr_action_map_editor.h b/modules/openxr/editor/openxr_action_map_editor.h
index 22e8853c8c..cfe5fed095 100644
--- a/modules/openxr/editor/openxr_action_map_editor.h
+++ b/modules/openxr/editor/openxr_action_map_editor.h
@@ -36,8 +36,8 @@
#include "openxr_interaction_profile_editor.h"
#include "openxr_select_interaction_profile_dialog.h"
-#include "editor/editor_plugin.h"
#include "editor/editor_undo_redo_manager.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/label.h"
diff --git a/modules/openxr/editor/openxr_editor_plugin.h b/modules/openxr/editor/openxr_editor_plugin.h
index b80f20d049..672df0de28 100644
--- a/modules/openxr/editor/openxr_editor_plugin.h
+++ b/modules/openxr/editor/openxr_editor_plugin.h
@@ -34,7 +34,7 @@
#include "openxr_action_map_editor.h"
#include "openxr_select_runtime.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class OpenXREditorPlugin : public EditorPlugin {
GDCLASS(OpenXREditorPlugin, EditorPlugin);
diff --git a/modules/openxr/extensions/openxr_composition_layer_extension.cpp b/modules/openxr/extensions/openxr_composition_layer_extension.cpp
index 1fba8e5f8b..51f4a03d52 100644
--- a/modules/openxr/extensions/openxr_composition_layer_extension.cpp
+++ b/modules/openxr/extensions/openxr_composition_layer_extension.cpp
@@ -274,7 +274,7 @@ bool OpenXRViewportCompositionLayerProvider::update_and_acquire_swapchain(bool p
if (swapchain_size == viewport_size && !p_static_image && !static_image) {
// We're all good! Just acquire it.
// We can ignore should_render here, return will be false.
- XrBool32 should_render = true;
+ bool should_render = true;
return swapchain_info.acquire(should_render);
}
@@ -296,7 +296,7 @@ bool OpenXRViewportCompositionLayerProvider::update_and_acquire_swapchain(bool p
// Acquire our image so we can start rendering into it,
// we can ignore should_render here, ret will be false.
- XrBool32 should_render = true;
+ bool should_render = true;
bool ret = swapchain_info.acquire(should_render);
swapchain_size = viewport_size;
diff --git a/modules/openxr/extensions/openxr_eye_gaze_interaction.cpp b/modules/openxr/extensions/openxr_eye_gaze_interaction.cpp
index 477a1c2609..eea996edd9 100644
--- a/modules/openxr/extensions/openxr_eye_gaze_interaction.cpp
+++ b/modules/openxr/extensions/openxr_eye_gaze_interaction.cpp
@@ -34,6 +34,7 @@
#include "core/os/os.h"
#include "../action_map/openxr_interaction_profile_metadata.h"
+#include "../openxr_api.h"
OpenXREyeGazeInteractionExtension *OpenXREyeGazeInteractionExtension::singleton = nullptr;
@@ -106,3 +107,38 @@ void OpenXREyeGazeInteractionExtension::on_register_metadata() {
metadata->register_interaction_profile("Eye gaze", "/interaction_profiles/ext/eye_gaze_interaction", XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME);
metadata->register_io_path("/interaction_profiles/ext/eye_gaze_interaction", "Gaze pose", "/user/eyes_ext", "/user/eyes_ext/input/gaze_ext/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
}
+
+bool OpenXREyeGazeInteractionExtension::get_eye_gaze_pose(double p_dist, Vector3 &r_eye_pose) {
+ OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
+ ERR_FAIL_NULL_V(openxr_api, false);
+
+ if (!init_eye_gaze_pose) {
+ init_eye_gaze_pose = true;
+
+ eye_tracker = openxr_api->find_tracker("/user/eyes_ext");
+ if (eye_tracker.is_null()) {
+ WARN_PRINT("Couldn't obtain eye tracker");
+ }
+
+ eye_action = openxr_api->find_action("eye_gaze_pose");
+ if (eye_action.is_null()) {
+ WARN_PRINT("Couldn't obtain pose action for `eye_gaze_pose`, make sure to add this to your action map.");
+ }
+ }
+
+ if (eye_tracker.is_null() || eye_action.is_null()) {
+ return false;
+ }
+
+ Transform3D eye_transform;
+ Vector3 linear_velocity;
+ Vector3 angular_velocity;
+ XRPose::TrackingConfidence confidence = openxr_api->get_action_pose(eye_action, eye_tracker, eye_transform, linear_velocity, angular_velocity);
+ if (confidence == XRPose::XR_TRACKING_CONFIDENCE_NONE) {
+ return false;
+ }
+
+ r_eye_pose = eye_transform.origin + eye_transform.basis[2] * p_dist;
+
+ return true;
+}
diff --git a/modules/openxr/extensions/openxr_eye_gaze_interaction.h b/modules/openxr/extensions/openxr_eye_gaze_interaction.h
index 2b99f8edff..114c1aacc7 100644
--- a/modules/openxr/extensions/openxr_eye_gaze_interaction.h
+++ b/modules/openxr/extensions/openxr_eye_gaze_interaction.h
@@ -50,11 +50,17 @@ public:
virtual void on_register_metadata() override;
+ bool get_eye_gaze_pose(double p_dist, Vector3 &r_eye_pose);
+
private:
static OpenXREyeGazeInteractionExtension *singleton;
bool available = false;
XrSystemEyeGazeInteractionPropertiesEXT properties;
+
+ bool init_eye_gaze_pose = false;
+ RID eye_tracker;
+ RID eye_action;
};
#endif // OPENXR_EYE_GAZE_INTERACTION_H
diff --git a/modules/openxr/extensions/openxr_hand_interaction_extension.cpp b/modules/openxr/extensions/openxr_hand_interaction_extension.cpp
new file mode 100644
index 0000000000..65de4b23c4
--- /dev/null
+++ b/modules/openxr/extensions/openxr_hand_interaction_extension.cpp
@@ -0,0 +1,97 @@
+/**************************************************************************/
+/* openxr_hand_interaction_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "openxr_hand_interaction_extension.h"
+
+#include "../action_map/openxr_interaction_profile_metadata.h"
+#include "core/config/project_settings.h"
+
+OpenXRHandInteractionExtension *OpenXRHandInteractionExtension::singleton = nullptr;
+
+OpenXRHandInteractionExtension *OpenXRHandInteractionExtension::get_singleton() {
+ return singleton;
+}
+
+OpenXRHandInteractionExtension::OpenXRHandInteractionExtension() {
+ singleton = this;
+}
+
+OpenXRHandInteractionExtension::~OpenXRHandInteractionExtension() {
+ singleton = nullptr;
+}
+
+HashMap<String, bool *> OpenXRHandInteractionExtension::get_requested_extensions() {
+ HashMap<String, bool *> request_extensions;
+
+ // Only enable this extension when requested.
+ // We still register our meta data or the action map editor will fail.
+ if (GLOBAL_GET("xr/openxr/extensions/hand_interaction_profile")) {
+ request_extensions[XR_EXT_HAND_INTERACTION_EXTENSION_NAME] = &available;
+ }
+
+ return request_extensions;
+}
+
+bool OpenXRHandInteractionExtension::is_available() {
+ return available;
+}
+
+void OpenXRHandInteractionExtension::on_register_metadata() {
+ OpenXRInteractionProfileMetadata *metadata = OpenXRInteractionProfileMetadata::get_singleton();
+ ERR_FAIL_NULL(metadata);
+
+ // Hand interaction profile
+ metadata->register_interaction_profile("Hand interaction", "/interaction_profiles/ext/hand_interaction_ext", XR_EXT_HAND_INTERACTION_EXTENSION_NAME);
+ metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Grip pose", "/user/hand/left", "/user/hand/left/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Grip pose", "/user/hand/right", "/user/hand/right/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Aim pose", "/user/hand/left", "/user/hand/left/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Aim pose", "/user/hand/right", "/user/hand/right/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Pinch pose", "/user/hand/left", "/user/hand/left/input/pinch_ext/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Pinch pose", "/user/hand/right", "/user/hand/right/input/pinch_ext/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Poke pose", "/user/hand/left", "/user/hand/left/input/poke_ext/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Poke pose", "/user/hand/right", "/user/hand/right/input/poke_ext/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Palm pose", "/user/hand/left", "/user/hand/left/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Palm pose", "/user/hand/right", "/user/hand/right/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+
+ metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Pinch", "/user/hand/left", "/user/hand/left/input/pinch_ext/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Pinch", "/user/hand/right", "/user/hand/right/input/pinch_ext/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Pinch ready", "/user/hand/left", "/user/hand/left/input/pinch_ext/ready_ext", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Pinch ready", "/user/hand/right", "/user/hand/right/input/pinch_ext/ready_ext", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Aim activate", "/user/hand/left", "/user/hand/left/input/aim_activate_ext/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Aim activate", "/user/hand/right", "/user/hand/right/input/aim_activate_ext/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Aim activate ready", "/user/hand/left", "/user/hand/left/input/aim_activate_ext/ready_ext", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Aim activate ready", "/user/hand/right", "/user/hand/right/input/aim_activate_ext/ready_ext", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Grasp", "/user/hand/left", "/user/hand/left/input/grasp_ext/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Grasp", "/user/hand/right", "/user/hand/right/input/grasp_ext/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Grasp ready", "/user/hand/left", "/user/hand/left/input/grasp_ext/ready_ext", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Grasp ready", "/user/hand/right", "/user/hand/right/input/grasp_ext/ready_ext", "", OpenXRAction::OPENXR_ACTION_BOOL);
+}
diff --git a/modules/openxr/extensions/openxr_hand_interaction_extension.h b/modules/openxr/extensions/openxr_hand_interaction_extension.h
new file mode 100644
index 0000000000..789e300c0b
--- /dev/null
+++ b/modules/openxr/extensions/openxr_hand_interaction_extension.h
@@ -0,0 +1,72 @@
+/**************************************************************************/
+/* openxr_hand_interaction_extension.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef OPENXR_HAND_INTERACTION_EXTENSION_H
+#define OPENXR_HAND_INTERACTION_EXTENSION_H
+
+#include "openxr_extension_wrapper.h"
+
+// When supported the hand interaction extension introduces an interaction
+// profile that becomes active when the user either lets go of their
+// controllers or isn't using controllers at all.
+//
+// The OpenXR specification states that all XR runtimes that support this
+// interaction profile should also allow it's controller to use this
+// interaction profile.
+// This means that if you only supply this interaction profile in your
+// action map, it should work both when the player is holding a controller
+// or using visual hand tracking.
+//
+// This allows easier portability between games that use controller
+// tracking or hand tracking.
+//
+// See: https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_EXT_hand_interaction
+// for more information.
+
+class OpenXRHandInteractionExtension : public OpenXRExtensionWrapper {
+public:
+ static OpenXRHandInteractionExtension *get_singleton();
+
+ OpenXRHandInteractionExtension();
+ virtual ~OpenXRHandInteractionExtension() override;
+
+ virtual HashMap<String, bool *> get_requested_extensions() override;
+
+ bool is_available();
+
+ virtual void on_register_metadata() override;
+
+private:
+ static OpenXRHandInteractionExtension *singleton;
+
+ bool available = false;
+};
+
+#endif // OPENXR_HAND_INTERACTION_EXTENSION_H
diff --git a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
index 12fa3bed7e..b8a2f58935 100644
--- a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
+++ b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
@@ -128,7 +128,7 @@ void OpenXRHandTrackingExtension::on_process() {
}
// process our hands
- const XrTime time = OpenXRAPI::get_singleton()->get_next_frame_time(); // This data will be used for the next frame we render
+ const XrTime time = OpenXRAPI::get_singleton()->get_predicted_display_time();
if (time == 0) {
// we don't have timing info yet, or we're skipping a frame...
return;
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index 1fe402341b..8731a36865 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -54,6 +54,7 @@
#endif
#include "extensions/openxr_composition_layer_depth_extension.h"
+#include "extensions/openxr_eye_gaze_interaction.h"
#include "extensions/openxr_fb_display_refresh_rate_extension.h"
#include "extensions/openxr_fb_foveation_extension.h"
#include "extensions/openxr_fb_update_swapchain_extension.h"
@@ -160,7 +161,7 @@ void OpenXRAPI::OpenXRSwapChainInfo::free() {
}
}
-bool OpenXRAPI::OpenXRSwapChainInfo::acquire(XrBool32 &p_should_render) {
+bool OpenXRAPI::OpenXRSwapChainInfo::acquire(bool &p_should_render) {
ERR_FAIL_COND_V(image_acquired, true); // This was not released when it should be, error out and reuse...
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
@@ -193,10 +194,18 @@ bool OpenXRAPI::OpenXRSwapChainInfo::acquire(XrBool32 &p_should_render) {
XrSwapchainImageWaitInfo swapchain_image_wait_info = {
XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO, // type
nullptr, // next
- 17000000 // timeout in nanoseconds
+ 1000000000 // 1s timeout in nanoseconds
};
- result = openxr_api->xrWaitSwapchainImage(swapchain, &swapchain_image_wait_info);
+ // Wait for a maximum of 10 seconds before calling it a critical failure...
+ for (int retry = 0; retry < 10; retry++) {
+ result = openxr_api->xrWaitSwapchainImage(swapchain, &swapchain_image_wait_info);
+ if (result != XR_TIMEOUT_EXPIRED) {
+ break;
+ }
+ WARN_PRINT("OpenXR: timed out waiting for swapchain image.");
+ }
+
if (!XR_UNQUALIFIED_SUCCESS(result)) {
// Make sure end_frame knows we need to submit an empty frame
p_should_render = false;
@@ -206,6 +215,8 @@ bool OpenXRAPI::OpenXRSwapChainInfo::acquire(XrBool32 &p_should_render) {
print_line("OpenXR: failed to wait for swapchain image [", openxr_api->get_error_string(result), "]");
return false;
} else {
+ WARN_PRINT("OpenXR: couldn't to wait for swapchain but not a complete error [" + openxr_api->get_error_string(result) + "]");
+
// Make sure to skip trying to acquire the swapchain image in the next frame
skip_acquire_swapchain = true;
return false;
@@ -760,21 +771,6 @@ bool OpenXRAPI::load_supported_view_configuration_views(XrViewConfigurationType
print_verbose(String(" - recommended render sample count: ") + itos(view_configuration_views[i].recommendedSwapchainSampleCount));
}
- // Allocate buffers we'll be populating with view information.
- views = (XrView *)memalloc(sizeof(XrView) * view_count);
- ERR_FAIL_NULL_V_MSG(views, false, "OpenXR Couldn't allocate memory for views");
- memset(views, 0, sizeof(XrView) * view_count);
-
- projection_views = (XrCompositionLayerProjectionView *)memalloc(sizeof(XrCompositionLayerProjectionView) * view_count);
- ERR_FAIL_NULL_V_MSG(projection_views, false, "OpenXR Couldn't allocate memory for projection views");
- memset(projection_views, 0, sizeof(XrCompositionLayerProjectionView) * view_count);
-
- if (submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available()) {
- depth_views = (XrCompositionLayerDepthInfoKHR *)memalloc(sizeof(XrCompositionLayerDepthInfoKHR) * view_count);
- ERR_FAIL_NULL_V_MSG(depth_views, false, "OpenXR Couldn't allocate memory for depth views");
- memset(depth_views, 0, sizeof(XrCompositionLayerDepthInfoKHR) * view_count);
- }
-
return true;
}
@@ -927,6 +923,9 @@ bool OpenXRAPI::setup_play_space() {
// If we've previously created a play space, clean it up first.
if (play_space != XR_NULL_HANDLE) {
+ // TODO Investigate if destroying our play space here is safe,
+ // it may still be used in the rendering thread.
+
xrDestroySpace(play_space);
}
play_space = new_play_space;
@@ -936,7 +935,11 @@ bool OpenXRAPI::setup_play_space() {
if (emulating_local_floor) {
// We'll use the STAGE space to get the floor height, but we can't do that until
// after xrWaitFrame(), so just set this flag for now.
+ // Render state will be updated then.
should_reset_emulated_floor_height = true;
+ } else {
+ // Update render state so this play space is used rendering the upcoming frame.
+ set_render_play_space(play_space);
}
return true;
@@ -1016,7 +1019,7 @@ bool OpenXRAPI::reset_emulated_floor_height() {
identityPose, // pose
};
- result = xrLocateSpace(stage_space, local_space, get_next_frame_time(), &stage_location);
+ result = xrLocateSpace(stage_space, local_space, get_predicted_display_time(), &stage_location);
xrDestroySpace(local_space);
xrDestroySpace(stage_space);
@@ -1042,6 +1045,9 @@ bool OpenXRAPI::reset_emulated_floor_height() {
// report that as the reference space to the outside world.
reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT;
+ // Update render state so this play space is used rendering the upcoming frame.
+ set_render_play_space(play_space);
+
return true;
}
@@ -1136,6 +1142,7 @@ bool OpenXRAPI::obtain_swapchain_formats() {
}
bool OpenXRAPI::create_main_swapchains(Size2i p_size) {
+ ERR_NOT_ON_RENDER_THREAD_V(false);
ERR_FAIL_NULL_V(graphics_extension, false);
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
@@ -1154,12 +1161,12 @@ bool OpenXRAPI::create_main_swapchains(Size2i p_size) {
as we render 3D content into internal buffers that are copied into the swapchain, we do now have (basic) VRS support
*/
- main_swapchain_size = p_size;
+ render_state.main_swapchain_size = p_size;
uint32_t sample_count = 1;
// We start with our color swapchain...
if (color_swapchain_format != 0) {
- if (!main_swapchains[OPENXR_SWAPCHAIN_COLOR].create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, color_swapchain_format, main_swapchain_size.width, main_swapchain_size.height, sample_count, view_count)) {
+ if (!render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, color_swapchain_format, render_state.main_swapchain_size.width, render_state.main_swapchain_size.height, sample_count, view_count)) {
return false;
}
}
@@ -1169,7 +1176,7 @@ bool OpenXRAPI::create_main_swapchains(Size2i p_size) {
// - we support our depth layer extension
// - we have our spacewarp extension (not yet implemented)
if (depth_swapchain_format != 0 && submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available()) {
- if (!main_swapchains[OPENXR_SWAPCHAIN_DEPTH].create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, depth_swapchain_format, main_swapchain_size.width, main_swapchain_size.height, sample_count, view_count)) {
+ if (!render_state.main_swapchains[OPENXR_SWAPCHAIN_DEPTH].create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, depth_swapchain_format, render_state.main_swapchain_size.width, render_state.main_swapchain_size.height, sample_count, view_count)) {
return false;
}
}
@@ -1180,36 +1187,36 @@ bool OpenXRAPI::create_main_swapchains(Size2i p_size) {
// TBD
}
- for (uint32_t i = 0; i < view_count; i++) {
- views[i].type = XR_TYPE_VIEW;
- views[i].next = nullptr;
-
- projection_views[i].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
- projection_views[i].next = nullptr;
- projection_views[i].subImage.swapchain = main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_swapchain();
- projection_views[i].subImage.imageArrayIndex = i;
- projection_views[i].subImage.imageRect.offset.x = 0;
- projection_views[i].subImage.imageRect.offset.y = 0;
- projection_views[i].subImage.imageRect.extent.width = main_swapchain_size.width;
- projection_views[i].subImage.imageRect.extent.height = main_swapchain_size.height;
-
- if (submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available() && depth_views) {
- projection_views[i].next = &depth_views[i];
-
- depth_views[i].type = XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR;
- depth_views[i].next = nullptr;
- depth_views[i].subImage.swapchain = main_swapchains[OPENXR_SWAPCHAIN_DEPTH].get_swapchain();
- depth_views[i].subImage.imageArrayIndex = i;
- depth_views[i].subImage.imageRect.offset.x = 0;
- depth_views[i].subImage.imageRect.offset.y = 0;
- depth_views[i].subImage.imageRect.extent.width = main_swapchain_size.width;
- depth_views[i].subImage.imageRect.extent.height = main_swapchain_size.height;
+ for (uint32_t i = 0; i < render_state.view_count; i++) {
+ render_state.views[i].type = XR_TYPE_VIEW;
+ render_state.views[i].next = nullptr;
+
+ render_state.projection_views[i].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
+ render_state.projection_views[i].next = nullptr;
+ render_state.projection_views[i].subImage.swapchain = render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_swapchain();
+ render_state.projection_views[i].subImage.imageArrayIndex = i;
+ render_state.projection_views[i].subImage.imageRect.offset.x = 0;
+ render_state.projection_views[i].subImage.imageRect.offset.y = 0;
+ render_state.projection_views[i].subImage.imageRect.extent.width = render_state.main_swapchain_size.width;
+ render_state.projection_views[i].subImage.imageRect.extent.height = render_state.main_swapchain_size.height;
+
+ if (render_state.submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available() && render_state.depth_views) {
+ render_state.projection_views[i].next = &render_state.depth_views[i];
+
+ render_state.depth_views[i].type = XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR;
+ render_state.depth_views[i].next = nullptr;
+ render_state.depth_views[i].subImage.swapchain = render_state.main_swapchains[OPENXR_SWAPCHAIN_DEPTH].get_swapchain();
+ render_state.depth_views[i].subImage.imageArrayIndex = i;
+ render_state.depth_views[i].subImage.imageRect.offset.x = 0;
+ render_state.depth_views[i].subImage.imageRect.offset.y = 0;
+ render_state.depth_views[i].subImage.imageRect.extent.width = render_state.main_swapchain_size.width;
+ render_state.depth_views[i].subImage.imageRect.extent.height = render_state.main_swapchain_size.height;
// OpenXR spec says that: minDepth < maxDepth.
- depth_views[i].minDepth = 0.0;
- depth_views[i].maxDepth = 1.0;
+ render_state.depth_views[i].minDepth = 0.0;
+ render_state.depth_views[i].maxDepth = 1.0;
// But we can reverse near and far for reverse-Z.
- depth_views[i].nearZ = 100.0; // Near and far Z will be set to the correct values in fill_projection_matrix
- depth_views[i].farZ = 0.01;
+ render_state.depth_views[i].nearZ = 100.0; // Near and far Z will be set to the correct values in fill_projection_matrix
+ render_state.depth_views[i].farZ = 0.01;
}
};
@@ -1217,23 +1224,33 @@ bool OpenXRAPI::create_main_swapchains(Size2i p_size) {
};
void OpenXRAPI::destroy_session() {
- if (running && session != XR_NULL_HANDLE) {
- xrEndSession(session);
+ // TODO need to figure out if we're still rendering our current frame
+ // in a separate rendering thread and if so,
+ // if we need to wait for completion.
+ // We could be pulling the rug from underneath rendering...
+
+ if (running) {
+ if (session != XR_NULL_HANDLE) {
+ xrEndSession(session);
+ }
+
+ running = false;
+ render_state.running = false;
}
- if (views != nullptr) {
- memfree(views);
- views = nullptr;
+ if (render_state.views != nullptr) {
+ memfree(render_state.views);
+ render_state.views = nullptr;
}
- if (projection_views != nullptr) {
- memfree(projection_views);
- projection_views = nullptr;
+ if (render_state.projection_views != nullptr) {
+ memfree(render_state.projection_views);
+ render_state.projection_views = nullptr;
}
- if (depth_views != nullptr) {
- memfree(depth_views);
- depth_views = nullptr;
+ if (render_state.depth_views != nullptr) {
+ memfree(render_state.depth_views);
+ render_state.depth_views = nullptr;
}
free_main_swapchains();
@@ -1248,6 +1265,7 @@ void OpenXRAPI::destroy_session() {
if (play_space != XR_NULL_HANDLE) {
xrDestroySpace(play_space);
play_space = XR_NULL_HANDLE;
+ render_state.play_space = XR_NULL_HANDLE;
}
if (view_space != XR_NULL_HANDLE) {
xrDestroySpace(view_space);
@@ -1298,6 +1316,7 @@ bool OpenXRAPI::on_state_ready() {
// we're running
running = true;
+ set_render_session_running(true);
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
wrapper->on_state_ready();
@@ -1374,34 +1393,37 @@ bool OpenXRAPI::on_state_stopping() {
}
running = false;
+ set_render_session_running(false);
}
- // TODO further cleanup
-
return true;
}
bool OpenXRAPI::on_state_loss_pending() {
print_verbose("On state loss pending");
+ if (xr_interface) {
+ xr_interface->on_state_loss_pending();
+ }
+
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
wrapper->on_state_loss_pending();
}
- // TODO need to look into the correct action here, read up on the spec but we may need to signal Godot to exit (if it's not already exiting)
-
return true;
}
bool OpenXRAPI::on_state_exiting() {
print_verbose("On state existing");
+ if (xr_interface) {
+ xr_interface->on_state_exiting();
+ }
+
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
wrapper->on_state_exiting();
}
- // TODO need to look into the correct action here, read up on the spec but we may need to signal Godot to exit (if it's not already exiting)
-
return true;
}
@@ -1419,10 +1441,7 @@ void OpenXRAPI::set_view_configuration(XrViewConfigurationType p_view_configurat
bool OpenXRAPI::set_requested_reference_space(XrReferenceSpaceType p_requested_reference_space) {
requested_reference_space = p_requested_reference_space;
-
- if (is_initialized()) {
- return setup_play_space();
- }
+ play_space_is_dirty = true;
return true;
}
@@ -1625,11 +1644,6 @@ bool OpenXRAPI::initialize_session() {
return false;
}
- if (!setup_play_space()) {
- destroy_session();
- return false;
- }
-
if (!setup_view_space()) {
destroy_session();
return false;
@@ -1645,6 +1659,8 @@ bool OpenXRAPI::initialize_session() {
return false;
}
+ allocate_view_buffers(view_count, submit_depth_buffer);
+
return true;
}
@@ -1696,12 +1712,18 @@ XrHandTrackerEXT OpenXRAPI::get_hand_tracker(int p_hand_index) {
}
Size2 OpenXRAPI::get_recommended_target_size() {
+ RenderingServer *rendering_server = RenderingServer::get_singleton();
ERR_FAIL_NULL_V(view_configuration_views, Size2());
Size2 target_size;
- target_size.width = view_configuration_views[0].recommendedImageRectWidth * render_target_size_multiplier;
- target_size.height = view_configuration_views[0].recommendedImageRectHeight * render_target_size_multiplier;
+ if (rendering_server && rendering_server->is_on_render_thread()) {
+ target_size.width = view_configuration_views[0].recommendedImageRectWidth * render_state.render_target_size_multiplier;
+ target_size.height = view_configuration_views[0].recommendedImageRectHeight * render_state.render_target_size_multiplier;
+ } else {
+ target_size.width = view_configuration_views[0].recommendedImageRectWidth * render_target_size_multiplier;
+ target_size.height = view_configuration_views[0].recommendedImageRectHeight * render_target_size_multiplier;
+ }
return target_size;
}
@@ -1713,14 +1735,12 @@ XRPose::TrackingConfidence OpenXRAPI::get_head_center(Transform3D &r_transform,
return XRPose::XR_TRACKING_CONFIDENCE_NONE;
}
- // xrWaitFrame not run yet
- if (frame_state.predictedDisplayTime == 0) {
+ // Get display time
+ XrTime display_time = get_predicted_display_time();
+ if (display_time == 0) {
return XRPose::XR_TRACKING_CONFIDENCE_NONE;
}
- // Get timing for the next frame, as that is the current frame we're processing
- XrTime display_time = get_next_frame_time();
-
XrSpaceVelocity velocity = {
XR_TYPE_SPACE_VELOCITY, // type
nullptr, // next
@@ -1764,54 +1784,87 @@ XRPose::TrackingConfidence OpenXRAPI::get_head_center(Transform3D &r_transform,
}
bool OpenXRAPI::get_view_transform(uint32_t p_view, Transform3D &r_transform) {
- if (!running) {
- return false;
- }
+ ERR_NOT_ON_RENDER_THREAD_V(false);
- // xrWaitFrame not run yet
- if (frame_state.predictedDisplayTime == 0) {
+ if (!render_state.running) {
return false;
}
// we don't have valid view info
- if (views == nullptr || !view_pose_valid) {
+ if (render_state.views == nullptr || !render_state.view_pose_valid) {
return false;
}
// Note, the timing of this is set right before rendering, which is what we need here.
- r_transform = transform_from_pose(views[p_view].pose);
+ r_transform = transform_from_pose(render_state.views[p_view].pose);
return true;
}
bool OpenXRAPI::get_view_projection(uint32_t p_view, double p_z_near, double p_z_far, Projection &p_camera_matrix) {
+ ERR_NOT_ON_RENDER_THREAD_V(false);
ERR_FAIL_NULL_V(graphics_extension, false);
- if (!running) {
- return false;
- }
-
- // xrWaitFrame not run yet
- if (frame_state.predictedDisplayTime == 0) {
+ if (!render_state.running) {
return false;
}
// we don't have valid view info
- if (views == nullptr || !view_pose_valid) {
+ if (render_state.views == nullptr || !render_state.view_pose_valid) {
return false;
}
// if we're using depth views, make sure we update our near and far there...
- if (depth_views != nullptr) {
- for (uint32_t i = 0; i < view_count; i++) {
+ if (render_state.depth_views != nullptr) {
+ for (uint32_t i = 0; i < render_state.view_count; i++) {
// As we are using reverse-Z these need to be flipped.
- depth_views[i].nearZ = p_z_far;
- depth_views[i].farZ = p_z_near;
+ render_state.depth_views[i].nearZ = p_z_far;
+ render_state.depth_views[i].farZ = p_z_near;
}
}
// now update our projection
- return graphics_extension->create_projection_fov(views[p_view].fov, p_z_near, p_z_far, p_camera_matrix);
+ return graphics_extension->create_projection_fov(render_state.views[p_view].fov, p_z_near, p_z_far, p_camera_matrix);
+}
+
+Vector2 OpenXRAPI::get_eye_focus(uint32_t p_view, float p_aspect) {
+ ERR_FAIL_NULL_V(graphics_extension, Vector2());
+
+ if (!render_state.running) {
+ return Vector2();
+ }
+
+ // xrWaitFrame not run yet
+ if (render_state.predicted_display_time == 0) {
+ return Vector2();
+ }
+
+ // we don't have valid view info
+ if (render_state.views == nullptr || !render_state.view_pose_valid) {
+ return Vector2();
+ }
+
+ Projection cm;
+ if (!graphics_extension->create_projection_fov(render_state.views[p_view].fov, 0.1, 1000.0, cm)) {
+ return Vector2();
+ }
+
+ // Default focus to center...
+ Vector3 focus = cm.xform(Vector3(0.0, 0.0, 999.9));
+
+ // Lets check for eye tracking...
+ OpenXREyeGazeInteractionExtension *eye_gaze_interaction = OpenXREyeGazeInteractionExtension::get_singleton();
+ if (eye_gaze_interaction && eye_gaze_interaction->supports_eye_gaze_interaction()) {
+ Vector3 eye_gaze_pose;
+ if (eye_gaze_interaction->get_eye_gaze_pose(1.0, eye_gaze_pose)) {
+ Transform3D view_transform = transform_from_pose(render_state.views[p_view].pose);
+
+ eye_gaze_pose = view_transform.xform_inv(eye_gaze_pose);
+ focus = cm.xform(eye_gaze_pose);
+ }
+ }
+
+ return Vector2(focus.x, focus.y);
}
bool OpenXRAPI::poll_events() {
@@ -1934,53 +1987,85 @@ bool OpenXRAPI::poll_events() {
}
}
-bool OpenXRAPI::process() {
- ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, false);
+void OpenXRAPI::_allocate_view_buffers(uint32_t p_view_count, bool p_submit_depth_buffer) {
+ // Must be called from rendering thread!
+ ERR_NOT_ON_RENDER_THREAD;
- if (!poll_events()) {
- return false;
- }
+ OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
+ ERR_FAIL_NULL(openxr_api);
- if (!running) {
- return false;
- }
+ openxr_api->render_state.view_count = p_view_count;
+ openxr_api->render_state.submit_depth_buffer = p_submit_depth_buffer;
- for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
- wrapper->on_process();
+ // Allocate buffers we'll be populating with view information.
+ openxr_api->render_state.views = (XrView *)memalloc(sizeof(XrView) * p_view_count);
+ ERR_FAIL_NULL_MSG(openxr_api->render_state.views, "OpenXR Couldn't allocate memory for views");
+ memset(openxr_api->render_state.views, 0, sizeof(XrView) * p_view_count);
+
+ openxr_api->render_state.projection_views = (XrCompositionLayerProjectionView *)memalloc(sizeof(XrCompositionLayerProjectionView) * p_view_count);
+ ERR_FAIL_NULL_MSG(openxr_api->render_state.projection_views, "OpenXR Couldn't allocate memory for projection views");
+ memset(openxr_api->render_state.projection_views, 0, sizeof(XrCompositionLayerProjectionView) * p_view_count);
+
+ if (p_submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available()) {
+ openxr_api->render_state.depth_views = (XrCompositionLayerDepthInfoKHR *)memalloc(sizeof(XrCompositionLayerDepthInfoKHR) * p_view_count);
+ ERR_FAIL_NULL_MSG(openxr_api->render_state.depth_views, "OpenXR Couldn't allocate memory for depth views");
+ memset(openxr_api->render_state.depth_views, 0, sizeof(XrCompositionLayerDepthInfoKHR) * p_view_count);
}
+}
- return true;
+void OpenXRAPI::_set_render_session_running(bool p_is_running) {
+ // Must be called from rendering thread!
+ ERR_NOT_ON_RENDER_THREAD;
+
+ OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
+ ERR_FAIL_NULL(openxr_api);
+ openxr_api->render_state.running = p_is_running;
}
-void OpenXRAPI::free_main_swapchains() {
- for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
- main_swapchains[i].queue_free();
- }
+void OpenXRAPI::_set_render_display_info(XrTime p_predicted_display_time, bool p_should_render) {
+ // Must be called from rendering thread!
+ ERR_NOT_ON_RENDER_THREAD;
+
+ OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
+ ERR_FAIL_NULL(openxr_api);
+ openxr_api->render_state.predicted_display_time = p_predicted_display_time;
+ openxr_api->render_state.should_render = p_should_render;
}
-void OpenXRAPI::pre_render() {
- ERR_FAIL_COND(instance == XR_NULL_HANDLE);
+void OpenXRAPI::_set_render_play_space(uint64_t p_play_space) {
+ // Must be called from rendering thread!
+ ERR_NOT_ON_RENDER_THREAD;
- if (!running) {
- return;
- }
+ OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
+ ERR_FAIL_NULL(openxr_api);
+ openxr_api->render_state.play_space = XrSpace(p_play_space);
+}
- // Process any swapchains that were queued to be freed
- OpenXRSwapChainInfo::free_queued();
+void OpenXRAPI::_set_render_state_multiplier(double p_render_target_size_multiplier) {
+ // Must be called from rendering thread!
+ ERR_NOT_ON_RENDER_THREAD;
- Size2i swapchain_size = get_recommended_target_size();
- if (swapchain_size != main_swapchain_size) {
- // Out with the old.
- free_main_swapchains();
+ OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
+ ERR_FAIL_NULL(openxr_api);
+ openxr_api->render_state.render_target_size_multiplier = p_render_target_size_multiplier;
+}
- // In with the new.
- create_main_swapchains(swapchain_size);
+bool OpenXRAPI::process() {
+ ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, false);
+
+ if (!poll_events()) {
+ return false;
+ }
+
+ if (!running) {
+ return false;
}
- // Waitframe does 2 important things in our process:
- // 1) It provides us with predictive timing, telling us when OpenXR expects to display the frame we're about to commit
- // 2) It will use the previous timing to pause our thread so that rendering starts as close to displaying as possible
- // This must thus be called as close to when we start rendering as possible
+ // We call xrWaitFrame as early as possible, this will allow OpenXR to get
+ // proper timing info between this point, and when we're ready to start rendering.
+ // As the name suggests, OpenXR can pause the thread to minimize the time between
+ // retrieving tracking data and using that tracking data to render.
+ // OpenXR thus works best if rendering is performed on a separate thread.
XrFrameWaitInfo frame_wait_info = { XR_TYPE_FRAME_WAIT_INFO, nullptr };
frame_state.predictedDisplayTime = 0;
frame_state.predictedDisplayPeriod = 0;
@@ -1995,7 +2080,9 @@ void OpenXRAPI::pre_render() {
frame_state.predictedDisplayPeriod = 0;
frame_state.shouldRender = false;
- return;
+ set_render_display_info(0, false);
+
+ return false;
}
if (frame_state.predictedDisplayPeriod > 500000000) {
@@ -2004,12 +2091,54 @@ void OpenXRAPI::pre_render() {
frame_state.predictedDisplayPeriod = 0;
}
+ set_render_display_info(frame_state.predictedDisplayTime, frame_state.shouldRender);
+
+ if (unlikely(play_space_is_dirty)) {
+ setup_play_space();
+ play_space_is_dirty = false;
+ }
+
if (unlikely(should_reset_emulated_floor_height)) {
reset_emulated_floor_height();
should_reset_emulated_floor_height = false;
}
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
+ wrapper->on_process();
+ }
+
+ return true;
+}
+
+void OpenXRAPI::free_main_swapchains() {
+ for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
+ render_state.main_swapchains[i].queue_free();
+ }
+}
+
+void OpenXRAPI::pre_render() {
+ ERR_FAIL_COND(session == XR_NULL_HANDLE);
+
+ // Must be called from rendering thread!
+ ERR_NOT_ON_RENDER_THREAD;
+
+ if (!render_state.running) {
+ return;
+ }
+
+ // Process any swapchains that were queued to be freed
+ OpenXRSwapChainInfo::free_queued();
+
+ Size2i swapchain_size = get_recommended_target_size();
+ if (swapchain_size != render_state.main_swapchain_size) {
+ // Out with the old.
+ free_main_swapchains();
+
+ // In with the new.
+ create_main_swapchains(swapchain_size);
+ }
+
+ for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
wrapper->on_pre_render();
}
@@ -2028,8 +2157,8 @@ void OpenXRAPI::pre_render() {
XR_TYPE_VIEW_LOCATE_INFO, // type
nullptr, // next
view_configuration, // viewConfigurationType
- frame_state.predictedDisplayTime, // displayTime
- play_space // space
+ render_state.predicted_display_time, // displayTime
+ render_state.play_space // space
};
XrViewState view_state = {
XR_TYPE_VIEW_STATE, // type
@@ -2037,7 +2166,7 @@ void OpenXRAPI::pre_render() {
0 // viewStateFlags
};
uint32_t view_count_output;
- result = xrLocateViews(session, &view_locate_info, &view_state, view_count, &view_count_output, views);
+ XrResult result = xrLocateViews(session, &view_locate_info, &view_state, render_state.view_count, &view_count_output, render_state.views);
if (XR_FAILED(result)) {
print_line("OpenXR: Couldn't locate views [", get_error_string(result), "]");
return;
@@ -2050,9 +2179,9 @@ void OpenXRAPI::pre_render() {
pose_valid = false;
}
}
- if (view_pose_valid != pose_valid) {
- view_pose_valid = pose_valid;
- if (!view_pose_valid) {
+ if (render_state.view_pose_valid != pose_valid) {
+ render_state.view_pose_valid = pose_valid;
+ if (!render_state.view_pose_valid) {
print_verbose("OpenXR View pose became invalid");
} else {
print_verbose("OpenXR View pose became valid");
@@ -2071,23 +2200,24 @@ void OpenXRAPI::pre_render() {
}
// Reset this, we haven't found a viewport for output yet
- has_xr_viewport = false;
+ render_state.has_xr_viewport = false;
}
bool OpenXRAPI::pre_draw_viewport(RID p_render_target) {
+ // Must be called from rendering thread!
+ ERR_NOT_ON_RENDER_THREAD_V(false);
+
// We found an XR viewport!
- has_xr_viewport = true;
+ render_state.has_xr_viewport = true;
- if (!can_render()) {
+ if (instance == XR_NULL_HANDLE || session == XR_NULL_HANDLE || !render_state.running || !render_state.view_pose_valid || !render_state.should_render) {
return false;
}
- // TODO: at some point in time we may support multiple viewports in which case we need to handle that...
-
// Acquire our images
for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
- if (!main_swapchains[i].is_image_acquired() && main_swapchains[i].get_swapchain() != XR_NULL_HANDLE) {
- if (!main_swapchains[i].acquire(frame_state.shouldRender)) {
+ if (!render_state.main_swapchains[i].is_image_acquired() && render_state.main_swapchains[i].get_swapchain() != XR_NULL_HANDLE) {
+ if (!render_state.main_swapchains[i].acquire(render_state.should_render)) {
return false;
}
}
@@ -2101,24 +2231,33 @@ bool OpenXRAPI::pre_draw_viewport(RID p_render_target) {
}
XrSwapchain OpenXRAPI::get_color_swapchain() {
- return main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_swapchain();
+ ERR_NOT_ON_RENDER_THREAD_V(XR_NULL_HANDLE);
+
+ return render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_swapchain();
}
RID OpenXRAPI::get_color_texture() {
- return main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_image();
+ ERR_NOT_ON_RENDER_THREAD_V(RID());
+
+ return render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_image();
}
RID OpenXRAPI::get_depth_texture() {
+ ERR_NOT_ON_RENDER_THREAD_V(RID());
+
// Note, image will not be acquired if we didn't have a suitable swap chain format.
- if (submit_depth_buffer) {
- return main_swapchains[OPENXR_SWAPCHAIN_DEPTH].get_image();
+ if (render_state.submit_depth_buffer && render_state.main_swapchains[OPENXR_SWAPCHAIN_DEPTH].is_image_acquired()) {
+ return render_state.main_swapchains[OPENXR_SWAPCHAIN_DEPTH].get_image();
} else {
return RID();
}
}
void OpenXRAPI::post_draw_viewport(RID p_render_target) {
- if (!can_render()) {
+ // Must be called from rendering thread!
+ ERR_NOT_ON_RENDER_THREAD;
+
+ if (instance == XR_NULL_HANDLE || session == XR_NULL_HANDLE || !render_state.running || !render_state.view_pose_valid || !render_state.should_render) {
return;
}
@@ -2130,30 +2269,33 @@ void OpenXRAPI::post_draw_viewport(RID p_render_target) {
void OpenXRAPI::end_frame() {
XrResult result;
- ERR_FAIL_COND(instance == XR_NULL_HANDLE);
+ ERR_FAIL_COND(session == XR_NULL_HANDLE);
- if (!running) {
+ // Must be called from rendering thread!
+ ERR_NOT_ON_RENDER_THREAD;
+
+ if (!render_state.running) {
return;
}
- if (frame_state.shouldRender && view_pose_valid) {
- if (!has_xr_viewport) {
+ if (render_state.should_render && render_state.view_pose_valid) {
+ if (!render_state.has_xr_viewport) {
print_line("OpenXR: No viewport was marked with use_xr, there is no rendered output!");
- } else if (!main_swapchains[OPENXR_SWAPCHAIN_COLOR].is_image_acquired()) {
+ } else if (!render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].is_image_acquired()) {
print_line("OpenXR: No swapchain could be acquired to render to!");
}
}
// must have:
- // - shouldRender set to true
+ // - should_render set to true
// - a valid view pose for projection_views[eye].pose to submit layer
// - an image to render
- if (!frame_state.shouldRender || !view_pose_valid || !main_swapchains[OPENXR_SWAPCHAIN_COLOR].is_image_acquired()) {
+ if (!render_state.should_render || !render_state.view_pose_valid || !render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].is_image_acquired()) {
// submit 0 layers when we shouldn't render
XrFrameEndInfo frame_end_info = {
XR_TYPE_FRAME_END_INFO, // type
nullptr, // next
- frame_state.predictedDisplayTime, // displayTime
+ render_state.predicted_display_time, // displayTime
environment_blend_mode, // environmentBlendMode
0, // layerCount
nullptr // layers
@@ -2170,14 +2312,14 @@ void OpenXRAPI::end_frame() {
// release our swapchain image if we acquired it
for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
- if (main_swapchains[i].is_image_acquired()) {
- main_swapchains[i].release();
+ if (render_state.main_swapchains[i].is_image_acquired()) {
+ render_state.main_swapchains[i].release();
}
}
- for (uint32_t eye = 0; eye < view_count; eye++) {
- projection_views[eye].fov = views[eye].fov;
- projection_views[eye].pose = views[eye].pose;
+ for (uint32_t eye = 0; eye < render_state.view_count; eye++) {
+ render_state.projection_views[eye].fov = render_state.views[eye].fov;
+ render_state.projection_views[eye].pose = render_state.views[eye].pose;
}
Vector<OrderedCompositionLayer> ordered_layers_list;
@@ -2210,9 +2352,9 @@ void OpenXRAPI::end_frame() {
XR_TYPE_COMPOSITION_LAYER_PROJECTION, // type
nullptr, // next
layer_flags, // layerFlags
- play_space, // space
- view_count, // viewCount
- projection_views, // views
+ render_state.play_space, // space
+ render_state.view_count, // viewCount
+ render_state.projection_views, // views
};
ordered_layers_list.push_back({ (const XrCompositionLayerBaseHeader *)&projection_layer, 0 });
@@ -2228,7 +2370,7 @@ void OpenXRAPI::end_frame() {
XrFrameEndInfo frame_end_info = {
XR_TYPE_FRAME_END_INFO, // type
nullptr, // next
- frame_state.predictedDisplayTime, // displayTime
+ render_state.predicted_display_time, // displayTime
environment_blend_mode, // environmentBlendMode
static_cast<uint32_t>(layers_list.size()), // layerCount
layers_list.ptr() // layers
@@ -2271,6 +2413,7 @@ double OpenXRAPI::get_render_target_size_multiplier() const {
void OpenXRAPI::set_render_target_size_multiplier(double multiplier) {
render_target_size_multiplier = multiplier;
+ set_render_state_multiplier(multiplier);
}
bool OpenXRAPI::is_foveation_supported() const {
@@ -2414,10 +2557,6 @@ OpenXRAPI::OpenXRAPI() {
submit_depth_buffer = GLOBAL_GET("xr/openxr/submit_depth_buffer");
}
-
- // Reset a few things that can't be done in our class definition.
- frame_state.predictedDisplayTime = 0;
- frame_state.predictedDisplayPeriod = 0;
}
OpenXRAPI::~OpenXRAPI() {
@@ -2535,10 +2674,23 @@ bool OpenXRAPI::xr_result(XrResult result, const char *format, Array args) const
RID OpenXRAPI::get_tracker_rid(XrPath p_path) {
List<RID> current;
tracker_owner.get_owned_list(&current);
- for (int i = 0; i < current.size(); i++) {
- Tracker *tracker = tracker_owner.get_or_null(current[i]);
+ for (const RID &E : current) {
+ Tracker *tracker = tracker_owner.get_or_null(E);
if (tracker && tracker->toplevel_path == p_path) {
- return current[i];
+ return E;
+ }
+ }
+
+ return RID();
+}
+
+RID OpenXRAPI::find_tracker(const String &p_name) {
+ List<RID> current;
+ tracker_owner.get_owned_list(&current);
+ for (const RID &E : current) {
+ Tracker *tracker = tracker_owner.get_or_null(E);
+ if (tracker && tracker->name == p_name) {
+ return E;
}
}
@@ -2729,10 +2881,23 @@ void OpenXRAPI::action_set_free(RID p_action_set) {
RID OpenXRAPI::get_action_rid(XrAction p_action) {
List<RID> current;
action_owner.get_owned_list(&current);
- for (int i = 0; i < current.size(); i++) {
- Action *action = action_owner.get_or_null(current[i]);
+ for (const RID &E : current) {
+ Action *action = action_owner.get_or_null(E);
if (action && action->handle == p_action) {
- return current[i];
+ return E;
+ }
+ }
+
+ return RID();
+}
+
+RID OpenXRAPI::find_action(const String &p_name) {
+ List<RID> current;
+ action_owner.get_owned_list(&current);
+ for (const RID &E : current) {
+ Action *action = action_owner.get_or_null(E);
+ if (action && action->name == p_name) {
+ return E;
}
}
@@ -2833,10 +2998,10 @@ void OpenXRAPI::action_free(RID p_action) {
RID OpenXRAPI::get_interaction_profile_rid(XrPath p_path) {
List<RID> current;
interaction_profile_owner.get_owned_list(&current);
- for (int i = 0; i < current.size(); i++) {
- InteractionProfile *ip = interaction_profile_owner.get_or_null(current[i]);
+ for (const RID &E : current) {
+ InteractionProfile *ip = interaction_profile_owner.get_or_null(E);
if (ip && ip->path == p_path) {
- return current[i];
+ return E;
}
}
@@ -3132,7 +3297,7 @@ XRPose::TrackingConfidence OpenXRAPI::get_action_pose(RID p_action, RID p_tracke
return XRPose::XR_TRACKING_CONFIDENCE_NONE;
}
- XrTime display_time = get_next_frame_time();
+ XrTime display_time = get_predicted_display_time();
if (display_time == 0) {
return XRPose::XR_TRACKING_CONFIDENCE_NONE;
}
diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h
index e835366200..f9d2e60148 100644
--- a/modules/openxr/openxr_api.h
+++ b/modules/openxr/openxr_api.h
@@ -46,13 +46,11 @@
#include "core/templates/rb_map.h"
#include "core/templates/rid_owner.h"
#include "core/templates/vector.h"
+#include "servers/rendering_server.h"
#include "servers/xr/xr_pose.h"
#include <openxr/openxr.h>
-// Note, OpenXR code that we wrote for our plugin makes use of C++20 notation for initializing structs which ensures zeroing out unspecified members.
-// Godot is currently restricted to C++17 which doesn't allow this notation. Make sure critical fields are set.
-
// forward declarations, we don't want to include these fully
class OpenXRInterface;
@@ -77,7 +75,7 @@ public:
static void free_queued();
void free();
- bool acquire(XrBool32 &p_should_render);
+ bool acquire(bool &p_should_render);
bool release();
RID get_image();
};
@@ -151,9 +149,6 @@ private:
uint32_t view_count = 0;
XrViewConfigurationView *view_configuration_views = nullptr;
- XrView *views = nullptr;
- XrCompositionLayerProjectionView *projection_views = nullptr;
- XrCompositionLayerDepthInfoKHR *depth_views = nullptr; // Only used by Composition Layer Depth Extension if available
enum OpenXRSwapChainTypes {
OPENXR_SWAPCHAIN_COLOR,
@@ -164,14 +159,11 @@ private:
int64_t color_swapchain_format = 0;
int64_t depth_swapchain_format = 0;
- Size2i main_swapchain_size = { 0, 0 };
- OpenXRSwapChainInfo main_swapchains[OPENXR_SWAPCHAIN_MAX];
+ bool play_space_is_dirty = true;
XrSpace play_space = XR_NULL_HANDLE;
XrSpace view_space = XR_NULL_HANDLE;
- bool view_pose_valid = false;
XRPose::TrackingConfidence head_pose_confidence = XRPose::XR_TRACKING_CONFIDENCE_NONE;
- bool has_xr_viewport = false;
bool emulating_local_floor = false;
bool should_reset_emulated_floor_height = false;
@@ -328,6 +320,72 @@ private:
// convenience
void copy_string_to_char_buffer(const String p_string, char *p_buffer, int p_buffer_len);
+ // Render state, Only accessible in rendering thread
+ struct RenderState {
+ bool running = false;
+ bool should_render = false;
+ bool has_xr_viewport = false;
+ XrTime predicted_display_time = 0;
+ XrSpace play_space = XR_NULL_HANDLE;
+ double render_target_size_multiplier = 1.0;
+
+ uint32_t view_count = 0;
+ XrView *views = nullptr;
+ XrCompositionLayerProjectionView *projection_views = nullptr;
+ XrCompositionLayerDepthInfoKHR *depth_views = nullptr; // Only used by Composition Layer Depth Extension if available
+ bool submit_depth_buffer = false; // if set to true we submit depth buffers to OpenXR if a suitable extension is enabled.
+ bool view_pose_valid = false;
+
+ Size2i main_swapchain_size;
+ OpenXRSwapChainInfo main_swapchains[OPENXR_SWAPCHAIN_MAX];
+ } render_state;
+
+ static void _allocate_view_buffers(uint32_t p_view_count, bool p_submit_depth_buffer);
+ static void _set_render_session_running(bool p_is_running);
+ static void _set_render_display_info(XrTime p_predicted_display_time, bool p_should_render);
+ static void _set_render_play_space(uint64_t p_play_space);
+ static void _set_render_state_multiplier(double p_render_target_size_multiplier);
+
+ _FORCE_INLINE_ void allocate_view_buffers(uint32_t p_view_count, bool p_submit_depth_buffer) {
+ // If we're rendering on a separate thread, we may still be processing the last frame, don't communicate this till we're ready...
+ RenderingServer *rendering_server = RenderingServer::get_singleton();
+ ERR_FAIL_NULL(rendering_server);
+
+ rendering_server->call_on_render_thread(callable_mp_static(&OpenXRAPI::_allocate_view_buffers).bind(p_view_count, p_submit_depth_buffer));
+ }
+
+ _FORCE_INLINE_ void set_render_session_running(bool p_is_running) {
+ // If we're rendering on a separate thread, we may still be processing the last frame, don't communicate this till we're ready...
+ RenderingServer *rendering_server = RenderingServer::get_singleton();
+ ERR_FAIL_NULL(rendering_server);
+
+ rendering_server->call_on_render_thread(callable_mp_static(&OpenXRAPI::_set_render_session_running).bind(p_is_running));
+ }
+
+ _FORCE_INLINE_ void set_render_display_info(XrTime p_predicted_display_time, bool p_should_render) {
+ // If we're rendering on a separate thread, we may still be processing the last frame, don't communicate this till we're ready...
+ RenderingServer *rendering_server = RenderingServer::get_singleton();
+ ERR_FAIL_NULL(rendering_server);
+
+ rendering_server->call_on_render_thread(callable_mp_static(&OpenXRAPI::_set_render_display_info).bind(p_predicted_display_time, p_should_render));
+ }
+
+ _FORCE_INLINE_ void set_render_play_space(XrSpace p_play_space) {
+ // If we're rendering on a separate thread, we may still be processing the last frame, don't communicate this till we're ready...
+ RenderingServer *rendering_server = RenderingServer::get_singleton();
+ ERR_FAIL_NULL(rendering_server);
+
+ rendering_server->call_on_render_thread(callable_mp_static(&OpenXRAPI::_set_render_play_space).bind(uint64_t(p_play_space)));
+ }
+
+ _FORCE_INLINE_ void set_render_state_multiplier(double p_render_target_size_multiplier) {
+ // If we're rendering on a separate thread, we may still be processing the last frame, don't communicate this till we're ready...
+ RenderingServer *rendering_server = RenderingServer::get_singleton();
+ ERR_FAIL_NULL(rendering_server);
+
+ rendering_server->call_on_render_thread(callable_mp_static(&OpenXRAPI::_set_render_state_multiplier).bind(p_render_target_size_multiplier));
+ }
+
public:
XrInstance get_instance() const { return instance; };
XrSystemId get_system_id() const { return system_id; };
@@ -384,9 +442,13 @@ public:
bool initialize_session();
void finish();
- XrSpace get_play_space() const { return play_space; }
- XrTime get_next_frame_time() { return frame_state.predictedDisplayTime + frame_state.predictedDisplayPeriod; }
- bool can_render() { return instance != XR_NULL_HANDLE && session != XR_NULL_HANDLE && running && view_pose_valid && frame_state.shouldRender; }
+ _FORCE_INLINE_ XrSpace get_play_space() const { return play_space; }
+ _FORCE_INLINE_ XrTime get_predicted_display_time() { return frame_state.predictedDisplayTime; }
+ _FORCE_INLINE_ XrTime get_next_frame_time() { return frame_state.predictedDisplayTime + frame_state.predictedDisplayPeriod; }
+ _FORCE_INLINE_ bool can_render() {
+ ERR_ON_RENDER_THREAD_V(false);
+ return instance != XR_NULL_HANDLE && session != XR_NULL_HANDLE && running && frame_state.shouldRender;
+ }
XrHandTrackerEXT get_hand_tracker(int p_hand_index);
@@ -394,6 +456,7 @@ public:
XRPose::TrackingConfidence get_head_center(Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity);
bool get_view_transform(uint32_t p_view, Transform3D &r_transform);
bool get_view_projection(uint32_t p_view, double p_z_near, double p_z_far, Projection &p_camera_matrix);
+ Vector2 get_eye_focus(uint32_t p_view, float p_aspect);
bool process();
void pre_render();
@@ -453,6 +516,9 @@ public:
bool interaction_profile_suggest_bindings(RID p_interaction_profile);
void interaction_profile_free(RID p_interaction_profile);
+ RID find_tracker(const String &p_name);
+ RID find_action(const String &p_name);
+
bool sync_action_sets(const Vector<RID> p_active_sets);
bool get_action_bool(RID p_action, RID p_tracker);
float get_action_float(RID p_action, RID p_tracker);
diff --git a/modules/openxr/openxr_api_extension.cpp b/modules/openxr/openxr_api_extension.cpp
index fae0fc13d3..a1744fa1db 100644
--- a/modules/openxr/openxr_api_extension.cpp
+++ b/modules/openxr/openxr_api_extension.cpp
@@ -48,6 +48,7 @@ void OpenXRAPIExtension::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_running"), &OpenXRAPIExtension::is_running);
ClassDB::bind_method(D_METHOD("get_play_space"), &OpenXRAPIExtension::get_play_space);
+ ClassDB::bind_method(D_METHOD("get_predicted_display_time"), &OpenXRAPIExtension::get_predicted_display_time);
ClassDB::bind_method(D_METHOD("get_next_frame_time"), &OpenXRAPIExtension::get_next_frame_time);
ClassDB::bind_method(D_METHOD("can_render"), &OpenXRAPIExtension::can_render);
@@ -130,8 +131,17 @@ uint64_t OpenXRAPIExtension::get_play_space() {
return (uint64_t)OpenXRAPI::get_singleton()->get_play_space();
}
+int64_t OpenXRAPIExtension::get_predicted_display_time() {
+ ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), 0);
+ return (XrTime)OpenXRAPI::get_singleton()->get_predicted_display_time();
+}
+
int64_t OpenXRAPIExtension::get_next_frame_time() {
ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), 0);
+
+ // In the past we needed to look a frame ahead, may be calling this unintentionally so lets warn the dev.
+ WARN_PRINT_ONCE("OpenXR: Next frame timing called, verify this is intended.");
+
return (XrTime)OpenXRAPI::get_singleton()->get_next_frame_time();
}
diff --git a/modules/openxr/openxr_api_extension.h b/modules/openxr/openxr_api_extension.h
index 576e497798..cff2c4738e 100644
--- a/modules/openxr/openxr_api_extension.h
+++ b/modules/openxr/openxr_api_extension.h
@@ -69,6 +69,7 @@ public:
bool is_running();
uint64_t get_play_space();
+ int64_t get_predicted_display_time();
int64_t get_next_frame_time();
bool can_render();
diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp
index aa68441f03..b92d1edb90 100644
--- a/modules/openxr/openxr_interface.cpp
+++ b/modules/openxr/openxr_interface.cpp
@@ -35,6 +35,7 @@
#include "servers/rendering/rendering_server_globals.h"
#include "extensions/openxr_eye_gaze_interaction.h"
+#include "extensions/openxr_hand_interaction_extension.h"
#include "thirdparty/openxr/include/openxr/openxr.h"
void OpenXRInterface::_bind_methods() {
@@ -43,6 +44,8 @@ void OpenXRInterface::_bind_methods() {
ADD_SIGNAL(MethodInfo("session_stopping"));
ADD_SIGNAL(MethodInfo("session_focussed"));
ADD_SIGNAL(MethodInfo("session_visible"));
+ ADD_SIGNAL(MethodInfo("session_loss_pending"));
+ ADD_SIGNAL(MethodInfo("instance_exiting"));
ADD_SIGNAL(MethodInfo("pose_recentered"));
ADD_SIGNAL(MethodInfo("refresh_rate_changed", PropertyInfo(Variant::FLOAT, "refresh_rate")));
@@ -91,8 +94,19 @@ void OpenXRInterface::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_hand_joint_angular_velocity", "hand", "joint"), &OpenXRInterface::get_hand_joint_angular_velocity);
ClassDB::bind_method(D_METHOD("is_hand_tracking_supported"), &OpenXRInterface::is_hand_tracking_supported);
+ ClassDB::bind_method(D_METHOD("is_hand_interaction_supported"), &OpenXRInterface::is_hand_interaction_supported);
ClassDB::bind_method(D_METHOD("is_eye_gaze_interaction_supported"), &OpenXRInterface::is_eye_gaze_interaction_supported);
+ // VRS
+ ClassDB::bind_method(D_METHOD("get_vrs_min_radius"), &OpenXRInterface::get_vrs_min_radius);
+ ClassDB::bind_method(D_METHOD("set_vrs_min_radius", "radius"), &OpenXRInterface::set_vrs_min_radius);
+ ClassDB::bind_method(D_METHOD("get_vrs_strength"), &OpenXRInterface::get_vrs_strength);
+ ClassDB::bind_method(D_METHOD("set_vrs_strength", "strength"), &OpenXRInterface::set_vrs_strength);
+
+ ADD_GROUP("Vulkan VRS", "vrs_");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "vrs_min_radius", PROPERTY_HINT_RANGE, "1.0,100.0,1.0"), "set_vrs_min_radius", "get_vrs_min_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "vrs_strength", PROPERTY_HINT_RANGE, "0.1,10.0,0.1"), "set_vrs_strength", "get_vrs_strength");
+
BIND_ENUM_CONSTANT(HAND_LEFT);
BIND_ENUM_CONSTANT(HAND_RIGHT);
BIND_ENUM_CONSTANT(HAND_MAX);
@@ -806,6 +820,21 @@ bool OpenXRInterface::is_hand_tracking_supported() {
}
}
+bool OpenXRInterface::is_hand_interaction_supported() const {
+ if (openxr_api == nullptr) {
+ return false;
+ } else if (!openxr_api->is_initialized()) {
+ return false;
+ } else {
+ OpenXRHandInteractionExtension *hand_interaction_ext = OpenXRHandInteractionExtension::get_singleton();
+ if (hand_interaction_ext == nullptr) {
+ return false;
+ } else {
+ return hand_interaction_ext->is_available();
+ }
+ }
+}
+
bool OpenXRInterface::is_eye_gaze_interaction_supported() {
if (openxr_api == nullptr) {
return false;
@@ -853,6 +882,22 @@ Array OpenXRInterface::get_action_sets() const {
return arr;
}
+float OpenXRInterface::get_vrs_min_radius() const {
+ return xr_vrs.get_vrs_min_radius();
+}
+
+void OpenXRInterface::set_vrs_min_radius(float p_vrs_min_radius) {
+ xr_vrs.set_vrs_min_radius(p_vrs_min_radius);
+}
+
+float OpenXRInterface::get_vrs_strength() const {
+ return xr_vrs.get_vrs_strength();
+}
+
+void OpenXRInterface::set_vrs_strength(float p_vrs_strength) {
+ xr_vrs.set_vrs_strength(p_vrs_strength);
+}
+
double OpenXRInterface::get_render_target_size_multiplier() const {
if (openxr_api == nullptr) {
return 1.0;
@@ -1258,6 +1303,14 @@ void OpenXRInterface::on_state_stopping() {
emit_signal(SNAME("session_stopping"));
}
+void OpenXRInterface::on_state_loss_pending() {
+ emit_signal(SNAME("session_loss_pending"));
+}
+
+void OpenXRInterface::on_state_exiting() {
+ emit_signal(SNAME("instance_exiting"));
+}
+
void OpenXRInterface::on_pose_recentered() {
emit_signal(SNAME("pose_recentered"));
}
@@ -1408,6 +1461,24 @@ Vector3 OpenXRInterface::get_hand_joint_angular_velocity(Hand p_hand, HandJoints
return Vector3();
}
+RID OpenXRInterface::get_vrs_texture() {
+ if (!openxr_api) {
+ return RID();
+ }
+
+ PackedVector2Array eye_foci;
+
+ Size2 target_size = get_render_target_size();
+ real_t aspect_ratio = target_size.x / target_size.y;
+ uint32_t view_count = get_view_count();
+
+ for (uint32_t v = 0; v < view_count; v++) {
+ eye_foci.push_back(openxr_api->get_eye_focus(v, aspect_ratio));
+ }
+
+ return xr_vrs.make_vrs_texture(target_size, eye_foci);
+}
+
OpenXRInterface::OpenXRInterface() {
openxr_api = OpenXRAPI::get_singleton();
if (openxr_api) {
diff --git a/modules/openxr/openxr_interface.h b/modules/openxr/openxr_interface.h
index e916c7dac2..f0ee0dc3c4 100644
--- a/modules/openxr/openxr_interface.h
+++ b/modules/openxr/openxr_interface.h
@@ -31,6 +31,29 @@
#ifndef OPENXR_INTERFACE_H
#define OPENXR_INTERFACE_H
+// A note on multithreading and thread safety in OpenXR.
+//
+// Most entry points will be called from the main thread in Godot
+// however a number of entry points will be called from the
+// rendering thread, potentially while we're already processing
+// the next frame on the main thread.
+//
+// OpenXR itself has been designed with threading in mind including
+// a high likelihood that the XR runtime runs in separate threads
+// as well.
+// Hence all the frame timing information, use of swapchains and
+// sync functions.
+// Do note that repeated calls to tracking APIs will provide
+// increasingly more accurate data for the same timestamp as
+// tracking data is continuously updated.
+//
+// For our code we mostly implement this in our OpenXRAPI class.
+// We store data accessed from the rendering thread in a separate
+// struct, setting values through our renderer command queue.
+//
+// As some data is setup before we start rendering, and cleaned up
+// after we've stopped, that is accessed directly from both threads.
+
#include "action_map/openxr_action_map.h"
#include "extensions/openxr_hand_tracking_extension.h"
#include "openxr_api.h"
@@ -57,6 +80,8 @@ private:
XRPose::TrackingConfidence head_confidence;
Transform3D transform_for_view[2]; // We currently assume 2, but could be 4 for VARJO which we do not support yet
+ XRVRS xr_vrs;
+
void _load_action_map();
struct Action { // An action we've registered with OpenXR
@@ -110,6 +135,7 @@ public:
virtual TrackingStatus get_tracking_status() const override;
bool is_hand_tracking_supported();
+ bool is_hand_interaction_supported() const;
bool is_eye_gaze_interaction_supported();
bool initialize_on_startup() const;
@@ -144,6 +170,12 @@ public:
bool get_foveation_dynamic() const;
void set_foveation_dynamic(bool p_foveation_dynamic);
+ float get_vrs_min_radius() const;
+ void set_vrs_min_radius(float p_vrs_min_radius);
+
+ float get_vrs_strength() const;
+ void set_vrs_strength(float p_vrs_strength);
+
virtual Size2 get_render_target_size() override;
virtual uint32_t get_view_count() override;
virtual Transform3D get_camera_transform() override;
@@ -173,6 +205,8 @@ public:
void on_state_visible();
void on_state_focused();
void on_state_stopping();
+ void on_state_loss_pending();
+ void on_state_exiting();
void on_pose_recentered();
void on_refresh_rate_changes(float p_new_rate);
void tracker_profile_changed(RID p_tracker, RID p_interaction_profile);
@@ -250,6 +284,8 @@ public:
Vector3 get_hand_joint_linear_velocity(Hand p_hand, HandJoints p_joint) const;
Vector3 get_hand_joint_angular_velocity(Hand p_hand, HandJoints p_joint) const;
+ virtual RID get_vrs_texture() override;
+
OpenXRInterface();
~OpenXRInterface();
};
diff --git a/modules/openxr/register_types.cpp b/modules/openxr/register_types.cpp
index eb0527f07c..85514737f2 100644
--- a/modules/openxr/register_types.cpp
+++ b/modules/openxr/register_types.cpp
@@ -49,6 +49,7 @@
#include "extensions/openxr_composition_layer_extension.h"
#include "extensions/openxr_eye_gaze_interaction.h"
#include "extensions/openxr_fb_display_refresh_rate_extension.h"
+#include "extensions/openxr_hand_interaction_extension.h"
#include "extensions/openxr_hand_tracking_extension.h"
#include "extensions/openxr_htc_controller_extension.h"
#include "extensions/openxr_htc_vive_tracker_extension.h"
@@ -124,6 +125,7 @@ void initialize_openxr_module(ModuleInitializationLevel p_level) {
OpenXRAPI::register_extension_wrapper(memnew(OpenXRML2ControllerExtension));
OpenXRAPI::register_extension_wrapper(memnew(OpenXRMetaControllerExtension));
OpenXRAPI::register_extension_wrapper(memnew(OpenXREyeGazeInteractionExtension));
+ OpenXRAPI::register_extension_wrapper(memnew(OpenXRHandInteractionExtension));
// register gated extensions
if (GLOBAL_GET("xr/openxr/extensions/hand_tracking")) {
diff --git a/modules/raycast/config.py b/modules/raycast/config.py
index 26329d813a..0fd35af528 100644
--- a/modules/raycast/config.py
+++ b/modules/raycast/config.py
@@ -1,8 +1,9 @@
def can_build(env, platform):
- # Supported architectures depend on the Embree library.
+ # Supported architectures and platforms depend on the Embree library.
+ if env["arch"] == "arm64" and platform == "windows":
+ return False
if env["arch"] in ["x86_64", "arm64", "wasm32"]:
return True
- # x86_32 only seems supported on Windows for now.
if env["arch"] == "x86_32" and platform == "windows":
return True
return False
diff --git a/modules/text_server_adv/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct
index 1d9d36fbbf..fcf3f64315 100644
--- a/modules/text_server_adv/gdextension_build/SConstruct
+++ b/modules/text_server_adv/gdextension_build/SConstruct
@@ -1,10 +1,20 @@
#!/usr/bin/env python
import atexit
-import os
import sys
import methods
import time
+# Enable ANSI escape code support on Windows 10 and later (for colored console output).
+# <https://github.com/python/cpython/issues/73245>
+if sys.platform == "win32":
+ from ctypes import windll, c_int, byref
+
+ stdout_handle = windll.kernel32.GetStdHandle(c_int(-11))
+ mode = c_int(0)
+ windll.kernel32.GetConsoleMode(c_int(stdout_handle), byref(mode))
+ mode = c_int(mode.value | 4)
+ windll.kernel32.SetConsoleMode(c_int(stdout_handle), mode)
+
# For the reference:
# - CCFLAGS are compilation flags shared between C and C++
# - CFLAGS are for C-specific compilation flags
@@ -30,7 +40,7 @@ opts.Add(BoolVariable("verbose", "Enable verbose output for the compilation", Fa
opts.Update(env)
if not env["verbose"]:
- methods.no_verbose(sys, env)
+ methods.no_verbose(env)
if env["platform"] == "windows" and not env["use_mingw"]:
env.AppendUnique(CCFLAGS=["/utf-8"]) # Force to use Unicode encoding.
@@ -764,9 +774,16 @@ Default(library)
def print_elapsed_time():
- elapsed_time_sec = round(time.time() - time_at_start, 3)
- time_ms = round((elapsed_time_sec % 1) * 1000)
- print("[Time elapsed: {}.{:03}]".format(time.strftime("%H:%M:%S", time.gmtime(elapsed_time_sec)), time_ms))
+ elapsed_time_sec = round(time.time() - time_at_start, 2)
+ time_centiseconds = round((elapsed_time_sec % 1) * 100)
+ print(
+ "{}[Time elapsed: {}.{:02}]{}".format(
+ methods.ANSI.GRAY,
+ time.strftime("%H:%M:%S", time.gmtime(elapsed_time_sec)),
+ time_centiseconds,
+ methods.ANSI.RESET,
+ )
+ )
atexit.register(print_elapsed_time)
diff --git a/modules/text_server_adv/gdextension_build/methods.py b/modules/text_server_adv/gdextension_build/methods.py
index 32dbc59fd4..3453c3e8f0 100644
--- a/modules/text_server_adv/gdextension_build/methods.py
+++ b/modules/text_server_adv/gdextension_build/methods.py
@@ -1,66 +1,75 @@
import os
import sys
+from enum import Enum
+# Colors are disabled in non-TTY environments such as pipes. This means
+# that if output is redirected to a file, it won't contain color codes.
+# Colors are always enabled on continuous integration.
+_colorize = bool(sys.stdout.isatty() or os.environ.get("CI"))
-def no_verbose(sys, env):
- colors = {}
- # Colors are disabled in non-TTY environments such as pipes. This means
- # that if output is redirected to a file, it will not contain color codes
- if sys.stdout.isatty():
- colors["blue"] = "\033[0;94m"
- colors["bold_blue"] = "\033[1;94m"
- colors["reset"] = "\033[0m"
- else:
- colors["blue"] = ""
- colors["bold_blue"] = ""
- colors["reset"] = ""
+class ANSI(Enum):
+ """
+ Enum class for adding ansi colorcodes directly into strings.
+ Automatically converts values to strings representing their
+ internal value, or an empty string in a non-colorized scope.
+ """
+
+ RESET = "\x1b[0m"
+
+ BOLD = "\x1b[1m"
+ ITALIC = "\x1b[3m"
+ UNDERLINE = "\x1b[4m"
+ STRIKETHROUGH = "\x1b[9m"
+ REGULAR = "\x1b[22;23;24;29m"
+
+ BLACK = "\x1b[30m"
+ RED = "\x1b[31m"
+ GREEN = "\x1b[32m"
+ YELLOW = "\x1b[33m"
+ BLUE = "\x1b[34m"
+ MAGENTA = "\x1b[35m"
+ CYAN = "\x1b[36m"
+ WHITE = "\x1b[37m"
+
+ PURPLE = "\x1b[38;5;93m"
+ PINK = "\x1b[38;5;206m"
+ ORANGE = "\x1b[38;5;214m"
+ GRAY = "\x1b[38;5;244m"
+
+ def __str__(self) -> str:
+ global _colorize
+ return str(self.value) if _colorize else ""
+
+
+def no_verbose(env):
+ colors = [ANSI.BLUE, ANSI.BOLD, ANSI.REGULAR, ANSI.RESET]
# There is a space before "..." to ensure that source file names can be
# Ctrl + clicked in the VS Code terminal.
- compile_source_message = "{}Compiling {}$SOURCE{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- java_compile_source_message = "{}Compiling {}$SOURCE{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- compile_shared_source_message = "{}Compiling shared {}$SOURCE{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- link_program_message = "{}Linking Program {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- link_library_message = "{}Linking Static Library {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- ranlib_library_message = "{}Ranlib Library {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- link_shared_library_message = "{}Linking Shared Library {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- java_library_message = "{}Creating Java Archive {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- compiled_resource_message = "{}Creating Compiled Resource {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- generated_file_message = "{}Generating {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
-
- env.Append(CXXCOMSTR=[compile_source_message])
- env.Append(CCCOMSTR=[compile_source_message])
- env.Append(SHCCCOMSTR=[compile_shared_source_message])
- env.Append(SHCXXCOMSTR=[compile_shared_source_message])
- env.Append(ARCOMSTR=[link_library_message])
- env.Append(RANLIBCOMSTR=[ranlib_library_message])
- env.Append(SHLINKCOMSTR=[link_shared_library_message])
- env.Append(LINKCOMSTR=[link_program_message])
- env.Append(JARCOMSTR=[java_library_message])
- env.Append(JAVACCOMSTR=[java_compile_source_message])
- env.Append(RCCOMSTR=[compiled_resource_message])
- env.Append(GENCOMSTR=[generated_file_message])
+ compile_source_message = "{}Compiling {}$SOURCE{} ...{}".format(*colors)
+ java_compile_source_message = "{}Compiling {}$SOURCE{} ...{}".format(*colors)
+ compile_shared_source_message = "{}Compiling shared {}$SOURCE{} ...{}".format(*colors)
+ link_program_message = "{}Linking Program {}$TARGET{} ...{}".format(*colors)
+ link_library_message = "{}Linking Static Library {}$TARGET{} ...{}".format(*colors)
+ ranlib_library_message = "{}Ranlib Library {}$TARGET{} ...{}".format(*colors)
+ link_shared_library_message = "{}Linking Shared Library {}$TARGET{} ...{}".format(*colors)
+ java_library_message = "{}Creating Java Archive {}$TARGET{} ...{}".format(*colors)
+ compiled_resource_message = "{}Creating Compiled Resource {}$TARGET{} ...{}".format(*colors)
+ generated_file_message = "{}Generating {}$TARGET{} ...{}".format(*colors)
+
+ env["CXXCOMSTR"] = compile_source_message
+ env["CCCOMSTR"] = compile_source_message
+ env["SHCCCOMSTR"] = compile_shared_source_message
+ env["SHCXXCOMSTR"] = compile_shared_source_message
+ env["ARCOMSTR"] = link_library_message
+ env["RANLIBCOMSTR"] = ranlib_library_message
+ env["SHLINKCOMSTR"] = link_shared_library_message
+ env["LINKCOMSTR"] = link_program_message
+ env["JARCOMSTR"] = java_library_message
+ env["JAVACCOMSTR"] = java_compile_source_message
+ env["RCCOMSTR"] = compiled_resource_message
+ env["GENCOMSTR"] = generated_file_message
def disable_warnings(self):
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 1ed335fe99..09a037fd28 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -7342,6 +7342,16 @@ bool TextServerAdvanced::_is_valid_identifier(const String &p_string) const {
return true;
}
+bool TextServerAdvanced::_is_valid_letter(char32_t p_unicode) const {
+#ifndef ICU_STATIC_DATA
+ if (!icu_data_loaded) {
+ return TextServer::is_valid_letter(p_unicode);
+ }
+#endif
+
+ return u_isalpha(p_unicode);
+}
+
TextServerAdvanced::TextServerAdvanced() {
_insert_num_systems_lang();
_insert_feature_sets();
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index 1cd73a6999..7e29f984c1 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -988,6 +988,7 @@ public:
MODBIND1RC(String, strip_diacritics, const String &);
MODBIND1RC(bool, is_valid_identifier, const String &);
+ MODBIND1RC(bool, is_valid_letter, char32_t);
MODBIND2RC(String, string_to_upper, const String &, const String &);
MODBIND2RC(String, string_to_lower, const String &, const String &);
diff --git a/modules/text_server_fb/gdextension_build/SConstruct b/modules/text_server_fb/gdextension_build/SConstruct
index 29801ede8e..07940719eb 100644
--- a/modules/text_server_fb/gdextension_build/SConstruct
+++ b/modules/text_server_fb/gdextension_build/SConstruct
@@ -1,10 +1,20 @@
#!/usr/bin/env python
import atexit
-import os
import sys
import methods
import time
+# Enable ANSI escape code support on Windows 10 and later (for colored console output).
+# <https://github.com/python/cpython/issues/73245>
+if sys.platform == "win32":
+ from ctypes import windll, c_int, byref
+
+ stdout_handle = windll.kernel32.GetStdHandle(c_int(-11))
+ mode = c_int(0)
+ windll.kernel32.GetConsoleMode(c_int(stdout_handle), byref(mode))
+ mode = c_int(mode.value | 4)
+ windll.kernel32.SetConsoleMode(c_int(stdout_handle), mode)
+
# For the reference:
# - CCFLAGS are compilation flags shared between C and C++
# - CFLAGS are for C-specific compilation flags
@@ -28,7 +38,7 @@ opts.Add(BoolVariable("verbose", "Enable verbose output for the compilation", Fa
opts.Update(env)
if not env["verbose"]:
- methods.no_verbose(sys, env)
+ methods.no_verbose(env)
# ThorVG
if env["thorvg_enabled"] and env["freetype_enabled"]:
@@ -311,9 +321,16 @@ Default(library)
def print_elapsed_time():
- elapsed_time_sec = round(time.time() - time_at_start, 3)
- time_ms = round((elapsed_time_sec % 1) * 1000)
- print("[Time elapsed: {}.{:03}]".format(time.strftime("%H:%M:%S", time.gmtime(elapsed_time_sec)), time_ms))
+ elapsed_time_sec = round(time.time() - time_at_start, 2)
+ time_centiseconds = round((elapsed_time_sec % 1) * 100)
+ print(
+ "{}[Time elapsed: {}.{:02}]{}".format(
+ methods.ANSI.GRAY,
+ time.strftime("%H:%M:%S", time.gmtime(elapsed_time_sec)),
+ time_centiseconds,
+ methods.ANSI.RESET,
+ )
+ )
atexit.register(print_elapsed_time)
diff --git a/modules/text_server_fb/gdextension_build/methods.py b/modules/text_server_fb/gdextension_build/methods.py
index 32dbc59fd4..3453c3e8f0 100644
--- a/modules/text_server_fb/gdextension_build/methods.py
+++ b/modules/text_server_fb/gdextension_build/methods.py
@@ -1,66 +1,75 @@
import os
import sys
+from enum import Enum
+# Colors are disabled in non-TTY environments such as pipes. This means
+# that if output is redirected to a file, it won't contain color codes.
+# Colors are always enabled on continuous integration.
+_colorize = bool(sys.stdout.isatty() or os.environ.get("CI"))
-def no_verbose(sys, env):
- colors = {}
- # Colors are disabled in non-TTY environments such as pipes. This means
- # that if output is redirected to a file, it will not contain color codes
- if sys.stdout.isatty():
- colors["blue"] = "\033[0;94m"
- colors["bold_blue"] = "\033[1;94m"
- colors["reset"] = "\033[0m"
- else:
- colors["blue"] = ""
- colors["bold_blue"] = ""
- colors["reset"] = ""
+class ANSI(Enum):
+ """
+ Enum class for adding ansi colorcodes directly into strings.
+ Automatically converts values to strings representing their
+ internal value, or an empty string in a non-colorized scope.
+ """
+
+ RESET = "\x1b[0m"
+
+ BOLD = "\x1b[1m"
+ ITALIC = "\x1b[3m"
+ UNDERLINE = "\x1b[4m"
+ STRIKETHROUGH = "\x1b[9m"
+ REGULAR = "\x1b[22;23;24;29m"
+
+ BLACK = "\x1b[30m"
+ RED = "\x1b[31m"
+ GREEN = "\x1b[32m"
+ YELLOW = "\x1b[33m"
+ BLUE = "\x1b[34m"
+ MAGENTA = "\x1b[35m"
+ CYAN = "\x1b[36m"
+ WHITE = "\x1b[37m"
+
+ PURPLE = "\x1b[38;5;93m"
+ PINK = "\x1b[38;5;206m"
+ ORANGE = "\x1b[38;5;214m"
+ GRAY = "\x1b[38;5;244m"
+
+ def __str__(self) -> str:
+ global _colorize
+ return str(self.value) if _colorize else ""
+
+
+def no_verbose(env):
+ colors = [ANSI.BLUE, ANSI.BOLD, ANSI.REGULAR, ANSI.RESET]
# There is a space before "..." to ensure that source file names can be
# Ctrl + clicked in the VS Code terminal.
- compile_source_message = "{}Compiling {}$SOURCE{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- java_compile_source_message = "{}Compiling {}$SOURCE{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- compile_shared_source_message = "{}Compiling shared {}$SOURCE{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- link_program_message = "{}Linking Program {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- link_library_message = "{}Linking Static Library {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- ranlib_library_message = "{}Ranlib Library {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- link_shared_library_message = "{}Linking Shared Library {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- java_library_message = "{}Creating Java Archive {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- compiled_resource_message = "{}Creating Compiled Resource {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- generated_file_message = "{}Generating {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
-
- env.Append(CXXCOMSTR=[compile_source_message])
- env.Append(CCCOMSTR=[compile_source_message])
- env.Append(SHCCCOMSTR=[compile_shared_source_message])
- env.Append(SHCXXCOMSTR=[compile_shared_source_message])
- env.Append(ARCOMSTR=[link_library_message])
- env.Append(RANLIBCOMSTR=[ranlib_library_message])
- env.Append(SHLINKCOMSTR=[link_shared_library_message])
- env.Append(LINKCOMSTR=[link_program_message])
- env.Append(JARCOMSTR=[java_library_message])
- env.Append(JAVACCOMSTR=[java_compile_source_message])
- env.Append(RCCOMSTR=[compiled_resource_message])
- env.Append(GENCOMSTR=[generated_file_message])
+ compile_source_message = "{}Compiling {}$SOURCE{} ...{}".format(*colors)
+ java_compile_source_message = "{}Compiling {}$SOURCE{} ...{}".format(*colors)
+ compile_shared_source_message = "{}Compiling shared {}$SOURCE{} ...{}".format(*colors)
+ link_program_message = "{}Linking Program {}$TARGET{} ...{}".format(*colors)
+ link_library_message = "{}Linking Static Library {}$TARGET{} ...{}".format(*colors)
+ ranlib_library_message = "{}Ranlib Library {}$TARGET{} ...{}".format(*colors)
+ link_shared_library_message = "{}Linking Shared Library {}$TARGET{} ...{}".format(*colors)
+ java_library_message = "{}Creating Java Archive {}$TARGET{} ...{}".format(*colors)
+ compiled_resource_message = "{}Creating Compiled Resource {}$TARGET{} ...{}".format(*colors)
+ generated_file_message = "{}Generating {}$TARGET{} ...{}".format(*colors)
+
+ env["CXXCOMSTR"] = compile_source_message
+ env["CCCOMSTR"] = compile_source_message
+ env["SHCCCOMSTR"] = compile_shared_source_message
+ env["SHCXXCOMSTR"] = compile_shared_source_message
+ env["ARCOMSTR"] = link_library_message
+ env["RANLIBCOMSTR"] = ranlib_library_message
+ env["SHLINKCOMSTR"] = link_shared_library_message
+ env["LINKCOMSTR"] = link_program_message
+ env["JARCOMSTR"] = java_library_message
+ env["JAVACCOMSTR"] = java_compile_source_message
+ env["RCCOMSTR"] = compiled_resource_message
+ env["GENCOMSTR"] = generated_file_message
def disable_warnings(self):