diff options
Diffstat (limited to 'modules')
99 files changed, 1293 insertions, 1047 deletions
diff --git a/modules/astcenc/SCsub b/modules/astcenc/SCsub new file mode 100644 index 0000000000..0f04f2bc28 --- /dev/null +++ b/modules/astcenc/SCsub @@ -0,0 +1,55 @@ +#!/usr/bin/env python + +Import("env") +Import("env_modules") + +env_astcenc = env_modules.Clone() + +# Thirdparty source files + +thirdparty_obj = [] + +thirdparty_dir = "#thirdparty/astcenc/" +thirdparty_sources = [ + "astcenc_averages_and_directions.cpp", + "astcenc_block_sizes.cpp", + "astcenc_color_quantize.cpp", + "astcenc_color_unquantize.cpp", + "astcenc_compress_symbolic.cpp", + "astcenc_compute_variance.cpp", + "astcenc_decompress_symbolic.cpp", + "astcenc_diagnostic_trace.cpp", + "astcenc_entry.cpp", + "astcenc_find_best_partitioning.cpp", + "astcenc_ideal_endpoints_and_weights.cpp", + "astcenc_image.cpp", + "astcenc_integer_sequence.cpp", + "astcenc_mathlib.cpp", + "astcenc_mathlib_softfloat.cpp", + "astcenc_partition_tables.cpp", + "astcenc_percentile_tables.cpp", + "astcenc_pick_best_endpoint_format.cpp", + "astcenc_platform_isa_detection.cpp", + "astcenc_quantization.cpp", + "astcenc_symbolic_physical.cpp", + "astcenc_weight_align.cpp", + "astcenc_weight_quant_xfer_tables.cpp", +] +thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + +env_astcenc.Prepend(CPPPATH=[thirdparty_dir]) + +env_thirdparty = env_astcenc.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) +env.modules_sources += thirdparty_obj + +# Godot source files + +module_obj = [] + +env_astcenc.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj + +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/astcenc/config.py b/modules/astcenc/config.py new file mode 100644 index 0000000000..eb565b85b9 --- /dev/null +++ b/modules/astcenc/config.py @@ -0,0 +1,6 @@ +def can_build(env, platform): + return env.editor_build + + +def configure(env): + pass diff --git a/modules/astcenc/image_compress_astcenc.cpp b/modules/astcenc/image_compress_astcenc.cpp new file mode 100644 index 0000000000..ce10201343 --- /dev/null +++ b/modules/astcenc/image_compress_astcenc.cpp @@ -0,0 +1,251 @@ +/**************************************************************************/ +/* image_compress_astcenc.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 "image_compress_astcenc.h" + +#include "core/os/os.h" +#include "core/string/print_string.h" + +#include <astcenc.h> + +void _compress_astc(Image *r_img, float p_lossy_quality, Image::ASTCFormat p_format) { + uint64_t start_time = OS::get_singleton()->get_ticks_msec(); + + // TODO: See how to handle lossy quality. + + Image::Format img_format = r_img->get_format(); + if (img_format >= Image::FORMAT_DXT1) { + return; // Do not compress, already compressed. + } + + bool is_hdr = false; + if ((img_format >= Image::FORMAT_RH) && (img_format <= Image::FORMAT_RGBE9995)) { + is_hdr = true; + r_img->convert(Image::FORMAT_RGBAF); + } else { + r_img->convert(Image::FORMAT_RGBA8); + } + + // Determine encoder output format from our enum. + + Image::Format target_format = Image::FORMAT_RGBA8; + astcenc_profile profile = ASTCENC_PRF_LDR; + unsigned int block_x = 4; + unsigned int block_y = 4; + + if (p_format == Image::ASTCFormat::ASTC_FORMAT_4x4) { + if (is_hdr) { + target_format = Image::FORMAT_ASTC_4x4_HDR; + profile = ASTCENC_PRF_HDR; + } else { + target_format = Image::FORMAT_ASTC_4x4; + } + } else if (p_format == Image::ASTCFormat::ASTC_FORMAT_8x8) { + if (is_hdr) { + target_format = Image::FORMAT_ASTC_8x8_HDR; + profile = ASTCENC_PRF_HDR; + } else { + target_format = Image::FORMAT_ASTC_8x8; + } + block_x = 8; + block_y = 8; + } + + // Compress image data and (if required) mipmaps. + + const bool mipmaps = r_img->has_mipmaps(); + int width = r_img->get_width(); + int height = r_img->get_height(); + + print_verbose(vformat("astcenc: Encoding image size %dx%d to format %s%s.", width, height, Image::get_format_name(target_format), mipmaps ? ", with mipmaps" : "")); + + // Initialize astcenc. + + astcenc_config config; + config.block_x = block_x; + config.block_y = block_y; + config.profile = profile; + const float quality = ASTCENC_PRE_MEDIUM; + + astcenc_error status = astcenc_config_init(profile, block_x, block_y, block_x, quality, 0, &config); + ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS, + vformat("astcenc: Configuration initialization failed: %s.", astcenc_get_error_string(status))); + + // Context allocation. + + astcenc_context *context; + const unsigned int thread_count = OS::get_singleton()->get_processor_count(); + + status = astcenc_context_alloc(&config, thread_count, &context); + ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS, + vformat("astcenc: Context allocation failed: %s.", astcenc_get_error_string(status))); + + // Compress image. + + Vector<uint8_t> image_data = r_img->get_data(); + uint8_t *slices = image_data.ptrw(); + + astcenc_image image; + image.dim_x = width; + image.dim_y = height; + image.dim_z = 1; + image.data_type = ASTCENC_TYPE_U8; + if (is_hdr) { + image.data_type = ASTCENC_TYPE_F32; + } + image.data = reinterpret_cast<void **>(&slices); + + // Compute the number of ASTC blocks in each dimension. + unsigned int block_count_x = (width + block_x - 1) / block_x; + unsigned int block_count_y = (height + block_y - 1) / block_y; + size_t comp_len = block_count_x * block_count_y * 16; + + Vector<uint8_t> compressed_data; + compressed_data.resize(comp_len); + compressed_data.fill(0); + + const astcenc_swizzle swizzle = { + ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A + }; + + status = astcenc_compress_image(context, &image, &swizzle, compressed_data.ptrw(), comp_len, 0); + ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS, + vformat("astcenc: ASTC image compression failed: %s.", astcenc_get_error_string(status))); + + // Replace original image with compressed one. + + r_img->set_data(width, height, mipmaps, target_format, compressed_data); + + print_verbose(vformat("astcenc: Encoding took %s ms.", rtos(OS::get_singleton()->get_ticks_msec() - start_time))); +} + +void _decompress_astc(Image *r_img) { + uint64_t start_time = OS::get_singleton()->get_ticks_msec(); + + // Determine decompression parameters from image format. + + Image::Format img_format = r_img->get_format(); + bool is_hdr = false; + unsigned int block_x = 0; + unsigned int block_y = 0; + if (img_format == Image::FORMAT_ASTC_4x4) { + block_x = 4; + block_y = 4; + is_hdr = false; + } else if (img_format == Image::FORMAT_ASTC_4x4_HDR) { + block_x = 4; + block_y = 4; + is_hdr = true; + } else if (img_format == Image::FORMAT_ASTC_8x8) { + block_x = 8; + block_y = 8; + is_hdr = false; + } else if (img_format == Image::FORMAT_ASTC_8x8_HDR) { + block_x = 8; + block_y = 8; + is_hdr = true; + } else { + ERR_FAIL_MSG("astcenc: Cannot decompress Image with a non-ASTC format."); + } + + // Initialize astcenc. + + astcenc_profile profile = ASTCENC_PRF_LDR; + if (is_hdr) { + profile = ASTCENC_PRF_HDR; + } + astcenc_config config; + const float quality = ASTCENC_PRE_MEDIUM; + + astcenc_error status = astcenc_config_init(profile, block_x, block_y, block_x, quality, 0, &config); + ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS, + vformat("astcenc: Configuration initialization failed: %s.", astcenc_get_error_string(status))); + + // Context allocation. + + astcenc_context *context = nullptr; + const unsigned int thread_count = OS::get_singleton()->get_processor_count(); + + status = astcenc_context_alloc(&config, thread_count, &context); + ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS, + vformat("astcenc: Context allocation failed: %s.", astcenc_get_error_string(status))); + + // Decompress image. + + const bool mipmaps = r_img->has_mipmaps(); + int width = r_img->get_width(); + int height = r_img->get_height(); + + astcenc_image image; + image.dim_x = width; + image.dim_y = height; + image.dim_z = 1; + image.data_type = ASTCENC_TYPE_U8; + Image::Format target_format = Image::FORMAT_RGBA8; + if (is_hdr) { + target_format = Image::FORMAT_RGBAF; + image.data_type = ASTCENC_TYPE_F32; + } + + Vector<uint8_t> image_data = r_img->get_data(); + + Vector<uint8_t> new_image_data; + new_image_data.resize(Image::get_image_data_size(width, height, target_format, false)); + new_image_data.fill(0); + uint8_t *slices = new_image_data.ptrw(); + image.data = reinterpret_cast<void **>(&slices); + + const astcenc_swizzle swizzle = { + ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A + }; + + status = astcenc_decompress_image(context, image_data.ptr(), image_data.size(), &image, &swizzle, 0); + ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS, + vformat("astcenc: ASTC decompression failed: %s.", astcenc_get_error_string(status))); + ERR_FAIL_COND_MSG(image.dim_z > 1, + "astcenc: ASTC decompression failed because this is a 3D texture, which is not supported."); + + // Replace original image with compressed one. + + Image::Format image_format = Image::FORMAT_RGBA8; + if (image.data_type == ASTCENC_TYPE_F32) { + image_format = Image::FORMAT_RGBAF; + } else if (image.data_type == ASTCENC_TYPE_U8) { + image_format = Image::FORMAT_RGBA8; + } else if (image.data_type == ASTCENC_TYPE_F16) { + image_format = Image::FORMAT_RGBAH; + } else { + ERR_FAIL_MSG("astcenc: ASTC decompression failed with an unknown format."); + } + + r_img->set_data(image.dim_x, image.dim_y, mipmaps, image_format, new_image_data); + + print_verbose(vformat("astcenc: Decompression took %s ms.", rtos(OS::get_singleton()->get_ticks_msec() - start_time))); +} diff --git a/modules/astcenc/image_compress_astcenc.h b/modules/astcenc/image_compress_astcenc.h new file mode 100644 index 0000000000..a197a91e0d --- /dev/null +++ b/modules/astcenc/image_compress_astcenc.h @@ -0,0 +1,39 @@ +/**************************************************************************/ +/* image_compress_astcenc.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 IMAGE_COMPRESS_ASTCENC_H +#define IMAGE_COMPRESS_ASTCENC_H + +#include "core/io/image.h" + +void _compress_astc(Image *r_img, float p_lossy_quality, Image::ASTCFormat p_format); +void _decompress_astc(Image *r_img); + +#endif // IMAGE_COMPRESS_ASTCENC_H diff --git a/modules/astcenc/register_types.cpp b/modules/astcenc/register_types.cpp new file mode 100644 index 0000000000..0bb1c3432f --- /dev/null +++ b/modules/astcenc/register_types.cpp @@ -0,0 +1,48 @@ +/**************************************************************************/ +/* register_types.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 "register_types.h" + +#include "image_compress_astcenc.h" + +void initialize_astcenc_module(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { + return; + } + + Image::_image_compress_astc_func = _compress_astc; + Image::_image_decompress_astc = _decompress_astc; +} + +void uninitialize_astcenc_module(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { + return; + } +} diff --git a/modules/astcenc/register_types.h b/modules/astcenc/register_types.h new file mode 100644 index 0000000000..636da9ff8b --- /dev/null +++ b/modules/astcenc/register_types.h @@ -0,0 +1,39 @@ +/**************************************************************************/ +/* register_types.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 ASTCENC_REGISTER_TYPES_H +#define ASTCENC_REGISTER_TYPES_H + +#include "modules/register_module_types.h" + +void initialize_astcenc_module(ModuleInitializationLevel p_level); +void uninitialize_astcenc_module(ModuleInitializationLevel p_level); + +#endif // ASTCENC_REGISTER_TYPES_H diff --git a/modules/bmp/image_loader_bmp.cpp b/modules/bmp/image_loader_bmp.cpp index 3b789a235f..5b451fbf6b 100644 --- a/modules/bmp/image_loader_bmp.cpp +++ b/modules/bmp/image_loader_bmp.cpp @@ -58,6 +58,13 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image, ERR_FAIL_COND_V_MSG(height % 8 != 0, ERR_UNAVAILABLE, vformat("1-bpp BMP images must have a height that is a multiple of 8, but the imported BMP is %d pixels tall.", int(height))); + } else if (bits_per_pixel == 2) { + // Requires bit unpacking... + ERR_FAIL_COND_V_MSG(width % 4 != 0, ERR_UNAVAILABLE, + vformat("2-bpp BMP images must have a width that is a multiple of 4, but the imported BMP is %d pixels wide.", int(width))); + ERR_FAIL_COND_V_MSG(height % 4 != 0, ERR_UNAVAILABLE, + vformat("2-bpp BMP images must have a height that is a multiple of 4, but the imported BMP is %d pixels tall.", int(height))); + } else if (bits_per_pixel == 4) { // Requires bit unpacking... ERR_FAIL_COND_V_MSG(width % 2 != 0, ERR_UNAVAILABLE, @@ -88,7 +95,7 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image, const uint32_t line_width = (width_bytes + 3) & ~3; // The actual data traversal is determined by - // the data width in case of 8/4/1 bit images + // the data width in case of 8/4/2/1 bit images const uint32_t w = bits_per_pixel >= 24 ? width : width_bytes; const uint8_t *line = p_buffer + (line_width * (height - 1)); const uint8_t *end_buffer = p_buffer + p_header.bmp_file_header.bmp_file_size - p_header.bmp_file_header.bmp_file_offset; @@ -114,6 +121,17 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image, index += 8; line_ptr += 1; } break; + case 2: { + uint8_t color_index = *line_ptr; + + write_buffer[index + 0] = (color_index >> 6) & 3; + write_buffer[index + 1] = (color_index >> 4) & 3; + write_buffer[index + 2] = (color_index >> 2) & 3; + write_buffer[index + 3] = color_index & 3; + + index += 4; + line_ptr += 1; + } break; case 4: { uint8_t color_index = *line_ptr; diff --git a/modules/csg/editor/csg_gizmos.cpp b/modules/csg/editor/csg_gizmos.cpp index 61787691a3..2c533cb36d 100644 --- a/modules/csg/editor/csg_gizmos.cpp +++ b/modules/csg/editor/csg_gizmos.cpp @@ -217,7 +217,7 @@ void CSGShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int return; } - Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); + EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); ur->create_action(TTR("Change Sphere Shape Radius")); ur->add_do_method(s, "set_radius", s->get_radius()); ur->add_undo_method(s, "set_radius", p_restore); @@ -231,7 +231,7 @@ void CSGShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int return; } - Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); + EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); ur->create_action(TTR("Change Box Shape Size")); ur->add_do_method(s, "set_size", s->get_size()); ur->add_undo_method(s, "set_size", p_restore); @@ -249,7 +249,7 @@ void CSGShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int return; } - Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); + EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); if (p_id == 0) { ur->create_action(TTR("Change Cylinder Radius")); ur->add_do_method(s, "set_radius", s->get_radius()); @@ -274,7 +274,7 @@ void CSGShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int return; } - Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); + EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); if (p_id == 0) { ur->create_action(TTR("Change Torus Inner Radius")); ur->add_do_method(s, "set_inner_radius", s->get_inner_radius()); diff --git a/modules/etcpak/image_compress_etcpak.cpp b/modules/etcpak/image_compress_etcpak.cpp index b5192bd664..a6aeec54cc 100644 --- a/modules/etcpak/image_compress_etcpak.cpp +++ b/modules/etcpak/image_compress_etcpak.cpp @@ -33,8 +33,8 @@ #include "core/os/os.h" #include "core/string/print_string.h" -#include "thirdparty/etcpak/ProcessDxtc.hpp" -#include "thirdparty/etcpak/ProcessRGB.hpp" +#include <ProcessDxtc.hpp> +#include <ProcessRGB.hpp> EtcpakType _determine_etc_type(Image::UsedChannels p_channels) { switch (p_channels) { @@ -130,7 +130,7 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_qua } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5) { target_format = Image::FORMAT_DXT5; } else { - ERR_FAIL_MSG("Invalid or unsupported Etcpak compression format."); + ERR_FAIL_MSG("Invalid or unsupported etcpak compression format, not ETC or DXT."); } // Compress image data and (if required) mipmaps. @@ -171,7 +171,7 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_qua const uint8_t *src_read = r_img->get_data().ptr(); - print_verbose(vformat("ETCPAK: Encoding image size %dx%d to format %s.", width, height, Image::get_format_name(target_format))); + print_verbose(vformat("etcpak: Encoding image size %dx%d to format %s%s.", width, height, Image::get_format_name(target_format), mipmaps ? ", with mipmaps" : "")); int dest_size = Image::get_image_data_size(width, height, target_format, mipmaps); Vector<uint8_t> dest_data; @@ -232,12 +232,12 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_qua } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5 || p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5_RA_AS_RG) { CompressDxt5(src_mip_read, dest_mip_write, blocks, mip_w); } else { - ERR_FAIL_MSG("Invalid or unsupported Etcpak compression format."); + ERR_FAIL_MSG("etcpak: Invalid or unsupported compression format."); } } // Replace original image with compressed one. r_img->set_data(width, height, mipmaps, target_format, dest_data); - print_verbose(vformat("ETCPAK encode took %s ms.", rtos(OS::get_singleton()->get_ticks_msec() - start_time))); + print_verbose(vformat("etcpak: Encoding took %s ms.", rtos(OS::get_singleton()->get_ticks_msec() - start_time))); } diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index fd748ea569..3fe741a582 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -553,6 +553,7 @@ @icon("res://path/to/class/icon.svg") [/codeblock] [b]Note:[/b] Only the script can have a custom icon. Inner classes are not supported. + [b]Note:[/b] As annotations describe their subject, the [code]@icon[/code] annotation must be placed before the class definition and inheritance. </description> </annotation> <annotation name="@onready"> @@ -585,6 +586,7 @@ @tool extends Node [/codeblock] + [b]Note:[/b] As annotations describe their subject, the [code]@tool[/code] annotation must be placed before the class definition and inheritance. </description> </annotation> <annotation name="@warning_ignore" qualifiers="vararg"> diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml index 8246c96c15..1a102bd16f 100644 --- a/modules/gdscript/doc_classes/GDScript.xml +++ b/modules/gdscript/doc_classes/GDScript.xml @@ -11,12 +11,6 @@ <link title="GDScript documentation index">$DOCS_URL/tutorials/scripting/gdscript/index.html</link> </tutorials> <methods> - <method name="get_as_byte_code" qualifiers="const"> - <return type="PackedByteArray" /> - <description> - Returns byte code for the script source code. - </description> - </method> <method name="new" qualifiers="vararg"> <return type="Variant" /> <description> diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 28f478e9cd..ffe01eaa18 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -1007,17 +1007,6 @@ void GDScript::_get_property_list(List<PropertyInfo> *p_properties) const { void GDScript::_bind_methods() { ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "new", &GDScript::_new, MethodInfo("new")); - - ClassDB::bind_method(D_METHOD("get_as_byte_code"), &GDScript::get_as_byte_code); -} - -Vector<uint8_t> GDScript::get_as_byte_code() const { - return Vector<uint8_t>(); -}; - -// TODO: Fully remove this. There's not this kind of "bytecode" anymore. -Error GDScript::load_byte_code(const String &p_path) { - return ERR_COMPILATION_FAILED; } void GDScript::set_path(const String &p_path, bool p_take_over) { @@ -2647,8 +2636,6 @@ Ref<Resource> ResourceFormatLoaderGDScript::load(const String &p_path, const Str Error err; Ref<GDScript> scr = GDScriptCache::get_full_script(p_path, err, "", p_cache_mode == CACHE_MODE_IGNORE); - // TODO: Reintroduce binary and encrypted scripts. - if (scr.is_null()) { // Don't fail loading because of parsing error. scr.instantiate(); @@ -2663,9 +2650,6 @@ Ref<Resource> ResourceFormatLoaderGDScript::load(const String &p_path, const Str void ResourceFormatLoaderGDScript::get_recognized_extensions(List<String> *p_extensions) const { p_extensions->push_back("gd"); - // TODO: Reintroduce binary and encrypted scripts. - // p_extensions->push_back("gdc"); - // p_extensions->push_back("gde"); } bool ResourceFormatLoaderGDScript::handles_type(const String &p_type) const { @@ -2674,8 +2658,7 @@ bool ResourceFormatLoaderGDScript::handles_type(const String &p_type) const { String ResourceFormatLoaderGDScript::get_resource_type(const String &p_path) const { String el = p_path.get_extension().to_lower(); - // TODO: Reintroduce binary and encrypted scripts. - if (el == "gd" /*|| el == "gdc" || el == "gde"*/) { + if (el == "gd") { return "GDScript"; } return ""; diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index a53785a98d..71184ac2da 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -250,9 +250,6 @@ public: virtual void set_path(const String &p_path, bool p_take_over = false) override; String get_script_path() const; Error load_source_code(const String &p_path); - Error load_byte_code(const String &p_path); - - Vector<uint8_t> get_as_byte_code() const; bool get_property_default_value(const StringName &p_property, Variant &r_value) const override; diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 1b488d560c..e04a962dcb 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -1722,21 +1722,52 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { if (list_resolved) { variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; variable_type.kind = GDScriptParser::DataType::BUILTIN; - variable_type.builtin_type = Variant::INT; // Can this ever be a float or something else? - p_for->variable->set_datatype(variable_type); + variable_type.builtin_type = Variant::INT; } else if (p_for->list) { resolve_node(p_for->list, false); - if (p_for->list->datatype.has_container_element_type()) { - variable_type = p_for->list->datatype.get_container_element_type(); - variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; - } else if (p_for->list->datatype.is_typed_container_type()) { - variable_type = p_for->list->datatype.get_typed_container_type(); - variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; - } else { - // Last resort - // TODO: Must other cases be handled? Must we mark as unsafe? - variable_type.type_source = GDScriptParser::DataType::UNDETECTED; + GDScriptParser::DataType list_type = p_for->list->get_datatype(); + if (!list_type.is_hard_type()) { + mark_node_unsafe(p_for->list); + } + if (list_type.is_variant()) { + variable_type.kind = GDScriptParser::DataType::VARIANT; + mark_node_unsafe(p_for->list); + } else if (list_type.has_container_element_type()) { + variable_type = list_type.get_container_element_type(); + variable_type.type_source = list_type.type_source; + } else if (list_type.is_typed_container_type()) { + variable_type = list_type.get_typed_container_type(); + variable_type.type_source = list_type.type_source; + } else if (list_type.builtin_type == Variant::INT || list_type.builtin_type == Variant::FLOAT || list_type.builtin_type == Variant::STRING) { + variable_type.type_source = list_type.type_source; + variable_type.kind = GDScriptParser::DataType::BUILTIN; + variable_type.builtin_type = list_type.builtin_type; + } else if (list_type.builtin_type == Variant::VECTOR2I || list_type.builtin_type == Variant::VECTOR3I) { + variable_type.type_source = list_type.type_source; + variable_type.kind = GDScriptParser::DataType::BUILTIN; + variable_type.builtin_type = Variant::INT; + } else if (list_type.builtin_type == Variant::VECTOR2 || list_type.builtin_type == Variant::VECTOR3) { + variable_type.type_source = list_type.type_source; + variable_type.kind = GDScriptParser::DataType::BUILTIN; + variable_type.builtin_type = Variant::FLOAT; + } else if (list_type.builtin_type == Variant::OBJECT) { + GDScriptParser::DataType return_type; + List<GDScriptParser::DataType> par_types; + int default_arg_count = 0; + bool is_static = false; + bool is_vararg = false; + if (get_function_signature(p_for->list, false, list_type, CoreStringNames::get_singleton()->_iter_get, return_type, par_types, default_arg_count, is_static, is_vararg)) { + variable_type = return_type; + variable_type.type_source = list_type.type_source; + } else if (!list_type.is_hard_type()) { + variable_type.kind = GDScriptParser::DataType::VARIANT; + } else { + push_error(vformat(R"(Unable to iterate on object of type "%s".)", list_type.to_string()), p_for->list); + } + } else if (list_type.builtin_type == Variant::ARRAY || list_type.builtin_type == Variant::DICTIONARY || !list_type.is_hard_type()) { variable_type.kind = GDScriptParser::DataType::VARIANT; + } else { + push_error(vformat(R"(Unable to iterate on value of type "%s".)", list_type.to_string()), p_for->list); } } if (p_for->variable) { @@ -2910,8 +2941,8 @@ void GDScriptAnalyzer::reduce_identifier_from_base_set_class(GDScriptParser::Ide p_identifier->set_datatype(p_identifier_datatype); Error err = OK; - GDScript *scr = GDScriptCache::get_full_script(p_identifier_datatype.script_path, err).ptr(); - ERR_FAIL_COND_MSG(err != OK, "Error while getting full script."); + GDScript *scr = GDScriptCache::get_shallow_script(p_identifier_datatype.script_path, err).ptr(); + ERR_FAIL_COND_MSG(err != OK, vformat(R"(Error while getting cache for script "%s".)", p_identifier_datatype.script_path)); scr = scr->find_class(p_identifier_datatype.class_type->fqcn); p_identifier->reduced_value = scr; p_identifier->is_constant = true; @@ -3140,7 +3171,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident for (int i = 0; i < current_enum->values.size(); i++) { const GDScriptParser::EnumNode::Value &element = current_enum->values[i]; if (element.identifier->name == p_identifier->name) { - StringName enum_name = current_enum->identifier->name ? current_enum->identifier->name : UNNAMED_ENUM; + StringName enum_name = current_enum->identifier ? current_enum->identifier->name : UNNAMED_ENUM; GDScriptParser::DataType type = make_enum_type(enum_name, parser->current_class->fqcn, false); if (element.parent_enum->identifier) { type.enum_type = element.parent_enum->identifier->name; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 77c6690d20..d63a1b4536 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -213,7 +213,7 @@ static bool _have_exact_arguments(const MethodBind *p_method, const Vector<GDScr } GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root, bool p_initializer, const GDScriptCodeGenerator::Address &p_index_addr) { - if (p_expression->is_constant && !p_expression->get_datatype().is_meta_type) { + if (p_expression->is_constant && !(p_expression->get_datatype().is_meta_type && p_expression->get_datatype().kind == GDScriptParser::DataType::CLASS)) { return codegen.add_constant(p_expression->reduced_value); } diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 0a1ae46927..3fc0924b4c 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -80,7 +80,7 @@ Ref<Script> GDScriptLanguage::make_template(const String &p_template, const Stri } processed_template = processed_template.replace("_BASE_", p_base_class_name) - .replace("_CLASS_", p_class_name) + .replace("_CLASS_", p_class_name.to_pascal_case()) .replace("_TS_", _get_indentation()); scr->set_source_code(processed_template); return scr; @@ -1275,6 +1275,14 @@ static void _find_identifiers(const GDScriptParser::CompletionContext &p_context } r_result.insert(option.display, option); } + + // Global classes + List<StringName> global_classes; + ScriptServer::get_global_class_list(&global_classes); + for (const StringName &E : global_classes) { + ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_OTHER_USER_CODE); + r_result.insert(option.display, option); + } } static GDScriptCompletionIdentifier _type_from_variant(const Variant &p_value) { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 97d5c1d8b2..f5d3306376 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -540,43 +540,28 @@ void GDScriptParser::parse_program() { head = alloc_node<ClassNode>(); head->fqcn = script_path; current_class = head; + bool can_have_class_or_extends = true; - // If we happen to parse an annotation before extends or class_name keywords, track it. - // @tool is allowed, but others should fail. - AnnotationNode *premature_annotation = nullptr; - - if (match(GDScriptTokenizer::Token::ANNOTATION)) { - // Check for @tool, script-level, or standalone annotation. + while (match(GDScriptTokenizer::Token::ANNOTATION)) { AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL); if (annotation != nullptr) { - if (annotation->name == SNAME("@tool")) { - // TODO: don't allow @tool anywhere else. (Should all script annotations be the first thing?). - _is_tool = true; - if (previous.type != GDScriptTokenizer::Token::NEWLINE) { - push_error(R"(Expected newline after "@tool" annotation.)"); - } - // @tool annotation has no specific target. - annotation->apply(this, nullptr); - } else if (annotation->applies_to(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE)) { - premature_annotation = annotation; - if (previous.type != GDScriptTokenizer::Token::NEWLINE) { - push_error(R"(Expected newline after a standalone annotation.)"); - } + if (annotation->applies_to(AnnotationInfo::SCRIPT)) { annotation->apply(this, head); } else { - premature_annotation = annotation; annotation_stack.push_back(annotation); + // This annotation must appear after script-level annotations + // and class_name/extends (ex: could be @onready or @export), + // so we stop looking for script-level stuff. + can_have_class_or_extends = false; + break; } } } - for (bool should_break = false; !should_break;) { + while (can_have_class_or_extends) { // Order here doesn't matter, but there should be only one of each at most. switch (current.type) { case GDScriptTokenizer::Token::CLASS_NAME: - if (premature_annotation != nullptr) { - push_error(R"("class_name" should be used before annotations (except @tool).)"); - } advance(); if (head->identifier != nullptr) { push_error(R"("class_name" can only be used once.)"); @@ -585,9 +570,6 @@ void GDScriptParser::parse_program() { } break; case GDScriptTokenizer::Token::EXTENDS: - if (premature_annotation != nullptr) { - push_error(R"("extends" should be used before annotations (except @tool).)"); - } advance(); if (head->extends_used) { push_error(R"("extends" can only be used once.)"); @@ -597,7 +579,8 @@ void GDScriptParser::parse_program() { } break; default: - should_break = true; + // No tokens are allowed between script annotations and class/extends. + can_have_class_or_extends = false; break; } @@ -606,21 +589,6 @@ void GDScriptParser::parse_program() { } } - if (match(GDScriptTokenizer::Token::ANNOTATION)) { - // Check for a script-level, or standalone annotation. - AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL); - if (annotation != nullptr) { - if (annotation->applies_to(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE)) { - if (previous.type != GDScriptTokenizer::Token::NEWLINE) { - push_error(R"(Expected newline after a standalone annotation.)"); - } - annotation->apply(this, head); - } else { - annotation_stack.push_back(annotation); - } - } - } - parse_class_body(true); complete_extents(head); diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp index edb2e8117a..b9e6921034 100644 --- a/modules/gdscript/language_server/gdscript_text_document.cpp +++ b/modules/gdscript/language_server/gdscript_text_document.cpp @@ -107,6 +107,7 @@ void GDScriptTextDocument::didSave(const Variant &p_param) { } else { scr->reload(true); } + scr->update_exports(); ScriptEditor::get_singleton()->update_docs_from_script(scr); } } diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp index 33b5ae6942..b6feaadccf 100644 --- a/modules/gdscript/register_types.cpp +++ b/modules/gdscript/register_types.cpp @@ -71,21 +71,18 @@ class EditorExportGDScript : public EditorExportPlugin { public: virtual void _export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features) override { - int script_mode = EditorExportPreset::MODE_SCRIPT_COMPILED; String script_key; const Ref<EditorExportPreset> &preset = get_export_preset(); if (preset.is_valid()) { - script_mode = preset->get_script_export_mode(); script_key = preset->get_script_encryption_key().to_lower(); } - if (!p_path.ends_with(".gd") || script_mode == EditorExportPreset::MODE_SCRIPT_TEXT) { + if (!p_path.ends_with(".gd")) { return; } - // TODO: Re-add compiled GDScript on export. return; } diff --git a/modules/gdscript/tests/README.md b/modules/gdscript/tests/README.md index 6e54085962..361d586d32 100644 --- a/modules/gdscript/tests/README.md +++ b/modules/gdscript/tests/README.md @@ -4,5 +4,5 @@ The `scripts/` folder contains integration tests in the form of GDScript files and output files. See the -[Integration tests for GDScript documentation](https://docs.godotengine.org/en/latest/development/cpp/unit_testing.html#integration-tests-for-gdscript) +[Integration tests for GDScript documentation](https://docs.godotengine.org/en/latest/contributing/development/core_and_modules/unit_testing.html#integration-tests-for-gdscript) for information about creating and running GDScript integration tests. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.gd new file mode 100644 index 0000000000..cf56a0a933 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.gd @@ -0,0 +1,6 @@ +const constant_float = 1.0 + +func test(): + for x in constant_float: + if x is String: + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.out new file mode 100644 index 0000000000..e309831b3e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Expression is of type "float" so it can't be of type "String". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.gd new file mode 100644 index 0000000000..5ee8ac19e1 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.gd @@ -0,0 +1,6 @@ +const constant_int = 1 + +func test(): + for x in constant_int: + if x is String: + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.out new file mode 100644 index 0000000000..54c190cf8a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Expression is of type "int" so it can't be of type "String". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.gd new file mode 100644 index 0000000000..b3db4f3b49 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.gd @@ -0,0 +1,6 @@ +enum { enum_value = 1 } + +func test(): + for x in enum_value: + if x is String: + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.out new file mode 100644 index 0000000000..54c190cf8a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Expression is of type "int" so it can't be of type "String". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.gd new file mode 100644 index 0000000000..87c54f7402 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.gd @@ -0,0 +1,6 @@ +func test(): + var hard_float := 1.0 + + for x in hard_float: + if x is String: + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.out new file mode 100644 index 0000000000..e309831b3e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Expression is of type "float" so it can't be of type "String". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.gd new file mode 100644 index 0000000000..2a43f5a930 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.gd @@ -0,0 +1,6 @@ +func test(): + var hard_int := 1 + + for x in hard_int: + if x is String: + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.out new file mode 100644 index 0000000000..54c190cf8a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Expression is of type "int" so it can't be of type "String". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.gd new file mode 100644 index 0000000000..c3920d35b3 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.gd @@ -0,0 +1,14 @@ +class Iterator: + func _iter_init(_count): + return true + func _iter_next(_count): + return false + func _iter_get(_count) -> StringName: + return &'custom' + +func test(): + var hard_iterator := Iterator.new() + + for x in hard_iterator: + if x is int: + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.out new file mode 100644 index 0000000000..a48591a3b4 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Expression is of type "StringName" so it can't be of type "int". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.gd new file mode 100644 index 0000000000..b36d87aabe --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.gd @@ -0,0 +1,6 @@ +func test(): + var hard_string := 'a' + + for x in hard_string: + if x is int: + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.out new file mode 100644 index 0000000000..92c5ebc599 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Expression is of type "String" so it can't be of type "int". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.gd new file mode 100644 index 0000000000..060a8bedf9 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.gd @@ -0,0 +1,3 @@ +func test(): + for x in true: + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.out new file mode 100644 index 0000000000..94cb038885 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Unable to iterate on value of type "bool". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.gd new file mode 100644 index 0000000000..6cfc822482 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.gd @@ -0,0 +1,4 @@ +func test(): + for x in 1: + if x is String: + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.out new file mode 100644 index 0000000000..54c190cf8a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Expression is of type "int" so it can't be of type "String". diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_unnamed_depend.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_unnamed_depend.gd new file mode 100644 index 0000000000..f351fc1f7b --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_unnamed_depend.gd @@ -0,0 +1,7 @@ +enum { + V1, + V2 = V1, +} + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_unnamed_depend.out b/modules/gdscript/tests/scripts/analyzer/features/enum_unnamed_depend.out new file mode 100644 index 0000000000..d73c5eb7cd --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_unnamed_depend.out @@ -0,0 +1 @@ +GDTEST_OK diff --git a/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.gd b/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.gd new file mode 100644 index 0000000000..7b74be6f2c --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.gd @@ -0,0 +1,15 @@ +func test(): + var variant_int: Variant = 1 + var weak_int = 1 + + for x in variant_int: + if x is String: + print('never') + print(x) + + for x in weak_int: + if x is String: + print('never') + print(x) + + print('ok') diff --git a/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.out b/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.out new file mode 100644 index 0000000000..7677671cfd --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.out @@ -0,0 +1,4 @@ +GDTEST_OK +0 +0 +ok diff --git a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd index 179e454073..0085b3f367 100644 --- a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd +++ b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd @@ -1,6 +1,6 @@ -# Error here. `class_name` should be used *before* annotations, not after (except @tool). -@icon("res://path/to/optional/icon.svg") +# Error here. Annotations should be used before `class_name`, not after. class_name HelloWorld +@icon("res://path/to/optional/icon.svg") func test(): pass diff --git a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out index 02b33c8692..a598ff8424 100644 --- a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out +++ b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out @@ -1,2 +1,2 @@ GDTEST_PARSER_ERROR -"class_name" should be used before annotations (except @tool). +Annotation "@icon" is not allowed in this level. diff --git a/modules/gdscript/tests/scripts/parser/features/class_name.gd b/modules/gdscript/tests/scripts/parser/features/class_name.gd index 8bd188e247..19009e433d 100644 --- a/modules/gdscript/tests/scripts/parser/features/class_name.gd +++ b/modules/gdscript/tests/scripts/parser/features/class_name.gd @@ -1,5 +1,5 @@ -class_name HelloWorld @icon("res://path/to/optional/icon.svg") +class_name HelloWorld func test(): pass diff --git a/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.gd b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.gd new file mode 100644 index 0000000000..81355e0255 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.gd @@ -0,0 +1,51 @@ +const constant_float = 1.0 +const constant_int = 1 +enum { enum_value = 1 } + +class Iterator: + func _iter_init(_count): + return true + func _iter_next(_count): + return false + func _iter_get(_count) -> StringName: + return &'custom' + +func test(): + var hard_float := 1.0 + var hard_int := 1 + var hard_string := '0' + var hard_iterator := Iterator.new() + + var variant_float: Variant = hard_float + var variant_int: Variant = hard_int + var variant_string: Variant = hard_string + var variant_iterator: Variant = hard_iterator + + for i in 1.0: + print(typeof(i) == TYPE_FLOAT) + for i in 1: + print(typeof(i) == TYPE_INT) + for i in 'a': + print(typeof(i) == TYPE_STRING) + for i in Iterator.new(): + print(typeof(i) == TYPE_STRING_NAME) + + for i in hard_float: + print(typeof(i) == TYPE_FLOAT) + for i in hard_int: + print(typeof(i) == TYPE_INT) + for i in hard_string: + print(typeof(i) == TYPE_STRING) + for i in hard_iterator: + print(typeof(i) == TYPE_STRING_NAME) + + for i in variant_float: + print(typeof(i) == TYPE_FLOAT) + for i in variant_int: + print(typeof(i) == TYPE_INT) + for i in variant_string: + print(typeof(i) == TYPE_STRING) + for i in variant_iterator: + print(typeof(i) == TYPE_STRING_NAME) + + print('ok') diff --git a/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.out b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.out new file mode 100644 index 0000000000..b3e82d52ef --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.out @@ -0,0 +1,14 @@ +GDTEST_OK +true +true +true +true +true +true +true +true +true +true +true +true +ok diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml index f313f4b28f..d7e8141eb1 100644 --- a/modules/gltf/doc_classes/GLTFDocument.xml +++ b/modules/gltf/doc_classes/GLTFDocument.xml @@ -15,7 +15,7 @@ <param index="2" name="state" type="GLTFState" /> <param index="3" name="flags" type="int" default="0" /> <description> - Takes a [PackedByteArray] defining a gLTF and returns a [GLTFState] object through the [param state] parameter. + Takes a [PackedByteArray] defining a GLTF and imports the data to the given [GLTFState] object through the [param state] parameter. [b]Note:[/b] The [param base_path] tells [method append_from_buffer] where to find dependencies and can be empty. </description> </method> @@ -26,7 +26,7 @@ <param index="2" name="flags" type="int" default="0" /> <param index="3" name="base_path" type="String" default="""" /> <description> - Takes a path to a gLTF file and returns a [GLTFState] object through the [param state] parameter. + Takes a path to a GLTF file and imports the data at that file path to the given [GLTFState] object through the [param state] parameter. [b]Note:[/b] The [param base_path] tells [method append_from_file] where to find dependencies and can be empty. </description> </method> @@ -36,14 +36,14 @@ <param index="1" name="state" type="GLTFState" /> <param index="2" name="flags" type="int" default="0" /> <description> - Takes a Godot Engine scene node and returns a [GLTFState] object through the [param state] parameter. + Takes a Godot Engine scene node and exports it and its descendants to the given [GLTFState] object through the [param state] parameter. </description> </method> <method name="generate_buffer"> <return type="PackedByteArray" /> <param index="0" name="state" type="GLTFState" /> <description> - Takes a [GLTFState] object through the [param state] parameter and returns a gLTF [PackedByteArray]. + Takes a [GLTFState] object through the [param state] parameter and returns a GLTF [PackedByteArray]. </description> </method> <method name="generate_scene"> diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 788a70f640..5634c51c61 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -3124,10 +3124,11 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p // API for that in Godot, so we'd have to load as a buffer (i.e. embedded in // the material), so we do this only as fallback. Ref<Texture2D> texture = ResourceLoader::load(uri); + String extension = uri.get_extension().to_lower(); if (texture.is_valid()) { p_state->images.push_back(texture); continue; - } else if (mimetype == "image/png" || mimetype == "image/jpeg") { + } else if (mimetype == "image/png" || mimetype == "image/jpeg" || extension == "png" || extension == "jpg" || extension == "jpeg") { // Fallback to loading as byte array. // This enables us to support the spec's requirement that we honor mimetype // regardless of file URI. diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp index 67bb99de16..183190460e 100644 --- a/modules/gridmap/editor/grid_map_editor_plugin.cpp +++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp @@ -461,7 +461,7 @@ void GridMapEditor::_delete_selection() { return; } - Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("GridMap Delete Selection")); for (int i = selection.begin.x; i <= selection.end.x; i++) { for (int j = selection.begin.y; j <= selection.end.y; j++) { @@ -482,7 +482,7 @@ void GridMapEditor::_fill_selection() { return; } - Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("GridMap Fill Selection")); for (int i = selection.begin.x; i <= selection.end.x; i++) { for (int j = selection.begin.y; j <= selection.end.y; j++) { @@ -576,7 +576,7 @@ void GridMapEditor::_do_paste() { rot = node->get_basis_with_orthogonal_index(paste_indicator.orientation); Vector3 ofs = paste_indicator.current - paste_indicator.click; - Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("GridMap Paste Selection")); for (const ClipboardItem &item : clipboard_items) { @@ -664,7 +664,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D } else { if ((mb->get_button_index() == MouseButton::RIGHT && input_action == INPUT_ERASE) || (mb->get_button_index() == MouseButton::LEFT && input_action == INPUT_PAINT)) { if (set_items.size()) { - Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("GridMap Paint")); for (const SetItem &si : set_items) { undo_redo->add_do_method(node, "set_cell_item", si.position, si.new_value, si.new_orientation); @@ -686,7 +686,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D } if (mb->get_button_index() == MouseButton::LEFT && input_action == INPUT_SELECT) { - Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("GridMap Selection")); undo_redo->add_do_method(this, "_set_selection", selection.active, selection.begin, selection.end); undo_redo->add_undo_method(this, "_set_selection", last_selection.active, last_selection.begin, last_selection.end); diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index e39127e0a1..7606465b8a 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -113,6 +113,11 @@ void CSharpLanguage::init() { BindingsGenerator::handle_cmdline_args(cmdline_args); #endif + GLOBAL_DEF("dotnet/project/assembly_name", ""); +#ifdef TOOLS_ENABLED + GLOBAL_DEF("dotnet/project/solution_directory", ""); +#endif + gdmono = memnew(GDMono); gdmono->initialize(); diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs index 745a8b73f8..db96003baf 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs @@ -17,6 +17,8 @@ namespace GodotTools.Export { public partial class ExportPlugin : EditorExportPlugin { + public override string _GetName() => "C#"; + private List<string> _tempFolders = new List<string>(); public void RegisterExportSettings() diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index de10c04e31..08147d9f6a 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -411,8 +411,6 @@ namespace GodotTools _editorSettings = editorInterface.GetEditorSettings(); - GodotSharpDirs.RegisterProjectSettings(); - _errorDialog = new AcceptDialog(); editorBaseControl.AddChild(_errorDialog); diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs index 4e892be55c..7624989092 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs @@ -48,12 +48,6 @@ namespace GodotTools.Internals } } - public static void RegisterProjectSettings() - { - GlobalDef("dotnet/project/assembly_name", ""); - GlobalDef("dotnet/project/solution_directory", ""); - } - public static void DetermineProjectLocation() { static string DetermineProjectName() diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index c0d88553ad..03cbfda1bd 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -1864,12 +1864,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte p_output.append("\n" OPEN_BLOCK_L1); if (getter) { - p_output.append(INDENT2 "get\n" - - // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that) - "#pragma warning disable CS0618 // Disable warning about obsolete method\n" - - OPEN_BLOCK_L2 INDENT3); + p_output.append(INDENT2 "get\n" OPEN_BLOCK_L2 INDENT3); p_output.append("return "); p_output.append(getter->proxy_name + "("); @@ -1884,21 +1879,11 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte p_output.append(itos(p_iprop.index)); } } - p_output.append(");\n" - - CLOSE_BLOCK_L2 - - // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that) - "#pragma warning restore CS0618\n"); + p_output.append(");\n" CLOSE_BLOCK_L2); } if (setter) { - p_output.append(INDENT2 "set\n" - - // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that) - "#pragma warning disable CS0618 // Disable warning about obsolete method\n" - - OPEN_BLOCK_L2 INDENT3); + p_output.append(INDENT2 "set\n" OPEN_BLOCK_L2 INDENT3); p_output.append(setter->proxy_name + "("); if (p_iprop.index != -1) { @@ -1912,12 +1897,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte p_output.append(itos(p_iprop.index) + ", "); } } - p_output.append("value);\n" - - CLOSE_BLOCK_L2 - - // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that) - "#pragma warning restore CS0618\n"); + p_output.append("value);\n" CLOSE_BLOCK_L2); } p_output.append(CLOSE_BLOCK_L1); @@ -3056,12 +3036,10 @@ bool BindingsGenerator::_populate_object_type_interfaces() { HashMap<StringName, StringName>::Iterator accessor = accessor_methods.find(imethod.cname); if (accessor) { - const PropertyInterface *accessor_property = itype.find_property_by_name(accessor->value); - - // We only deprecate an accessor method if it's in the same class as the property. It's easier this way, but also - // we don't know if an accessor method in a different class could have other purposes, so better leave those untouched. - imethod.is_deprecated = true; - imethod.deprecation_message = imethod.proxy_name + " is deprecated. Use the " + accessor_property->proxy_name + " property instead."; + // We only make internal an accessor method if it's in the same class as the property. + // It's easier this way, but also we don't know if an accessor method in a different class + // could have other purposes, so better leave those untouched. + imethod.is_internal = true; } if (itype.class_doc) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs index a2916d4ae8..0e46b63b59 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs @@ -437,48 +437,25 @@ namespace Godot /// <summary> /// Returns <see langword="true"/> if the <see cref="AABB"/> overlaps with <paramref name="with"/> /// (i.e. they have at least one point in common). - /// - /// If <paramref name="includeBorders"/> is <see langword="true"/>, - /// they will also be considered overlapping if their borders touch, - /// even without intersection. /// </summary> /// <param name="with">The other <see cref="AABB"/> to check for intersections with.</param> - /// <param name="includeBorders">Whether or not to consider borders.</param> /// <returns> /// A <see langword="bool"/> for whether or not they are intersecting. /// </returns> - public readonly bool Intersects(AABB with, bool includeBorders = false) + public readonly bool Intersects(AABB with) { - if (includeBorders) - { - if (_position.x > with._position.x + with._size.x) - return false; - if (_position.x + _size.x < with._position.x) - return false; - if (_position.y > with._position.y + with._size.y) - return false; - if (_position.y + _size.y < with._position.y) - return false; - if (_position.z > with._position.z + with._size.z) - return false; - if (_position.z + _size.z < with._position.z) - return false; - } - else - { - if (_position.x >= with._position.x + with._size.x) - return false; - if (_position.x + _size.x <= with._position.x) - return false; - if (_position.y >= with._position.y + with._size.y) - return false; - if (_position.y + _size.y <= with._position.y) - return false; - if (_position.z >= with._position.z + with._size.z) - return false; - if (_position.z + _size.z <= with._position.z) - return false; - } + if (_position.x >= with._position.x + with._size.x) + return false; + if (_position.x + _size.x <= with._position.x) + return false; + if (_position.y >= with._position.y + with._size.y) + return false; + if (_position.y + _size.y <= with._position.y) + return false; + if (_position.z >= with._position.z + with._size.z) + return false; + if (_position.z + _size.z <= with._position.z) + return false; return true; } @@ -586,6 +563,16 @@ namespace Godot } /// <summary> + /// Returns <see langword="true"/> if this <see cref="AABB"/> is finite, by calling + /// <see cref="Mathf.IsFinite"/> on each component. + /// </summary> + /// <returns>Whether this vector is finite or not.</returns> + public readonly bool IsFinite() + { + return _position.IsFinite() && _size.IsFinite(); + } + + /// <summary> /// Returns a larger <see cref="AABB"/> that contains this <see cref="AABB"/> and <paramref name="with"/>. /// </summary> /// <param name="with">The other <see cref="AABB"/>.</param> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs index 5d390a298d..b57317e1d0 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs @@ -120,31 +120,6 @@ namespace Godot } /// <summary> - /// The scale of this basis. - /// </summary> - /// <value>Equivalent to the lengths of each column vector, but negative if the determinant is negative.</value> - public Vector3 Scale - { - readonly get - { - real_t detSign = Mathf.Sign(Determinant()); - return detSign * new Vector3 - ( - Column0.Length(), - Column1.Length(), - Column2.Length() - ); - } - set - { - value /= Scale; // Value becomes what's called "delta_scale" in core. - Column0 *= value.x; - Column1 *= value.y; - Column2 *= value.z; - } - } - - /// <summary> /// Access whole columns in the form of <see cref="Vector3"/>. /// </summary> /// <param name="column">Which column vector.</param> @@ -493,12 +468,6 @@ namespace Godot } } - /// <summary> - /// Returns the basis's rotation in the form of a quaternion. - /// See <see cref="GetEuler"/> if you need Euler angles, but keep in - /// mind that quaternions should generally be preferred to Euler angles. - /// </summary> - /// <returns>A <see cref="Quaternion"/> representing the basis's rotation.</returns> internal readonly Quaternion GetQuaternion() { real_t trace = Row0[0] + Row1[1] + Row2[2]; @@ -573,106 +542,18 @@ namespace Godot } /// <summary> - /// Get rows by index. Rows are not very useful for user code, - /// but are more efficient for some internal calculations. - /// </summary> - /// <param name="index">Which row.</param> - /// <exception cref="ArgumentOutOfRangeException"> - /// <paramref name="index"/> is not 0, 1 or 2. - /// </exception> - /// <returns>One of <c>Row0</c>, <c>Row1</c>, or <c>Row2</c>.</returns> - public readonly Vector3 GetRow(int index) - { - switch (index) - { - case 0: - return Row0; - case 1: - return Row1; - case 2: - return Row2; - default: - throw new ArgumentOutOfRangeException(nameof(index)); - } - } - - /// <summary> - /// Sets rows by index. Rows are not very useful for user code, - /// but are more efficient for some internal calculations. + /// Assuming that the matrix is the combination of a rotation and scaling, + /// return the absolute value of scaling factors along each axis. /// </summary> - /// <param name="index">Which row.</param> - /// <param name="value">The vector to set the row to.</param> - /// <exception cref="ArgumentOutOfRangeException"> - /// <paramref name="index"/> is not 0, 1 or 2. - /// </exception> - public void SetRow(int index, Vector3 value) + public readonly Vector3 GetScale() { - switch (index) - { - case 0: - Row0 = value; - return; - case 1: - Row1 = value; - return; - case 2: - Row2 = value; - return; - default: - throw new ArgumentOutOfRangeException(nameof(index)); - } - } - - /// <summary> - /// This function considers a discretization of rotations into - /// 24 points on unit sphere, lying along the vectors (x, y, z) with - /// each component being either -1, 0, or 1, and returns the index - /// of the point best representing the orientation of the object. - /// It is mainly used by the <see cref="GridMap"/> editor. - /// - /// For further details, refer to the Godot source code. - /// </summary> - /// <returns>The orthogonal index.</returns> - public readonly int GetOrthogonalIndex() - { - var orth = this; - - for (int i = 0; i < 3; i++) - { - for (int j = 0; j < 3; j++) - { - var row = orth.GetRow(i); - - real_t v = row[j]; - - if (v > 0.5f) - { - v = 1.0f; - } - else if (v < -0.5f) - { - v = -1.0f; - } - else - { - v = 0f; - } - - row[j] = v; - - orth.SetRow(i, row); - } - } - - for (int i = 0; i < 24; i++) - { - if (orth == _orthoBases[i]) - { - return i; - } - } - - return 0; + real_t detSign = Mathf.Sign(Determinant()); + return detSign * new Vector3 + ( + Column0.Length(), + Column1.Length(), + Column2.Length() + ); } /// <summary> @@ -709,6 +590,16 @@ namespace Godot ); } + /// <summary> + /// Returns <see langword="true"/> if this basis is finite, by calling + /// <see cref="Mathf.IsFinite"/> on each component. + /// </summary> + /// <returns>Whether this vector is finite or not.</returns> + public readonly bool IsFinite() + { + return Row0.IsFinite() && Row1.IsFinite() && Row2.IsFinite(); + } + internal readonly Basis Lerp(Basis to, real_t weight) { Basis b = this; @@ -896,7 +787,7 @@ namespace Godot /// <param name="quaternion">The quaternion to create the basis from.</param> public Basis(Quaternion quaternion) { - real_t s = 2.0f / quaternion.LengthSquared; + real_t s = 2.0f / quaternion.LengthSquared(); real_t xs = quaternion.x * s; real_t ys = quaternion.y * s; @@ -925,26 +816,26 @@ namespace Godot public Basis(Vector3 axis, real_t angle) { Vector3 axisSq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z); - real_t cosine = Mathf.Cos(angle); - Row0.x = axisSq.x + cosine * (1.0f - axisSq.x); - Row1.y = axisSq.y + cosine * (1.0f - axisSq.y); - Row2.z = axisSq.z + cosine * (1.0f - axisSq.z); + (real_t sin, real_t cos) = Mathf.SinCos(angle); + + Row0.x = axisSq.x + cos * (1.0f - axisSq.x); + Row1.y = axisSq.y + cos * (1.0f - axisSq.y); + Row2.z = axisSq.z + cos * (1.0f - axisSq.z); - real_t sine = Mathf.Sin(angle); - real_t t = 1.0f - cosine; + real_t t = 1.0f - cos; real_t xyzt = axis.x * axis.y * t; - real_t zyxs = axis.z * sine; + real_t zyxs = axis.z * sin; Row0.y = xyzt - zyxs; Row1.x = xyzt + zyxs; xyzt = axis.x * axis.z * t; - zyxs = axis.y * sine; + zyxs = axis.y * sin; Row0.z = xyzt + zyxs; Row2.x = xyzt - zyxs; xyzt = axis.y * axis.z * t; - zyxs = axis.x * sine; + zyxs = axis.x * sin; Row1.z = xyzt - zyxs; Row2.y = xyzt + zyxs; } @@ -967,8 +858,20 @@ namespace Godot // We need to assign the struct fields here first so we can't do it that way... } - // Arguments are named such that xy is equal to calling x.y - internal Basis(real_t xx, real_t yx, real_t zx, real_t xy, real_t yy, real_t zy, real_t xz, real_t yz, real_t zz) + /// <summary> + /// Constructs a transformation matrix from the given components. + /// Arguments are named such that xy is equal to calling <c>x.y</c>. + /// </summary> + /// <param name="xx">The X component of the X column vector, accessed via <c>b.x.x</c> or <c>[0][0]</c>.</param> + /// <param name="yx">The X component of the Y column vector, accessed via <c>b.y.x</c> or <c>[1][0]</c>.</param> + /// <param name="zx">The X component of the Z column vector, accessed via <c>b.z.x</c> or <c>[2][0]</c>.</param> + /// <param name="xy">The Y component of the X column vector, accessed via <c>b.x.y</c> or <c>[0][1]</c>.</param> + /// <param name="yy">The Y component of the Y column vector, accessed via <c>b.y.y</c> or <c>[1][1]</c>.</param> + /// <param name="zy">The Y component of the Z column vector, accessed via <c>b.y.y</c> or <c>[2][1]</c>.</param> + /// <param name="xz">The Z component of the X column vector, accessed via <c>b.x.y</c> or <c>[0][2]</c>.</param> + /// <param name="yz">The Z component of the Y column vector, accessed via <c>b.y.y</c> or <c>[1][2]</c>.</param> + /// <param name="zz">The Z component of the Z column vector, accessed via <c>b.y.y</c> or <c>[2][2]</c>.</param> + public Basis(real_t xx, real_t yx, real_t zx, real_t xy, real_t yy, real_t zy, real_t xz, real_t yz, real_t zz) { Row0 = new Vector3(xx, yx, zx); Row1 = new Vector3(xy, yy, zy); @@ -982,19 +885,29 @@ namespace Godot /// <param name="order">The order to compose the Euler angles.</param> public static Basis FromEuler(Vector3 euler, EulerOrder order = EulerOrder.Yxz) { - real_t c, s; - - c = Mathf.Cos(euler.x); - s = Mathf.Sin(euler.x); - Basis xmat = new Basis(new Vector3(1, 0, 0), new Vector3(0, c, s), new Vector3(0, -s, c)); + (real_t sin, real_t cos) = Mathf.SinCos(euler.x); + Basis xmat = new Basis + ( + new Vector3(1, 0, 0), + new Vector3(0, cos, sin), + new Vector3(0, -sin, cos) + ); - c = Mathf.Cos(euler.y); - s = Mathf.Sin(euler.y); - Basis ymat = new Basis(new Vector3(c, 0, -s), new Vector3(0, 1, 0), new Vector3(s, 0, c)); + (sin, cos) = Mathf.SinCos(euler.y); + Basis ymat = new Basis + ( + new Vector3(cos, 0, -sin), + new Vector3(0, 1, 0), + new Vector3(sin, 0, cos) + ); - c = Mathf.Cos(euler.z); - s = Mathf.Sin(euler.z); - Basis zmat = new Basis(new Vector3(c, s, 0), new Vector3(-s, c, 0), new Vector3(0, 0, 1)); + (sin, cos) = Mathf.SinCos(euler.z); + Basis zmat = new Basis + ( + new Vector3(cos, sin, 0), + new Vector3(-sin, cos, 0), + new Vector3(0, 0, 1) + ); switch (order) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs index 4075a878d2..2effdecf40 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs @@ -359,24 +359,6 @@ namespace Godot } /// <summary> - /// Returns the result of the linear interpolation between - /// this color and <paramref name="to"/> by color amount <paramref name="weight"/>. - /// </summary> - /// <param name="to">The destination color for interpolation.</param> - /// <param name="weight">A color with components on the range of 0.0 to 1.0, representing the amount of interpolation.</param> - /// <returns>The resulting color of the interpolation.</returns> - public readonly Color Lerp(Color to, Color weight) - { - return new Color - ( - (float)Mathf.Lerp(r, to.r, weight.r), - (float)Mathf.Lerp(g, to.g, weight.g), - (float)Mathf.Lerp(b, to.b, weight.b), - (float)Mathf.Lerp(a, to.a, weight.a) - ); - } - - /// <summary> /// Returns the color converted to the sRGB color space. /// This method assumes the original color is in the linear color space. /// See also <see cref="SrgbToLinear"/> which performs the opposite operation. diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs index 3f9e986f62..b2cb0f5e6b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs @@ -440,6 +440,17 @@ namespace Godot } /// <summary> + /// Returns whether <paramref name="s"/> is a finite value, i.e. it is not + /// <see cref="NaN"/>, positive infinite, or negative infinity. + /// </summary> + /// <param name="s">The value to check.</param> + /// <returns>A <see langword="bool"/> for whether or not the value is a finite value.</returns> + public static bool IsFinite(real_t s) + { + return real_t.IsFinite(s); + } + + /// <summary> /// Returns whether <paramref name="s"/> is an infinity value (either positive infinity or negative infinity). /// </summary> /// <param name="s">The value to check.</param> @@ -460,10 +471,11 @@ namespace Godot } /// <summary> - /// Returns <see langword="true"/> if <paramref name="s"/> is approximately zero. + /// Returns <see langword="true"/> if <paramref name="s"/> is zero or almost zero. /// The comparison is done using a tolerance calculation with <see cref="Epsilon"/>. /// - /// This method is faster than using <see cref="IsEqualApprox(real_t, real_t)"/> with one value as zero. + /// This method is faster than using <see cref="IsEqualApprox(real_t, real_t)"/> with + /// one value as zero. /// </summary> /// <param name="s">The value to check.</param> /// <returns>A <see langword="bool"/> for whether or not the value is nearly zero.</returns> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs index ea05c1547c..72a1868964 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs @@ -83,6 +83,17 @@ namespace Godot } /// <summary> + /// Returns the sine and cosine of angle <paramref name="s"/> in radians. + /// </summary> + /// <param name="s">The angle in radians.</param> + /// <returns>The sine and cosine of that angle.</returns> + public static (real_t Sin, real_t Cos) SinCos(real_t s) + { + (double sin, double cos) = Math.SinCos(s); + return ((real_t)sin, (real_t)cos); + } + + /// <summary> /// Returns <see langword="true"/> if <paramref name="a"/> and <paramref name="b"/> are approximately /// equal to each other. /// The comparison is done using the provided tolerance value. diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs index 42c6b0a37e..8a125e3c73 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs @@ -15,7 +15,7 @@ namespace Godot private Vector3 _normal; /// <summary> - /// The normal of the plane, which must be normalized. + /// The normal of the plane, which must be a unit vector. /// In the scalar equation of the plane <c>ax + by + cz = d</c>, this is /// the vector <c>(a, b, c)</c>, where <c>d</c> is the <see cref="D"/> property. /// </summary> @@ -85,23 +85,6 @@ namespace Godot public real_t D { get; set; } /// <summary> - /// The center of the plane, the point where the normal line intersects the plane. - /// </summary> - /// <value>Equivalent to <see cref="Normal"/> multiplied by <see cref="D"/>.</value> - public Vector3 Center - { - readonly get - { - return _normal * D; - } - set - { - _normal = value.Normalized(); - D = value.Length(); - } - } - - /// <summary> /// Returns the shortest distance from this plane to the position <paramref name="point"/>. /// </summary> /// <param name="point">The position to use for the calculation.</param> @@ -112,6 +95,16 @@ namespace Godot } /// <summary> + /// Returns the center of the plane, the point on the plane closest to the origin. + /// The point where the normal line going through the origin intersects the plane. + /// </summary> + /// <value>Equivalent to <see cref="Normal"/> multiplied by <see cref="D"/>.</value> + public readonly Vector3 GetCenter() + { + return _normal * D; + } + + /// <summary> /// Returns <see langword="true"/> if point is inside the plane. /// Comparison uses a custom minimum tolerance threshold. /// </summary> @@ -155,7 +148,7 @@ namespace Godot /// <param name="from">The start of the ray.</param> /// <param name="dir">The direction of the ray, normalized.</param> /// <returns>The intersection, or <see langword="null"/> if none is found.</returns> - public readonly Vector3? IntersectRay(Vector3 from, Vector3 dir) + public readonly Vector3? IntersectsRay(Vector3 from, Vector3 dir) { real_t den = _normal.Dot(dir); @@ -183,7 +176,7 @@ namespace Godot /// <param name="begin">The start of the line segment.</param> /// <param name="end">The end of the line segment.</param> /// <returns>The intersection, or <see langword="null"/> if none is found.</returns> - public readonly Vector3? IntersectSegment(Vector3 begin, Vector3 end) + public readonly Vector3? IntersectsSegment(Vector3 begin, Vector3 end) { Vector3 segment = begin - end; real_t den = _normal.Dot(segment); @@ -205,6 +198,16 @@ namespace Godot } /// <summary> + /// Returns <see langword="true"/> if this plane is finite, by calling + /// <see cref="Mathf.IsFinite"/> on each component. + /// </summary> + /// <returns>Whether this vector is finite or not.</returns> + public readonly bool IsFinite() + { + return _normal.IsFinite() && Mathf.IsFinite(D); + } + + /// <summary> /// Returns <see langword="true"/> if <paramref name="point"/> is located above the plane. /// </summary> /// <param name="point">The point to check.</param> @@ -280,10 +283,21 @@ namespace Godot } /// <summary> + /// Constructs a <see cref="Plane"/> from a <paramref name="normal"/> vector. + /// The plane will intersect the origin. + /// </summary> + /// <param name="normal">The normal of the plane, must be a unit vector.</param> + public Plane(Vector3 normal) + { + _normal = normal; + D = 0; + } + + /// <summary> /// Constructs a <see cref="Plane"/> from a <paramref name="normal"/> vector and /// the plane's distance to the origin <paramref name="d"/>. /// </summary> - /// <param name="normal">The normal of the plane, must be normalized.</param> + /// <param name="normal">The normal of the plane, must be a unit vector.</param> /// <param name="d">The plane's distance from the origin. This value is typically non-negative.</param> public Plane(Vector3 normal, real_t d) { @@ -295,7 +309,7 @@ namespace Godot /// Constructs a <see cref="Plane"/> from a <paramref name="normal"/> vector and /// a <paramref name="point"/> on the plane. /// </summary> - /// <param name="normal">The normal of the plane, must be normalized.</param> + /// <param name="normal">The normal of the plane, must be a unit vector.</param> /// <param name="point">The point on the plane.</param> public Plane(Vector3 normal, Vector3 point) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs index fd37f8d9e8..f11b3c553a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs @@ -381,14 +381,14 @@ namespace Godot } real_t radians = Mathf.DegToRad(fovyDegrees / (real_t)2.0); real_t deltaZ = zFar - zNear; - real_t sine = Mathf.Sin(radians); + (real_t sin, real_t cos) = Mathf.SinCos(radians); - if ((deltaZ == 0) || (sine == 0) || (aspect == 0)) + if ((deltaZ == 0) || (sin == 0) || (aspect == 0)) { return Zero; } - real_t cotangent = Mathf.Cos(radians) / sine; + real_t cotangent = cos / sin; Projection proj = Projection.Identity; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs index bd0dea0c1c..8e4f9178f7 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs @@ -97,27 +97,6 @@ namespace Godot } /// <summary> - /// Returns the length (magnitude) of the quaternion. - /// </summary> - /// <seealso cref="LengthSquared"/> - /// <value>Equivalent to <c>Mathf.Sqrt(LengthSquared)</c>.</value> - public readonly real_t Length - { - get { return Mathf.Sqrt(LengthSquared); } - } - - /// <summary> - /// Returns the squared length (squared magnitude) of the quaternion. - /// This method runs faster than <see cref="Length"/>, so prefer it if - /// you need to compare quaternions or need the squared length for some formula. - /// </summary> - /// <value>Equivalent to <c>Dot(this)</c>.</value> - public readonly real_t LengthSquared - { - get { return Dot(this); } - } - - /// <summary> /// Returns the angle between this quaternion and <paramref name="to"/>. /// This is the magnitude of the angle you would need to rotate /// by to get from one to the other. @@ -340,12 +319,22 @@ namespace Godot } /// <summary> + /// Returns <see langword="true"/> if this quaternion is finite, by calling + /// <see cref="Mathf.IsFinite"/> on each component. + /// </summary> + /// <returns>Whether this vector is finite or not.</returns> + public readonly bool IsFinite() + { + return Mathf.IsFinite(x) && Mathf.IsFinite(y) && Mathf.IsFinite(z) && Mathf.IsFinite(w); + } + + /// <summary> /// Returns whether the quaternion is normalized or not. /// </summary> /// <returns>A <see langword="bool"/> for whether the quaternion is normalized or not.</returns> public readonly bool IsNormalized() { - return Mathf.Abs(LengthSquared - 1) <= Mathf.Epsilon; + return Mathf.Abs(LengthSquared() - 1) <= Mathf.Epsilon; } public readonly Quaternion Log() @@ -355,12 +344,33 @@ namespace Godot } /// <summary> + /// Returns the length (magnitude) of the quaternion. + /// </summary> + /// <seealso cref="LengthSquared"/> + /// <value>Equivalent to <c>Mathf.Sqrt(LengthSquared)</c>.</value> + public readonly real_t Length() + { + return Mathf.Sqrt(LengthSquared()); + } + + /// <summary> + /// Returns the squared length (squared magnitude) of the quaternion. + /// This method runs faster than <see cref="Length"/>, so prefer it if + /// you need to compare quaternions or need the squared length for some formula. + /// </summary> + /// <value>Equivalent to <c>Dot(this)</c>.</value> + public readonly real_t LengthSquared() + { + return Dot(this); + } + + /// <summary> /// Returns a copy of the quaternion, normalized to unit length. /// </summary> /// <returns>The normalized quaternion.</returns> public readonly Quaternion Normalized() { - return this / Length; + return this / Length(); } /// <summary> @@ -532,14 +542,13 @@ namespace Godot } else { - real_t sinAngle = Mathf.Sin(angle * 0.5f); - real_t cosAngle = Mathf.Cos(angle * 0.5f); - real_t s = sinAngle / d; + (real_t sin, real_t cos) = Mathf.SinCos(angle * 0.5f); + real_t s = sin / d; x = axis.x * s; y = axis.y * s; z = axis.z * s; - w = cosAngle; + w = cos; } } @@ -583,12 +592,9 @@ namespace Godot // Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-6) // a3 is the angle of the first rotation, following the notation in this reference. - real_t cosA1 = Mathf.Cos(halfA1); - real_t sinA1 = Mathf.Sin(halfA1); - real_t cosA2 = Mathf.Cos(halfA2); - real_t sinA2 = Mathf.Sin(halfA2); - real_t cosA3 = Mathf.Cos(halfA3); - real_t sinA3 = Mathf.Sin(halfA3); + (real_t sinA1, real_t cosA1) = Mathf.SinCos(halfA1); + (real_t sinA2, real_t cosA2) = Mathf.SinCos(halfA2); + (real_t sinA3, real_t cosA3) = Mathf.SinCos(halfA3); return new Quaternion( (sinA1 * cosA2 * sinA3) + (cosA1 * sinA2 * cosA3), diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs index b0e0e75a34..1a8696d3bc 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs @@ -101,6 +101,16 @@ namespace Godot } /// <summary> + /// Returns <see langword="true"/> if this <see cref="Rect2"/> is finite, by calling + /// <see cref="Mathf.IsFinite"/> on each component. + /// </summary> + /// <returns>Whether this vector is finite or not.</returns> + public bool IsFinite() + { + return _position.IsFinite() && _size.IsFinite(); + } + + /// <summary> /// Returns <see langword="true"/> if this <see cref="Rect2"/> completely encloses another one. /// </summary> /// <param name="b">The other <see cref="Rect2"/> that may be enclosed.</param> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs index faee81a98a..cf8939a859 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs @@ -275,38 +275,19 @@ namespace Godot /// <summary> /// Returns <see langword="true"/> if the <see cref="Rect2i"/> overlaps with <paramref name="b"/> /// (i.e. they have at least one point in common). - /// - /// If <paramref name="includeBorders"/> is <see langword="true"/>, - /// they will also be considered overlapping if their borders touch, - /// even without intersection. /// </summary> /// <param name="b">The other <see cref="Rect2i"/> to check for intersections with.</param> - /// <param name="includeBorders">Whether or not to consider borders.</param> /// <returns>A <see langword="bool"/> for whether or not they are intersecting.</returns> - public readonly bool Intersects(Rect2i b, bool includeBorders = false) + public readonly bool Intersects(Rect2i b) { - if (includeBorders) - { - if (_position.x > b._position.x + b._size.x) - return false; - if (_position.x + _size.x < b._position.x) - return false; - if (_position.y > b._position.y + b._size.y) - return false; - if (_position.y + _size.y < b._position.y) - return false; - } - else - { - if (_position.x >= b._position.x + b._size.x) - return false; - if (_position.x + _size.x <= b._position.x) - return false; - if (_position.y >= b._position.y + b._size.y) - return false; - if (_position.y + _size.y <= b._position.y) - return false; - } + if (_position.x >= b._position.x + b._size.x) + return false; + if (_position.x + _size.x <= b._position.x) + return false; + if (_position.y >= b._position.y + b._size.y) + return false; + if (_position.y + _size.y <= b._position.y) + return false; return true; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs index 756f71e5b2..fa060e3a53 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs @@ -32,45 +32,6 @@ namespace Godot public Vector2 origin; /// <summary> - /// The rotation of this transformation matrix. - /// </summary> - /// <value>Getting is equivalent to calling <see cref="Mathf.Atan2(real_t, real_t)"/> with the values of <see cref="x"/>.</value> - public real_t Rotation - { - readonly get - { - return Mathf.Atan2(x.y, x.x); - } - set - { - Vector2 scale = Scale; - x.x = y.y = Mathf.Cos(value); - x.y = y.x = Mathf.Sin(value); - y.x *= -1; - Scale = scale; - } - } - - /// <summary> - /// The scale of this transformation matrix. - /// </summary> - /// <value>Equivalent to the lengths of each column vector, but Y is negative if the determinant is negative.</value> - public Vector2 Scale - { - readonly get - { - real_t detSign = Mathf.Sign(BasisDeterminant()); - return new Vector2(x.Length(), detSign * y.Length()); - } - set - { - value /= Scale; // Value becomes what's called "delta_scale" in core. - x *= value.x; - y *= value.y; - } - } - - /// <summary> /// Access whole columns in the form of <see cref="Vector2"/>. /// The third column is the <see cref="origin"/> vector. /// </summary> @@ -203,6 +164,23 @@ namespace Godot } /// <summary> + /// Returns the transform's rotation (in radians). + /// </summary> + public readonly real_t GetRotation() + { + return Mathf.Atan2(x.y, x.x); + } + + /// <summary> + /// Returns the scale. + /// </summary> + public readonly Vector2 GetScale() + { + real_t detSign = Mathf.Sign(BasisDeterminant()); + return new Vector2(x.Length(), detSign * y.Length()); + } + + /// <summary> /// Interpolates this transform to the other <paramref name="transform"/> by <paramref name="weight"/>. /// </summary> /// <param name="transform">The other transform.</param> @@ -210,15 +188,17 @@ namespace Godot /// <returns>The interpolated transform.</returns> public readonly Transform2D InterpolateWith(Transform2D transform, real_t weight) { - real_t r1 = Rotation; - real_t r2 = transform.Rotation; + real_t r1 = GetRotation(); + real_t r2 = transform.GetRotation(); - Vector2 s1 = Scale; - Vector2 s2 = transform.Scale; + Vector2 s1 = GetScale(); + Vector2 s2 = transform.GetScale(); // Slerp rotation - var v1 = new Vector2(Mathf.Cos(r1), Mathf.Sin(r1)); - var v2 = new Vector2(Mathf.Cos(r2), Mathf.Sin(r2)); + (real_t sin1, real_t cos1) = Mathf.SinCos(r1); + (real_t sin2, real_t cos2) = Mathf.SinCos(r2); + var v1 = new Vector2(cos1, sin1); + var v2 = new Vector2(cos2, sin2); real_t dot = v1.Dot(v2); @@ -235,7 +215,8 @@ namespace Godot { real_t angle = weight * Mathf.Acos(dot); Vector2 v3 = (v2 - (v1 * dot)).Normalized(); - v = (v1 * Mathf.Cos(angle)) + (v3 * Mathf.Sin(angle)); + (real_t sine, real_t cos) = Mathf.SinCos(angle); + v = (v1 * sine) + (v3 * cos); } // Extract parameters @@ -271,6 +252,16 @@ namespace Godot } /// <summary> + /// Returns <see langword="true"/> if this transform is finite, by calling + /// <see cref="Mathf.IsFinite"/> on each component. + /// </summary> + /// <returns>Whether this vector is finite or not.</returns> + public readonly bool IsFinite() + { + return x.IsFinite() && y.IsFinite() && origin.IsFinite(); + } + + /// <summary> /// Returns the transform with the basis orthogonal (90 degrees), /// and normalized axis vectors (scale of 1 or -1). /// </summary> @@ -423,7 +414,7 @@ namespace Godot /// <summary> /// Constructs a transformation matrix from the given components. - /// Arguments are named such that xy is equal to calling x.y + /// Arguments are named such that xy is equal to calling <c>x.y</c>. /// </summary> /// <param name="xx">The X component of the X column vector, accessed via <c>t.x.x</c> or <c>[0][0]</c>.</param> /// <param name="xy">The Y component of the X column vector, accessed via <c>t.x.y</c> or <c>[0][1]</c>.</param> @@ -446,13 +437,34 @@ namespace Godot /// <param name="origin">The origin vector, or column index 2.</param> public Transform2D(real_t rotation, Vector2 origin) { - x.x = y.y = Mathf.Cos(rotation); - x.y = y.x = Mathf.Sin(rotation); + (real_t sin, real_t cos) = Mathf.SinCos(rotation); + x.x = y.y = cos; + x.y = y.x = sin; y.x *= -1; this.origin = origin; } /// <summary> + /// Constructs a transformation matrix from a <paramref name="rotation"/> value, + /// <paramref name="scale"/> vector, <paramref name="skew"/> value, and + /// <paramref name="origin"/> vector. + /// </summary> + /// <param name="rotation">The rotation of the new transform, in radians.</param> + /// <param name="scale">The scale of the new transform.</param> + /// <param name="skew">The skew of the new transform, in radians.</param> + /// <param name="origin">The origin vector, or column index 2.</param> + public Transform2D(real_t rotation, Vector2 scale, real_t skew, Vector2 origin) + { + (real_t rotationSin, real_t rotationCos) = Mathf.SinCos(rotation); + (real_t rotationSkewSin, real_t rotationSkewCos) = Mathf.SinCos(rotation + skew); + x.x = rotationCos * scale.x; + y.y = rotationSkewCos * scale.y; + y.x = -rotationSkewSin * scale.y; + x.y = rotationSin * scale.x; + this.origin = origin; + } + + /// <summary> /// Composes these two transformation matrices by multiplying them /// together. This has the effect of transforming the second transform /// (the child) by the first transform (the parent). diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs index 39167bd116..6b2475fc59 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs @@ -115,16 +115,30 @@ namespace Godot } /// <summary> - /// Interpolates this transform to the other <paramref name="transform"/> by <paramref name="weight"/>. + /// Returns a transform interpolated between this transform and another + /// <paramref name="transform"/> by a given <paramref name="weight"/> + /// (on the range of 0.0 to 1.0). /// </summary> /// <param name="transform">The other transform.</param> /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> /// <returns>The interpolated transform.</returns> public readonly Transform3D InterpolateWith(Transform3D transform, real_t weight) { - Basis retBasis = basis.Lerp(transform.basis, weight); - Vector3 retOrigin = origin.Lerp(transform.origin, weight); - return new Transform3D(retBasis, retOrigin); + Vector3 sourceScale = basis.GetScale(); + Quaternion sourceRotation = basis.GetRotationQuaternion(); + Vector3 sourceLocation = origin; + + Vector3 destinationScale = transform.basis.GetScale(); + Quaternion destinationRotation = transform.basis.GetRotationQuaternion(); + Vector3 destinationLocation = transform.origin; + + var interpolated = new Transform3D(); + Quaternion quaternion = sourceRotation.Slerp(destinationRotation, weight).Normalized(); + Vector3 scale = sourceScale.Lerp(destinationScale, weight); + interpolated.basis.SetQuaternionScale(quaternion, scale); + interpolated.origin = sourceLocation.Lerp(destinationLocation, weight); + + return interpolated; } /// <summary> @@ -140,6 +154,16 @@ namespace Godot } /// <summary> + /// Returns <see langword="true"/> if this transform is finite, by calling + /// <see cref="Mathf.IsFinite"/> on each component. + /// </summary> + /// <returns>Whether this vector is finite or not.</returns> + public readonly bool IsFinite() + { + return basis.IsFinite() && origin.IsFinite(); + } + + /// <summary> /// Returns a copy of the transform rotated such that its /// -Z axis (forward) points towards the <paramref name="target"/> position. /// @@ -223,34 +247,6 @@ namespace Godot return new Transform3D(basis * tmpBasis, origin); } - /// <summary> - /// Returns a transform spherically interpolated between this transform and - /// another <paramref name="transform"/> by <paramref name="weight"/>. - /// </summary> - /// <param name="transform">The other transform.</param> - /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> - /// <returns>The interpolated transform.</returns> - public readonly Transform3D SphericalInterpolateWith(Transform3D transform, real_t weight) - { - /* not sure if very "efficient" but good enough? */ - - Vector3 sourceScale = basis.Scale; - Quaternion sourceRotation = basis.GetRotationQuaternion(); - Vector3 sourceLocation = origin; - - Vector3 destinationScale = transform.basis.Scale; - Quaternion destinationRotation = transform.basis.GetRotationQuaternion(); - Vector3 destinationLocation = transform.origin; - - var interpolated = new Transform3D(); - Quaternion quaternion = sourceRotation.Slerp(destinationRotation, weight).Normalized(); - Vector3 scale = sourceScale.Lerp(destinationScale, weight); - interpolated.basis.SetQuaternionScale(quaternion, scale); - interpolated.origin = sourceLocation.Lerp(destinationLocation, weight); - - return interpolated; - } - private void SetLookAt(Vector3 eye, Vector3 target, Vector3 up) { // Make rotation matrix @@ -346,15 +342,25 @@ namespace Godot } /// <summary> - /// Constructs a transformation matrix from the given <paramref name="quaternion"/> - /// and <paramref name="origin"/> vector. + /// Constructs a transformation matrix from the given components. + /// Arguments are named such that xy is equal to calling <c>basis.x.y</c>. /// </summary> - /// <param name="quaternion">The <see cref="Quaternion"/> to create the basis from.</param> - /// <param name="origin">The origin vector, or column index 3.</param> - public Transform3D(Quaternion quaternion, Vector3 origin) + /// <param name="xx">The X component of the X column vector, accessed via <c>t.basis.x.x</c> or <c>[0][0]</c>.</param> + /// <param name="yx">The X component of the Y column vector, accessed via <c>t.basis.y.x</c> or <c>[1][0]</c>.</param> + /// <param name="zx">The X component of the Z column vector, accessed via <c>t.basis.z.x</c> or <c>[2][0]</c>.</param> + /// <param name="xy">The Y component of the X column vector, accessed via <c>t.basis.x.y</c> or <c>[0][1]</c>.</param> + /// <param name="yy">The Y component of the Y column vector, accessed via <c>t.basis.y.y</c> or <c>[1][1]</c>.</param> + /// <param name="zy">The Y component of the Z column vector, accessed via <c>t.basis.y.y</c> or <c>[2][1]</c>.</param> + /// <param name="xz">The Z component of the X column vector, accessed via <c>t.basis.x.y</c> or <c>[0][2]</c>.</param> + /// <param name="yz">The Z component of the Y column vector, accessed via <c>t.basis.y.y</c> or <c>[1][2]</c>.</param> + /// <param name="zz">The Z component of the Z column vector, accessed via <c>t.basis.y.y</c> or <c>[2][2]</c>.</param> + /// <param name="ox">The X component of the origin vector, accessed via <c>t.origin.x</c> or <c>[2][0]</c>.</param> + /// <param name="oy">The Y component of the origin vector, accessed via <c>t.origin.y</c> or <c>[2][1]</c>.</param> + /// <param name="oz">The Z component of the origin vector, accessed via <c>t.origin.z</c> or <c>[2][2]</c>.</param> + public Transform3D(real_t xx, real_t yx, real_t zx, real_t xy, real_t yy, real_t zy, real_t xz, real_t yz, real_t zz, real_t ox, real_t oy, real_t oz) { - basis = new Basis(quaternion); - this.origin = origin; + basis = new Basis(xx, yx, zx, xy, yy, zy, xz, yz, zz); + origin = new Vector3(ox, oy, oz); } /// <summary> @@ -370,6 +376,29 @@ namespace Godot } /// <summary> + /// Constructs a transformation matrix from the given <paramref name="projection"/> + /// by trimming the last row of the projection matrix (<c>projection.x.w</c>, + /// <c>projection.y.w</c>, <c>projection.z.w</c>, and <c>projection.w.w</c> + /// are not copied over). + /// </summary> + /// <param name="projection">The <see cref="Projection"/> to create the transform from.</param> + public Transform3D(Projection projection) + { + basis = new Basis + ( + projection.x.x, projection.y.x, projection.z.x, + projection.x.y, projection.y.y, projection.z.y, + projection.x.z, projection.y.z, projection.z.z + ); + origin = new Vector3 + ( + projection.w.x, + projection.w.y, + projection.w.z + ); + } + + /// <summary> /// Composes these two transformation matrices by multiplying them /// together. This has the effect of transforming the second transform /// (the child) by the first transform (the parent). diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs index 4c60080ee9..07cb34cadd 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs @@ -334,6 +334,16 @@ namespace Godot } /// <summary> + /// Returns <see langword="true"/> if this vector is finite, by calling + /// <see cref="Mathf.IsFinite"/> on each component. + /// </summary> + /// <returns>Whether this vector is finite or not.</returns> + public readonly bool IsFinite() + { + return Mathf.IsFinite(x) && Mathf.IsFinite(y); + } + + /// <summary> /// Returns <see langword="true"/> if the vector is normalized, and <see langword="false"/> otherwise. /// </summary> /// <returns>A <see langword="bool"/> indicating whether or not the vector is normalized.</returns> @@ -380,24 +390,6 @@ namespace Godot } /// <summary> - /// Returns the result of the linear interpolation between - /// this vector and <paramref name="to"/> by the vector amount <paramref name="weight"/>. - /// </summary> - /// <param name="to">The destination vector for interpolation.</param> - /// <param name="weight"> - /// A vector with components on the range of 0.0 to 1.0, representing the amount of interpolation. - /// </param> - /// <returns>The resulting vector of the interpolation.</returns> - public readonly Vector2 Lerp(Vector2 to, Vector2 weight) - { - return new Vector2 - ( - Mathf.Lerp(x, to.x, weight.x), - Mathf.Lerp(y, to.y, weight.y) - ); - } - - /// <summary> /// Returns the vector with a maximum length by limiting its length to <paramref name="length"/>. /// </summary> /// <param name="length">The length to limit to.</param> @@ -529,11 +521,12 @@ namespace Godot /// <returns>The rotated vector.</returns> public readonly Vector2 Rotated(real_t angle) { - real_t sine = Mathf.Sin(angle); - real_t cosi = Mathf.Cos(angle); - return new Vector2( - x * cosi - y * sine, - x * sine + y * cosi); + (real_t sin, real_t cos) = Mathf.SinCos(angle); + return new Vector2 + ( + x * cos - y * sin, + x * sin + y * cos + ); } /// <summary> @@ -683,7 +676,8 @@ namespace Godot /// <returns>The resulting vector.</returns> public static Vector2 FromAngle(real_t angle) { - return new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)); + (real_t sin, real_t cos) = Mathf.SinCos(angle); + return new Vector2(cos, sin); } /// <summary> @@ -989,6 +983,18 @@ namespace Godot } /// <summary> + /// Returns <see langword="true"/> if this vector's values are approximately zero, + /// by running <see cref="Mathf.IsZeroApprox(real_t)"/> on each component. + /// This method is faster than using <see cref="IsEqualApprox"/> with one value + /// as a zero vector. + /// </summary> + /// <returns>Whether or not the vector is approximately zero.</returns> + public readonly bool IsZeroApprox() + { + return Mathf.IsZeroApprox(x) && Mathf.IsZeroApprox(y); + } + + /// <summary> /// Serves as the hash function for <see cref="Vector2"/>. /// </summary> /// <returns>A hash code for this vector.</returns> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs index 91be548a21..740fedec66 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs @@ -95,38 +95,6 @@ namespace Godot } /// <summary> - /// Returns this vector's angle with respect to the X axis, or (1, 0) vector, in radians. - /// - /// Equivalent to the result of <see cref="Mathf.Atan2(real_t, real_t)"/> when - /// called with the vector's <see cref="y"/> and <see cref="x"/> as parameters: <c>Mathf.Atan2(v.y, v.x)</c>. - /// </summary> - /// <returns>The angle of this vector, in radians.</returns> - public readonly real_t Angle() - { - return Mathf.Atan2(y, x); - } - - /// <summary> - /// Returns the angle to the given vector, in radians. - /// </summary> - /// <param name="to">The other vector to compare this vector to.</param> - /// <returns>The angle between the two vectors, in radians.</returns> - public readonly real_t AngleTo(Vector2i to) - { - return Mathf.Atan2(Cross(to), Dot(to)); - } - - /// <summary> - /// Returns the angle between the line connecting the two points and the X axis, in radians. - /// </summary> - /// <param name="to">The other vector to compare this vector to.</param> - /// <returns>The angle between the two vectors, in radians.</returns> - public readonly real_t AngleToPoint(Vector2i to) - { - return Mathf.Atan2(to.y - y, to.x - x); - } - - /// <summary> /// Returns the aspect ratio of this vector, the ratio of <see cref="x"/> to <see cref="y"/>. /// </summary> /// <returns>The <see cref="x"/> component divided by the <see cref="y"/> component.</returns> @@ -153,48 +121,6 @@ namespace Godot } /// <summary> - /// Returns the cross product of this vector and <paramref name="with"/>. - /// </summary> - /// <param name="with">The other vector.</param> - /// <returns>The cross product vector.</returns> - public readonly int Cross(Vector2i with) - { - return x * with.y - y * with.x; - } - - /// <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. - /// </summary> - /// <param name="to">The other vector to use.</param> - /// <returns>The squared distance between the two vectors.</returns> - public readonly int DistanceSquaredTo(Vector2i to) - { - return (to - this).LengthSquared(); - } - - /// <summary> - /// Returns the distance between this vector and <paramref name="to"/>. - /// </summary> - /// <param name="to">The other vector to use.</param> - /// <returns>The distance between the two vectors.</returns> - public readonly real_t DistanceTo(Vector2i to) - { - return (to - this).Length(); - } - - /// <summary> - /// Returns the dot product of this vector and <paramref name="with"/>. - /// </summary> - /// <param name="with">The other vector to use.</param> - /// <returns>The dot product of the two vectors.</returns> - public readonly int Dot(Vector2i with) - { - return x * with.x + y * with.y; - } - - /// <summary> /// Returns the length (magnitude) of this vector. /// </summary> /// <seealso cref="LengthSquared"/> @@ -242,38 +168,6 @@ namespace Godot } /// <summary> - /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components - /// and <paramref name="mod"/>. - /// </summary> - /// <param name="mod">A value representing the divisor of the operation.</param> - /// <returns> - /// A vector with each component <see cref="Mathf.PosMod(int, int)"/> by <paramref name="mod"/>. - /// </returns> - public readonly Vector2i PosMod(int mod) - { - Vector2i v = this; - v.x = Mathf.PosMod(v.x, mod); - v.y = Mathf.PosMod(v.y, mod); - return v; - } - - /// <summary> - /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components - /// and <paramref name="modv"/>'s components. - /// </summary> - /// <param name="modv">A vector representing the divisors of the operation.</param> - /// <returns> - /// A vector with each component <see cref="Mathf.PosMod(int, int)"/> by <paramref name="modv"/>'s components. - /// </returns> - public readonly Vector2i PosMod(Vector2i modv) - { - Vector2i v = this; - v.x = Mathf.PosMod(v.x, modv.x); - v.y = Mathf.PosMod(v.y, modv.y); - return v; - } - - /// <summary> /// Returns a vector with each component set to one or negative one, depending /// on the signs of this vector's components, or zero if the component is zero, /// by calling <see cref="Mathf.Sign(int)"/> on each component. @@ -287,16 +181,6 @@ namespace Godot return v; } - /// <summary> - /// Returns a perpendicular vector rotated 90 degrees counter-clockwise - /// compared to the original, with the same length. - /// </summary> - /// <returns>The perpendicular vector.</returns> - public readonly Vector2i Orthogonal() - { - return new Vector2i(y, -x); - } - // Constants private static readonly Vector2i _zero = new Vector2i(0, 0); private static readonly Vector2i _one = new Vector2i(1, 1); @@ -467,7 +351,7 @@ namespace Godot /// with the components of the given <see langword="int"/>. /// This operation uses truncated division, which is often not desired /// as it does not work well with negative numbers. - /// Consider using <see cref="PosMod(int)"/> instead + /// Consider using <see cref="Mathf.PosMod(int, int)"/> instead /// if you want to handle negative numbers. /// </summary> /// <example> @@ -490,7 +374,7 @@ namespace Godot /// with the components of the given <see cref="Vector2i"/>. /// This operation uses truncated division, which is often not desired /// as it does not work well with negative numbers. - /// Consider using <see cref="PosMod(Vector2i)"/> instead + /// Consider using <see cref="Mathf.PosMod(int, int)"/> instead /// if you want to handle negative numbers. /// </summary> /// <example> @@ -509,34 +393,6 @@ namespace Godot } /// <summary> - /// Performs a bitwise AND operation with this <see cref="Vector2i"/> - /// and the given <see langword="int"/>. - /// </summary> - /// <param name="vec">The vector to AND with.</param> - /// <param name="and">The integer to AND with.</param> - /// <returns>The result of the bitwise AND.</returns> - public static Vector2i operator &(Vector2i vec, int and) - { - vec.x &= and; - vec.y &= and; - return vec; - } - - /// <summary> - /// Performs a bitwise AND operation with this <see cref="Vector2i"/> - /// and the given <see cref="Vector2i"/>. - /// </summary> - /// <param name="vec">The left vector to AND with.</param> - /// <param name="andv">The right vector to AND with.</param> - /// <returns>The result of the bitwise AND.</returns> - public static Vector2i operator &(Vector2i vec, Vector2i andv) - { - vec.x &= andv.x; - vec.y &= andv.y; - return vec; - } - - /// <summary> /// Returns <see langword="true"/> if the vectors are equal. /// </summary> /// <param name="left">The left vector.</param> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs index fefdee33a5..b017ba5853 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs @@ -331,6 +331,16 @@ namespace Godot } /// <summary> + /// Returns <see langword="true"/> if this vector is finite, by calling + /// <see cref="Mathf.IsFinite"/> on each component. + /// </summary> + /// <returns>Whether this vector is finite or not.</returns> + public readonly bool IsFinite() + { + return Mathf.IsFinite(x) && Mathf.IsFinite(y) && Mathf.IsFinite(z); + } + + /// <summary> /// Returns <see langword="true"/> if the vector is normalized, and <see langword="false"/> otherwise. /// </summary> /// <returns>A <see langword="bool"/> indicating whether or not the vector is normalized.</returns> @@ -386,23 +396,6 @@ namespace Godot } /// <summary> - /// Returns the result of the linear interpolation between - /// this vector and <paramref name="to"/> by the vector amount <paramref name="weight"/>. - /// </summary> - /// <param name="to">The destination vector for interpolation.</param> - /// <param name="weight">A vector with components on the range of 0.0 to 1.0, representing the amount of interpolation.</param> - /// <returns>The resulting vector of the interpolation.</returns> - public readonly Vector3 Lerp(Vector3 to, Vector3 weight) - { - return new Vector3 - ( - Mathf.Lerp(x, to.x, weight.x), - Mathf.Lerp(y, to.y, weight.y), - Mathf.Lerp(z, to.z, weight.z) - ); - } - - /// <summary> /// Returns the vector with a maximum length by limiting its length to <paramref name="length"/>. /// </summary> /// <param name="length">The length to limit to.</param> @@ -1060,6 +1053,18 @@ namespace Godot } /// <summary> + /// Returns <see langword="true"/> if this vector's values are approximately zero, + /// by running <see cref="Mathf.IsZeroApprox(real_t)"/> on each component. + /// This method is faster than using <see cref="IsEqualApprox"/> with one value + /// as a zero vector. + /// </summary> + /// <returns>Whether or not the vector is approximately zero.</returns> + public readonly bool IsZeroApprox() + { + return Mathf.IsZeroApprox(x) && Mathf.IsZeroApprox(y) && Mathf.IsZeroApprox(z); + } + + /// <summary> /// Serves as the hash function for <see cref="Vector3"/>. /// </summary> /// <returns>A hash code for this vector.</returns> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs index e631a9f443..de0c6d27e7 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs @@ -129,39 +129,6 @@ namespace Godot } /// <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. - /// </summary> - /// <param name="to">The other vector to use.</param> - /// <returns>The squared distance between the two vectors.</returns> - public readonly int DistanceSquaredTo(Vector3i to) - { - return (to - this).LengthSquared(); - } - - /// <summary> - /// Returns the distance between this vector and <paramref name="to"/>. - /// </summary> - /// <seealso cref="DistanceSquaredTo(Vector3i)"/> - /// <param name="to">The other vector to use.</param> - /// <returns>The distance between the two vectors.</returns> - public readonly real_t DistanceTo(Vector3i to) - { - return (to - this).Length(); - } - - /// <summary> - /// Returns the dot product of this vector and <paramref name="with"/>. - /// </summary> - /// <param name="with">The other vector to use.</param> - /// <returns>The dot product of the two vectors.</returns> - public readonly int Dot(Vector3i with) - { - return x * with.x + y * with.y + z * with.z; - } - - /// <summary> /// Returns the length (magnitude) of this vector. /// </summary> /// <seealso cref="LengthSquared"/> @@ -211,40 +178,6 @@ namespace Godot } /// <summary> - /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components - /// and <paramref name="mod"/>. - /// </summary> - /// <param name="mod">A value representing the divisor of the operation.</param> - /// <returns> - /// A vector with each component <see cref="Mathf.PosMod(int, int)"/> by <paramref name="mod"/>. - /// </returns> - public readonly Vector3i PosMod(int mod) - { - Vector3i v = this; - v.x = Mathf.PosMod(v.x, mod); - v.y = Mathf.PosMod(v.y, mod); - v.z = Mathf.PosMod(v.z, mod); - return v; - } - - /// <summary> - /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components - /// and <paramref name="modv"/>'s components. - /// </summary> - /// <param name="modv">A vector representing the divisors of the operation.</param> - /// <returns> - /// A vector with each component <see cref="Mathf.PosMod(int, int)"/> by <paramref name="modv"/>'s components. - /// </returns> - public readonly Vector3i PosMod(Vector3i modv) - { - Vector3i v = this; - v.x = Mathf.PosMod(v.x, modv.x); - v.y = Mathf.PosMod(v.y, modv.y); - v.z = Mathf.PosMod(v.z, modv.z); - return v; - } - - /// <summary> /// Returns a vector with each component set to one or negative one, depending /// on the signs of this vector's components, or zero if the component is zero, /// by calling <see cref="Mathf.Sign(int)"/> on each component. @@ -455,7 +388,7 @@ namespace Godot /// with the components of the given <see langword="int"/>. /// This operation uses truncated division, which is often not desired /// as it does not work well with negative numbers. - /// Consider using <see cref="PosMod(int)"/> instead + /// Consider using <see cref="Mathf.PosMod(int, int)"/> instead /// if you want to handle negative numbers. /// </summary> /// <example> @@ -479,7 +412,7 @@ namespace Godot /// with the components of the given <see cref="Vector3i"/>. /// This operation uses truncated division, which is often not desired /// as it does not work well with negative numbers. - /// Consider using <see cref="PosMod(Vector3i)"/> instead + /// Consider using <see cref="Mathf.PosMod(int, int)"/> instead /// if you want to handle negative numbers. /// </summary> /// <example> @@ -499,36 +432,6 @@ namespace Godot } /// <summary> - /// Performs a bitwise AND operation with this <see cref="Vector3i"/> - /// and the given <see langword="int"/>. - /// </summary> - /// <param name="vec">The vector to AND with.</param> - /// <param name="and">The integer to AND with.</param> - /// <returns>The result of the bitwise AND.</returns> - public static Vector3i operator &(Vector3i vec, int and) - { - vec.x &= and; - vec.y &= and; - vec.z &= and; - return vec; - } - - /// <summary> - /// Performs a bitwise AND operation with this <see cref="Vector3i"/> - /// and the given <see cref="Vector3i"/>. - /// </summary> - /// <param name="vec">The left vector to AND with.</param> - /// <param name="andv">The right vector to AND with.</param> - /// <returns>The result of the bitwise AND.</returns> - public static Vector3i operator &(Vector3i vec, Vector3i andv) - { - vec.x &= andv.x; - vec.y &= andv.y; - vec.z &= andv.z; - return vec; - } - - /// <summary> /// Returns <see langword="true"/> if the vectors are equal. /// </summary> /// <param name="left">The left vector.</param> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs index 3191e8adc0..0f4528bb40 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs @@ -280,6 +280,16 @@ namespace Godot } /// <summary> + /// Returns <see langword="true"/> if this vector is finite, by calling + /// <see cref="Mathf.IsFinite"/> on each component. + /// </summary> + /// <returns>Whether this vector is finite or not.</returns> + public readonly bool IsFinite() + { + return Mathf.IsFinite(x) && Mathf.IsFinite(y) && Mathf.IsFinite(z) && Mathf.IsFinite(w); + } + + /// <summary> /// Returns <see langword="true"/> if the vector is normalized, and <see langword="false"/> otherwise. /// </summary> /// <returns>A <see langword="bool"/> indicating whether or not the vector is normalized.</returns> @@ -857,6 +867,18 @@ namespace Godot } /// <summary> + /// Returns <see langword="true"/> if this vector's values are approximately zero, + /// by running <see cref="Mathf.IsZeroApprox(real_t)"/> on each component. + /// This method is faster than using <see cref="IsEqualApprox"/> with one value + /// as a zero vector. + /// </summary> + /// <returns>Whether or not the vector is approximately zero.</returns> + public readonly bool IsZeroApprox() + { + return Mathf.IsZeroApprox(x) && Mathf.IsZeroApprox(y) && Mathf.IsZeroApprox(z) && Mathf.IsZeroApprox(w); + } + + /// <summary> /// Serves as the hash function for <see cref="Vector4"/>. /// </summary> /// <returns>A hash code for this vector.</returns> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs index 8146991fd7..00ecc64856 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs @@ -433,38 +433,6 @@ namespace Godot } /// <summary> - /// Performs a bitwise AND operation with this <see cref="Vector4i"/> - /// and the given <see langword="int"/>. - /// </summary> - /// <param name="vec">The vector to AND with.</param> - /// <param name="and">The integer to AND with.</param> - /// <returns>The result of the bitwise AND.</returns> - public static Vector4i operator &(Vector4i vec, int and) - { - vec.x &= and; - vec.y &= and; - vec.z &= and; - vec.w &= and; - return vec; - } - - /// <summary> - /// Performs a bitwise AND operation with this <see cref="Vector4i"/> - /// and the given <see cref="Vector4i"/>. - /// </summary> - /// <param name="vec">The left vector to AND with.</param> - /// <param name="andv">The right vector to AND with.</param> - /// <returns>The result of the bitwise AND.</returns> - public static Vector4i operator &(Vector4i vec, Vector4i andv) - { - vec.x &= andv.x; - vec.y &= andv.y; - vec.z &= andv.z; - vec.w &= andv.w; - return vec; - } - - /// <summary> /// Returns <see langword="true"/> if the vectors are equal. /// </summary> /// <param name="left">The left vector.</param> diff --git a/modules/multiplayer/editor/replication_editor.cpp b/modules/multiplayer/editor/replication_editor.cpp index 4ab41cfcb0..66e12a338a 100644 --- a/modules/multiplayer/editor/replication_editor.cpp +++ b/modules/multiplayer/editor/replication_editor.cpp @@ -142,7 +142,7 @@ void ReplicationEditor::_add_sync_property(String p_path) { return; } - Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_singleton()->get_undo_redo(); + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("Add property to synchronizer")); if (config.is_null()) { @@ -250,14 +250,12 @@ ReplicationEditor::ReplicationEditor() { tree->add_child(drop_label); drop_label->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); - tree->set_drag_forwarding_compat(this); + SET_DRAG_FORWARDING_CDU(tree, ReplicationEditor); } void ReplicationEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_config"), &ReplicationEditor::_update_config); ClassDB::bind_method(D_METHOD("_update_checked", "property", "column", "checked"), &ReplicationEditor::_update_checked); - ClassDB::bind_method("_can_drop_data_fw", &ReplicationEditor::_can_drop_data_fw); - ClassDB::bind_method("_drop_data_fw", &ReplicationEditor::_drop_data_fw); } bool ReplicationEditor::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { @@ -357,7 +355,7 @@ void ReplicationEditor::_tree_item_edited() { int column = tree->get_edited_column(); ERR_FAIL_COND(column < 1 || column > 2); const NodePath prop = ti->get_metadata(0); - Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); bool value = ti->is_checked(column); String method; if (column == 1) { @@ -397,7 +395,7 @@ void ReplicationEditor::_dialog_closed(bool p_confirmed) { int idx = config->property_get_index(prop); bool spawn = config->property_get_spawn(prop); bool sync = config->property_get_sync(prop); - Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("Remove Property")); undo_redo->add_do_method(config.ptr(), "remove_property", prop); undo_redo->add_undo_method(config.ptr(), "add_property", prop, idx); diff --git a/modules/multiplayer/multiplayer_spawner.cpp b/modules/multiplayer/multiplayer_spawner.cpp index 7ed69a84d0..0aa54b69f9 100644 --- a/modules/multiplayer/multiplayer_spawner.cpp +++ b/modules/multiplayer/multiplayer_spawner.cpp @@ -199,10 +199,6 @@ void MultiplayerSpawner::_notification(int p_what) { Node *node = Object::cast_to<Node>(ObjectDB::get_instance(E.key)); ERR_CONTINUE(!node); node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &MultiplayerSpawner::_node_exit)); - // This is unlikely, but might still crash the engine. - if (node->is_connected(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready))) { - node->disconnect(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready)); - } get_multiplayer()->object_configuration_remove(node, this); } tracked_nodes.clear(); @@ -244,11 +240,11 @@ void MultiplayerSpawner::_track(Node *p_node, const Variant &p_argument, int p_s if (!tracked_nodes.has(oid)) { tracked_nodes[oid] = SpawnInfo(p_argument.duplicate(true), p_scene_id); p_node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &MultiplayerSpawner::_node_exit).bind(p_node->get_instance_id()), CONNECT_ONE_SHOT); - p_node->connect(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready).bind(p_node->get_instance_id()), CONNECT_ONE_SHOT); + _spawn_notify(p_node->get_instance_id()); } } -void MultiplayerSpawner::_node_ready(ObjectID p_id) { +void MultiplayerSpawner::_spawn_notify(ObjectID p_id) { get_multiplayer()->object_configuration_add(ObjectDB::get_instance(p_id), this); } diff --git a/modules/multiplayer/multiplayer_spawner.h b/modules/multiplayer/multiplayer_spawner.h index 8d401a6818..8a54140e32 100644 --- a/modules/multiplayer/multiplayer_spawner.h +++ b/modules/multiplayer/multiplayer_spawner.h @@ -77,7 +77,7 @@ private: void _track(Node *p_node, const Variant &p_argument, int p_scene_id = INVALID_ID); void _node_added(Node *p_node); void _node_exit(ObjectID p_id); - void _node_ready(ObjectID p_id); + void _spawn_notify(ObjectID p_id); Vector<String> _get_spawnable_scenes() const; void _set_spawnable_scenes(const Vector<String> &p_scenes); diff --git a/modules/multiplayer/scene_replication_interface.cpp b/modules/multiplayer/scene_replication_interface.cpp index e1b7b0c346..233ff76c7d 100644 --- a/modules/multiplayer/scene_replication_interface.cpp +++ b/modules/multiplayer/scene_replication_interface.cpp @@ -125,6 +125,20 @@ void SceneReplicationInterface::on_reset() { } void SceneReplicationInterface::on_network_process() { + // Prevent endless stalling in case of unforseen spawn errors. + if (spawn_queue.size()) { + ERR_PRINT("An error happened during last spawn, this usually means the 'ready' signal was not emitted by the spawned node."); + for (const ObjectID &oid : spawn_queue) { + Node *node = get_id_as<Node>(oid); + ERR_CONTINUE(!node); + if (node->is_connected(SceneStringNames::get_singleton()->ready, callable_mp(this, &SceneReplicationInterface::_node_ready))) { + node->disconnect(SceneStringNames::get_singleton()->ready, callable_mp(this, &SceneReplicationInterface::_node_ready)); + } + } + spawn_queue.clear(); + } + + // Process timed syncs. uint64_t msec = OS::get_singleton()->get_ticks_msec(); for (KeyValue<int, PeerInfo> &E : peers_info) { const HashSet<ObjectID> to_sync = E.value.sync_nodes; @@ -144,17 +158,39 @@ Error SceneReplicationInterface::on_spawn(Object *p_obj, Variant p_config) { // Track node. const ObjectID oid = node->get_instance_id(); TrackedNode &tobj = _track(oid); + + // Spawn state needs to be callected after "ready", but the spawn order follows "enter_tree". ERR_FAIL_COND_V(tobj.spawner != ObjectID(), ERR_ALREADY_IN_USE); tobj.spawner = spawner->get_instance_id(); - spawned_nodes.insert(oid); + spawn_queue.insert(oid); + node->connect(SceneStringNames::get_singleton()->ready, callable_mp(this, &SceneReplicationInterface::_node_ready).bind(oid), Node::CONNECT_ONE_SHOT); + return OK; +} + +void SceneReplicationInterface::_node_ready(const ObjectID &p_oid) { + ERR_FAIL_COND(!spawn_queue.has(p_oid)); // Bug. - if (multiplayer->has_multiplayer_peer() && spawner->is_multiplayer_authority()) { - if (tobj.net_id == 0) { - tobj.net_id = ++last_net_id; + // If we are a nested spawn, we need to wait until the parent is ready. + if (p_oid != *(spawn_queue.begin())) { + return; + } + + for (const ObjectID &oid : spawn_queue) { + ERR_CONTINUE(!tracked_nodes.has(oid)); + + TrackedNode &tobj = tracked_nodes[oid]; + MultiplayerSpawner *spawner = get_id_as<MultiplayerSpawner>(tobj.spawner); + ERR_CONTINUE(!spawner); + + spawned_nodes.insert(oid); + if (multiplayer->has_multiplayer_peer() && spawner->is_multiplayer_authority()) { + if (tobj.net_id == 0) { + tobj.net_id = ++last_net_id; + } + _update_spawn_visibility(0, oid); } - _update_spawn_visibility(0, oid); } - return OK; + spawn_queue.clear(); } Error SceneReplicationInterface::on_despawn(Object *p_obj, Variant p_config) { diff --git a/modules/multiplayer/scene_replication_interface.h b/modules/multiplayer/scene_replication_interface.h index a5e610cff6..cf45db2138 100644 --- a/modules/multiplayer/scene_replication_interface.h +++ b/modules/multiplayer/scene_replication_interface.h @@ -51,7 +51,6 @@ private: bool operator==(const ObjectID &p_other) { return id == p_other; } - _FORCE_INLINE_ MultiplayerSpawner *get_spawner() const { return spawner.is_valid() ? Object::cast_to<MultiplayerSpawner>(ObjectDB::get_instance(spawner)) : nullptr; } TrackedNode() {} TrackedNode(const ObjectID &p_id) { id = p_id; } TrackedNode(const ObjectID &p_id, uint32_t p_net_id) { @@ -75,7 +74,10 @@ private: HashSet<ObjectID> spawned_nodes; HashSet<ObjectID> sync_nodes; - // Pending spawn information. + // Pending local spawn information (handles spawning nested nodes during ready). + HashSet<ObjectID> spawn_queue; + + // Pending remote spawn information. ObjectID pending_spawn; int pending_spawn_remote = 0; const uint8_t *pending_buffer = nullptr; @@ -89,6 +91,7 @@ private: TrackedNode &_track(const ObjectID &p_id); void _untrack(const ObjectID &p_id); + void _node_ready(const ObjectID &p_oid); void _send_sync(int p_peer, const HashSet<ObjectID> p_synchronizers, uint16_t p_sync_net_time, uint64_t p_msec); Error _make_spawn_packet(Node *p_node, MultiplayerSpawner *p_spawner, int &r_len); diff --git a/modules/ogg/ogg_packet_sequence.cpp b/modules/ogg/ogg_packet_sequence.cpp index 0acaaf5fc9..d473f3b4a0 100644 --- a/modules/ogg/ogg_packet_sequence.cpp +++ b/modules/ogg/ogg_packet_sequence.cpp @@ -136,6 +136,8 @@ bool OggPacketSequencePlayback::next_ogg_packet(ogg_packet **p_packet) const { ERR_FAIL_COND_V(data_version != ogg_packet_sequence->data_version, false); ERR_FAIL_COND_V(ogg_packet_sequence->page_data.is_empty(), false); ERR_FAIL_COND_V(ogg_packet_sequence->page_granule_positions.is_empty(), false); + ERR_FAIL_COND_V(page_cursor >= ogg_packet_sequence->page_data.size(), false); + // Move on to the next page if need be. This happens first to help simplify seek logic. while (packet_cursor >= ogg_packet_sequence->page_data[page_cursor].size()) { packet_cursor = 0; diff --git a/modules/openxr/editor/openxr_action_editor.cpp b/modules/openxr/editor/openxr_action_editor.cpp index e3fe06c6f7..586b0b0697 100644 --- a/modules/openxr/editor/openxr_action_editor.cpp +++ b/modules/openxr/editor/openxr_action_editor.cpp @@ -29,7 +29,6 @@ /**************************************************************************/ #include "openxr_action_editor.h" -#include "editor/editor_node.h" void OpenXRActionEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_do_set_name", "name"), &OpenXRActionEditor::_do_set_name); @@ -125,7 +124,7 @@ void OpenXRActionEditor::_on_remove_action() { } OpenXRActionEditor::OpenXRActionEditor(Ref<OpenXRAction> p_action) { - undo_redo = EditorNode::get_undo_redo(); + undo_redo = EditorUndoRedoManager::get_singleton(); action = p_action; set_h_size_flags(Control::SIZE_EXPAND_FILL); diff --git a/modules/openxr/editor/openxr_action_editor.h b/modules/openxr/editor/openxr_action_editor.h index 8af5448aed..765b3ef378 100644 --- a/modules/openxr/editor/openxr_action_editor.h +++ b/modules/openxr/editor/openxr_action_editor.h @@ -43,7 +43,7 @@ class OpenXRActionEditor : public HBoxContainer { GDCLASS(OpenXRActionEditor, HBoxContainer); private: - Ref<EditorUndoRedoManager> undo_redo; + EditorUndoRedoManager *undo_redo; Ref<OpenXRAction> action; LineEdit *action_name = nullptr; diff --git a/modules/openxr/editor/openxr_action_map_editor.cpp b/modules/openxr/editor/openxr_action_map_editor.cpp index 12c95f8978..ad5a515a01 100644 --- a/modules/openxr/editor/openxr_action_map_editor.cpp +++ b/modules/openxr/editor/openxr_action_map_editor.cpp @@ -387,7 +387,7 @@ void OpenXRActionMapEditor::_clear_action_map() { } OpenXRActionMapEditor::OpenXRActionMapEditor() { - undo_redo = EditorNode::get_undo_redo(); + undo_redo = EditorUndoRedoManager::get_singleton(); set_custom_minimum_size(Size2(0.0, 300.0)); top_hb = memnew(HBoxContainer); diff --git a/modules/openxr/editor/openxr_action_map_editor.h b/modules/openxr/editor/openxr_action_map_editor.h index 47bfda3425..a04bae4a6e 100644 --- a/modules/openxr/editor/openxr_action_map_editor.h +++ b/modules/openxr/editor/openxr_action_map_editor.h @@ -48,7 +48,7 @@ class OpenXRActionMapEditor : public VBoxContainer { GDCLASS(OpenXRActionMapEditor, VBoxContainer); private: - Ref<EditorUndoRedoManager> undo_redo; + EditorUndoRedoManager *undo_redo; String edited_path; Ref<OpenXRActionMap> action_map; diff --git a/modules/openxr/editor/openxr_action_set_editor.cpp b/modules/openxr/editor/openxr_action_set_editor.cpp index 078d83f5f2..bcb0f5f8b1 100644 --- a/modules/openxr/editor/openxr_action_set_editor.cpp +++ b/modules/openxr/editor/openxr_action_set_editor.cpp @@ -29,7 +29,6 @@ /**************************************************************************/ #include "openxr_action_set_editor.h" -#include "editor/editor_node.h" #include "openxr_action_editor.h" void OpenXRActionSetEditor::_bind_methods() { @@ -212,7 +211,7 @@ void OpenXRActionSetEditor::set_focus_on_entry() { } OpenXRActionSetEditor::OpenXRActionSetEditor(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRActionSet> p_action_set) { - undo_redo = EditorNode::get_undo_redo(); + undo_redo = EditorUndoRedoManager::get_singleton(); action_map = p_action_map; action_set = p_action_set; diff --git a/modules/openxr/editor/openxr_action_set_editor.h b/modules/openxr/editor/openxr_action_set_editor.h index 6040f7fb4e..129f800abe 100644 --- a/modules/openxr/editor/openxr_action_set_editor.h +++ b/modules/openxr/editor/openxr_action_set_editor.h @@ -44,7 +44,7 @@ class OpenXRActionSetEditor : public HBoxContainer { GDCLASS(OpenXRActionSetEditor, HBoxContainer); private: - Ref<EditorUndoRedoManager> undo_redo; + EditorUndoRedoManager *undo_redo; Ref<OpenXRActionMap> action_map; Ref<OpenXRActionSet> action_set; diff --git a/modules/openxr/editor/openxr_interaction_profile_editor.cpp b/modules/openxr/editor/openxr_interaction_profile_editor.cpp index 76b0ae5a3c..6a848dd430 100644 --- a/modules/openxr/editor/openxr_interaction_profile_editor.cpp +++ b/modules/openxr/editor/openxr_interaction_profile_editor.cpp @@ -29,7 +29,6 @@ /**************************************************************************/ #include "openxr_interaction_profile_editor.h" -#include "editor/editor_node.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" #include "scene/gui/label.h" @@ -141,7 +140,7 @@ void OpenXRInteractionProfileEditorBase::remove_all_bindings_for_action(Ref<Open } OpenXRInteractionProfileEditorBase::OpenXRInteractionProfileEditorBase(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile) { - undo_redo = EditorNode::get_undo_redo(); + undo_redo = EditorUndoRedoManager::get_singleton(); action_map = p_action_map; interaction_profile = p_interaction_profile; diff --git a/modules/openxr/editor/openxr_interaction_profile_editor.h b/modules/openxr/editor/openxr_interaction_profile_editor.h index 73dba71cbd..fa25a000a9 100644 --- a/modules/openxr/editor/openxr_interaction_profile_editor.h +++ b/modules/openxr/editor/openxr_interaction_profile_editor.h @@ -43,7 +43,7 @@ class OpenXRInteractionProfileEditorBase : public ScrollContainer { GDCLASS(OpenXRInteractionProfileEditorBase, ScrollContainer); protected: - Ref<EditorUndoRedoManager> undo_redo; + EditorUndoRedoManager *undo_redo; Ref<OpenXRInteractionProfile> interaction_profile; Ref<OpenXRActionMap> action_map; diff --git a/modules/openxr/extensions/openxr_opengl_extension.cpp b/modules/openxr/extensions/openxr_opengl_extension.cpp index 6ce8f0805f..0d201161f1 100644 --- a/modules/openxr/extensions/openxr_opengl_extension.cpp +++ b/modules/openxr/extensions/openxr_opengl_extension.cpp @@ -158,12 +158,14 @@ void *OpenXROpenGLExtension::set_session_create_and_get_next_pointer(void *p_nex void OpenXROpenGLExtension::get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) { p_usable_swap_chains.push_back(GL_RGBA8); + p_usable_swap_chains.push_back(GL_SRGB8_ALPHA8); } void OpenXROpenGLExtension::get_usable_depth_formats(Vector<int64_t> &p_usable_depth_formats) { p_usable_depth_formats.push_back(GL_DEPTH_COMPONENT32F); p_usable_depth_formats.push_back(GL_DEPTH24_STENCIL8); p_usable_depth_formats.push_back(GL_DEPTH32F_STENCIL8); + p_usable_depth_formats.push_back(GL_DEPTH_COMPONENT24); } bool OpenXROpenGLExtension::get_swapchain_image_data(XrSwapchain p_swapchain, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, void **r_swapchain_graphics_data) { diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index d556f475d2..6b8f140923 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -560,6 +560,12 @@ void OpenXRAPI::destroy_instance() { instance = XR_NULL_HANDLE; } enabled_extensions.clear(); + + if (graphics_extension != nullptr) { + unregister_extension_wrapper(graphics_extension); + memdelete(graphics_extension); + graphics_extension = nullptr; + } } bool OpenXRAPI::create_session() { @@ -1347,6 +1353,10 @@ void OpenXRAPI::register_extension_wrapper(OpenXRExtensionWrapper *p_extension_w registered_extension_wrappers.push_back(p_extension_wrapper); } +void OpenXRAPI::unregister_extension_wrapper(OpenXRExtensionWrapper *p_extension_wrapper) { + registered_extension_wrappers.erase(p_extension_wrapper); +} + void OpenXRAPI::register_extension_metadata() { for (OpenXRExtensionWrapper *extension_wrapper : registered_extension_wrappers) { extension_wrapper->on_register_metadata(); diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h index 5fb8de660e..52a1af5a09 100644 --- a/modules/openxr/openxr_api.h +++ b/modules/openxr/openxr_api.h @@ -312,6 +312,7 @@ public: void set_xr_interface(OpenXRInterface *p_xr_interface); static void register_extension_wrapper(OpenXRExtensionWrapper *p_extension_wrapper); + static void unregister_extension_wrapper(OpenXRExtensionWrapper *p_extension_wrapper); static void register_extension_metadata(); static void cleanup_extension_wrappers(); diff --git a/modules/openxr/register_types.cpp b/modules/openxr/register_types.cpp index 4e2fe3dab5..1c4d53c43a 100644 --- a/modules/openxr/register_types.cpp +++ b/modules/openxr/register_types.cpp @@ -111,6 +111,10 @@ void initialize_openxr_module(ModuleInitializationLevel p_level) { ERR_FAIL_NULL(openxr_api); if (!openxr_api->initialize(Main::get_rendering_driver_name())) { + OS::get_singleton()->alert("OpenXR was requested but failed to start.\n" + "Please check if your HMD is connected.\n" + "When using Windows MR please note that WMR only has DirectX support, make sure SteamVR is your default OpenXR runtime.\n" + "Godot will start in normal mode.\n"); memdelete(openxr_api); openxr_api = nullptr; return; diff --git a/modules/raycast/SCsub b/modules/raycast/SCsub index 37c8a95905..209ebab388 100644 --- a/modules/raycast/SCsub +++ b/modules/raycast/SCsub @@ -96,7 +96,7 @@ if env["builtin_embree"]: if not env.msvc: # Flags synced with upstream gnu.cmake. - if env["arch"] == "arm64" and env["platform"] == "linuxbsd": + if env["arch"] == "arm64" and env["platform"] == "linuxbsd" and not env["use_llvm"]: env_thirdparty.Append(CXXFLAGS=["-flax-vector-conversions"]) env_thirdparty.Append( diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index e52b87741e..79ca4a7024 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -423,11 +423,7 @@ bool TextServerAdvanced::_load_support_data(const String &p_filename) { return false; } uint64_t len = f->get_length(); -#ifdef GDEXTENSION PackedByteArray icu_data = f->get_buffer(len); -#else - PackedByteArray icu_data = f->_get_buffer(len); -#endif UErrorCode err = U_ZERO_ERROR; udata_setCommonData(icu_data.ptr(), &err); @@ -476,11 +472,7 @@ bool TextServerAdvanced::_save_support_data(const String &p_filename) const { PackedByteArray icu_data; icu_data.resize(U_ICUDATA_SIZE); memcpy(icu_data.ptrw(), U_ICUDATA_ENTRY_POINT, U_ICUDATA_SIZE); -#ifdef GDEXTENSION f->store_buffer(icu_data); -#else - f->_store_buffer(icu_data); -#endif return true; #else @@ -824,29 +816,17 @@ _FORCE_INLINE_ TextServerAdvanced::FontTexturePosition TextServerAdvanced::find_ // Could not find texture to fit, create one. int texsize = MAX(p_data->size.x * p_data->oversampling * 8, 256); -#ifdef GDEXTENSION - texsize = Math::next_power_of_2(texsize); -#else texsize = next_power_of_2(texsize); -#endif if (p_msdf) { texsize = MIN(texsize, 2048); } else { texsize = MIN(texsize, 1024); } if (mw > texsize) { // Special case, adapt to it? -#ifdef GDEXTENSION - texsize = Math::next_power_of_2(mw); -#else texsize = next_power_of_2(mw); -#endif } if (mh > texsize) { // Special case, adapt to it? -#ifdef GDEXTENSION - texsize = Math::next_power_of_2(mh); -#else texsize = next_power_of_2(mh); -#endif } ShelfPackTexture tex = ShelfPackTexture(texsize, texsize); @@ -949,14 +929,14 @@ static int ft_cubic_to(const FT_Vector *control1, const FT_Vector *control2, con return 0; } -void TextServerAdvanced::_generateMTSDF_threaded(uint32_t y, void *p_td) const { +void TextServerAdvanced::_generateMTSDF_threaded(void *p_td, uint32_t p_y) { MSDFThreadData *td = static_cast<MSDFThreadData *>(p_td); msdfgen::ShapeDistanceFinder<msdfgen::OverlappingContourCombiner<msdfgen::MultiAndTrueDistanceSelector>> distanceFinder(*td->shape); - int row = td->shape->inverseYAxis ? td->output->height() - y - 1 : y; + int row = td->shape->inverseYAxis ? td->output->height() - p_y - 1 : p_y; for (int col = 0; col < td->output->width(); ++col) { - int x = (y % 2) ? td->output->width() - col - 1 : col; - msdfgen::Point2 p = td->projection->unproject(msdfgen::Point2(x + .5, y + .5)); + int x = (p_y % 2) ? td->output->width() - col - 1 : col; + msdfgen::Point2 p = td->projection->unproject(msdfgen::Point2(x + .5, p_y + .5)); msdfgen::MultiAndTrueDistance distance = distanceFinder.distance(p); td->distancePixelConversion->operator()(td->output->operator()(x, row), distance); } @@ -1026,14 +1006,8 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf( td.projection = &projection; td.distancePixelConversion = &distancePixelConversion; -#ifdef GDEXTENSION - for (int i = 0; i < h; i++) { - _generateMTSDF_threaded(i, &td); - } -#else - WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &TextServerAdvanced::_generateMTSDF_threaded, &td, h, -1, true, SNAME("FontServerRasterizeMSDF")); + WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_native_group_task(&TextServerAdvanced::_generateMTSDF_threaded, &td, h, -1, true, String("FontServerRasterizeMSDF")); WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task); -#endif msdfgen::msdfErrorCorrection(image, shape, projection, p_pixel_range, config); @@ -3680,6 +3654,7 @@ void TextServerAdvanced::full_copy(ShapedTextDataAdvanced *p_shaped) { RID TextServerAdvanced::_create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) { _THREAD_SAFE_METHOD_ + ERR_FAIL_COND_V_MSG(p_direction == DIRECTION_INHERITED, RID(), "Invalid text direction."); ShapedTextDataAdvanced *sd = memnew(ShapedTextDataAdvanced); sd->hb_buffer = hb_buffer_create(); @@ -3705,6 +3680,7 @@ void TextServerAdvanced::_shaped_text_clear(const RID &p_shaped) { void TextServerAdvanced::_shaped_text_set_direction(const RID &p_shaped, TextServer::Direction p_direction) { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); + ERR_FAIL_COND_MSG(p_direction == DIRECTION_INHERITED, "Invalid text direction."); ERR_FAIL_COND(!sd); MutexLock lock(sd->mutex); @@ -3764,8 +3740,12 @@ void TextServerAdvanced::_shaped_text_set_bidi_override(const RID &p_shaped, con } sd->bidi_override.clear(); for (int i = 0; i < p_override.size(); i++) { - if (p_override[i].get_type() == Variant::VECTOR2I) { - sd->bidi_override.push_back(p_override[i]); + if (p_override[i].get_type() == Variant::VECTOR3I) { + const Vector3i &r = p_override[i]; + sd->bidi_override.push_back(r); + } else if (p_override[i].get_type() == Variant::VECTOR2I) { + const Vector2i &r = p_override[i]; + sd->bidi_override.push_back(Vector3i(r.x, r.y, DIRECTION_INHERITED)); } } invalidate(sd, false); @@ -5570,8 +5550,31 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) { sd->script_iter = memnew(ScriptIterator(sd->text, 0, sd->text.length())); } + int base_para_direction = UBIDI_DEFAULT_LTR; + switch (sd->direction) { + case DIRECTION_LTR: { + sd->para_direction = DIRECTION_LTR; + base_para_direction = UBIDI_LTR; + } break; + case DIRECTION_RTL: { + sd->para_direction = DIRECTION_RTL; + base_para_direction = UBIDI_RTL; + } break; + case DIRECTION_INHERITED: + case DIRECTION_AUTO: { + UBiDiDirection direction = ubidi_getBaseDirection(data, sd->utf16.length()); + if (direction != UBIDI_NEUTRAL) { + sd->para_direction = (direction == UBIDI_RTL) ? DIRECTION_RTL : DIRECTION_LTR; + base_para_direction = direction; + } else { + sd->para_direction = DIRECTION_LTR; + base_para_direction = UBIDI_DEFAULT_LTR; + } + } break; + } + if (sd->bidi_override.is_empty()) { - sd->bidi_override.push_back(Vector2i(sd->start, sd->end)); + sd->bidi_override.push_back(Vector3i(sd->start, sd->end, DIRECTION_INHERITED)); } for (int ov = 0; ov < sd->bidi_override.size(); ov++) { @@ -5587,23 +5590,22 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) { UBiDi *bidi_iter = ubidi_openSized(end, 0, &err); ERR_FAIL_COND_V_MSG(U_FAILURE(err), false, u_errorName(err)); - switch (sd->direction) { + switch (static_cast<TextServer::Direction>(sd->bidi_override[ov].z)) { case DIRECTION_LTR: { ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_LTR, nullptr, &err); - sd->para_direction = DIRECTION_LTR; } break; case DIRECTION_RTL: { ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_RTL, nullptr, &err); - sd->para_direction = DIRECTION_RTL; + } break; + case DIRECTION_INHERITED: { + ubidi_setPara(bidi_iter, data + start, end - start, base_para_direction, nullptr, &err); } break; case DIRECTION_AUTO: { UBiDiDirection direction = ubidi_getBaseDirection(data + start, end - start); if (direction != UBIDI_NEUTRAL) { ubidi_setPara(bidi_iter, data + start, end - start, direction, nullptr, &err); - sd->para_direction = (direction == UBIDI_RTL) ? DIRECTION_RTL : DIRECTION_LTR; } else { ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_DEFAULT_LTR, nullptr, &err); - sd->para_direction = DIRECTION_LTR; } } break; } diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index e8a3a10ab8..c7fe46d554 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -72,7 +72,6 @@ #include <godot_cpp/templates/hash_map.hpp> #include <godot_cpp/templates/hash_set.hpp> #include <godot_cpp/templates/rid_owner.hpp> - #include <godot_cpp/templates/vector.hpp> using namespace godot; @@ -350,7 +349,7 @@ class TextServerAdvanced : public TextServerExtension { _FORCE_INLINE_ bool _ensure_glyph(FontAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph) const; _FORCE_INLINE_ bool _ensure_cache_for_size(FontAdvanced *p_font_data, const Vector2i &p_size) const; _FORCE_INLINE_ void _font_clear_cache(FontAdvanced *p_font_data); - void _generateMTSDF_threaded(uint32_t y, void *p_td) const; + static void _generateMTSDF_threaded(void *p_td, uint32_t p_y); _FORCE_INLINE_ Vector2i _get_size(const FontAdvanced *p_font_data, int p_size) const { if (p_font_data->msdf) { @@ -500,7 +499,7 @@ class TextServerAdvanced : public TextServerExtension { /* Intermediate data */ Char16String utf16; Vector<UBiDi *> bidi_iter; - Vector<Vector2i> bidi_override; + Vector<Vector3i> bidi_override; ScriptIterator *script_iter = nullptr; hb_buffer_t *hb_buffer = nullptr; diff --git a/modules/text_server_adv/thorvg_svg_in_ot.cpp b/modules/text_server_adv/thorvg_svg_in_ot.cpp index 9354d3f9b3..1406e3aaa0 100644 --- a/modules/text_server_adv/thorvg_svg_in_ot.cpp +++ b/modules/text_server_adv/thorvg_svg_in_ot.cpp @@ -88,24 +88,13 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin if (!gl_state.ready) { Ref<XMLParser> parser; parser.instantiate(); -#ifdef GDEXTENSION - PackedByteArray data; - data.resize(document->svg_document_length); - memcpy(data.ptrw(), document->svg_document, document->svg_document_length); - parser->open_buffer(data); -#else parser->_open_buffer((const uint8_t *)document->svg_document, document->svg_document_length); -#endif float aspect = 1.0f; String xml_body; while (parser->read() == OK) { if (parser->has_attribute("id")) { -#ifdef GDEXTENSION const String &gl_name = parser->get_named_attribute_value("id"); -#else - const String &gl_name = parser->get_attribute_value("id"); -#endif if (gl_name.begins_with("glyph")) { int dot_pos = gl_name.find("."); int64_t gl_idx = gl_name.substr(5, (dot_pos > 0) ? dot_pos - 5 : -1).to_int(); @@ -117,11 +106,7 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin } if (parser->get_node_type() == XMLParser::NODE_ELEMENT && parser->get_node_name() == "svg") { if (parser->has_attribute("viewBox")) { -#ifdef GDEXTENSION PackedStringArray vb = parser->get_named_attribute_value("viewBox").split(" "); -#else - Vector<String> vb = parser->get_attribute_value("viewBox").split(" "); -#endif if (vb.size() == 4) { aspect = vb[2].to_float() / vb[3].to_float(); @@ -129,19 +114,6 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin } continue; } -#ifdef GDEXTENSION - if (parser->get_node_type() == XMLParser::NODE_ELEMENT) { - xml_body = xml_body + "<" + parser->get_node_name(); - for (int i = 0; i < parser->get_attribute_count(); i++) { - xml_body = xml_body + " " + parser->get_attribute_name(i) + "=\"" + parser->get_attribute_value(i) + "\""; - } - xml_body = xml_body + ">"; - } else if (parser->get_node_type() == XMLParser::NODE_TEXT) { - xml_body = xml_body + parser->get_node_data(); - } else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END) { - xml_body = xml_body + "</" + parser->get_node_name() + ">"; - } -#else if (parser->get_node_type() == XMLParser::NODE_ELEMENT) { xml_body += vformat("<%s", parser->get_node_name()); for (int i = 0; i < parser->get_attribute_count(); i++) { @@ -153,7 +125,6 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin } else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END) { xml_body += vformat("</%s>", parser->get_node_name()); } -#endif } String temp_xml = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 0 0\">" + xml_body; @@ -175,11 +146,7 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin new_h = (new_w / aspect); } -#ifdef GDEXTENSION gl_state.xml_code = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"" + rtos(min_x) + " " + rtos(min_y) + " " + rtos(new_w) + " " + rtos(new_h) + "\">" + xml_body; -#else - gl_state.xml_code = vformat("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"%f %f %f %f\">", min_x, min_y, new_w, new_h) + xml_body; -#endif picture = tvg::Picture::gen(); result = picture->load(gl_state.xml_code.utf8().get_data(), gl_state.xml_code.utf8().length(), "svg+xml", false); diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index 034f88e387..b5d7d3a3cf 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -51,7 +51,6 @@ using namespace godot; #include "core/error/error_macros.h" #include "core/string/print_string.h" #include "core/string/translation.h" -#include "core/string/ucaps.h" #include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg. @@ -248,11 +247,7 @@ _FORCE_INLINE_ TextServerFallback::FontTexturePosition TextServerFallback::find_ // Could not find texture to fit, create one. int texsize = MAX(p_data->size.x * p_data->oversampling * 8, 256); -#ifdef GDEXTENSION - texsize = Math::next_power_of_2(texsize); -#else texsize = next_power_of_2(texsize); -#endif if (p_msdf) { texsize = MIN(texsize, 2048); @@ -260,18 +255,10 @@ _FORCE_INLINE_ TextServerFallback::FontTexturePosition TextServerFallback::find_ texsize = MIN(texsize, 1024); } if (mw > texsize) { // Special case, adapt to it? -#ifdef GDEXTENSION - texsize = Math::next_power_of_2(mw); -#else texsize = next_power_of_2(mw); -#endif } if (mh > texsize) { // Special case, adapt to it? -#ifdef GDEXTENSION - texsize = Math::next_power_of_2(mh); -#else texsize = next_power_of_2(mh); -#endif } ShelfPackTexture tex = ShelfPackTexture(texsize, texsize); @@ -374,14 +361,14 @@ static int ft_cubic_to(const FT_Vector *control1, const FT_Vector *control2, con return 0; } -void TextServerFallback::_generateMTSDF_threaded(uint32_t y, void *p_td) const { +void TextServerFallback::_generateMTSDF_threaded(void *p_td, uint32_t p_y) { MSDFThreadData *td = static_cast<MSDFThreadData *>(p_td); msdfgen::ShapeDistanceFinder<msdfgen::OverlappingContourCombiner<msdfgen::MultiAndTrueDistanceSelector>> distanceFinder(*td->shape); - int row = td->shape->inverseYAxis ? td->output->height() - y - 1 : y; + int row = td->shape->inverseYAxis ? td->output->height() - p_y - 1 : p_y; for (int col = 0; col < td->output->width(); ++col) { - int x = (y % 2) ? td->output->width() - col - 1 : col; - msdfgen::Point2 p = td->projection->unproject(msdfgen::Point2(x + .5, y + .5)); + int x = (p_y % 2) ? td->output->width() - col - 1 : col; + msdfgen::Point2 p = td->projection->unproject(msdfgen::Point2(x + .5, p_y + .5)); msdfgen::MultiAndTrueDistance distance = distanceFinder.distance(p); td->distancePixelConversion->operator()(td->output->operator()(x, row), distance); } @@ -451,14 +438,8 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf( td.projection = &projection; td.distancePixelConversion = &distancePixelConversion; -#ifdef GDEXTENSION - for (int i = 0; i < h; i++) { - _generateMTSDF_threaded(i, &td); - } -#else - WorkerThreadPool::GroupID group_id = WorkerThreadPool::get_singleton()->add_template_group_task(this, &TextServerFallback::_generateMTSDF_threaded, &td, h, -1, true, SNAME("TextServerFBRenderMSDF")); - WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_id); -#endif + WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_native_group_task(&TextServerFallback::_generateMTSDF_threaded, &td, h, -1, true, String("TextServerFBRenderMSDF")); + WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task); msdfgen::msdfErrorCorrection(image, shape, projection, p_pixel_range, config); @@ -2683,6 +2664,7 @@ void TextServerFallback::full_copy(ShapedTextDataFallback *p_shaped) { RID TextServerFallback::_create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) { _THREAD_SAFE_METHOD_ + ERR_FAIL_COND_V_MSG(p_direction == DIRECTION_INHERITED, RID(), "Invalid text direction."); ShapedTextDataFallback *sd = memnew(ShapedTextDataFallback); sd->direction = p_direction; @@ -2706,6 +2688,7 @@ void TextServerFallback::_shaped_text_clear(const RID &p_shaped) { } void TextServerFallback::_shaped_text_set_direction(const RID &p_shaped, TextServer::Direction p_direction) { + ERR_FAIL_COND_MSG(p_direction == DIRECTION_INHERITED, "Invalid text direction."); if (p_direction == DIRECTION_RTL) { ERR_PRINT_ONCE("Right-to-left layout is not supported by this text server."); } @@ -4078,31 +4061,11 @@ double TextServerFallback::_shaped_text_get_underline_thickness(const RID &p_sha } String TextServerFallback::_string_to_upper(const String &p_string, const String &p_language) const { - String upper = p_string; - - for (int i = 0; i <= upper.length(); i++) { - const char32_t s = upper[i]; - const char32_t t = _find_upper(s); - if (s != t) { // avoid copy on write - upper[i] = t; - } - } - - return upper; + return p_string.to_upper(); } String TextServerFallback::_string_to_lower(const String &p_string, const String &p_language) const { - String lower = p_string; - - for (int i = 0; i <= lower.length(); i++) { - const char32_t s = lower[i]; - const char32_t t = _find_lower(s); - if (s != t) { // avoid copy on write - lower[i] = t; - } - } - - return lower; + return p_string.to_lower(); } PackedInt32Array TextServerFallback::_string_get_word_breaks(const String &p_string, const String &p_language, int p_chars_per_line) const { diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h index b42f564534..9fb048a581 100644 --- a/modules/text_server_fb/text_server_fb.h +++ b/modules/text_server_fb/text_server_fb.h @@ -67,11 +67,11 @@ #include <godot_cpp/classes/image.hpp> #include <godot_cpp/classes/image_texture.hpp> #include <godot_cpp/classes/ref.hpp> +#include <godot_cpp/classes/worker_thread_pool.hpp> #include <godot_cpp/templates/hash_map.hpp> #include <godot_cpp/templates/hash_set.hpp> #include <godot_cpp/templates/rid_owner.hpp> -#include <godot_cpp/templates/thread_work_pool.hpp> #include <godot_cpp/templates/vector.hpp> using namespace godot; @@ -303,7 +303,7 @@ class TextServerFallback : public TextServerExtension { _FORCE_INLINE_ bool _ensure_glyph(FontFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph) const; _FORCE_INLINE_ bool _ensure_cache_for_size(FontFallback *p_font_data, const Vector2i &p_size) const; _FORCE_INLINE_ void _font_clear_cache(FontFallback *p_font_data); - void _generateMTSDF_threaded(uint32_t y, void *p_td) const; + static void _generateMTSDF_threaded(void *p_td, uint32_t p_y); _FORCE_INLINE_ Vector2i _get_size(const FontFallback *p_font_data, int p_size) const { if (p_font_data->msdf) { diff --git a/modules/text_server_fb/thorvg_svg_in_ot.cpp b/modules/text_server_fb/thorvg_svg_in_ot.cpp index 9354d3f9b3..1406e3aaa0 100644 --- a/modules/text_server_fb/thorvg_svg_in_ot.cpp +++ b/modules/text_server_fb/thorvg_svg_in_ot.cpp @@ -88,24 +88,13 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin if (!gl_state.ready) { Ref<XMLParser> parser; parser.instantiate(); -#ifdef GDEXTENSION - PackedByteArray data; - data.resize(document->svg_document_length); - memcpy(data.ptrw(), document->svg_document, document->svg_document_length); - parser->open_buffer(data); -#else parser->_open_buffer((const uint8_t *)document->svg_document, document->svg_document_length); -#endif float aspect = 1.0f; String xml_body; while (parser->read() == OK) { if (parser->has_attribute("id")) { -#ifdef GDEXTENSION const String &gl_name = parser->get_named_attribute_value("id"); -#else - const String &gl_name = parser->get_attribute_value("id"); -#endif if (gl_name.begins_with("glyph")) { int dot_pos = gl_name.find("."); int64_t gl_idx = gl_name.substr(5, (dot_pos > 0) ? dot_pos - 5 : -1).to_int(); @@ -117,11 +106,7 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin } if (parser->get_node_type() == XMLParser::NODE_ELEMENT && parser->get_node_name() == "svg") { if (parser->has_attribute("viewBox")) { -#ifdef GDEXTENSION PackedStringArray vb = parser->get_named_attribute_value("viewBox").split(" "); -#else - Vector<String> vb = parser->get_attribute_value("viewBox").split(" "); -#endif if (vb.size() == 4) { aspect = vb[2].to_float() / vb[3].to_float(); @@ -129,19 +114,6 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin } continue; } -#ifdef GDEXTENSION - if (parser->get_node_type() == XMLParser::NODE_ELEMENT) { - xml_body = xml_body + "<" + parser->get_node_name(); - for (int i = 0; i < parser->get_attribute_count(); i++) { - xml_body = xml_body + " " + parser->get_attribute_name(i) + "=\"" + parser->get_attribute_value(i) + "\""; - } - xml_body = xml_body + ">"; - } else if (parser->get_node_type() == XMLParser::NODE_TEXT) { - xml_body = xml_body + parser->get_node_data(); - } else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END) { - xml_body = xml_body + "</" + parser->get_node_name() + ">"; - } -#else if (parser->get_node_type() == XMLParser::NODE_ELEMENT) { xml_body += vformat("<%s", parser->get_node_name()); for (int i = 0; i < parser->get_attribute_count(); i++) { @@ -153,7 +125,6 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin } else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END) { xml_body += vformat("</%s>", parser->get_node_name()); } -#endif } String temp_xml = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 0 0\">" + xml_body; @@ -175,11 +146,7 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin new_h = (new_w / aspect); } -#ifdef GDEXTENSION gl_state.xml_code = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"" + rtos(min_x) + " " + rtos(min_y) + " " + rtos(new_w) + " " + rtos(new_h) + "\">" + xml_body; -#else - gl_state.xml_code = vformat("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"%f %f %f %f\">", min_x, min_y, new_w, new_h) + xml_body; -#endif picture = tvg::Picture::gen(); result = picture->load(gl_state.xml_code.utf8().get_data(), gl_state.xml_code.utf8().length(), "svg+xml", false); diff --git a/modules/webxr/doc_classes/WebXRInterface.xml b/modules/webxr/doc_classes/WebXRInterface.xml index f5964eb4d1..ba1750386f 100644 --- a/modules/webxr/doc_classes/WebXRInterface.xml +++ b/modules/webxr/doc_classes/WebXRInterface.xml @@ -18,7 +18,7 @@ func _ready(): # We assume this node has a button as a child. # This button is for the user to consent to entering immersive VR mode. - $Button.pressed.connect(self._on_Button_pressed) + $Button.pressed.connect(self._on_button_pressed) webxr_interface = XRServer.find_interface("WebXR") if webxr_interface: @@ -38,7 +38,7 @@ if session_mode == 'immersive-vr': vr_supported = supported - func _on_Button_pressed(): + func _on_button_pressed(): if not vr_supported: OS.alert("Your browser doesn't support VR") return |