diff options
378 files changed, 7273 insertions, 3140 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 37a2608c10..5b04986020 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -1489,15 +1489,6 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF(PropertyInfo(Variant::INT, "audio/general/ios/session_category", PROPERTY_HINT_ENUM, "Ambient,Multi Route,Play and Record,Playback,Record,Solo Ambient"), 0); GLOBAL_DEF("audio/general/ios/mix_with_others", false); - PackedStringArray extensions; - extensions.push_back("gd"); - if (ClassDB::class_exists("CSharpScript")) { - extensions.push_back("cs"); - } - extensions.push_back("gdshader"); - - GLOBAL_DEF(PropertyInfo(Variant::PACKED_STRING_ARRAY, "editor/script/search_in_file_extensions"), extensions); - _add_builtin_input_map(); // Keep the enum values in sync with the `DisplayServer::ScreenOrientation` enum. @@ -1570,6 +1561,11 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF("collada/use_ambient", false); + // Input settings + GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_long_press_as_right_click", false); + GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_pan_and_scale_gestures", false); + GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "input_devices/pointing/android/rotary_input_scroll_axis", PROPERTY_HINT_ENUM, "Horizontal,Vertical"), 1); + // These properties will not show up in the dialog. If you want to exclude whole groups, use add_hidden_prefix(). GLOBAL_DEF_INTERNAL("application/config/features", PackedStringArray()); GLOBAL_DEF_INTERNAL("internationalization/locale/translation_remaps", PackedStringArray()); diff --git a/core/core_bind.cpp b/core/core_bind.cpp index a1b7b81111..a15085bcde 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -1440,6 +1440,14 @@ TypedArray<Dictionary> ClassDB::class_get_property_list(const StringName &p_clas return ret; } +StringName ClassDB::class_get_property_getter(const StringName &p_class, const StringName &p_property) { + return ::ClassDB::get_property_getter(p_class, p_property); +} + +StringName ClassDB::class_get_property_setter(const StringName &p_class, const StringName &p_property) { + return ::ClassDB::get_property_setter(p_class, p_property); +} + Variant ClassDB::class_get_property(Object *p_object, const StringName &p_property) const { Variant ret; ::ClassDB::get_property(p_object, p_property, ret); @@ -1601,6 +1609,8 @@ void ClassDB::_bind_methods() { ::ClassDB::bind_method(D_METHOD("class_get_signal_list", "class", "no_inheritance"), &ClassDB::class_get_signal_list, DEFVAL(false)); ::ClassDB::bind_method(D_METHOD("class_get_property_list", "class", "no_inheritance"), &ClassDB::class_get_property_list, DEFVAL(false)); + ::ClassDB::bind_method(D_METHOD("class_get_property_getter", "class", "property"), &ClassDB::class_get_property_getter); + ::ClassDB::bind_method(D_METHOD("class_get_property_setter", "class", "property"), &ClassDB::class_get_property_setter); ::ClassDB::bind_method(D_METHOD("class_get_property", "object", "property"), &ClassDB::class_get_property); ::ClassDB::bind_method(D_METHOD("class_set_property", "object", "property", "value"), &ClassDB::class_set_property); diff --git a/core/core_bind.h b/core/core_bind.h index b142a2fbbd..69a359e4ed 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -447,6 +447,8 @@ public: TypedArray<Dictionary> class_get_signal_list(const StringName &p_class, bool p_no_inheritance = false) const; TypedArray<Dictionary> class_get_property_list(const StringName &p_class, bool p_no_inheritance = false) const; + StringName class_get_property_getter(const StringName &p_class, const StringName &p_property); + StringName class_get_property_setter(const StringName &p_class, const StringName &p_property); Variant class_get_property(Object *p_object, const StringName &p_property) const; Error class_set_property(Object *p_object, const StringName &p_property, const Variant &p_value) const; diff --git a/core/error/error_macros.cpp b/core/error/error_macros.cpp index 8376c0aaf8..813ee7684f 100644 --- a/core/error/error_macros.cpp +++ b/core/error/error_macros.cpp @@ -34,6 +34,12 @@ #include "core/os/os.h" #include "core/string/ustring.h" +// Optional physics interpolation warnings try to include the path to the relevant node. +#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) +#include "core/config/project_settings.h" +#include "scene/main/node.h" +#endif + static ErrorHandlerList *error_handler_list = nullptr; void add_error_handler(ErrorHandlerList *p_handler) { @@ -128,3 +134,48 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li void _err_flush_stdout() { fflush(stdout); } + +// Prevent error spam by limiting the warnings to a certain frequency. +void _physics_interpolation_warning(const char *p_function, const char *p_file, int p_line, ObjectID p_id, const char *p_warn_string) { +#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) + const uint32_t warn_max = 2048; + const uint32_t warn_timeout_seconds = 15; + + static uint32_t warn_count = warn_max; + static uint32_t warn_timeout = warn_timeout_seconds; + + uint32_t time_now = UINT32_MAX; + + if (warn_count) { + warn_count--; + } + + if (!warn_count) { + time_now = OS::get_singleton()->get_ticks_msec() / 1000; + } + + if ((warn_count == 0) && (time_now >= warn_timeout)) { + warn_count = warn_max; + warn_timeout = time_now + warn_timeout_seconds; + + if (GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) { + // UINT64_MAX means unused. + if (p_id.operator uint64_t() == UINT64_MAX) { + _err_print_error(p_function, p_file, p_line, "[Physics interpolation] " + String(p_warn_string) + " (possibly benign).", false, ERR_HANDLER_WARNING); + } else { + String node_name; + if (p_id.is_valid()) { + Node *node = Object::cast_to<Node>(ObjectDB::get_instance(p_id)); + if (node && node->is_inside_tree()) { + node_name = "\"" + String(node->get_path()) + "\""; + } else { + node_name = "\"unknown\""; + } + } + + _err_print_error(p_function, p_file, p_line, "[Physics interpolation] " + String(p_warn_string) + ": " + node_name + " (possibly benign).", false, ERR_HANDLER_WARNING); + } + } + } +#endif +} diff --git a/core/error/error_macros.h b/core/error/error_macros.h index ab7dbcbd44..d31adb72be 100644 --- a/core/error/error_macros.h +++ b/core/error/error_macros.h @@ -31,6 +31,7 @@ #ifndef ERROR_MACROS_H #define ERROR_MACROS_H +#include "core/object/object_id.h" #include "core/typedefs.h" #include <atomic> // We'd normally use safe_refcount.h, but that would cause circular includes. @@ -71,6 +72,8 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool p_editor_notify = false, bool fatal = false); void _err_flush_stdout(); +void _physics_interpolation_warning(const char *p_function, const char *p_file, int p_line, ObjectID p_id, const char *p_warn_string); + #ifdef __GNUC__ //#define FUNCTION_STR __PRETTY_FUNCTION__ - too annoying #define FUNCTION_STR __FUNCTION__ @@ -832,4 +835,14 @@ void _err_flush_stdout(); #define DEV_CHECK_ONCE(m_cond) #endif +/** + * Physics Interpolation warnings. + * These are spam protection warnings. + */ +#define PHYSICS_INTERPOLATION_NODE_WARNING(m_object_id, m_string) \ + _physics_interpolation_warning(FUNCTION_STR, __FILE__, __LINE__, m_object_id, m_string) + +#define PHYSICS_INTERPOLATION_WARNING(m_string) \ + _physics_interpolation_warning(FUNCTION_STR, __FILE__, __LINE__, UINT64_MAX, m_string) + #endif // ERROR_MACROS_H diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index 8e2366fc95..cb6832ea39 100644 --- a/core/extension/gdextension.cpp +++ b/core/extension/gdextension.cpp @@ -781,23 +781,14 @@ Error GDExtension::open_library(const String &p_path, const String &p_entry_symb } } - String actual_lib_path; OS::GDExtensionData data = { true, // also_set_library_path - &actual_lib_path, // r_resolved_path + &library_path, // r_resolved_path Engine::get_singleton()->is_editor_hint(), // generate_temp_files &abs_dependencies_paths, // library_dependencies }; Error err = OS::get_singleton()->open_dynamic_library(abs_path, library, &data); - if (actual_lib_path.get_file() != abs_path.get_file()) { - // If temporary files are generated, let's change the library path to point at the original, - // because that's what we want to check to see if it's changed. - library_path = actual_lib_path.get_base_dir().path_join(p_path.get_file()); - } else { - library_path = actual_lib_path; - } - ERR_FAIL_COND_V_MSG(err == ERR_FILE_NOT_FOUND, err, "GDExtension dynamic library not found: " + abs_path); ERR_FAIL_COND_V_MSG(err != OK, err, "Can't open GDExtension dynamic library: " + abs_path); diff --git a/core/input/godotcontrollerdb.txt b/core/input/godotcontrollerdb.txt index f5158bfabb..8e8ec4c718 100644 --- a/core/input/godotcontrollerdb.txt +++ b/core/input/godotcontrollerdb.txt @@ -8,7 +8,7 @@ __XINPUT_DEVICE__,XInput Gamepad,a:b12,b:b13,x:b14,y:b15,start:b4,guide:b10,back Default Android Gamepad,Default Controller,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b8,rightshoulder:b10,rightx:a2,start:b6,righty:a3,dpleft:h0.8,lefttrigger:a4,x:b2,dpup:h0.1,back:b4,leftstick:b7,leftshoulder:b9,y:b3,a:b0,dpright:h0.2,righttrigger:a5,b:b1,platform:Android, # Web -standard,Standard Gamepad Mapping,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b8,start:b9,leftstick:b10,rightstick:b11,dpup:b12,dpdown:b13,dpleft:b14,dpright:b15,guide:b16,leftstick:b10,rightstick:b11,platform:Web, +standard,Standard Gamepad Mapping,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:+a4,righttrigger:+a5,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b8,start:b9,leftstick:b10,rightstick:b11,dpup:b12,dpdown:b13,dpleft:b14,dpright:b15,guide:b16,leftstick:b10,rightstick:b11,platform:Web, Linux24c6581a,PowerA Xbox One Cabled,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web, Linux0e6f0301,Logic 3 Controller (xbox compatible),a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web, Linux045e028e,Microsoft X-Box 360 pad,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web, diff --git a/core/input/input.cpp b/core/input/input.cpp index 91378591b0..eba7ded267 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -513,21 +513,49 @@ void Input::joy_connection_changed(int p_idx, bool p_connected, const String &p_ Vector3 Input::get_gravity() const { _THREAD_SAFE_METHOD_ + +#ifdef DEBUG_ENABLED + if (!gravity_enabled) { + WARN_PRINT_ONCE("`input_devices/sensors/enable_gravity` is not enabled in project settings."); + } +#endif + return gravity; } Vector3 Input::get_accelerometer() const { _THREAD_SAFE_METHOD_ + +#ifdef DEBUG_ENABLED + if (!accelerometer_enabled) { + WARN_PRINT_ONCE("`input_devices/sensors/enable_accelerometer` is not enabled in project settings."); + } +#endif + return accelerometer; } Vector3 Input::get_magnetometer() const { _THREAD_SAFE_METHOD_ + +#ifdef DEBUG_ENABLED + if (!magnetometer_enabled) { + WARN_PRINT_ONCE("`input_devices/sensors/enable_magnetometer` is not enabled in project settings."); + } +#endif + return magnetometer; } Vector3 Input::get_gyroscope() const { _THREAD_SAFE_METHOD_ + +#ifdef DEBUG_ENABLED + if (!gyroscope_enabled) { + WARN_PRINT_ONCE("`input_devices/sensors/enable_gyroscope` is not enabled in project settings."); + } +#endif + return gyroscope; } @@ -1683,6 +1711,11 @@ Input::Input() { // Always use standard behavior in the editor. legacy_just_pressed_behavior = false; } + + accelerometer_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_accelerometer", false); + gravity_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_gravity", false); + gyroscope_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_gyroscope", false); + magnetometer_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_magnetometer", false); } Input::~Input() { diff --git a/core/input/input.h b/core/input/input.h index 89e48f53d7..95dd623cc0 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -92,9 +92,13 @@ private: RBSet<JoyButton> joy_buttons_pressed; RBMap<JoyAxis, float> _joy_axis; //RBMap<StringName,int> custom_action_press; + bool gravity_enabled = false; Vector3 gravity; + bool accelerometer_enabled = false; Vector3 accelerometer; + bool magnetometer_enabled = false; Vector3 magnetometer; + bool gyroscope_enabled = false; Vector3 gyroscope; Vector2 mouse_pos; int64_t mouse_window = 0; diff --git a/core/io/image.cpp b/core/io/image.cpp index d0598e4dc6..499364826d 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -501,6 +501,38 @@ static void _convert(int p_width, int p_height, const uint8_t *p_src, uint8_t *p } } +template <typename T, uint32_t read_channels, uint32_t write_channels, T def_zero, T def_one> +static void _convert_fast(int p_width, int p_height, const T *p_src, T *p_dst) { + uint32_t dst_count = 0; + uint32_t src_count = 0; + + const int resolution = p_width * p_height; + + for (int i = 0; i < resolution; i++) { + memcpy(p_dst + dst_count, p_src + src_count, MIN(read_channels, write_channels) * sizeof(T)); + + if constexpr (write_channels > read_channels) { + const T def_value[4] = { def_zero, def_zero, def_zero, def_one }; + memcpy(p_dst + dst_count + read_channels, &def_value[read_channels], (write_channels - read_channels) * sizeof(T)); + } + + dst_count += write_channels; + src_count += read_channels; + } +} + +static bool _are_formats_compatible(Image::Format p_format0, Image::Format p_format1) { + if (p_format0 <= Image::FORMAT_RGBA8 && p_format1 <= Image::FORMAT_RGBA8) { + return true; + } else if (p_format0 <= Image::FORMAT_RGBAH && p_format0 >= Image::FORMAT_RH && p_format1 <= Image::FORMAT_RGBAH && p_format1 >= Image::FORMAT_RH) { + return true; + } else if (p_format0 <= Image::FORMAT_RGBAF && p_format0 >= Image::FORMAT_RF && p_format1 <= Image::FORMAT_RGBAF && p_format1 >= Image::FORMAT_RF) { + return true; + } + + return false; +} + void Image::convert(Format p_new_format) { ERR_FAIL_INDEX_MSG(p_new_format, FORMAT_MAX, "The Image format specified (" + itos(p_new_format) + ") is out of range. See Image's Format enum."); if (data.size() == 0) { @@ -517,7 +549,7 @@ void Image::convert(Format p_new_format) { if (Image::is_format_compressed(format) || Image::is_format_compressed(p_new_format)) { ERR_FAIL_MSG("Cannot convert to <-> from compressed formats. Use compress() and decompress() instead."); - } else if (format > FORMAT_RGBA8 || p_new_format > FORMAT_RGBA8) { + } else if (!_are_formats_compatible(format, p_new_format)) { //use put/set pixel which is slower but works with non byte formats Image new_img(width, height, mipmaps, p_new_format); @@ -648,6 +680,78 @@ void Image::convert(Format p_new_format) { case FORMAT_RGBA8 | (FORMAT_RGB8 << 8): _convert<3, true, 3, false, false, false>(mip_width, mip_height, rptr, wptr); break; + case FORMAT_RH | (FORMAT_RGH << 8): + _convert_fast<uint16_t, 1, 2, 0x0000, 0x3C00>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr); + break; + case FORMAT_RH | (FORMAT_RGBH << 8): + _convert_fast<uint16_t, 1, 3, 0x0000, 0x3C00>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr); + break; + case FORMAT_RH | (FORMAT_RGBAH << 8): + _convert_fast<uint16_t, 1, 4, 0x0000, 0x3C00>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr); + break; + case FORMAT_RGH | (FORMAT_RH << 8): + _convert_fast<uint16_t, 2, 1, 0x0000, 0x3C00>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr); + break; + case FORMAT_RGH | (FORMAT_RGBH << 8): + _convert_fast<uint16_t, 2, 3, 0x0000, 0x3C00>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr); + break; + case FORMAT_RGH | (FORMAT_RGBAH << 8): + _convert_fast<uint16_t, 2, 4, 0x0000, 0x3C00>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr); + break; + case FORMAT_RGBH | (FORMAT_RH << 8): + _convert_fast<uint16_t, 3, 1, 0x0000, 0x3C00>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr); + break; + case FORMAT_RGBH | (FORMAT_RGH << 8): + _convert_fast<uint16_t, 3, 2, 0x0000, 0x3C00>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr); + break; + case FORMAT_RGBH | (FORMAT_RGBAH << 8): + _convert_fast<uint16_t, 3, 4, 0x0000, 0x3C00>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr); + break; + case FORMAT_RGBAH | (FORMAT_RH << 8): + _convert_fast<uint16_t, 4, 1, 0x0000, 0x3C00>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr); + break; + case FORMAT_RGBAH | (FORMAT_RGH << 8): + _convert_fast<uint16_t, 4, 2, 0x0000, 0x3C00>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr); + break; + case FORMAT_RGBAH | (FORMAT_RGBH << 8): + _convert_fast<uint16_t, 4, 3, 0x0000, 0x3C00>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr); + break; + case FORMAT_RF | (FORMAT_RGF << 8): + _convert_fast<uint32_t, 1, 2, 0x00000000, 0x3F800000>(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr); + break; + case FORMAT_RF | (FORMAT_RGBF << 8): + _convert_fast<uint32_t, 1, 3, 0x00000000, 0x3F800000>(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr); + break; + case FORMAT_RF | (FORMAT_RGBAF << 8): + _convert_fast<uint32_t, 1, 4, 0x00000000, 0x3F800000>(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr); + break; + case FORMAT_RGF | (FORMAT_RF << 8): + _convert_fast<uint32_t, 2, 1, 0x00000000, 0x3F800000>(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr); + break; + case FORMAT_RGF | (FORMAT_RGBF << 8): + _convert_fast<uint32_t, 2, 3, 0x00000000, 0x3F800000>(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr); + break; + case FORMAT_RGF | (FORMAT_RGBAF << 8): + _convert_fast<uint32_t, 2, 4, 0x00000000, 0x3F800000>(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr); + break; + case FORMAT_RGBF | (FORMAT_RF << 8): + _convert_fast<uint32_t, 3, 1, 0x00000000, 0x3F800000>(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr); + break; + case FORMAT_RGBF | (FORMAT_RGF << 8): + _convert_fast<uint32_t, 3, 2, 0x00000000, 0x3F800000>(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr); + break; + case FORMAT_RGBF | (FORMAT_RGBAF << 8): + _convert_fast<uint32_t, 3, 4, 0x00000000, 0x3F800000>(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr); + break; + case FORMAT_RGBAF | (FORMAT_RF << 8): + _convert_fast<uint32_t, 4, 1, 0x00000000, 0x3F800000>(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr); + break; + case FORMAT_RGBAF | (FORMAT_RGF << 8): + _convert_fast<uint32_t, 4, 2, 0x00000000, 0x3F800000>(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr); + break; + case FORMAT_RGBAF | (FORMAT_RGBF << 8): + _convert_fast<uint32_t, 4, 3, 0x00000000, 0x3F800000>(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr); + break; } } @@ -3460,6 +3564,7 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("fix_alpha_edges"), &Image::fix_alpha_edges); ClassDB::bind_method(D_METHOD("premultiply_alpha"), &Image::premultiply_alpha); ClassDB::bind_method(D_METHOD("srgb_to_linear"), &Image::srgb_to_linear); + ClassDB::bind_method(D_METHOD("linear_to_srgb"), &Image::linear_to_srgb); ClassDB::bind_method(D_METHOD("normal_map_to_xy"), &Image::normal_map_to_xy); ClassDB::bind_method(D_METHOD("rgbe_to_srgb"), &Image::rgbe_to_srgb); ClassDB::bind_method(D_METHOD("bump_map_to_normal_map", "bump_scale"), &Image::bump_map_to_normal_map, DEFVAL(1.0)); @@ -3727,6 +3832,37 @@ void Image::srgb_to_linear() { } } +void Image::linear_to_srgb() { + if (data.size() == 0) { + return; + } + + static const uint8_t lin2srgb[256] = { 0, 12, 21, 28, 33, 38, 42, 46, 49, 52, 55, 58, 61, 63, 66, 68, 70, 73, 75, 77, 79, 81, 82, 84, 86, 88, 89, 91, 93, 94, 96, 97, 99, 100, 102, 103, 104, 106, 107, 109, 110, 111, 112, 114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 126, 127, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 151, 152, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 161, 162, 163, 164, 165, 165, 166, 167, 168, 168, 169, 170, 171, 171, 172, 173, 174, 174, 175, 176, 176, 177, 178, 179, 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191, 191, 192, 193, 193, 194, 194, 195, 196, 196, 197, 197, 198, 199, 199, 200, 201, 201, 202, 202, 203, 204, 204, 205, 205, 206, 206, 207, 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223, 224, 224, 225, 226, 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 236, 237, 237, 237, 238, 238, 239, 239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, 251, 251, 252, 252, 253, 253, 254, 254, 255 }; + + ERR_FAIL_COND(format != FORMAT_RGB8 && format != FORMAT_RGBA8); + + if (format == FORMAT_RGBA8) { + int len = data.size() / 4; + uint8_t *data_ptr = data.ptrw(); + + for (int i = 0; i < len; i++) { + data_ptr[(i << 2) + 0] = lin2srgb[data_ptr[(i << 2) + 0]]; + data_ptr[(i << 2) + 1] = lin2srgb[data_ptr[(i << 2) + 1]]; + data_ptr[(i << 2) + 2] = lin2srgb[data_ptr[(i << 2) + 2]]; + } + + } else if (format == FORMAT_RGB8) { + int len = data.size() / 3; + uint8_t *data_ptr = data.ptrw(); + + for (int i = 0; i < len; i++) { + data_ptr[(i * 3) + 0] = lin2srgb[data_ptr[(i * 3) + 0]]; + data_ptr[(i * 3) + 1] = lin2srgb[data_ptr[(i * 3) + 1]]; + data_ptr[(i * 3) + 2] = lin2srgb[data_ptr[(i * 3) + 2]]; + } + } +} + void Image::premultiply_alpha() { if (data.size() == 0) { return; diff --git a/core/io/image.h b/core/io/image.h index d55cc39dbb..cc285dabe3 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -383,6 +383,7 @@ public: void fix_alpha_edges(); void premultiply_alpha(); void srgb_to_linear(); + void linear_to_srgb(); void normal_map_to_xy(); Ref<Image> rgbe_to_srgb(); Ref<Image> get_image_from_mipmap(int p_mipmap) const; diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 928bb95de3..4988e73624 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -38,7 +38,7 @@ #include "core/os/os.h" #include "core/os/safe_binary_mutex.h" #include "core/string/print_string.h" -#include "core/string/translation.h" +#include "core/string/translation_server.h" #include "core/variant/variant_parser.h" #include "servers/rendering_server.h" @@ -583,13 +583,7 @@ ResourceLoader::ThreadLoadStatus ResourceLoader::load_threaded_get_status(const } String local_path = _validate_local_path(p_path); - if (!thread_load_tasks.has(local_path)) { -#ifdef DEV_ENABLED - CRASH_NOW(); -#endif - // On non-dev, be defensive and at least avoid crashing (at this point at least). - return THREAD_LOAD_INVALID_RESOURCE; - } + ERR_FAIL_COND_V_MSG(!thread_load_tasks.has(local_path), THREAD_LOAD_INVALID_RESOURCE, "Bug in ResourceLoader logic, please report."); ThreadLoadTask &load_task = thread_load_tasks[local_path]; status = load_task.status; @@ -678,14 +672,10 @@ Ref<Resource> ResourceLoader::_load_complete_inner(LoadToken &p_load_token, Erro if (!p_load_token.local_path.is_empty()) { if (!thread_load_tasks.has(p_load_token.local_path)) { -#ifdef DEV_ENABLED - CRASH_NOW(); -#endif - // On non-dev, be defensive and at least avoid crashing (at this point at least). if (r_error) { *r_error = ERR_BUG; } - return Ref<Resource>(); + ERR_FAIL_V_MSG(Ref<Resource>(), "Bug in ResourceLoader logic, please report."); } ThreadLoadTask &load_task = thread_load_tasks[p_load_token.local_path]; diff --git a/core/math/color.h b/core/math/color.h index e17b8c9fd7..70fad78acb 100644 --- a/core/math/color.h +++ b/core/math/color.h @@ -129,33 +129,46 @@ struct [[nodiscard]] Color { } _FORCE_INLINE_ uint32_t to_rgbe9995() const { - const float pow2to9 = 512.0f; - const float B = 15.0f; - const float N = 9.0f; - - float sharedexp = 65408.000f; // Result of: ((pow2to9 - 1.0f) / pow2to9) * powf(2.0f, 31.0f - 15.0f) - - float cRed = MAX(0.0f, MIN(sharedexp, r)); - float cGreen = MAX(0.0f, MIN(sharedexp, g)); - float cBlue = MAX(0.0f, MIN(sharedexp, b)); - - float cMax = MAX(cRed, MAX(cGreen, cBlue)); - - float expp = MAX(-B - 1.0f, floor(Math::log(cMax) / (real_t)Math_LN2)) + 1.0f + B; - - float sMax = (float)floor((cMax / Math::pow(2.0f, expp - B - N)) + 0.5f); - - float exps = expp + 1.0f; - - if (0.0f <= sMax && sMax < pow2to9) { - exps = expp; - } - - float sRed = Math::floor((cRed / pow(2.0f, exps - B - N)) + 0.5f); - float sGreen = Math::floor((cGreen / pow(2.0f, exps - B - N)) + 0.5f); - float sBlue = Math::floor((cBlue / pow(2.0f, exps - B - N)) + 0.5f); - - return (uint32_t(Math::fast_ftoi(sRed)) & 0x1FF) | ((uint32_t(Math::fast_ftoi(sGreen)) & 0x1FF) << 9) | ((uint32_t(Math::fast_ftoi(sBlue)) & 0x1FF) << 18) | ((uint32_t(Math::fast_ftoi(exps)) & 0x1F) << 27); + // https://github.com/microsoft/DirectX-Graphics-Samples/blob/v10.0.19041.0/MiniEngine/Core/Color.cpp + static const float kMaxVal = float(0x1FF << 7); + static const float kMinVal = float(1.f / (1 << 16)); + + // Clamp RGB to [0, 1.FF*2^16] + const float _r = CLAMP(r, 0.0f, kMaxVal); + const float _g = CLAMP(g, 0.0f, kMaxVal); + const float _b = CLAMP(b, 0.0f, kMaxVal); + + // Compute the maximum channel, no less than 1.0*2^-15 + const float MaxChannel = MAX(MAX(_r, _g), MAX(_b, kMinVal)); + + // Take the exponent of the maximum channel (rounding up the 9th bit) and + // add 15 to it. When added to the channels, it causes the implicit '1.0' + // bit and the first 8 mantissa bits to be shifted down to the low 9 bits + // of the mantissa, rounding the truncated bits. + union { + float f; + int32_t i; + } R, G, B, E; + + E.f = MaxChannel; + E.i += 0x07804000; // Add 15 to the exponent and 0x4000 to the mantissa + E.i &= 0x7F800000; // Zero the mantissa + + // This shifts the 9-bit values we need into the lowest bits, rounding as + // needed. Note that if the channel has a smaller exponent than the max + // channel, it will shift even more. This is intentional. + R.f = _r + E.f; + G.f = _g + E.f; + B.f = _b + E.f; + + // Convert the Bias to the correct exponent in the upper 5 bits. + E.i <<= 4; + E.i += 0x10000000; + + // Combine the fields. RGB floats have unwanted data in the upper 9 + // bits. Only red needs to mask them off because green and blue shift + // it out to the left. + return E.i | (B.i << 18) | (G.i << 9) | (R.i & 511); } _FORCE_INLINE_ Color blend(const Color &p_over) const { diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h index 3060f31970..fd53ed28fd 100644 --- a/core/math/math_funcs.h +++ b/core/math/math_funcs.h @@ -447,14 +447,22 @@ public: static _ALWAYS_INLINE_ double smoothstep(double p_from, double p_to, double p_s) { if (is_equal_approx(p_from, p_to)) { - return p_from; + if (likely(p_from <= p_to)) { + return p_s <= p_from ? 0.0 : 1.0; + } else { + return p_s <= p_to ? 1.0 : 0.0; + } } double s = CLAMP((p_s - p_from) / (p_to - p_from), 0.0, 1.0); return s * s * (3.0 - 2.0 * s); } static _ALWAYS_INLINE_ float smoothstep(float p_from, float p_to, float p_s) { if (is_equal_approx(p_from, p_to)) { - return p_from; + if (likely(p_from <= p_to)) { + return p_s <= p_from ? 0.0f : 1.0f; + } else { + return p_s <= p_to ? 1.0f : 0.0f; + } } float s = CLAMP((p_s - p_from) / (p_to - p_from), 0.0f, 1.0f); return s * s * (3.0f - 2.0f * s); diff --git a/core/math/transform_interpolator.cpp b/core/math/transform_interpolator.cpp index 6a564b0ca7..1cd35b3d1a 100644 --- a/core/math/transform_interpolator.cpp +++ b/core/math/transform_interpolator.cpp @@ -31,6 +31,7 @@ #include "transform_interpolator.h" #include "core/math/transform_2d.h" +#include "core/math/transform_3d.h" void TransformInterpolator::interpolate_transform_2d(const Transform2D &p_prev, const Transform2D &p_curr, Transform2D &r_result, real_t p_fraction) { // Special case for physics interpolation, if flipping, don't interpolate basis. @@ -44,3 +45,340 @@ void TransformInterpolator::interpolate_transform_2d(const Transform2D &p_prev, r_result = p_prev.interpolate_with(p_curr, p_fraction); } + +void TransformInterpolator::interpolate_transform_3d(const Transform3D &p_prev, const Transform3D &p_curr, Transform3D &r_result, real_t p_fraction) { + r_result.origin = p_prev.origin + ((p_curr.origin - p_prev.origin) * p_fraction); + interpolate_basis(p_prev.basis, p_curr.basis, r_result.basis, p_fraction); +} + +void TransformInterpolator::interpolate_basis(const Basis &p_prev, const Basis &p_curr, Basis &r_result, real_t p_fraction) { + Method method = find_method(p_prev, p_curr); + interpolate_basis_via_method(p_prev, p_curr, r_result, p_fraction, method); +} + +void TransformInterpolator::interpolate_transform_3d_via_method(const Transform3D &p_prev, const Transform3D &p_curr, Transform3D &r_result, real_t p_fraction, Method p_method) { + r_result.origin = p_prev.origin + ((p_curr.origin - p_prev.origin) * p_fraction); + interpolate_basis_via_method(p_prev.basis, p_curr.basis, r_result.basis, p_fraction, p_method); +} + +void TransformInterpolator::interpolate_basis_via_method(const Basis &p_prev, const Basis &p_curr, Basis &r_result, real_t p_fraction, Method p_method) { + switch (p_method) { + default: { + interpolate_basis_linear(p_prev, p_curr, r_result, p_fraction); + } break; + case INTERP_SLERP: { + r_result = _basis_slerp_unchecked(p_prev, p_curr, p_fraction); + } break; + case INTERP_SCALED_SLERP: { + interpolate_basis_scaled_slerp(p_prev, p_curr, r_result, p_fraction); + } break; + } +} + +Quaternion TransformInterpolator::_basis_to_quat_unchecked(const Basis &p_basis) { + Basis m = p_basis; + real_t trace = m.rows[0][0] + m.rows[1][1] + m.rows[2][2]; + real_t temp[4]; + + if (trace > 0.0) { + real_t s = Math::sqrt(trace + 1.0f); + temp[3] = (s * 0.5f); + s = 0.5f / s; + + temp[0] = ((m.rows[2][1] - m.rows[1][2]) * s); + temp[1] = ((m.rows[0][2] - m.rows[2][0]) * s); + temp[2] = ((m.rows[1][0] - m.rows[0][1]) * s); + } else { + int i = m.rows[0][0] < m.rows[1][1] + ? (m.rows[1][1] < m.rows[2][2] ? 2 : 1) + : (m.rows[0][0] < m.rows[2][2] ? 2 : 0); + int j = (i + 1) % 3; + int k = (i + 2) % 3; + + real_t s = Math::sqrt(m.rows[i][i] - m.rows[j][j] - m.rows[k][k] + 1.0f); + temp[i] = s * 0.5f; + s = 0.5f / s; + + temp[3] = (m.rows[k][j] - m.rows[j][k]) * s; + temp[j] = (m.rows[j][i] + m.rows[i][j]) * s; + temp[k] = (m.rows[k][i] + m.rows[i][k]) * s; + } + + return Quaternion(temp[0], temp[1], temp[2], temp[3]); +} + +Quaternion TransformInterpolator::_quat_slerp_unchecked(const Quaternion &p_from, const Quaternion &p_to, real_t p_fraction) { + Quaternion to1; + real_t omega, cosom, sinom, scale0, scale1; + + // Calculate cosine. + cosom = p_from.dot(p_to); + + // Adjust signs (if necessary) + if (cosom < 0.0f) { + cosom = -cosom; + to1.x = -p_to.x; + to1.y = -p_to.y; + to1.z = -p_to.z; + to1.w = -p_to.w; + } else { + to1.x = p_to.x; + to1.y = p_to.y; + to1.z = p_to.z; + to1.w = p_to.w; + } + + // Calculate coefficients. + + // This check could possibly be removed as we dealt with this + // case in the find_method() function, but is left for safety, it probably + // isn't a bottleneck. + if ((1.0f - cosom) > (real_t)CMP_EPSILON) { + // standard case (slerp) + omega = Math::acos(cosom); + sinom = Math::sin(omega); + scale0 = Math::sin((1.0f - p_fraction) * omega) / sinom; + scale1 = Math::sin(p_fraction * omega) / sinom; + } else { + // "from" and "to" quaternions are very close + // ... so we can do a linear interpolation + scale0 = 1.0f - p_fraction; + scale1 = p_fraction; + } + // Calculate final values. + return Quaternion( + scale0 * p_from.x + scale1 * to1.x, + scale0 * p_from.y + scale1 * to1.y, + scale0 * p_from.z + scale1 * to1.z, + scale0 * p_from.w + scale1 * to1.w); +} + +Basis TransformInterpolator::_basis_slerp_unchecked(Basis p_from, Basis p_to, real_t p_fraction) { + Quaternion from = _basis_to_quat_unchecked(p_from); + Quaternion to = _basis_to_quat_unchecked(p_to); + + Basis b(_quat_slerp_unchecked(from, to, p_fraction)); + return b; +} + +void TransformInterpolator::interpolate_basis_scaled_slerp(Basis p_prev, Basis p_curr, Basis &r_result, real_t p_fraction) { + // Normalize both and find lengths. + Vector3 lengths_prev = _basis_orthonormalize(p_prev); + Vector3 lengths_curr = _basis_orthonormalize(p_curr); + + r_result = _basis_slerp_unchecked(p_prev, p_curr, p_fraction); + + // Now the result is unit length basis, we need to scale. + Vector3 lengths_lerped = lengths_prev + ((lengths_curr - lengths_prev) * p_fraction); + + // Keep a note that the column / row order of the basis is weird, + // so keep an eye for bugs with this. + r_result[0] *= lengths_lerped; + r_result[1] *= lengths_lerped; + r_result[2] *= lengths_lerped; +} + +void TransformInterpolator::interpolate_basis_linear(const Basis &p_prev, const Basis &p_curr, Basis &r_result, real_t p_fraction) { + // Interpolate basis. + r_result = p_prev.lerp(p_curr, p_fraction); + + // It turns out we need to guard against zero scale basis. + // This is kind of silly, as we should probably fix the bugs elsewhere in Godot that can't deal with + // zero scale, but until that time... + for (int n = 0; n < 3; n++) { + Vector3 &axis = r_result[n]; + + // Not ok, this could cause errors due to bugs elsewhere, + // so we will bodge set this to a small value. + const real_t smallest = 0.0001f; + const real_t smallest_squared = smallest * smallest; + if (axis.length_squared() < smallest_squared) { + // Setting a different component to the smallest + // helps prevent the situation where all the axes are pointing in the same direction, + // which could be a problem for e.g. cross products... + axis[n] = smallest; + } + } +} + +// Returns length. +real_t TransformInterpolator::_vec3_normalize(Vector3 &p_vec) { + real_t lengthsq = p_vec.length_squared(); + if (lengthsq == 0.0f) { + p_vec.x = p_vec.y = p_vec.z = 0.0f; + return 0.0f; + } + real_t length = Math::sqrt(lengthsq); + p_vec.x /= length; + p_vec.y /= length; + p_vec.z /= length; + return length; +} + +// Returns lengths. +Vector3 TransformInterpolator::_basis_orthonormalize(Basis &r_basis) { + // Gram-Schmidt Process. + + Vector3 x = r_basis.get_column(0); + Vector3 y = r_basis.get_column(1); + Vector3 z = r_basis.get_column(2); + + Vector3 lengths; + + lengths.x = _vec3_normalize(x); + y = (y - x * (x.dot(y))); + lengths.y = _vec3_normalize(y); + z = (z - x * (x.dot(z)) - y * (y.dot(z))); + lengths.z = _vec3_normalize(z); + + r_basis.set_column(0, x); + r_basis.set_column(1, y); + r_basis.set_column(2, z); + + return lengths; +} + +TransformInterpolator::Method TransformInterpolator::_test_basis(Basis p_basis, bool r_needed_normalize, Quaternion &r_quat) { + // Axis lengths. + Vector3 al = Vector3(p_basis.get_column(0).length_squared(), + p_basis.get_column(1).length_squared(), + p_basis.get_column(2).length_squared()); + + // Non unit scale? + if (r_needed_normalize || !_vec3_is_equal_approx(al, Vector3(1.0, 1.0, 1.0), (real_t)0.001f)) { + // If the basis is not normalized (at least approximately), it will fail the checks needed for slerp. + // So we try to detect a scaled (but not sheared) basis, which we *can* slerp by normalizing first, + // and lerping the scales separately. + + // If any of the axes are really small, it is unlikely to be a valid rotation, or is scaled too small to deal with float error. + const real_t sl_epsilon = 0.00001f; + if ((al.x < sl_epsilon) || + (al.y < sl_epsilon) || + (al.z < sl_epsilon)) { + return INTERP_LERP; + } + + // Normalize the basis. + Basis norm_basis = p_basis; + + al.x = Math::sqrt(al.x); + al.y = Math::sqrt(al.y); + al.z = Math::sqrt(al.z); + + norm_basis.set_column(0, norm_basis.get_column(0) / al.x); + norm_basis.set_column(1, norm_basis.get_column(1) / al.y); + norm_basis.set_column(2, norm_basis.get_column(2) / al.z); + + // This doesn't appear necessary, as the later checks will catch it. + // if (!_basis_is_orthogonal_any_scale(norm_basis)) { + // return INTERP_LERP; + // } + + p_basis = norm_basis; + + // Orthonormalize not necessary as normal normalization(!) works if the + // axes are orthonormal. + // p_basis.orthonormalize(); + + // If we needed to normalize one of the two bases, we will need to normalize both, + // regardless of whether the 2nd needs it, just to make sure it takes the path to return + // INTERP_SCALED_LERP on the 2nd call of _test_basis. + r_needed_normalize = true; + } + + // Apply less stringent tests than the built in slerp, the standard Godot slerp + // is too susceptible to float error to be useful. + real_t det = p_basis.determinant(); + if (!Math::is_equal_approx(det, 1, (real_t)0.01f)) { + return INTERP_LERP; + } + + if (!_basis_is_orthogonal(p_basis)) { + return INTERP_LERP; + } + + // TODO: This could possibly be less stringent too, check this. + r_quat = _basis_to_quat_unchecked(p_basis); + if (!r_quat.is_normalized()) { + return INTERP_LERP; + } + + return r_needed_normalize ? INTERP_SCALED_SLERP : INTERP_SLERP; +} + +// This check doesn't seem to be needed but is preserved in case of bugs. +bool TransformInterpolator::_basis_is_orthogonal_any_scale(const Basis &p_basis) { + Vector3 cross = p_basis.get_column(0).cross(p_basis.get_column(1)); + real_t l = _vec3_normalize(cross); + // Too small numbers, revert to lerp. + if (l < 0.001f) { + return false; + } + + const real_t epsilon = 0.9995f; + + real_t dot = cross.dot(p_basis.get_column(2)); + if (dot < epsilon) { + return false; + } + + cross = p_basis.get_column(1).cross(p_basis.get_column(2)); + l = _vec3_normalize(cross); + // Too small numbers, revert to lerp. + if (l < 0.001f) { + return false; + } + + dot = cross.dot(p_basis.get_column(0)); + if (dot < epsilon) { + return false; + } + + return true; +} + +bool TransformInterpolator::_basis_is_orthogonal(const Basis &p_basis, real_t p_epsilon) { + Basis identity; + Basis m = p_basis * p_basis.transposed(); + + // Less stringent tests than the standard Godot slerp. + if (!_vec3_is_equal_approx(m[0], identity[0], p_epsilon) || !_vec3_is_equal_approx(m[1], identity[1], p_epsilon) || !_vec3_is_equal_approx(m[2], identity[2], p_epsilon)) { + return false; + } + return true; +} + +real_t TransformInterpolator::checksum_transform_3d(const Transform3D &p_transform) { + // just a really basic checksum, this can probably be improved + real_t sum = _vec3_sum(p_transform.origin); + sum -= _vec3_sum(p_transform.basis.rows[0]); + sum += _vec3_sum(p_transform.basis.rows[1]); + sum -= _vec3_sum(p_transform.basis.rows[2]); + return sum; +} + +TransformInterpolator::Method TransformInterpolator::find_method(const Basis &p_a, const Basis &p_b) { + bool needed_normalize = false; + + Quaternion q0; + Method method = _test_basis(p_a, needed_normalize, q0); + if (method == INTERP_LERP) { + return method; + } + + Quaternion q1; + method = _test_basis(p_b, needed_normalize, q1); + if (method == INTERP_LERP) { + return method; + } + + // Are they close together? + // Apply the same test that will revert to lerp as is present in the slerp routine. + // Calculate cosine. + real_t cosom = Math::abs(q0.dot(q1)); + if ((1.0f - cosom) <= (real_t)CMP_EPSILON) { + return INTERP_LERP; + } + + return method; +} diff --git a/core/math/transform_interpolator.h b/core/math/transform_interpolator.h index a9bce2bd7f..cc556707e4 100644 --- a/core/math/transform_interpolator.h +++ b/core/math/transform_interpolator.h @@ -32,15 +32,64 @@ #define TRANSFORM_INTERPOLATOR_H #include "core/math/math_defs.h" +#include "core/math/vector3.h" + +// Keep all the functions for fixed timestep interpolation together. +// There are two stages involved: +// Finding a method, for determining the interpolation method between two +// keyframes (which are physics ticks). +// And applying that pre-determined method. + +// Pre-determining the method makes sense because it is expensive and often +// several frames may occur between each physics tick, which will make it cheaper +// than performing every frame. struct Transform2D; +struct Transform3D; +struct Basis; +struct Quaternion; class TransformInterpolator { +public: + enum Method { + INTERP_LERP, + INTERP_SLERP, + INTERP_SCALED_SLERP, + }; + private: - static bool _sign(real_t p_val) { return p_val >= 0; } + _FORCE_INLINE_ static bool _sign(real_t p_val) { return p_val >= 0; } + static real_t _vec3_sum(const Vector3 &p_pt) { return p_pt.x + p_pt.y + p_pt.z; } + static real_t _vec3_normalize(Vector3 &p_vec); + _FORCE_INLINE_ static bool _vec3_is_equal_approx(const Vector3 &p_a, const Vector3 &p_b, real_t p_tolerance) { + return Math::is_equal_approx(p_a.x, p_b.x, p_tolerance) && Math::is_equal_approx(p_a.y, p_b.y, p_tolerance) && Math::is_equal_approx(p_a.z, p_b.z, p_tolerance); + } + static Vector3 _basis_orthonormalize(Basis &r_basis); + static Method _test_basis(Basis p_basis, bool r_needed_normalize, Quaternion &r_quat); + static Basis _basis_slerp_unchecked(Basis p_from, Basis p_to, real_t p_fraction); + static Quaternion _quat_slerp_unchecked(const Quaternion &p_from, const Quaternion &p_to, real_t p_fraction); + static Quaternion _basis_to_quat_unchecked(const Basis &p_basis); + static bool _basis_is_orthogonal(const Basis &p_basis, real_t p_epsilon = 0.01f); + static bool _basis_is_orthogonal_any_scale(const Basis &p_basis); + + static void interpolate_basis_linear(const Basis &p_prev, const Basis &p_curr, Basis &r_result, real_t p_fraction); + static void interpolate_basis_scaled_slerp(Basis p_prev, Basis p_curr, Basis &r_result, real_t p_fraction); public: static void interpolate_transform_2d(const Transform2D &p_prev, const Transform2D &p_curr, Transform2D &r_result, real_t p_fraction); + + // Generic functions, use when you don't know what method should be used, e.g. from GDScript. + // These will be slower. + static void interpolate_transform_3d(const Transform3D &p_prev, const Transform3D &p_curr, Transform3D &r_result, real_t p_fraction); + static void interpolate_basis(const Basis &p_prev, const Basis &p_curr, Basis &r_result, real_t p_fraction); + + // Optimized function when you know ahead of time the method. + static void interpolate_transform_3d_via_method(const Transform3D &p_prev, const Transform3D &p_curr, Transform3D &r_result, real_t p_fraction, Method p_method); + static void interpolate_basis_via_method(const Basis &p_prev, const Basis &p_curr, Basis &r_result, real_t p_fraction, Method p_method); + + static real_t checksum_transform_3d(const Transform3D &p_transform); + + static Method find_method(const Basis &p_a, const Basis &p_b); }; #endif // TRANSFORM_INTERPOLATOR_H diff --git a/core/object/object.cpp b/core/object/object.cpp index e4d1a8fc9a..ac23bf831f 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -38,7 +38,7 @@ #include "core/object/script_language.h" #include "core/os/os.h" #include "core/string/print_string.h" -#include "core/string/translation.h" +#include "core/string/translation_server.h" #include "core/templates/local_vector.h" #include "core/variant/typed_array.h" diff --git a/core/os/main_loop.h b/core/os/main_loop.h index e48541d074..9c22cbaf3c 100644 --- a/core/os/main_loop.h +++ b/core/os/main_loop.h @@ -64,6 +64,7 @@ public: virtual void initialize(); virtual void iteration_prepare() {} virtual bool physics_process(double p_time); + virtual void iteration_end() {} virtual bool process(double p_time); virtual void finalize(); diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index c0a86e9fb7..c866ff0415 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -79,6 +79,7 @@ #include "core/os/time.h" #include "core/string/optimized_translation.h" #include "core/string/translation.h" +#include "core/string/translation_server.h" static Ref<ResourceFormatSaverBinary> resource_saver_binary; static Ref<ResourceFormatLoaderBinary> resource_loader_binary; diff --git a/core/string/translation.compat.inc b/core/string/translation.compat.inc index d792d4a6fc..68bd1831e4 100644 --- a/core/string/translation.compat.inc +++ b/core/string/translation.compat.inc @@ -38,9 +38,4 @@ void Translation::_bind_compatibility_methods() { ClassDB::bind_compatibility_method(D_METHOD("erase_message", "src_message", "context"), &Translation::erase_message, DEFVAL("")); } -void TranslationServer::_bind_compatibility_methods() { - ClassDB::bind_compatibility_method(D_METHOD("translate", "message", "context"), &TranslationServer::translate, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("translate_plural", "message", "plural_message", "n", "context"), &TranslationServer::translate_plural, DEFVAL("")); -} - #endif diff --git a/core/string/translation.cpp b/core/string/translation.cpp index 432016284a..33d4a1bcde 100644 --- a/core/string/translation.cpp +++ b/core/string/translation.cpp @@ -31,14 +31,9 @@ #include "translation.h" #include "translation.compat.inc" -#include "core/config/project_settings.h" -#include "core/io/resource_loader.h" #include "core/os/os.h" -#include "core/string/locales.h" - -#ifdef TOOLS_ENABLED -#include "main/main.h" -#endif +#include "core/os/thread.h" +#include "core/string/translation_server.h" Dictionary Translation::_get_messages() const { Dictionary d; @@ -173,911 +168,3 @@ void Translation::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "messages", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_messages", "_get_messages"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "locale"), "set_locale", "get_locale"); } - -/////////////////////////////////////////////// - -struct _character_accent_pair { - const char32_t character; - const char32_t *accented_character; -}; - -static _character_accent_pair _character_to_accented[] = { - { 'A', U"Å" }, - { 'B', U"ß" }, - { 'C', U"Ç" }, - { 'D', U"Ð" }, - { 'E', U"É" }, - { 'F', U"F́" }, - { 'G', U"Ĝ" }, - { 'H', U"Ĥ" }, - { 'I', U"Ĩ" }, - { 'J', U"Ĵ" }, - { 'K', U"ĸ" }, - { 'L', U"Ł" }, - { 'M', U"Ḿ" }, - { 'N', U"й" }, - { 'O', U"Ö" }, - { 'P', U"Ṕ" }, - { 'Q', U"Q́" }, - { 'R', U"Ř" }, - { 'S', U"Ŝ" }, - { 'T', U"Ŧ" }, - { 'U', U"Ũ" }, - { 'V', U"Ṽ" }, - { 'W', U"Ŵ" }, - { 'X', U"X́" }, - { 'Y', U"Ÿ" }, - { 'Z', U"Ž" }, - { 'a', U"á" }, - { 'b', U"ḅ" }, - { 'c', U"ć" }, - { 'd', U"d́" }, - { 'e', U"é" }, - { 'f', U"f́" }, - { 'g', U"ǵ" }, - { 'h', U"h̀" }, - { 'i', U"í" }, - { 'j', U"ǰ" }, - { 'k', U"ḱ" }, - { 'l', U"ł" }, - { 'm', U"m̀" }, - { 'n', U"ή" }, - { 'o', U"ô" }, - { 'p', U"ṕ" }, - { 'q', U"q́" }, - { 'r', U"ŕ" }, - { 's', U"š" }, - { 't', U"ŧ" }, - { 'u', U"ü" }, - { 'v', U"ṽ" }, - { 'w', U"ŵ" }, - { 'x', U"x́" }, - { 'y', U"ý" }, - { 'z', U"ź" }, -}; - -Vector<TranslationServer::LocaleScriptInfo> TranslationServer::locale_script_info; - -HashMap<String, String> TranslationServer::language_map; -HashMap<String, String> TranslationServer::script_map; -HashMap<String, String> TranslationServer::locale_rename_map; -HashMap<String, String> TranslationServer::country_name_map; -HashMap<String, String> TranslationServer::variant_map; -HashMap<String, String> TranslationServer::country_rename_map; - -void TranslationServer::init_locale_info() { - // Init locale info. - language_map.clear(); - int idx = 0; - while (language_list[idx][0] != nullptr) { - language_map[language_list[idx][0]] = String::utf8(language_list[idx][1]); - idx++; - } - - // Init locale-script map. - locale_script_info.clear(); - idx = 0; - while (locale_scripts[idx][0] != nullptr) { - LocaleScriptInfo info; - info.name = locale_scripts[idx][0]; - info.script = locale_scripts[idx][1]; - info.default_country = locale_scripts[idx][2]; - Vector<String> supported_countries = String(locale_scripts[idx][3]).split(",", false); - for (int i = 0; i < supported_countries.size(); i++) { - info.supported_countries.insert(supported_countries[i]); - } - locale_script_info.push_back(info); - idx++; - } - - // Init supported script list. - script_map.clear(); - idx = 0; - while (script_list[idx][0] != nullptr) { - script_map[script_list[idx][1]] = String::utf8(script_list[idx][0]); - idx++; - } - - // Init regional variant map. - variant_map.clear(); - idx = 0; - while (locale_variants[idx][0] != nullptr) { - variant_map[locale_variants[idx][0]] = locale_variants[idx][1]; - idx++; - } - - // Init locale renames. - locale_rename_map.clear(); - idx = 0; - while (locale_renames[idx][0] != nullptr) { - if (!String(locale_renames[idx][1]).is_empty()) { - locale_rename_map[locale_renames[idx][0]] = locale_renames[idx][1]; - } - idx++; - } - - // Init country names. - country_name_map.clear(); - idx = 0; - while (country_names[idx][0] != nullptr) { - country_name_map[String(country_names[idx][0])] = String::utf8(country_names[idx][1]); - idx++; - } - - // Init country renames. - country_rename_map.clear(); - idx = 0; - while (country_renames[idx][0] != nullptr) { - if (!String(country_renames[idx][1]).is_empty()) { - country_rename_map[country_renames[idx][0]] = country_renames[idx][1]; - } - idx++; - } -} - -String TranslationServer::standardize_locale(const String &p_locale) const { - return _standardize_locale(p_locale, false); -} - -String TranslationServer::_standardize_locale(const String &p_locale, bool p_add_defaults) const { - // Replaces '-' with '_' for macOS style locales. - String univ_locale = p_locale.replace("-", "_"); - - // Extract locale elements. - String lang_name, script_name, country_name, variant_name; - Vector<String> locale_elements = univ_locale.get_slice("@", 0).split("_"); - lang_name = locale_elements[0]; - if (locale_elements.size() >= 2) { - if (locale_elements[1].length() == 4 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_lower_case(locale_elements[1][1]) && is_ascii_lower_case(locale_elements[1][2]) && is_ascii_lower_case(locale_elements[1][3])) { - script_name = locale_elements[1]; - } - if (locale_elements[1].length() == 2 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_upper_case(locale_elements[1][1])) { - country_name = locale_elements[1]; - } - } - if (locale_elements.size() >= 3) { - if (locale_elements[2].length() == 2 && is_ascii_upper_case(locale_elements[2][0]) && is_ascii_upper_case(locale_elements[2][1])) { - country_name = locale_elements[2]; - } else if (variant_map.has(locale_elements[2].to_lower()) && variant_map[locale_elements[2].to_lower()] == lang_name) { - variant_name = locale_elements[2].to_lower(); - } - } - if (locale_elements.size() >= 4) { - if (variant_map.has(locale_elements[3].to_lower()) && variant_map[locale_elements[3].to_lower()] == lang_name) { - variant_name = locale_elements[3].to_lower(); - } - } - - // Try extract script and variant from the extra part. - Vector<String> script_extra = univ_locale.get_slice("@", 1).split(";"); - for (int i = 0; i < script_extra.size(); i++) { - if (script_extra[i].to_lower() == "cyrillic") { - script_name = "Cyrl"; - break; - } else if (script_extra[i].to_lower() == "latin") { - script_name = "Latn"; - break; - } else if (script_extra[i].to_lower() == "devanagari") { - script_name = "Deva"; - break; - } else if (variant_map.has(script_extra[i].to_lower()) && variant_map[script_extra[i].to_lower()] == lang_name) { - variant_name = script_extra[i].to_lower(); - } - } - - // Handles known non-ISO language names used e.g. on Windows. - if (locale_rename_map.has(lang_name)) { - lang_name = locale_rename_map[lang_name]; - } - - // Handle country renames. - if (country_rename_map.has(country_name)) { - country_name = country_rename_map[country_name]; - } - - // Remove unsupported script codes. - if (!script_map.has(script_name)) { - script_name = ""; - } - - // Add script code base on language and country codes for some ambiguous cases. - if (p_add_defaults) { - if (script_name.is_empty()) { - for (int i = 0; i < locale_script_info.size(); i++) { - const LocaleScriptInfo &info = locale_script_info[i]; - if (info.name == lang_name) { - if (country_name.is_empty() || info.supported_countries.has(country_name)) { - script_name = info.script; - break; - } - } - } - } - if (!script_name.is_empty() && country_name.is_empty()) { - // Add conntry code based on script for some ambiguous cases. - for (int i = 0; i < locale_script_info.size(); i++) { - const LocaleScriptInfo &info = locale_script_info[i]; - if (info.name == lang_name && info.script == script_name) { - country_name = info.default_country; - break; - } - } - } - } - - // Combine results. - String out = lang_name; - if (!script_name.is_empty()) { - out = out + "_" + script_name; - } - if (!country_name.is_empty()) { - out = out + "_" + country_name; - } - if (!variant_name.is_empty()) { - out = out + "_" + variant_name; - } - return out; -} - -int TranslationServer::compare_locales(const String &p_locale_a, const String &p_locale_b) const { - String locale_a = _standardize_locale(p_locale_a, true); - String locale_b = _standardize_locale(p_locale_b, true); - - if (locale_a == locale_b) { - // Exact match. - return 10; - } - - Vector<String> locale_a_elements = locale_a.split("_"); - Vector<String> locale_b_elements = locale_b.split("_"); - if (locale_a_elements[0] == locale_b_elements[0]) { - // Matching language, both locales have extra parts. - // Return number of matching elements. - int matching_elements = 1; - for (int i = 1; i < locale_a_elements.size(); i++) { - for (int j = 1; j < locale_b_elements.size(); j++) { - if (locale_a_elements[i] == locale_b_elements[j]) { - matching_elements++; - } - } - } - return matching_elements; - } else { - // No match. - return 0; - } -} - -String TranslationServer::get_locale_name(const String &p_locale) const { - String lang_name, script_name, country_name; - Vector<String> locale_elements = standardize_locale(p_locale).split("_"); - lang_name = locale_elements[0]; - if (locale_elements.size() >= 2) { - if (locale_elements[1].length() == 4 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_lower_case(locale_elements[1][1]) && is_ascii_lower_case(locale_elements[1][2]) && is_ascii_lower_case(locale_elements[1][3])) { - script_name = locale_elements[1]; - } - if (locale_elements[1].length() == 2 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_upper_case(locale_elements[1][1])) { - country_name = locale_elements[1]; - } - } - if (locale_elements.size() >= 3) { - if (locale_elements[2].length() == 2 && is_ascii_upper_case(locale_elements[2][0]) && is_ascii_upper_case(locale_elements[2][1])) { - country_name = locale_elements[2]; - } - } - - String name = language_map[lang_name]; - if (!script_name.is_empty()) { - name = name + " (" + script_map[script_name] + ")"; - } - if (!country_name.is_empty()) { - name = name + ", " + country_name_map[country_name]; - } - return name; -} - -Vector<String> TranslationServer::get_all_languages() const { - Vector<String> languages; - - for (const KeyValue<String, String> &E : language_map) { - languages.push_back(E.key); - } - - return languages; -} - -String TranslationServer::get_language_name(const String &p_language) const { - return language_map[p_language]; -} - -Vector<String> TranslationServer::get_all_scripts() const { - Vector<String> scripts; - - for (const KeyValue<String, String> &E : script_map) { - scripts.push_back(E.key); - } - - return scripts; -} - -String TranslationServer::get_script_name(const String &p_script) const { - return script_map[p_script]; -} - -Vector<String> TranslationServer::get_all_countries() const { - Vector<String> countries; - - for (const KeyValue<String, String> &E : country_name_map) { - countries.push_back(E.key); - } - - return countries; -} - -String TranslationServer::get_country_name(const String &p_country) const { - return country_name_map[p_country]; -} - -void TranslationServer::set_locale(const String &p_locale) { - String new_locale = standardize_locale(p_locale); - if (locale == new_locale) { - return; - } - - locale = new_locale; - ResourceLoader::reload_translation_remaps(); - - if (OS::get_singleton()->get_main_loop()) { - OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); - } -} - -String TranslationServer::get_locale() const { - return locale; -} - -PackedStringArray TranslationServer::get_loaded_locales() const { - PackedStringArray locales; - for (const Ref<Translation> &E : translations) { - const Ref<Translation> &t = E; - ERR_FAIL_COND_V(t.is_null(), PackedStringArray()); - String l = t->get_locale(); - - locales.push_back(l); - } - - return locales; -} - -void TranslationServer::add_translation(const Ref<Translation> &p_translation) { - translations.insert(p_translation); -} - -void TranslationServer::remove_translation(const Ref<Translation> &p_translation) { - translations.erase(p_translation); -} - -Ref<Translation> TranslationServer::get_translation_object(const String &p_locale) { - Ref<Translation> res; - int best_score = 0; - - for (const Ref<Translation> &E : translations) { - const Ref<Translation> &t = E; - ERR_FAIL_COND_V(t.is_null(), nullptr); - String l = t->get_locale(); - - int score = compare_locales(p_locale, l); - if (score > 0 && score >= best_score) { - res = t; - best_score = score; - if (score == 10) { - break; // Exact match, skip the rest. - } - } - } - return res; -} - -void TranslationServer::clear() { - translations.clear(); -} - -StringName TranslationServer::translate(const StringName &p_message, const StringName &p_context) const { - // Match given message against the translation catalog for the project locale. - - if (!enabled) { - return p_message; - } - - StringName res = _get_message_from_translations(p_message, p_context, locale, false); - - if (!res && fallback.length() >= 2) { - res = _get_message_from_translations(p_message, p_context, fallback, false); - } - - if (!res) { - return pseudolocalization_enabled ? pseudolocalize(p_message) : p_message; - } - - return pseudolocalization_enabled ? pseudolocalize(res) : res; -} - -StringName TranslationServer::translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { - if (!enabled) { - if (p_n == 1) { - return p_message; - } - return p_message_plural; - } - - StringName res = _get_message_from_translations(p_message, p_context, locale, true, p_message_plural, p_n); - - if (!res && fallback.length() >= 2) { - res = _get_message_from_translations(p_message, p_context, fallback, true, p_message_plural, p_n); - } - - if (!res) { - if (p_n == 1) { - return p_message; - } - return p_message_plural; - } - - return res; -} - -StringName TranslationServer::_get_message_from_translations(const StringName &p_message, const StringName &p_context, const String &p_locale, bool plural, const String &p_message_plural, int p_n) const { - StringName res; - int best_score = 0; - - for (const Ref<Translation> &E : translations) { - const Ref<Translation> &t = E; - ERR_FAIL_COND_V(t.is_null(), p_message); - String l = t->get_locale(); - - int score = compare_locales(p_locale, l); - if (score > 0 && score >= best_score) { - StringName r; - if (!plural) { - r = t->get_message(p_message, p_context); - } else { - r = t->get_plural_message(p_message, p_message_plural, p_n, p_context); - } - if (!r) { - continue; - } - res = r; - best_score = score; - if (score == 10) { - break; // Exact match, skip the rest. - } - } - } - - return res; -} - -TranslationServer *TranslationServer::singleton = nullptr; - -bool TranslationServer::_load_translations(const String &p_from) { - if (ProjectSettings::get_singleton()->has_setting(p_from)) { - const Vector<String> &translation_names = GLOBAL_GET(p_from); - - int tcount = translation_names.size(); - - if (tcount) { - const String *r = translation_names.ptr(); - - for (int i = 0; i < tcount; i++) { - Ref<Translation> tr = ResourceLoader::load(r[i]); - if (tr.is_valid()) { - add_translation(tr); - } - } - } - return true; - } - - return false; -} - -void TranslationServer::setup() { - String test = GLOBAL_DEF("internationalization/locale/test", ""); - test = test.strip_edges(); - if (!test.is_empty()) { - set_locale(test); - } else { - set_locale(OS::get_singleton()->get_locale()); - } - - fallback = GLOBAL_DEF("internationalization/locale/fallback", "en"); - pseudolocalization_enabled = GLOBAL_DEF("internationalization/pseudolocalization/use_pseudolocalization", false); - pseudolocalization_accents_enabled = GLOBAL_DEF("internationalization/pseudolocalization/replace_with_accents", true); - pseudolocalization_double_vowels_enabled = GLOBAL_DEF("internationalization/pseudolocalization/double_vowels", false); - pseudolocalization_fake_bidi_enabled = GLOBAL_DEF("internationalization/pseudolocalization/fake_bidi", false); - pseudolocalization_override_enabled = GLOBAL_DEF("internationalization/pseudolocalization/override", false); - expansion_ratio = GLOBAL_DEF("internationalization/pseudolocalization/expansion_ratio", 0.0); - pseudolocalization_prefix = GLOBAL_DEF("internationalization/pseudolocalization/prefix", "["); - pseudolocalization_suffix = GLOBAL_DEF("internationalization/pseudolocalization/suffix", "]"); - pseudolocalization_skip_placeholders_enabled = GLOBAL_DEF("internationalization/pseudolocalization/skip_placeholders", true); - -#ifdef TOOLS_ENABLED - ProjectSettings::get_singleton()->set_custom_property_info(PropertyInfo(Variant::STRING, "internationalization/locale/fallback", PROPERTY_HINT_LOCALE_ID, "")); -#endif -} - -void TranslationServer::set_tool_translation(const Ref<Translation> &p_translation) { - tool_translation = p_translation; -} - -Ref<Translation> TranslationServer::get_tool_translation() const { - return tool_translation; -} - -String TranslationServer::get_tool_locale() { -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint() || Engine::get_singleton()->is_project_manager_hint()) { - if (TranslationServer::get_singleton()->get_tool_translation().is_valid()) { - return tool_translation->get_locale(); - } else { - return "en"; - } - } else { -#else - { -#endif - // Look for best matching loaded translation. - String best_locale = "en"; - int best_score = 0; - - for (const Ref<Translation> &E : translations) { - const Ref<Translation> &t = E; - ERR_FAIL_COND_V(t.is_null(), best_locale); - String l = t->get_locale(); - - int score = compare_locales(locale, l); - if (score > 0 && score >= best_score) { - best_locale = l; - best_score = score; - if (score == 10) { - break; // Exact match, skip the rest. - } - } - } - return best_locale; - } -} - -StringName TranslationServer::tool_translate(const StringName &p_message, const StringName &p_context) const { - if (tool_translation.is_valid()) { - StringName r = tool_translation->get_message(p_message, p_context); - if (r) { - return r; - } - } - return p_message; -} - -StringName TranslationServer::tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { - if (tool_translation.is_valid()) { - StringName r = tool_translation->get_plural_message(p_message, p_message_plural, p_n, p_context); - if (r) { - return r; - } - } - - if (p_n == 1) { - return p_message; - } - return p_message_plural; -} - -void TranslationServer::set_property_translation(const Ref<Translation> &p_translation) { - property_translation = p_translation; -} - -StringName TranslationServer::property_translate(const StringName &p_message, const StringName &p_context) const { - if (property_translation.is_valid()) { - StringName r = property_translation->get_message(p_message, p_context); - if (r) { - return r; - } - } - return p_message; -} - -void TranslationServer::set_doc_translation(const Ref<Translation> &p_translation) { - doc_translation = p_translation; -} - -StringName TranslationServer::doc_translate(const StringName &p_message, const StringName &p_context) const { - if (doc_translation.is_valid()) { - StringName r = doc_translation->get_message(p_message, p_context); - if (r) { - return r; - } - } - return p_message; -} - -StringName TranslationServer::doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { - if (doc_translation.is_valid()) { - StringName r = doc_translation->get_plural_message(p_message, p_message_plural, p_n, p_context); - if (r) { - return r; - } - } - - if (p_n == 1) { - return p_message; - } - return p_message_plural; -} - -void TranslationServer::set_extractable_translation(const Ref<Translation> &p_translation) { - extractable_translation = p_translation; -} - -StringName TranslationServer::extractable_translate(const StringName &p_message, const StringName &p_context) const { - if (extractable_translation.is_valid()) { - StringName r = extractable_translation->get_message(p_message, p_context); - if (r) { - return r; - } - } - return p_message; -} - -StringName TranslationServer::extractable_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { - if (extractable_translation.is_valid()) { - StringName r = extractable_translation->get_plural_message(p_message, p_message_plural, p_n, p_context); - if (r) { - return r; - } - } - - if (p_n == 1) { - return p_message; - } - return p_message_plural; -} - -bool TranslationServer::is_pseudolocalization_enabled() const { - return pseudolocalization_enabled; -} - -void TranslationServer::set_pseudolocalization_enabled(bool p_enabled) { - pseudolocalization_enabled = p_enabled; - - ResourceLoader::reload_translation_remaps(); - - if (OS::get_singleton()->get_main_loop()) { - OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); - } -} - -void TranslationServer::reload_pseudolocalization() { - pseudolocalization_accents_enabled = GLOBAL_GET("internationalization/pseudolocalization/replace_with_accents"); - pseudolocalization_double_vowels_enabled = GLOBAL_GET("internationalization/pseudolocalization/double_vowels"); - pseudolocalization_fake_bidi_enabled = GLOBAL_GET("internationalization/pseudolocalization/fake_bidi"); - pseudolocalization_override_enabled = GLOBAL_GET("internationalization/pseudolocalization/override"); - expansion_ratio = GLOBAL_GET("internationalization/pseudolocalization/expansion_ratio"); - pseudolocalization_prefix = GLOBAL_GET("internationalization/pseudolocalization/prefix"); - pseudolocalization_suffix = GLOBAL_GET("internationalization/pseudolocalization/suffix"); - pseudolocalization_skip_placeholders_enabled = GLOBAL_GET("internationalization/pseudolocalization/skip_placeholders"); - - ResourceLoader::reload_translation_remaps(); - - if (OS::get_singleton()->get_main_loop()) { - OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); - } -} - -StringName TranslationServer::pseudolocalize(const StringName &p_message) const { - String message = p_message; - int length = message.length(); - if (pseudolocalization_override_enabled) { - message = get_override_string(message); - } - - if (pseudolocalization_double_vowels_enabled) { - message = double_vowels(message); - } - - if (pseudolocalization_accents_enabled) { - message = replace_with_accented_string(message); - } - - if (pseudolocalization_fake_bidi_enabled) { - message = wrap_with_fakebidi_characters(message); - } - - StringName res = add_padding(message, length); - return res; -} - -StringName TranslationServer::tool_pseudolocalize(const StringName &p_message) const { - String message = p_message; - message = double_vowels(message); - message = replace_with_accented_string(message); - StringName res = "[!!! " + message + " !!!]"; - return res; -} - -String TranslationServer::get_override_string(String &p_message) const { - String res; - for (int i = 0; i < p_message.length(); i++) { - if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { - res += p_message[i]; - res += p_message[i + 1]; - i++; - continue; - } - res += '*'; - } - return res; -} - -String TranslationServer::double_vowels(String &p_message) const { - String res; - for (int i = 0; i < p_message.length(); i++) { - if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { - res += p_message[i]; - res += p_message[i + 1]; - i++; - continue; - } - res += p_message[i]; - if (p_message[i] == 'a' || p_message[i] == 'e' || p_message[i] == 'i' || p_message[i] == 'o' || p_message[i] == 'u' || - p_message[i] == 'A' || p_message[i] == 'E' || p_message[i] == 'I' || p_message[i] == 'O' || p_message[i] == 'U') { - res += p_message[i]; - } - } - return res; -}; - -String TranslationServer::replace_with_accented_string(String &p_message) const { - String res; - for (int i = 0; i < p_message.length(); i++) { - if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { - res += p_message[i]; - res += p_message[i + 1]; - i++; - continue; - } - const char32_t *accented = get_accented_version(p_message[i]); - if (accented) { - res += accented; - } else { - res += p_message[i]; - } - } - return res; -} - -String TranslationServer::wrap_with_fakebidi_characters(String &p_message) const { - String res; - char32_t fakebidiprefix = U'\u202e'; - char32_t fakebidisuffix = U'\u202c'; - res += fakebidiprefix; - // The fake bidi unicode gets popped at every newline so pushing it back at every newline. - for (int i = 0; i < p_message.length(); i++) { - if (p_message[i] == '\n') { - res += fakebidisuffix; - res += p_message[i]; - res += fakebidiprefix; - } else if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { - res += fakebidisuffix; - res += p_message[i]; - res += p_message[i + 1]; - res += fakebidiprefix; - i++; - } else { - res += p_message[i]; - } - } - res += fakebidisuffix; - return res; -} - -String TranslationServer::add_padding(const String &p_message, int p_length) const { - String underscores = String("_").repeat(p_length * expansion_ratio / 2); - String prefix = pseudolocalization_prefix + underscores; - String suffix = underscores + pseudolocalization_suffix; - - return prefix + p_message + suffix; -} - -const char32_t *TranslationServer::get_accented_version(char32_t p_character) const { - if (!is_ascii_alphabet_char(p_character)) { - return nullptr; - } - - for (unsigned int i = 0; i < sizeof(_character_to_accented) / sizeof(_character_to_accented[0]); i++) { - if (_character_to_accented[i].character == p_character) { - return _character_to_accented[i].accented_character; - } - } - - return nullptr; -} - -bool TranslationServer::is_placeholder(String &p_message, int p_index) const { - return p_index < p_message.length() - 1 && p_message[p_index] == '%' && - (p_message[p_index + 1] == 's' || p_message[p_index + 1] == 'c' || p_message[p_index + 1] == 'd' || - p_message[p_index + 1] == 'o' || p_message[p_index + 1] == 'x' || p_message[p_index + 1] == 'X' || p_message[p_index + 1] == 'f'); -} - -#ifdef TOOLS_ENABLED -void TranslationServer::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { - const String pf = p_function; - if (p_idx == 0) { - HashMap<String, String> *target_hash_map = nullptr; - if (pf == "get_language_name") { - target_hash_map = &language_map; - } else if (pf == "get_script_name") { - target_hash_map = &script_map; - } else if (pf == "get_country_name") { - target_hash_map = &country_name_map; - } - - if (target_hash_map) { - for (const KeyValue<String, String> &E : *target_hash_map) { - r_options->push_back(E.key.quote()); - } - } - } - Object::get_argument_options(p_function, p_idx, r_options); -} -#endif // TOOLS_ENABLED - -void TranslationServer::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_locale", "locale"), &TranslationServer::set_locale); - ClassDB::bind_method(D_METHOD("get_locale"), &TranslationServer::get_locale); - ClassDB::bind_method(D_METHOD("get_tool_locale"), &TranslationServer::get_tool_locale); - - ClassDB::bind_method(D_METHOD("compare_locales", "locale_a", "locale_b"), &TranslationServer::compare_locales); - ClassDB::bind_method(D_METHOD("standardize_locale", "locale"), &TranslationServer::standardize_locale); - - ClassDB::bind_method(D_METHOD("get_all_languages"), &TranslationServer::get_all_languages); - ClassDB::bind_method(D_METHOD("get_language_name", "language"), &TranslationServer::get_language_name); - - ClassDB::bind_method(D_METHOD("get_all_scripts"), &TranslationServer::get_all_scripts); - ClassDB::bind_method(D_METHOD("get_script_name", "script"), &TranslationServer::get_script_name); - - ClassDB::bind_method(D_METHOD("get_all_countries"), &TranslationServer::get_all_countries); - ClassDB::bind_method(D_METHOD("get_country_name", "country"), &TranslationServer::get_country_name); - - ClassDB::bind_method(D_METHOD("get_locale_name", "locale"), &TranslationServer::get_locale_name); - - ClassDB::bind_method(D_METHOD("translate", "message", "context"), &TranslationServer::translate, DEFVAL(StringName())); - ClassDB::bind_method(D_METHOD("translate_plural", "message", "plural_message", "n", "context"), &TranslationServer::translate_plural, DEFVAL(StringName())); - - ClassDB::bind_method(D_METHOD("add_translation", "translation"), &TranslationServer::add_translation); - ClassDB::bind_method(D_METHOD("remove_translation", "translation"), &TranslationServer::remove_translation); - ClassDB::bind_method(D_METHOD("get_translation_object", "locale"), &TranslationServer::get_translation_object); - - ClassDB::bind_method(D_METHOD("clear"), &TranslationServer::clear); - - ClassDB::bind_method(D_METHOD("get_loaded_locales"), &TranslationServer::get_loaded_locales); - - ClassDB::bind_method(D_METHOD("is_pseudolocalization_enabled"), &TranslationServer::is_pseudolocalization_enabled); - ClassDB::bind_method(D_METHOD("set_pseudolocalization_enabled", "enabled"), &TranslationServer::set_pseudolocalization_enabled); - ClassDB::bind_method(D_METHOD("reload_pseudolocalization"), &TranslationServer::reload_pseudolocalization); - ClassDB::bind_method(D_METHOD("pseudolocalize", "message"), &TranslationServer::pseudolocalize); - ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_enabled"), "set_pseudolocalization_enabled", "is_pseudolocalization_enabled"); -} - -void TranslationServer::load_translations() { - _load_translations("internationalization/locale/translations"); //all - _load_translations("internationalization/locale/translations_" + locale.substr(0, 2)); - - if (locale.substr(0, 2) != locale) { - _load_translations("internationalization/locale/translations_" + locale); - } -} - -TranslationServer::TranslationServer() { - singleton = this; - init_locale_info(); -} diff --git a/core/string/translation.h b/core/string/translation.h index 0a7eacc45f..2c5baae8b7 100644 --- a/core/string/translation.h +++ b/core/string/translation.h @@ -74,132 +74,4 @@ public: Translation() {} }; -class TranslationServer : public Object { - GDCLASS(TranslationServer, Object); - - String locale = "en"; - String fallback; - - HashSet<Ref<Translation>> translations; - Ref<Translation> tool_translation; - Ref<Translation> property_translation; - Ref<Translation> doc_translation; - Ref<Translation> extractable_translation; - - bool enabled = true; - - bool pseudolocalization_enabled = false; - bool pseudolocalization_accents_enabled = false; - bool pseudolocalization_double_vowels_enabled = false; - bool pseudolocalization_fake_bidi_enabled = false; - bool pseudolocalization_override_enabled = false; - bool pseudolocalization_skip_placeholders_enabled = false; - float expansion_ratio = 0.0; - String pseudolocalization_prefix; - String pseudolocalization_suffix; - - StringName tool_pseudolocalize(const StringName &p_message) const; - String get_override_string(String &p_message) const; - String double_vowels(String &p_message) const; - String replace_with_accented_string(String &p_message) const; - String wrap_with_fakebidi_characters(String &p_message) const; - String add_padding(const String &p_message, int p_length) const; - const char32_t *get_accented_version(char32_t p_character) const; - bool is_placeholder(String &p_message, int p_index) const; - - static TranslationServer *singleton; - bool _load_translations(const String &p_from); - String _standardize_locale(const String &p_locale, bool p_add_defaults) const; - - StringName _get_message_from_translations(const StringName &p_message, const StringName &p_context, const String &p_locale, bool plural, const String &p_message_plural = "", int p_n = 0) const; - - static void _bind_methods(); - -#ifndef DISABLE_DEPRECATED - static void _bind_compatibility_methods(); -#endif - - struct LocaleScriptInfo { - String name; - String script; - String default_country; - HashSet<String> supported_countries; - }; - static Vector<LocaleScriptInfo> locale_script_info; - - static HashMap<String, String> language_map; - static HashMap<String, String> script_map; - static HashMap<String, String> locale_rename_map; - static HashMap<String, String> country_name_map; - static HashMap<String, String> country_rename_map; - static HashMap<String, String> variant_map; - - void init_locale_info(); - -public: - _FORCE_INLINE_ static TranslationServer *get_singleton() { return singleton; } - - void set_enabled(bool p_enabled) { enabled = p_enabled; } - _FORCE_INLINE_ bool is_enabled() const { return enabled; } - - void set_locale(const String &p_locale); - String get_locale() const; - Ref<Translation> get_translation_object(const String &p_locale); - - Vector<String> get_all_languages() const; - String get_language_name(const String &p_language) const; - - Vector<String> get_all_scripts() const; - String get_script_name(const String &p_script) const; - - Vector<String> get_all_countries() const; - String get_country_name(const String &p_country) const; - - String get_locale_name(const String &p_locale) const; - - PackedStringArray get_loaded_locales() const; - - void add_translation(const Ref<Translation> &p_translation); - void remove_translation(const Ref<Translation> &p_translation); - - StringName translate(const StringName &p_message, const StringName &p_context = "") const; - StringName translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const; - - StringName pseudolocalize(const StringName &p_message) const; - - bool is_pseudolocalization_enabled() const; - void set_pseudolocalization_enabled(bool p_enabled); - void reload_pseudolocalization(); - - String standardize_locale(const String &p_locale) const; - - int compare_locales(const String &p_locale_a, const String &p_locale_b) const; - - String get_tool_locale(); - void set_tool_translation(const Ref<Translation> &p_translation); - Ref<Translation> get_tool_translation() const; - StringName tool_translate(const StringName &p_message, const StringName &p_context = "") const; - StringName tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const; - void set_property_translation(const Ref<Translation> &p_translation); - StringName property_translate(const StringName &p_message, const StringName &p_context = "") const; - void set_doc_translation(const Ref<Translation> &p_translation); - StringName doc_translate(const StringName &p_message, const StringName &p_context = "") const; - StringName doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const; - void set_extractable_translation(const Ref<Translation> &p_translation); - StringName extractable_translate(const StringName &p_message, const StringName &p_context = "") const; - StringName extractable_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const; - - void setup(); - - void clear(); - - void load_translations(); - -#ifdef TOOLS_ENABLED - virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; -#endif // TOOLS_ENABLED - - TranslationServer(); -}; - #endif // TRANSLATION_H diff --git a/core/string/translation_server.compat.inc b/core/string/translation_server.compat.inc new file mode 100644 index 0000000000..9d1ee8b9df --- /dev/null +++ b/core/string/translation_server.compat.inc @@ -0,0 +1,38 @@ +/**************************************************************************/ +/* translation_server.compat.inc */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef DISABLE_DEPRECATED + +void TranslationServer::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("translate", "message", "context"), &TranslationServer::translate, DEFVAL("")); + ClassDB::bind_compatibility_method(D_METHOD("translate_plural", "message", "plural_message", "n", "context"), &TranslationServer::translate_plural, DEFVAL("")); +} + +#endif diff --git a/core/string/translation_server.cpp b/core/string/translation_server.cpp new file mode 100644 index 0000000000..6e784881d0 --- /dev/null +++ b/core/string/translation_server.cpp @@ -0,0 +1,947 @@ +/**************************************************************************/ +/* translation_server.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 "translation_server.h" +#include "translation_server.compat.inc" + +#include "core/config/project_settings.h" +#include "core/io/resource_loader.h" +#include "core/os/os.h" +#include "core/string/locales.h" + +#ifdef TOOLS_ENABLED +#include "main/main.h" +#endif + +struct _character_accent_pair { + const char32_t character; + const char32_t *accented_character; +}; + +static _character_accent_pair _character_to_accented[] = { + { 'A', U"Å" }, + { 'B', U"ß" }, + { 'C', U"Ç" }, + { 'D', U"Ð" }, + { 'E', U"É" }, + { 'F', U"F́" }, + { 'G', U"Ĝ" }, + { 'H', U"Ĥ" }, + { 'I', U"Ĩ" }, + { 'J', U"Ĵ" }, + { 'K', U"ĸ" }, + { 'L', U"Ł" }, + { 'M', U"Ḿ" }, + { 'N', U"й" }, + { 'O', U"Ö" }, + { 'P', U"Ṕ" }, + { 'Q', U"Q́" }, + { 'R', U"Ř" }, + { 'S', U"Ŝ" }, + { 'T', U"Ŧ" }, + { 'U', U"Ũ" }, + { 'V', U"Ṽ" }, + { 'W', U"Ŵ" }, + { 'X', U"X́" }, + { 'Y', U"Ÿ" }, + { 'Z', U"Ž" }, + { 'a', U"á" }, + { 'b', U"ḅ" }, + { 'c', U"ć" }, + { 'd', U"d́" }, + { 'e', U"é" }, + { 'f', U"f́" }, + { 'g', U"ǵ" }, + { 'h', U"h̀" }, + { 'i', U"í" }, + { 'j', U"ǰ" }, + { 'k', U"ḱ" }, + { 'l', U"ł" }, + { 'm', U"m̀" }, + { 'n', U"ή" }, + { 'o', U"ô" }, + { 'p', U"ṕ" }, + { 'q', U"q́" }, + { 'r', U"ŕ" }, + { 's', U"š" }, + { 't', U"ŧ" }, + { 'u', U"ü" }, + { 'v', U"ṽ" }, + { 'w', U"ŵ" }, + { 'x', U"x́" }, + { 'y', U"ý" }, + { 'z', U"ź" }, +}; + +Vector<TranslationServer::LocaleScriptInfo> TranslationServer::locale_script_info; + +HashMap<String, String> TranslationServer::language_map; +HashMap<String, String> TranslationServer::script_map; +HashMap<String, String> TranslationServer::locale_rename_map; +HashMap<String, String> TranslationServer::country_name_map; +HashMap<String, String> TranslationServer::variant_map; +HashMap<String, String> TranslationServer::country_rename_map; + +void TranslationServer::init_locale_info() { + // Init locale info. + language_map.clear(); + int idx = 0; + while (language_list[idx][0] != nullptr) { + language_map[language_list[idx][0]] = String::utf8(language_list[idx][1]); + idx++; + } + + // Init locale-script map. + locale_script_info.clear(); + idx = 0; + while (locale_scripts[idx][0] != nullptr) { + LocaleScriptInfo info; + info.name = locale_scripts[idx][0]; + info.script = locale_scripts[idx][1]; + info.default_country = locale_scripts[idx][2]; + Vector<String> supported_countries = String(locale_scripts[idx][3]).split(",", false); + for (int i = 0; i < supported_countries.size(); i++) { + info.supported_countries.insert(supported_countries[i]); + } + locale_script_info.push_back(info); + idx++; + } + + // Init supported script list. + script_map.clear(); + idx = 0; + while (script_list[idx][0] != nullptr) { + script_map[script_list[idx][1]] = String::utf8(script_list[idx][0]); + idx++; + } + + // Init regional variant map. + variant_map.clear(); + idx = 0; + while (locale_variants[idx][0] != nullptr) { + variant_map[locale_variants[idx][0]] = locale_variants[idx][1]; + idx++; + } + + // Init locale renames. + locale_rename_map.clear(); + idx = 0; + while (locale_renames[idx][0] != nullptr) { + if (!String(locale_renames[idx][1]).is_empty()) { + locale_rename_map[locale_renames[idx][0]] = locale_renames[idx][1]; + } + idx++; + } + + // Init country names. + country_name_map.clear(); + idx = 0; + while (country_names[idx][0] != nullptr) { + country_name_map[String(country_names[idx][0])] = String::utf8(country_names[idx][1]); + idx++; + } + + // Init country renames. + country_rename_map.clear(); + idx = 0; + while (country_renames[idx][0] != nullptr) { + if (!String(country_renames[idx][1]).is_empty()) { + country_rename_map[country_renames[idx][0]] = country_renames[idx][1]; + } + idx++; + } +} + +String TranslationServer::standardize_locale(const String &p_locale) const { + return _standardize_locale(p_locale, false); +} + +String TranslationServer::_standardize_locale(const String &p_locale, bool p_add_defaults) const { + // Replaces '-' with '_' for macOS style locales. + String univ_locale = p_locale.replace("-", "_"); + + // Extract locale elements. + String lang_name, script_name, country_name, variant_name; + Vector<String> locale_elements = univ_locale.get_slice("@", 0).split("_"); + lang_name = locale_elements[0]; + if (locale_elements.size() >= 2) { + if (locale_elements[1].length() == 4 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_lower_case(locale_elements[1][1]) && is_ascii_lower_case(locale_elements[1][2]) && is_ascii_lower_case(locale_elements[1][3])) { + script_name = locale_elements[1]; + } + if (locale_elements[1].length() == 2 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_upper_case(locale_elements[1][1])) { + country_name = locale_elements[1]; + } + } + if (locale_elements.size() >= 3) { + if (locale_elements[2].length() == 2 && is_ascii_upper_case(locale_elements[2][0]) && is_ascii_upper_case(locale_elements[2][1])) { + country_name = locale_elements[2]; + } else if (variant_map.has(locale_elements[2].to_lower()) && variant_map[locale_elements[2].to_lower()] == lang_name) { + variant_name = locale_elements[2].to_lower(); + } + } + if (locale_elements.size() >= 4) { + if (variant_map.has(locale_elements[3].to_lower()) && variant_map[locale_elements[3].to_lower()] == lang_name) { + variant_name = locale_elements[3].to_lower(); + } + } + + // Try extract script and variant from the extra part. + Vector<String> script_extra = univ_locale.get_slice("@", 1).split(";"); + for (int i = 0; i < script_extra.size(); i++) { + if (script_extra[i].to_lower() == "cyrillic") { + script_name = "Cyrl"; + break; + } else if (script_extra[i].to_lower() == "latin") { + script_name = "Latn"; + break; + } else if (script_extra[i].to_lower() == "devanagari") { + script_name = "Deva"; + break; + } else if (variant_map.has(script_extra[i].to_lower()) && variant_map[script_extra[i].to_lower()] == lang_name) { + variant_name = script_extra[i].to_lower(); + } + } + + // Handles known non-ISO language names used e.g. on Windows. + if (locale_rename_map.has(lang_name)) { + lang_name = locale_rename_map[lang_name]; + } + + // Handle country renames. + if (country_rename_map.has(country_name)) { + country_name = country_rename_map[country_name]; + } + + // Remove unsupported script codes. + if (!script_map.has(script_name)) { + script_name = ""; + } + + // Add script code base on language and country codes for some ambiguous cases. + if (p_add_defaults) { + if (script_name.is_empty()) { + for (int i = 0; i < locale_script_info.size(); i++) { + const LocaleScriptInfo &info = locale_script_info[i]; + if (info.name == lang_name) { + if (country_name.is_empty() || info.supported_countries.has(country_name)) { + script_name = info.script; + break; + } + } + } + } + if (!script_name.is_empty() && country_name.is_empty()) { + // Add conntry code based on script for some ambiguous cases. + for (int i = 0; i < locale_script_info.size(); i++) { + const LocaleScriptInfo &info = locale_script_info[i]; + if (info.name == lang_name && info.script == script_name) { + country_name = info.default_country; + break; + } + } + } + } + + // Combine results. + String out = lang_name; + if (!script_name.is_empty()) { + out = out + "_" + script_name; + } + if (!country_name.is_empty()) { + out = out + "_" + country_name; + } + if (!variant_name.is_empty()) { + out = out + "_" + variant_name; + } + return out; +} + +int TranslationServer::compare_locales(const String &p_locale_a, const String &p_locale_b) const { + String locale_a = _standardize_locale(p_locale_a, true); + String locale_b = _standardize_locale(p_locale_b, true); + + if (locale_a == locale_b) { + // Exact match. + return 10; + } + + Vector<String> locale_a_elements = locale_a.split("_"); + Vector<String> locale_b_elements = locale_b.split("_"); + if (locale_a_elements[0] == locale_b_elements[0]) { + // Matching language, both locales have extra parts. + // Return number of matching elements. + int matching_elements = 1; + for (int i = 1; i < locale_a_elements.size(); i++) { + for (int j = 1; j < locale_b_elements.size(); j++) { + if (locale_a_elements[i] == locale_b_elements[j]) { + matching_elements++; + } + } + } + return matching_elements; + } else { + // No match. + return 0; + } +} + +String TranslationServer::get_locale_name(const String &p_locale) const { + String lang_name, script_name, country_name; + Vector<String> locale_elements = standardize_locale(p_locale).split("_"); + lang_name = locale_elements[0]; + if (locale_elements.size() >= 2) { + if (locale_elements[1].length() == 4 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_lower_case(locale_elements[1][1]) && is_ascii_lower_case(locale_elements[1][2]) && is_ascii_lower_case(locale_elements[1][3])) { + script_name = locale_elements[1]; + } + if (locale_elements[1].length() == 2 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_upper_case(locale_elements[1][1])) { + country_name = locale_elements[1]; + } + } + if (locale_elements.size() >= 3) { + if (locale_elements[2].length() == 2 && is_ascii_upper_case(locale_elements[2][0]) && is_ascii_upper_case(locale_elements[2][1])) { + country_name = locale_elements[2]; + } + } + + String name = language_map[lang_name]; + if (!script_name.is_empty()) { + name = name + " (" + script_map[script_name] + ")"; + } + if (!country_name.is_empty()) { + name = name + ", " + country_name_map[country_name]; + } + return name; +} + +Vector<String> TranslationServer::get_all_languages() const { + Vector<String> languages; + + for (const KeyValue<String, String> &E : language_map) { + languages.push_back(E.key); + } + + return languages; +} + +String TranslationServer::get_language_name(const String &p_language) const { + return language_map[p_language]; +} + +Vector<String> TranslationServer::get_all_scripts() const { + Vector<String> scripts; + + for (const KeyValue<String, String> &E : script_map) { + scripts.push_back(E.key); + } + + return scripts; +} + +String TranslationServer::get_script_name(const String &p_script) const { + return script_map[p_script]; +} + +Vector<String> TranslationServer::get_all_countries() const { + Vector<String> countries; + + for (const KeyValue<String, String> &E : country_name_map) { + countries.push_back(E.key); + } + + return countries; +} + +String TranslationServer::get_country_name(const String &p_country) const { + return country_name_map[p_country]; +} + +void TranslationServer::set_locale(const String &p_locale) { + String new_locale = standardize_locale(p_locale); + if (locale == new_locale) { + return; + } + + locale = new_locale; + ResourceLoader::reload_translation_remaps(); + + if (OS::get_singleton()->get_main_loop()) { + OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); + } +} + +String TranslationServer::get_locale() const { + return locale; +} + +PackedStringArray TranslationServer::get_loaded_locales() const { + PackedStringArray locales; + for (const Ref<Translation> &E : translations) { + const Ref<Translation> &t = E; + ERR_FAIL_COND_V(t.is_null(), PackedStringArray()); + String l = t->get_locale(); + + locales.push_back(l); + } + + return locales; +} + +void TranslationServer::add_translation(const Ref<Translation> &p_translation) { + translations.insert(p_translation); +} + +void TranslationServer::remove_translation(const Ref<Translation> &p_translation) { + translations.erase(p_translation); +} + +Ref<Translation> TranslationServer::get_translation_object(const String &p_locale) { + Ref<Translation> res; + int best_score = 0; + + for (const Ref<Translation> &E : translations) { + const Ref<Translation> &t = E; + ERR_FAIL_COND_V(t.is_null(), nullptr); + String l = t->get_locale(); + + int score = compare_locales(p_locale, l); + if (score > 0 && score >= best_score) { + res = t; + best_score = score; + if (score == 10) { + break; // Exact match, skip the rest. + } + } + } + return res; +} + +void TranslationServer::clear() { + translations.clear(); +} + +StringName TranslationServer::translate(const StringName &p_message, const StringName &p_context) const { + // Match given message against the translation catalog for the project locale. + + if (!enabled) { + return p_message; + } + + StringName res = _get_message_from_translations(p_message, p_context, locale, false); + + if (!res && fallback.length() >= 2) { + res = _get_message_from_translations(p_message, p_context, fallback, false); + } + + if (!res) { + return pseudolocalization_enabled ? pseudolocalize(p_message) : p_message; + } + + return pseudolocalization_enabled ? pseudolocalize(res) : res; +} + +StringName TranslationServer::translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { + if (!enabled) { + if (p_n == 1) { + return p_message; + } + return p_message_plural; + } + + StringName res = _get_message_from_translations(p_message, p_context, locale, true, p_message_plural, p_n); + + if (!res && fallback.length() >= 2) { + res = _get_message_from_translations(p_message, p_context, fallback, true, p_message_plural, p_n); + } + + if (!res) { + if (p_n == 1) { + return p_message; + } + return p_message_plural; + } + + return res; +} + +StringName TranslationServer::_get_message_from_translations(const StringName &p_message, const StringName &p_context, const String &p_locale, bool plural, const String &p_message_plural, int p_n) const { + StringName res; + int best_score = 0; + + for (const Ref<Translation> &E : translations) { + const Ref<Translation> &t = E; + ERR_FAIL_COND_V(t.is_null(), p_message); + String l = t->get_locale(); + + int score = compare_locales(p_locale, l); + if (score > 0 && score >= best_score) { + StringName r; + if (!plural) { + r = t->get_message(p_message, p_context); + } else { + r = t->get_plural_message(p_message, p_message_plural, p_n, p_context); + } + if (!r) { + continue; + } + res = r; + best_score = score; + if (score == 10) { + break; // Exact match, skip the rest. + } + } + } + + return res; +} + +TranslationServer *TranslationServer::singleton = nullptr; + +bool TranslationServer::_load_translations(const String &p_from) { + if (ProjectSettings::get_singleton()->has_setting(p_from)) { + const Vector<String> &translation_names = GLOBAL_GET(p_from); + + int tcount = translation_names.size(); + + if (tcount) { + const String *r = translation_names.ptr(); + + for (int i = 0; i < tcount; i++) { + Ref<Translation> tr = ResourceLoader::load(r[i]); + if (tr.is_valid()) { + add_translation(tr); + } + } + } + return true; + } + + return false; +} + +void TranslationServer::setup() { + String test = GLOBAL_DEF("internationalization/locale/test", ""); + test = test.strip_edges(); + if (!test.is_empty()) { + set_locale(test); + } else { + set_locale(OS::get_singleton()->get_locale()); + } + + fallback = GLOBAL_DEF("internationalization/locale/fallback", "en"); + pseudolocalization_enabled = GLOBAL_DEF("internationalization/pseudolocalization/use_pseudolocalization", false); + pseudolocalization_accents_enabled = GLOBAL_DEF("internationalization/pseudolocalization/replace_with_accents", true); + pseudolocalization_double_vowels_enabled = GLOBAL_DEF("internationalization/pseudolocalization/double_vowels", false); + pseudolocalization_fake_bidi_enabled = GLOBAL_DEF("internationalization/pseudolocalization/fake_bidi", false); + pseudolocalization_override_enabled = GLOBAL_DEF("internationalization/pseudolocalization/override", false); + expansion_ratio = GLOBAL_DEF("internationalization/pseudolocalization/expansion_ratio", 0.0); + pseudolocalization_prefix = GLOBAL_DEF("internationalization/pseudolocalization/prefix", "["); + pseudolocalization_suffix = GLOBAL_DEF("internationalization/pseudolocalization/suffix", "]"); + pseudolocalization_skip_placeholders_enabled = GLOBAL_DEF("internationalization/pseudolocalization/skip_placeholders", true); + +#ifdef TOOLS_ENABLED + ProjectSettings::get_singleton()->set_custom_property_info(PropertyInfo(Variant::STRING, "internationalization/locale/fallback", PROPERTY_HINT_LOCALE_ID, "")); +#endif +} + +void TranslationServer::set_tool_translation(const Ref<Translation> &p_translation) { + tool_translation = p_translation; +} + +Ref<Translation> TranslationServer::get_tool_translation() const { + return tool_translation; +} + +String TranslationServer::get_tool_locale() { +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint() || Engine::get_singleton()->is_project_manager_hint()) { + if (TranslationServer::get_singleton()->get_tool_translation().is_valid()) { + return tool_translation->get_locale(); + } else { + return "en"; + } + } else { +#else + { +#endif + // Look for best matching loaded translation. + String best_locale = "en"; + int best_score = 0; + + for (const Ref<Translation> &E : translations) { + const Ref<Translation> &t = E; + ERR_FAIL_COND_V(t.is_null(), best_locale); + String l = t->get_locale(); + + int score = compare_locales(locale, l); + if (score > 0 && score >= best_score) { + best_locale = l; + best_score = score; + if (score == 10) { + break; // Exact match, skip the rest. + } + } + } + return best_locale; + } +} + +StringName TranslationServer::tool_translate(const StringName &p_message, const StringName &p_context) const { + if (tool_translation.is_valid()) { + StringName r = tool_translation->get_message(p_message, p_context); + if (r) { + return r; + } + } + return p_message; +} + +StringName TranslationServer::tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { + if (tool_translation.is_valid()) { + StringName r = tool_translation->get_plural_message(p_message, p_message_plural, p_n, p_context); + if (r) { + return r; + } + } + + if (p_n == 1) { + return p_message; + } + return p_message_plural; +} + +void TranslationServer::set_property_translation(const Ref<Translation> &p_translation) { + property_translation = p_translation; +} + +StringName TranslationServer::property_translate(const StringName &p_message, const StringName &p_context) const { + if (property_translation.is_valid()) { + StringName r = property_translation->get_message(p_message, p_context); + if (r) { + return r; + } + } + return p_message; +} + +void TranslationServer::set_doc_translation(const Ref<Translation> &p_translation) { + doc_translation = p_translation; +} + +StringName TranslationServer::doc_translate(const StringName &p_message, const StringName &p_context) const { + if (doc_translation.is_valid()) { + StringName r = doc_translation->get_message(p_message, p_context); + if (r) { + return r; + } + } + return p_message; +} + +StringName TranslationServer::doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { + if (doc_translation.is_valid()) { + StringName r = doc_translation->get_plural_message(p_message, p_message_plural, p_n, p_context); + if (r) { + return r; + } + } + + if (p_n == 1) { + return p_message; + } + return p_message_plural; +} + +void TranslationServer::set_extractable_translation(const Ref<Translation> &p_translation) { + extractable_translation = p_translation; +} + +StringName TranslationServer::extractable_translate(const StringName &p_message, const StringName &p_context) const { + if (extractable_translation.is_valid()) { + StringName r = extractable_translation->get_message(p_message, p_context); + if (r) { + return r; + } + } + return p_message; +} + +StringName TranslationServer::extractable_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { + if (extractable_translation.is_valid()) { + StringName r = extractable_translation->get_plural_message(p_message, p_message_plural, p_n, p_context); + if (r) { + return r; + } + } + + if (p_n == 1) { + return p_message; + } + return p_message_plural; +} + +bool TranslationServer::is_pseudolocalization_enabled() const { + return pseudolocalization_enabled; +} + +void TranslationServer::set_pseudolocalization_enabled(bool p_enabled) { + pseudolocalization_enabled = p_enabled; + + ResourceLoader::reload_translation_remaps(); + + if (OS::get_singleton()->get_main_loop()) { + OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); + } +} + +void TranslationServer::reload_pseudolocalization() { + pseudolocalization_accents_enabled = GLOBAL_GET("internationalization/pseudolocalization/replace_with_accents"); + pseudolocalization_double_vowels_enabled = GLOBAL_GET("internationalization/pseudolocalization/double_vowels"); + pseudolocalization_fake_bidi_enabled = GLOBAL_GET("internationalization/pseudolocalization/fake_bidi"); + pseudolocalization_override_enabled = GLOBAL_GET("internationalization/pseudolocalization/override"); + expansion_ratio = GLOBAL_GET("internationalization/pseudolocalization/expansion_ratio"); + pseudolocalization_prefix = GLOBAL_GET("internationalization/pseudolocalization/prefix"); + pseudolocalization_suffix = GLOBAL_GET("internationalization/pseudolocalization/suffix"); + pseudolocalization_skip_placeholders_enabled = GLOBAL_GET("internationalization/pseudolocalization/skip_placeholders"); + + ResourceLoader::reload_translation_remaps(); + + if (OS::get_singleton()->get_main_loop()) { + OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); + } +} + +StringName TranslationServer::pseudolocalize(const StringName &p_message) const { + String message = p_message; + int length = message.length(); + if (pseudolocalization_override_enabled) { + message = get_override_string(message); + } + + if (pseudolocalization_double_vowels_enabled) { + message = double_vowels(message); + } + + if (pseudolocalization_accents_enabled) { + message = replace_with_accented_string(message); + } + + if (pseudolocalization_fake_bidi_enabled) { + message = wrap_with_fakebidi_characters(message); + } + + StringName res = add_padding(message, length); + return res; +} + +StringName TranslationServer::tool_pseudolocalize(const StringName &p_message) const { + String message = p_message; + message = double_vowels(message); + message = replace_with_accented_string(message); + StringName res = "[!!! " + message + " !!!]"; + return res; +} + +String TranslationServer::get_override_string(String &p_message) const { + String res; + for (int i = 0; i < p_message.length(); i++) { + if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { + res += p_message[i]; + res += p_message[i + 1]; + i++; + continue; + } + res += '*'; + } + return res; +} + +String TranslationServer::double_vowels(String &p_message) const { + String res; + for (int i = 0; i < p_message.length(); i++) { + if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { + res += p_message[i]; + res += p_message[i + 1]; + i++; + continue; + } + res += p_message[i]; + if (p_message[i] == 'a' || p_message[i] == 'e' || p_message[i] == 'i' || p_message[i] == 'o' || p_message[i] == 'u' || + p_message[i] == 'A' || p_message[i] == 'E' || p_message[i] == 'I' || p_message[i] == 'O' || p_message[i] == 'U') { + res += p_message[i]; + } + } + return res; +}; + +String TranslationServer::replace_with_accented_string(String &p_message) const { + String res; + for (int i = 0; i < p_message.length(); i++) { + if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { + res += p_message[i]; + res += p_message[i + 1]; + i++; + continue; + } + const char32_t *accented = get_accented_version(p_message[i]); + if (accented) { + res += accented; + } else { + res += p_message[i]; + } + } + return res; +} + +String TranslationServer::wrap_with_fakebidi_characters(String &p_message) const { + String res; + char32_t fakebidiprefix = U'\u202e'; + char32_t fakebidisuffix = U'\u202c'; + res += fakebidiprefix; + // The fake bidi unicode gets popped at every newline so pushing it back at every newline. + for (int i = 0; i < p_message.length(); i++) { + if (p_message[i] == '\n') { + res += fakebidisuffix; + res += p_message[i]; + res += fakebidiprefix; + } else if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { + res += fakebidisuffix; + res += p_message[i]; + res += p_message[i + 1]; + res += fakebidiprefix; + i++; + } else { + res += p_message[i]; + } + } + res += fakebidisuffix; + return res; +} + +String TranslationServer::add_padding(const String &p_message, int p_length) const { + String underscores = String("_").repeat(p_length * expansion_ratio / 2); + String prefix = pseudolocalization_prefix + underscores; + String suffix = underscores + pseudolocalization_suffix; + + return prefix + p_message + suffix; +} + +const char32_t *TranslationServer::get_accented_version(char32_t p_character) const { + if (!is_ascii_alphabet_char(p_character)) { + return nullptr; + } + + for (unsigned int i = 0; i < sizeof(_character_to_accented) / sizeof(_character_to_accented[0]); i++) { + if (_character_to_accented[i].character == p_character) { + return _character_to_accented[i].accented_character; + } + } + + return nullptr; +} + +bool TranslationServer::is_placeholder(String &p_message, int p_index) const { + return p_index < p_message.length() - 1 && p_message[p_index] == '%' && + (p_message[p_index + 1] == 's' || p_message[p_index + 1] == 'c' || p_message[p_index + 1] == 'd' || + p_message[p_index + 1] == 'o' || p_message[p_index + 1] == 'x' || p_message[p_index + 1] == 'X' || p_message[p_index + 1] == 'f'); +} + +#ifdef TOOLS_ENABLED +void TranslationServer::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { + const String pf = p_function; + if (p_idx == 0) { + HashMap<String, String> *target_hash_map = nullptr; + if (pf == "get_language_name") { + target_hash_map = &language_map; + } else if (pf == "get_script_name") { + target_hash_map = &script_map; + } else if (pf == "get_country_name") { + target_hash_map = &country_name_map; + } + + if (target_hash_map) { + for (const KeyValue<String, String> &E : *target_hash_map) { + r_options->push_back(E.key.quote()); + } + } + } + Object::get_argument_options(p_function, p_idx, r_options); +} +#endif // TOOLS_ENABLED + +void TranslationServer::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_locale", "locale"), &TranslationServer::set_locale); + ClassDB::bind_method(D_METHOD("get_locale"), &TranslationServer::get_locale); + ClassDB::bind_method(D_METHOD("get_tool_locale"), &TranslationServer::get_tool_locale); + + ClassDB::bind_method(D_METHOD("compare_locales", "locale_a", "locale_b"), &TranslationServer::compare_locales); + ClassDB::bind_method(D_METHOD("standardize_locale", "locale"), &TranslationServer::standardize_locale); + + ClassDB::bind_method(D_METHOD("get_all_languages"), &TranslationServer::get_all_languages); + ClassDB::bind_method(D_METHOD("get_language_name", "language"), &TranslationServer::get_language_name); + + ClassDB::bind_method(D_METHOD("get_all_scripts"), &TranslationServer::get_all_scripts); + ClassDB::bind_method(D_METHOD("get_script_name", "script"), &TranslationServer::get_script_name); + + ClassDB::bind_method(D_METHOD("get_all_countries"), &TranslationServer::get_all_countries); + ClassDB::bind_method(D_METHOD("get_country_name", "country"), &TranslationServer::get_country_name); + + ClassDB::bind_method(D_METHOD("get_locale_name", "locale"), &TranslationServer::get_locale_name); + + ClassDB::bind_method(D_METHOD("translate", "message", "context"), &TranslationServer::translate, DEFVAL(StringName())); + ClassDB::bind_method(D_METHOD("translate_plural", "message", "plural_message", "n", "context"), &TranslationServer::translate_plural, DEFVAL(StringName())); + + ClassDB::bind_method(D_METHOD("add_translation", "translation"), &TranslationServer::add_translation); + ClassDB::bind_method(D_METHOD("remove_translation", "translation"), &TranslationServer::remove_translation); + ClassDB::bind_method(D_METHOD("get_translation_object", "locale"), &TranslationServer::get_translation_object); + + ClassDB::bind_method(D_METHOD("clear"), &TranslationServer::clear); + + ClassDB::bind_method(D_METHOD("get_loaded_locales"), &TranslationServer::get_loaded_locales); + + ClassDB::bind_method(D_METHOD("is_pseudolocalization_enabled"), &TranslationServer::is_pseudolocalization_enabled); + ClassDB::bind_method(D_METHOD("set_pseudolocalization_enabled", "enabled"), &TranslationServer::set_pseudolocalization_enabled); + ClassDB::bind_method(D_METHOD("reload_pseudolocalization"), &TranslationServer::reload_pseudolocalization); + ClassDB::bind_method(D_METHOD("pseudolocalize", "message"), &TranslationServer::pseudolocalize); + ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_enabled"), "set_pseudolocalization_enabled", "is_pseudolocalization_enabled"); +} + +void TranslationServer::load_translations() { + _load_translations("internationalization/locale/translations"); //all + _load_translations("internationalization/locale/translations_" + locale.substr(0, 2)); + + if (locale.substr(0, 2) != locale) { + _load_translations("internationalization/locale/translations_" + locale); + } +} + +TranslationServer::TranslationServer() { + singleton = this; + init_locale_info(); +} diff --git a/core/string/translation_server.h b/core/string/translation_server.h new file mode 100644 index 0000000000..ebe81d9712 --- /dev/null +++ b/core/string/translation_server.h @@ -0,0 +1,164 @@ +/**************************************************************************/ +/* translation_server.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 TRANSLATION_SERVER_H +#define TRANSLATION_SERVER_H + +#include "core/string/translation.h" + +class TranslationServer : public Object { + GDCLASS(TranslationServer, Object); + + String locale = "en"; + String fallback; + + HashSet<Ref<Translation>> translations; + Ref<Translation> tool_translation; + Ref<Translation> property_translation; + Ref<Translation> doc_translation; + Ref<Translation> extractable_translation; + + bool enabled = true; + + bool pseudolocalization_enabled = false; + bool pseudolocalization_accents_enabled = false; + bool pseudolocalization_double_vowels_enabled = false; + bool pseudolocalization_fake_bidi_enabled = false; + bool pseudolocalization_override_enabled = false; + bool pseudolocalization_skip_placeholders_enabled = false; + float expansion_ratio = 0.0; + String pseudolocalization_prefix; + String pseudolocalization_suffix; + + StringName tool_pseudolocalize(const StringName &p_message) const; + String get_override_string(String &p_message) const; + String double_vowels(String &p_message) const; + String replace_with_accented_string(String &p_message) const; + String wrap_with_fakebidi_characters(String &p_message) const; + String add_padding(const String &p_message, int p_length) const; + const char32_t *get_accented_version(char32_t p_character) const; + bool is_placeholder(String &p_message, int p_index) const; + + static TranslationServer *singleton; + bool _load_translations(const String &p_from); + String _standardize_locale(const String &p_locale, bool p_add_defaults) const; + + StringName _get_message_from_translations(const StringName &p_message, const StringName &p_context, const String &p_locale, bool plural, const String &p_message_plural = "", int p_n = 0) const; + + static void _bind_methods(); + +#ifndef DISABLE_DEPRECATED + static void _bind_compatibility_methods(); +#endif + + struct LocaleScriptInfo { + String name; + String script; + String default_country; + HashSet<String> supported_countries; + }; + static Vector<LocaleScriptInfo> locale_script_info; + + static HashMap<String, String> language_map; + static HashMap<String, String> script_map; + static HashMap<String, String> locale_rename_map; + static HashMap<String, String> country_name_map; + static HashMap<String, String> country_rename_map; + static HashMap<String, String> variant_map; + + void init_locale_info(); + +public: + _FORCE_INLINE_ static TranslationServer *get_singleton() { return singleton; } + + void set_enabled(bool p_enabled) { enabled = p_enabled; } + _FORCE_INLINE_ bool is_enabled() const { return enabled; } + + void set_locale(const String &p_locale); + String get_locale() const; + Ref<Translation> get_translation_object(const String &p_locale); + + Vector<String> get_all_languages() const; + String get_language_name(const String &p_language) const; + + Vector<String> get_all_scripts() const; + String get_script_name(const String &p_script) const; + + Vector<String> get_all_countries() const; + String get_country_name(const String &p_country) const; + + String get_locale_name(const String &p_locale) const; + + PackedStringArray get_loaded_locales() const; + + void add_translation(const Ref<Translation> &p_translation); + void remove_translation(const Ref<Translation> &p_translation); + + StringName translate(const StringName &p_message, const StringName &p_context = "") const; + StringName translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const; + + StringName pseudolocalize(const StringName &p_message) const; + + bool is_pseudolocalization_enabled() const; + void set_pseudolocalization_enabled(bool p_enabled); + void reload_pseudolocalization(); + + String standardize_locale(const String &p_locale) const; + + int compare_locales(const String &p_locale_a, const String &p_locale_b) const; + + String get_tool_locale(); + void set_tool_translation(const Ref<Translation> &p_translation); + Ref<Translation> get_tool_translation() const; + StringName tool_translate(const StringName &p_message, const StringName &p_context = "") const; + StringName tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const; + void set_property_translation(const Ref<Translation> &p_translation); + StringName property_translate(const StringName &p_message, const StringName &p_context = "") const; + void set_doc_translation(const Ref<Translation> &p_translation); + StringName doc_translate(const StringName &p_message, const StringName &p_context = "") const; + StringName doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const; + void set_extractable_translation(const Ref<Translation> &p_translation); + StringName extractable_translate(const StringName &p_message, const StringName &p_context = "") const; + StringName extractable_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const; + + void setup(); + + void clear(); + + void load_translations(); + +#ifdef TOOLS_ENABLED + virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; +#endif // TOOLS_ENABLED + + TranslationServer(); +}; + +#endif // TRANSLATION_SERVER_H diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index a9dbfd427f..2cfc48d395 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -36,7 +36,7 @@ #include "core/os/memory.h" #include "core/string/print_string.h" #include "core/string/string_name.h" -#include "core/string/translation.h" +#include "core/string/translation_server.h" #include "core/string/ucaps.h" #include "core/variant/variant.h" #include "core/version_generated.gen.h" @@ -1537,13 +1537,16 @@ Vector<double> String::split_floats(const String &p_splitter, bool p_allow_empty int from = 0; int len = length(); + String buffer = *this; while (true) { int end = find(p_splitter, from); if (end < 0) { end = len; } if (p_allow_empty || (end > from)) { - ret.push_back(String::to_float(&get_data()[from])); + buffer[end] = 0; + ret.push_back(String::to_float(&buffer.get_data()[from])); + buffer[end] = _cowdata.get(end); } if (end == len) { @@ -1561,6 +1564,7 @@ Vector<float> String::split_floats_mk(const Vector<String> &p_splitters, bool p_ int from = 0; int len = length(); + String buffer = *this; while (true) { int idx; int end = findmk(p_splitters, from, &idx); @@ -1572,7 +1576,9 @@ Vector<float> String::split_floats_mk(const Vector<String> &p_splitters, bool p_ } if (p_allow_empty || (end > from)) { - ret.push_back(String::to_float(&get_data()[from])); + buffer[end] = 0; + ret.push_back(String::to_float(&buffer.get_data()[from])); + buffer[end] = _cowdata.get(end); } if (end == len) { @@ -1639,13 +1645,43 @@ Vector<int> String::split_ints_mk(const Vector<String> &p_splitters, bool p_allo } String String::join(const Vector<String> &parts) const { + if (parts.is_empty()) { + return String(); + } else if (parts.size() == 1) { + return parts[0]; + } + + const int this_length = length(); + + int new_size = (parts.size() - 1) * this_length; + for (const String &part : parts) { + new_size += part.length(); + } + new_size += 1; + String ret; - for (int i = 0; i < parts.size(); ++i) { - if (i > 0) { - ret += *this; + ret.resize(new_size); + char32_t *ret_ptrw = ret.ptrw(); + const char32_t *this_ptr = ptr(); + + bool first = true; + for (const String &part : parts) { + if (first) { + first = false; + } else if (this_length) { + memcpy(ret_ptrw, this_ptr, this_length * sizeof(char32_t)); + ret_ptrw += this_length; + } + + const int part_length = part.length(); + if (part_length) { + memcpy(ret_ptrw, part.ptr(), part_length * sizeof(char32_t)); + ret_ptrw += part_length; } - ret += parts[i]; } + + *ret_ptrw = 0; + return ret; } @@ -3149,7 +3185,7 @@ Vector<uint8_t> String::sha256_buffer() const { } String String::insert(int p_at_pos, const String &p_string) const { - if (p_at_pos < 0) { + if (p_string.is_empty() || p_at_pos < 0) { return *this; } @@ -3157,17 +3193,27 @@ String String::insert(int p_at_pos, const String &p_string) const { p_at_pos = length(); } - String pre; + String ret; + ret.resize(length() + p_string.length() + 1); + char32_t *ret_ptrw = ret.ptrw(); + const char32_t *this_ptr = ptr(); + if (p_at_pos > 0) { - pre = substr(0, p_at_pos); + memcpy(ret_ptrw, this_ptr, p_at_pos * sizeof(char32_t)); + ret_ptrw += p_at_pos; } - String post; + memcpy(ret_ptrw, p_string.ptr(), p_string.length() * sizeof(char32_t)); + ret_ptrw += p_string.length(); + if (p_at_pos < length()) { - post = substr(p_at_pos, length() - p_at_pos); + memcpy(ret_ptrw, this_ptr + p_at_pos, (length() - p_at_pos) * sizeof(char32_t)); + ret_ptrw += length() - p_at_pos; } - return pre + p_string + post; + *ret_ptrw = 0; + + return ret; } String String::erase(int p_pos, int p_chars) const { @@ -4481,10 +4527,7 @@ String String::simplify_path() const { dirs.remove_at(i); i--; } else if (d == "..") { - if (i == 0) { - dirs.remove_at(i); - i--; - } else { + if (i != 0) { dirs.remove_at(i); dirs.remove_at(i - 1); i -= 2; @@ -5418,6 +5461,11 @@ String String::lpad(int min_length, const String &character) const { // "fish %s %d pie" % ["frog", 12] // In case of an error, the string returned is the error description and "error" is true. String String::sprintf(const Array &values, bool *error) const { + static const String ZERO("0"); + static const String SPACE(" "); + static const String MINUS("-"); + static const String PLUS("+"); + String formatted; char32_t *self = (char32_t *)get_data(); bool in_format = false; @@ -5440,7 +5488,7 @@ String String::sprintf(const Array &values, bool *error) const { if (in_format) { // We have % - let's see what else we get. switch (c) { case '%': { // Replace %% with % - formatted += chr(c); + formatted += c; in_format = false; break; } @@ -5490,7 +5538,7 @@ String String::sprintf(const Array &values, bool *error) const { // Padding. int pad_chars_count = (negative || show_sign) ? min_chars - 1 : min_chars; - String pad_char = pad_with_zeros ? String("0") : String(" "); + const String &pad_char = pad_with_zeros ? ZERO : SPACE; if (left_justified) { str = str.rpad(pad_chars_count, pad_char); } else { @@ -5499,7 +5547,7 @@ String String::sprintf(const Array &values, bool *error) const { // Sign. if (show_sign || negative) { - String sign_char = negative ? "-" : "+"; + const String &sign_char = negative ? MINUS : PLUS; if (left_justified) { str = str.insert(0, sign_char); } else { @@ -5536,7 +5584,7 @@ String String::sprintf(const Array &values, bool *error) const { // Padding. Leave room for sign later if required. int pad_chars_count = (is_negative || show_sign) ? min_chars - 1 : min_chars; - String pad_char = (pad_with_zeros && is_finite) ? String("0") : String(" "); // Never pad NaN or inf with zeros + const String &pad_char = (pad_with_zeros && is_finite) ? ZERO : SPACE; // Never pad NaN or inf with zeros if (left_justified) { str = str.rpad(pad_chars_count, pad_char); } else { @@ -5545,7 +5593,7 @@ String String::sprintf(const Array &values, bool *error) const { // Add sign if needed. if (show_sign || is_negative) { - String sign_char = is_negative ? "-" : "+"; + const String &sign_char = is_negative ? MINUS : PLUS; if (left_justified) { str = str.insert(0, sign_char); } else { @@ -5598,7 +5646,7 @@ String String::sprintf(const Array &values, bool *error) const { // Padding. Leave room for sign later if required. int pad_chars_count = val < 0 ? min_chars - 1 : min_chars; - String pad_char = (pad_with_zeros && is_finite) ? String("0") : String(" "); // Never pad NaN or inf with zeros + const String &pad_char = (pad_with_zeros && is_finite) ? ZERO : SPACE; // Never pad NaN or inf with zeros if (left_justified) { number_str = number_str.rpad(pad_chars_count, pad_char); } else { @@ -5608,9 +5656,9 @@ String String::sprintf(const Array &values, bool *error) const { // Add sign if needed. if (val < 0) { if (left_justified) { - number_str = number_str.insert(0, "-"); + number_str = number_str.insert(0, MINUS); } else { - number_str = number_str.insert(pad_with_zeros ? 0 : number_str.length() - initial_len, "-"); + number_str = number_str.insert(pad_with_zeros ? 0 : number_str.length() - initial_len, MINUS); } } @@ -5775,7 +5823,7 @@ String String::sprintf(const Array &values, bool *error) const { in_decimals = false; break; default: - formatted += chr(c); + formatted += c; } } } diff --git a/core/templates/cowdata.h b/core/templates/cowdata.h index f22ae1f1d3..fedcfaec3b 100644 --- a/core/templates/cowdata.h +++ b/core/templates/cowdata.h @@ -160,7 +160,7 @@ private: return *out; } - void _unref(void *p_data); + void _unref(); void _ref(const CowData *p_from); void _ref(const CowData &p_from); USize _copy_on_write(); @@ -222,12 +222,15 @@ public: } Error insert(Size p_pos, const T &p_val) { - ERR_FAIL_INDEX_V(p_pos, size() + 1, ERR_INVALID_PARAMETER); - resize(size() + 1); - for (Size i = (size() - 1); i > p_pos; i--) { - set(i, get(i - 1)); + Size new_size = size() + 1; + ERR_FAIL_INDEX_V(p_pos, new_size, ERR_INVALID_PARAMETER); + Error err = resize(new_size); + ERR_FAIL_COND_V(err, err); + T *p = ptrw(); + for (Size i = new_size - 1; i > p_pos; i--) { + p[i] = p[i - 1]; } - set(p_pos, p_val); + p[p_pos] = p_val; return OK; } @@ -242,30 +245,29 @@ public: }; template <typename T> -void CowData<T>::_unref(void *p_data) { - if (!p_data) { +void CowData<T>::_unref() { + if (!_ptr) { return; } SafeNumeric<USize> *refc = _get_refcount(); - if (refc->decrement() > 0) { return; // still in use } // clean up if constexpr (!std::is_trivially_destructible_v<T>) { - USize *count = _get_size(); - T *data = (T *)(count + 1); + USize current_size = *_get_size(); - for (USize i = 0; i < *count; ++i) { + for (USize i = 0; i < current_size; ++i) { // call destructors - data[i].~T(); + T *t = &_ptr[i]; + t->~T(); } } // free mem - Memory::free_static(((uint8_t *)p_data) - DATA_OFFSET, false); + Memory::free_static(((uint8_t *)_ptr) - DATA_OFFSET, false); } template <typename T> @@ -300,7 +302,7 @@ typename CowData<T>::USize CowData<T>::_copy_on_write() { } } - _unref(_ptr); + _unref(); _ptr = _data_ptr; rc = 1; @@ -321,7 +323,7 @@ Error CowData<T>::resize(Size p_size) { if (p_size == 0) { // wants to clean up - _unref(_ptr); + _unref(); _ptr = nullptr; return OK; } @@ -460,7 +462,7 @@ void CowData<T>::_ref(const CowData &p_from) { return; // self assign, do nothing. } - _unref(_ptr); + _unref(); _ptr = nullptr; if (!p_from._ptr) { @@ -474,7 +476,7 @@ void CowData<T>::_ref(const CowData &p_from) { template <typename T> CowData<T>::~CowData() { - _unref(_ptr); + _unref(); } #if defined(__GNUC__) && !defined(__clang__) diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 2cd3a51722..339bbb71dd 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -1248,8 +1248,9 @@ <param index="1" name="to" type="float" /> <param index="2" name="x" type="float" /> <description> - Returns the result of smoothly interpolating the value of [param x] between [code]0[/code] and [code]1[/code], based on the where [param x] lies with respect to the edges [param from] and [param to]. - The return value is [code]0[/code] if [code]x <= from[/code], and [code]1[/code] if [code]x >= to[/code]. If [param x] lies between [param from] and [param to], the returned value follows an S-shaped curve that maps [param x] between [code]0[/code] and [code]1[/code]. + Returns a smooth cubic Hermite interpolation between [code]0[/code] and [code]1[/code]. + For positive ranges (when [code]from <= to[/code]) the return value is [code]0[/code] when [code]x <= from[/code], and [code]1[/code] when [code]x >= to[/code]. If [param x] lies between [param from] and [param to], the return value follows an S-shaped curve that smoothly transitions from [code]0[/code] to [code]1[/code]. + For negative ranges (when [code]from > to[/code]) the function is mirrored and returns [code]1[/code] when [code]x <= to[/code] and [code]0[/code] when [code]x >= from[/code]. This S-shaped curve is the cubic Hermite interpolator, given by [code]f(y) = 3*y^2 - 2*y^3[/code] where [code]y = (x-from) / (to-from)[/code]. [codeblock] smoothstep(0, 2, -5.0) # Returns 0.0 @@ -1259,6 +1260,7 @@ [/codeblock] Compared to [method ease] with a curve value of [code]-1.6521[/code], [method smoothstep] returns the smoothest possible curve with no sudden changes in the derivative. If you need to perform more advanced transitions, use [Tween] or [AnimationPlayer]. [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/smoothstep_ease_comparison.png]Comparison between smoothstep() and ease(x, -1.6521) return values[/url] + [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/smoothstep_range.webp]Smoothstep() return values with positive, zero, and negative ranges[/url] </description> </method> <method name="snapped"> diff --git a/doc/classes/AnimationNodeBlendSpace2D.xml b/doc/classes/AnimationNodeBlendSpace2D.xml index 596767599c..da79e15be1 100644 --- a/doc/classes/AnimationNodeBlendSpace2D.xml +++ b/doc/classes/AnimationNodeBlendSpace2D.xml @@ -5,7 +5,7 @@ </brief_description> <description> A resource used by [AnimationNodeBlendTree]. - [AnimationNodeBlendSpace1D] represents a virtual 2D space on which [AnimationRootNode]s are placed. Outputs the linear blend of the three adjacent animations using a [Vector2] weight. Adjacent in this context means the three [AnimationRootNode]s making up the triangle that contains the current value. + [AnimationNodeBlendSpace2D] represents a virtual 2D space on which [AnimationRootNode]s are placed. Outputs the linear blend of the three adjacent animations using a [Vector2] weight. Adjacent in this context means the three [AnimationRootNode]s making up the triangle that contains the current value. You can add vertices to the blend space with [method add_blend_point] and automatically triangulate it by setting [member auto_triangles] to [code]true[/code]. Otherwise, use [method add_triangle] and [method remove_triangle] to triangulate the blend space by hand. </description> <tutorials> @@ -93,7 +93,7 @@ <param index="0" name="point" type="int" /> <param index="1" name="pos" type="Vector2" /> <description> - Updates the position of the point at index [param point] on the blend axis. + Updates the position of the point at index [param point] in the blend space. </description> </method> </methods> diff --git a/doc/classes/ClassDB.xml b/doc/classes/ClassDB.xml index 59ed0b8fe7..66b67d1a59 100644 --- a/doc/classes/ClassDB.xml +++ b/doc/classes/ClassDB.xml @@ -99,6 +99,14 @@ Returns the default value of [param property] of [param class] or its ancestor classes. </description> </method> + <method name="class_get_property_getter"> + <return type="StringName" /> + <param index="0" name="class" type="StringName" /> + <param index="1" name="property" type="StringName" /> + <description> + Returns the getter method name of [param property] of [param class]. + </description> + </method> <method name="class_get_property_list" qualifiers="const"> <return type="Dictionary[]" /> <param index="0" name="class" type="StringName" /> @@ -107,6 +115,14 @@ Returns an array with all the properties of [param class] or its ancestry if [param no_inheritance] is [code]false[/code]. </description> </method> + <method name="class_get_property_setter"> + <return type="StringName" /> + <param index="0" name="class" type="StringName" /> + <param index="1" name="property" type="StringName" /> + <description> + Returns the setter method name of [param property] of [param class]. + </description> + </method> <method name="class_get_signal" qualifiers="const"> <return type="Dictionary" /> <param index="0" name="class" type="StringName" /> diff --git a/doc/classes/Compositor.xml b/doc/classes/Compositor.xml index 7605083319..0732526957 100644 --- a/doc/classes/Compositor.xml +++ b/doc/classes/Compositor.xml @@ -7,6 +7,7 @@ The compositor resource stores attributes used to customize how a [Viewport] is rendered. </description> <tutorials> + <link title="The Compositor">$DOCS_URL/tutorials/rendering/compositor.html</link> </tutorials> <members> <member name="compositor_effects" type="CompositorEffect[]" setter="set_compositor_effects" getter="get_compositor_effects" default="[]"> diff --git a/doc/classes/CompositorEffect.xml b/doc/classes/CompositorEffect.xml index cf3aa9fca4..76a3887918 100644 --- a/doc/classes/CompositorEffect.xml +++ b/doc/classes/CompositorEffect.xml @@ -7,6 +7,7 @@ This resource defines a custom rendering effect that can be applied to [Viewport]s through the viewports' [Environment]. You can implement a callback that is called during rendering at a given stage of the rendering pipeline and allows you to insert additional passes. Note that this callback happens on the rendering thread. CompositorEffect is an abstract base class and must be extended to implement specific rendering logic. </description> <tutorials> + <link title="The Compositor">$DOCS_URL/tutorials/rendering/compositor.html</link> </tutorials> <methods> <method name="_render_callback" qualifiers="virtual"> diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index 4d82a32dae..320b119b6a 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -311,13 +311,13 @@ The grid size in units. Higher values prevent the grid from appearing "cut off" at certain angles, but make the grid more demanding to render. Depending on the camera's position, the grid may not be fully visible since a shader is used to fade it progressively. </member> <member name="editors/3d/grid_xy_plane" type="bool" setter="" getter=""> - If [code]true[/code], render the grid on an XY plane. This can be useful for 3D side-scrolling games. + If [code]true[/code], renders the grid on the XY plane in perspective view. This can be useful for 3D side-scrolling games. </member> <member name="editors/3d/grid_xz_plane" type="bool" setter="" getter=""> - If [code]true[/code], render the grid on an XZ plane. + If [code]true[/code], renders the grid on the XZ plane in perspective view. </member> <member name="editors/3d/grid_yz_plane" type="bool" setter="" getter=""> - If [code]true[/code], render the grid on a YZ plane. This can be useful for 3D side-scrolling games. + If [code]true[/code], renders the grid on the YZ plane in perspective view. This can be useful for 3D side-scrolling games. </member> <member name="editors/3d/navigation/emulate_3_button_mouse" type="bool" setter="" getter=""> If [code]true[/code], enables 3-button mouse emulation mode. This is useful on laptops when using a trackpad. @@ -656,6 +656,9 @@ Expanding main editor window content to the title, if supported by [DisplayServer]. See [constant DisplayServer.WINDOW_FLAG_EXTEND_TO_TITLE]. Specific to the macOS platform. </member> + <member name="interface/editor/font_allow_msdf" type="bool" setter="" getter=""> + If set to [code]true[/code], MSDF font rendering will be used for the visual shader graph editor. You may need to set this to [code]false[/code] when using a custom main font, as some fonts will look broken due to the use of self-intersecting outlines in their font data. Downloading the font from the font maker's official website as opposed to a service like Google Fonts can help resolve this issue. + </member> <member name="interface/editor/font_antialiasing" type="int" setter="" getter=""> FreeType's font anti-aliasing mode used to render the editor fonts. Most fonts are not designed to look good with anti-aliasing disabled, so it's recommended to leave this enabled unless you're using a pixel art font. </member> diff --git a/doc/classes/Geometry2D.xml b/doc/classes/Geometry2D.xml index f21696d02c..2dd76ad989 100644 --- a/doc/classes/Geometry2D.xml +++ b/doc/classes/Geometry2D.xml @@ -126,8 +126,34 @@ <param index="2" name="from_b" type="Vector2" /> <param index="3" name="dir_b" type="Vector2" /> <description> - Checks if the two lines ([param from_a], [param dir_a]) and ([param from_b], [param dir_b]) intersect. If yes, return the point of intersection as [Vector2]. If no intersection takes place, returns [code]null[/code]. - [b]Note:[/b] The lines are specified using direction vectors, not end points. + Returns the point of intersection between the two lines ([param from_a], [param dir_a]) and ([param from_b], [param dir_b]). Returns a [Vector2], or [code]null[/code] if the lines are parallel. + [code]from[/code] and [code]dir[/code] are [i]not[/i] endpoints of a line segment or ray but the slope ([code]dir[/code]) and a known point ([code]from[/code]) on that line. + [codeblocks] + [gdscript] + var from_a = Vector2.ZERO + var dir_a = Vector2.RIGHT + var from_b = Vector2.DOWN + + # Returns Vector2(1, 0) + Geometry2D.line_intersects_line(from_a, dir_a, from_b, Vector2(1, -1)) + # Returns Vector2(-1, 0) + Geometry2D.line_intersects_line(from_a, dir_a, from_b, Vector2(-1, -1)) + # Returns null + Geometry2D.line_intersects_line(from_a, dir_a, from_b, Vector2.RIGHT) + [/gdscript] + [csharp] + var fromA = Vector2.Zero; + var dirA = Vector2.Right; + var fromB = Vector2.Down; + + // Returns new Vector2(1, 0) + Geometry2D.LineIntersectsLine(fromA, dirA, fromB, new Vector2(1, -1)); + // Returns new Vector2(-1, 0) + Geometry2D.LineIntersectsLine(fromA, dirA, fromB, new Vector2(-1, -1)); + // Returns null + Geometry2D.LineIntersectsLine(fromA, dirA, fromB, Vector2.Right); + [/csharp] + [/codeblocks] </description> </method> <method name="make_atlas"> diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index db7796778e..e254fd56e9 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -321,6 +321,12 @@ Returns [code]true[/code] if all the image's pixels have an alpha value of 0. Returns [code]false[/code] if any pixel has an alpha value higher than 0. </description> </method> + <method name="linear_to_srgb"> + <return type="void" /> + <description> + Converts the entire image from the linear colorspace to the sRGB colorspace. Only works on images with [constant FORMAT_RGB8] or [constant FORMAT_RGBA8] formats. + </description> + </method> <method name="load"> <return type="int" enum="Error" /> <param index="0" name="path" type="String" /> @@ -590,7 +596,7 @@ <method name="srgb_to_linear"> <return type="void" /> <description> - Converts the raw data from the sRGB colorspace to a linear scale. + Converts the raw data from the sRGB colorspace to a linear scale. Only works on images with [constant FORMAT_RGB8] or [constant FORMAT_RGBA8] formats. </description> </method> </methods> diff --git a/doc/classes/NavigationLink2D.xml b/doc/classes/NavigationLink2D.xml index 0892c9ec44..2e1c962dd1 100644 --- a/doc/classes/NavigationLink2D.xml +++ b/doc/classes/NavigationLink2D.xml @@ -29,6 +29,12 @@ Returns whether or not the specified layer of the [member navigation_layers] bitmask is enabled, given a [param layer_number] between 1 and 32. </description> </method> + <method name="get_navigation_map" qualifiers="const"> + <return type="RID" /> + <description> + Returns the current navigation map [RID] used by this link. + </description> + </method> <method name="get_rid" qualifiers="const"> <return type="RID" /> <description> @@ -57,6 +63,13 @@ Based on [param value], enables or disables the specified layer in the [member navigation_layers] bitmask, given a [param layer_number] between 1 and 32. </description> </method> + <method name="set_navigation_map"> + <return type="void" /> + <param index="0" name="navigation_map" type="RID" /> + <description> + Sets the [RID] of the navigation map this link should use. By default the link will automatically join the [World2D] default navigation map so this function is only required to override the default map. + </description> + </method> </methods> <members> <member name="bidirectional" type="bool" setter="set_bidirectional" getter="is_bidirectional" default="true"> diff --git a/doc/classes/NavigationLink3D.xml b/doc/classes/NavigationLink3D.xml index 0fcc106beb..174228ea5b 100644 --- a/doc/classes/NavigationLink3D.xml +++ b/doc/classes/NavigationLink3D.xml @@ -29,6 +29,12 @@ Returns whether or not the specified layer of the [member navigation_layers] bitmask is enabled, given a [param layer_number] between 1 and 32. </description> </method> + <method name="get_navigation_map" qualifiers="const"> + <return type="RID" /> + <description> + Returns the current navigation map [RID] used by this link. + </description> + </method> <method name="get_rid" qualifiers="const"> <return type="RID" /> <description> @@ -57,6 +63,13 @@ Based on [param value], enables or disables the specified layer in the [member navigation_layers] bitmask, given a [param layer_number] between 1 and 32. </description> </method> + <method name="set_navigation_map"> + <return type="void" /> + <param index="0" name="navigation_map" type="RID" /> + <description> + Sets the [RID] of the navigation map this link should use. By default the link will automatically join the [World3D] default navigation map so this function is only required to override the default map. + </description> + </method> </methods> <members> <member name="bidirectional" type="bool" setter="set_bidirectional" getter="is_bidirectional" default="true"> diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml index ff3e6fd8a6..42f6235f8b 100644 --- a/doc/classes/NavigationServer3D.xml +++ b/doc/classes/NavigationServer3D.xml @@ -1167,5 +1167,8 @@ <constant name="INFO_EDGE_FREE_COUNT" value="8" enum="ProcessInfo"> Constant to get the number of navigation mesh polygon edges that could not be merged but may be still connected by edge proximity or with links. </constant> + <constant name="INFO_OBSTACLE_COUNT" value="9" enum="ProcessInfo"> + Constant to get the number of active navigation obstacles. + </constant> </constants> </class> diff --git a/doc/classes/Node3D.xml b/doc/classes/Node3D.xml index 125c7ef3ee..ae13af4b82 100644 --- a/doc/classes/Node3D.xml +++ b/doc/classes/Node3D.xml @@ -46,6 +46,14 @@ Returns all the gizmos attached to this [Node3D]. </description> </method> + <method name="get_global_transform_interpolated"> + <return type="Transform3D" /> + <description> + When using physics interpolation, there will be circumstances in which you want to know the interpolated (displayed) transform of a node rather than the standard transform (which may only be accurate to the most recent physics tick). + This is particularly important for frame-based operations that take place in [method Node._process], rather than [method Node._physics_process]. Examples include [Camera3D]s focusing on a node, or finding where to fire lasers from on a frame rather than physics tick. + [b]Note:[/b] This function creates an interpolation pump on the [Node3D] the first time it is called, which can respond to physics interpolation resets. If you get problems with "streaking" when initially following a [Node3D], be sure to call [method get_global_transform_interpolated] at least once [i]before[/i] resetting the [Node3D] physics interpolation. + </description> + </method> <method name="get_parent_node_3d" qualifiers="const"> <return type="Node3D" /> <description> diff --git a/doc/classes/Performance.xml b/doc/classes/Performance.xml index 4d01fe1760..6bb71932dd 100644 --- a/doc/classes/Performance.xml +++ b/doc/classes/Performance.xml @@ -221,7 +221,10 @@ <constant name="NAVIGATION_EDGE_FREE_COUNT" value="32" enum="Monitor"> Number of navigation mesh polygon edges that could not be merged in the [NavigationServer3D]. The edges still may be connected by edge proximity or with links. </constant> - <constant name="MONITOR_MAX" value="33" enum="Monitor"> + <constant name="NAVIGATION_OBSTACLE_COUNT" value="33" enum="Monitor"> + Number of active navigation obstacles in the [NavigationServer3D]. + </constant> + <constant name="MONITOR_MAX" value="34" enum="Monitor"> Represents the size of the [enum Monitor] enum. </constant> </constants> diff --git a/doc/classes/Polygon2D.xml b/doc/classes/Polygon2D.xml index 90d3522002..4e9208d9bb 100644 --- a/doc/classes/Polygon2D.xml +++ b/doc/classes/Polygon2D.xml @@ -71,9 +71,6 @@ <member name="antialiased" type="bool" setter="set_antialiased" getter="get_antialiased" default="false"> If [code]true[/code], polygon edges will be anti-aliased. </member> - <member name="bones" type="Array" setter="_set_bones" getter="_get_bones" default="[]"> - Internal list of [Bone2D] nodes used by the assigned [member skeleton]. Edited using the Polygon2D editor ("UV" button on the top toolbar). - </member> <member name="color" type="Color" setter="set_color" getter="get_color" default="Color(1, 1, 1, 1)" keywords="colour"> The polygon's fill color. If [member texture] is set, it will be multiplied by this color. It will also be the default color for vertices not set in [member vertex_colors]. </member> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index b20b374382..1d65f6fb9a 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -630,6 +630,10 @@ <member name="debug/settings/gdscript/max_call_stack" type="int" setter="" getter="" default="1024"> Maximum call stack allowed for debugging GDScript. </member> + <member name="debug/settings/physics_interpolation/enable_warnings" type="bool" setter="" getter="" default="true"> + If [code]true[/code], enables warnings which can help pinpoint where nodes are being incorrectly updated, which will result in incorrect interpolation and visual glitches. + When a node is being interpolated, it is essential that the transform is set during [method Node._physics_process] (during a physics tick) rather than [method Node._process] (during a frame). + </member> <member name="debug/settings/profiler/max_functions" type="int" setter="" getter="" default="16384"> Maximum number of functions per frame allowed when profiling. </member> @@ -1000,7 +1004,7 @@ prime-run %command% [/codeblock] </member> - <member name="editor/script/search_in_file_extensions" type="PackedStringArray" setter="" getter="" default="PackedStringArray("gd", "gdshader")"> + <member name="editor/script/search_in_file_extensions" type="PackedStringArray" setter="" getter=""> Text-based file extensions to include in the script editor's "Find in Files" feature. You can add e.g. [code]tscn[/code] if you wish to also parse your scene files, especially if you use built-in scripts which are serialized in the scene files. </member> <member name="editor/script/templates_search_path" type="String" setter="" getter="" default=""res://script_templates""> @@ -1424,6 +1428,18 @@ <member name="input_devices/pointing/emulate_touch_from_mouse" type="bool" setter="" getter="" default="false"> If [code]true[/code], sends touch input events when clicking or dragging the mouse. </member> + <member name="input_devices/sensors/enable_accelerometer" type="bool" setter="" getter="" default="false"> + If [code]true[/code], the accelerometer sensor is enabled and [method Input.get_accelerometer] returns valid data. + </member> + <member name="input_devices/sensors/enable_gravity" type="bool" setter="" getter="" default="false"> + If [code]true[/code], the gravity sensor is enabled and [method Input.get_gravity] returns valid data. + </member> + <member name="input_devices/sensors/enable_gyroscope" type="bool" setter="" getter="" default="false"> + If [code]true[/code], the gyroscope sensor is enabled and [method Input.get_gyroscope] returns valid data. + </member> + <member name="input_devices/sensors/enable_magnetometer" type="bool" setter="" getter="" default="false"> + If [code]true[/code], the magnetometer sensor is enabled and [method Input.get_magnetometer] returns valid data. + </member> <member name="internationalization/locale/fallback" type="String" setter="" getter="" default=""en""> The locale to fall back to if a translation isn't available in a given language. If left empty, [code]en[/code] (English) will be used. </member> @@ -2322,7 +2338,8 @@ </member> <member name="physics/common/physics_jitter_fix" type="float" setter="" getter="" default="0.5"> Controls how much physics ticks are synchronized with real time. For 0 or less, the ticks are synchronized. Such values are recommended for network games, where clock synchronization matters. Higher values cause higher deviation of in-game clock and real clock, but allows smoothing out framerate jitters. The default value of 0.5 should be good enough for most; values above 2 could cause the game to react to dropped frames with a noticeable delay and are not recommended. - [b]Note:[/b] When using a physics interpolation solution (such as enabling [member physics/common/physics_interpolation] or using a custom solution), the physics jitter fix should be disabled by setting [member physics/common/physics_jitter_fix] to [code]0.0[/code]. + [b]Note:[/b] Jitter fix is automatically disabled at runtime when [member physics/common/physics_interpolation] is enabled. + [b]Note:[/b] When using a custom physics interpolation solution, the physics jitter fix should be disabled by setting [member physics/common/physics_jitter_fix] to [code]0.0[/code]. [b]Note:[/b] This property is only read when the project starts. To change the physics jitter fix at runtime, set [member Engine.physics_jitter_fix] instead. </member> <member name="physics/common/physics_ticks_per_second" type="int" setter="" getter="" default="60"> diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 3c9f0fc7af..d86b82b72a 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -1855,6 +1855,14 @@ Sets the visibility range values for the given geometry instance. Equivalent to [member GeometryInstance3D.visibility_range_begin] and related properties. </description> </method> + <method name="instance_reset_physics_interpolation"> + <return type="void" /> + <param index="0" name="instance" type="RID" /> + <description> + Prevents physics interpolation for the current physics tick. + This is useful when moving an instance to a new location, to give an instantaneous change rather than interpolation from the previous location. + </description> + </method> <method name="instance_set_base"> <return type="void" /> <param index="0" name="instance" type="RID" /> @@ -1896,6 +1904,14 @@ If [code]true[/code], ignores both frustum and occlusion culling on the specified 3D geometry instance. This is not the same as [member GeometryInstance3D.ignore_occlusion_culling], which only ignores occlusion culling and leaves frustum culling intact. </description> </method> + <method name="instance_set_interpolated"> + <return type="void" /> + <param index="0" name="instance" type="RID" /> + <param index="1" name="interpolated" type="bool" /> + <description> + Turns on and off physics interpolation for the instance. + </description> + </method> <method name="instance_set_layer_mask"> <return type="void" /> <param index="0" name="instance" type="RID" /> diff --git a/doc/classes/ResourceImporterDynamicFont.xml b/doc/classes/ResourceImporterDynamicFont.xml index f100670e08..b678a04e34 100644 --- a/doc/classes/ResourceImporterDynamicFont.xml +++ b/doc/classes/ResourceImporterDynamicFont.xml @@ -69,12 +69,13 @@ <member name="script_support" type="Dictionary" setter="" getter="" default="{}"> Override the list of language scripts supported by this font. If left empty, this is supplied by the font metadata. There is usually no need to change this. See also [member language_support]. </member> - <member name="subpixel_positioning" type="int" setter="" getter="" default="1"> + <member name="subpixel_positioning" type="int" setter="" getter="" default="4"> Subpixel positioning improves font rendering appearance, especially at smaller font sizes. The downside is that it takes more time to initially render the font, which can cause stuttering during gameplay, especially if used with large font sizes. This should be set to [b]Disabled[/b] for fonts with a pixel art appearance. [b]Disabled:[/b] No subpixel positioning. Lowest quality, fastest rendering. [b]Auto:[/b] Use subpixel positioning at small font sizes (the chosen quality varies depending on font size). Large fonts will not use subpixel positioning. This is a good tradeoff between performance and quality. [b]One Half of a Pixel:[/b] Always perform intermediate subpixel positioning regardless of font size. High quality, slow rendering. [b]One Quarter of a Pixel:[/b] Always perform precise subpixel positioning regardless of font size. Highest quality, slowest rendering. + [b]Auto (Except Pixel Fonts):[/b] [b]Disabled[/b] for the pixel style fonts (each glyph contours contain only straight horizontal and vertical lines), [b]Auto[/b] for the other fonts. </member> </members> </class> diff --git a/doc/classes/ResourceImporterImageFont.xml b/doc/classes/ResourceImporterImageFont.xml index 6dbfa1d651..663c906001 100644 --- a/doc/classes/ResourceImporterImageFont.xml +++ b/doc/classes/ResourceImporterImageFont.xml @@ -40,7 +40,7 @@ </member> <member name="kerning_pairs" type="PackedStringArray" setter="" getter="" default="PackedStringArray()"> Kerning pairs for the font. Kerning pair adjust the spacing between two characters. - Each string consist of three space separated values: "from" string, "to" string and integer offset. Each combination form the two string for a kerning pair, e.g, [code]ab cd -3[/code] will create kerning pairs [code]ac[/code], [code]ad[/code], [code]bc[/code], and [code]bd[/code] with offset [code]-3[/code]. + Each string consist of three space separated values: "from" string, "to" string and integer offset. Each combination form the two string for a kerning pair, e.g, [code]ab cd -3[/code] will create kerning pairs [code]ac[/code], [code]ad[/code], [code]bc[/code], and [code]bd[/code] with offset [code]-3[/code]. [code]\uXXXX[/code] escape sequences can be used to add Unicode characters. </member> <member name="rows" type="int" setter="" getter="" default="1"> Number of rows in the font image. See also [member columns]. diff --git a/doc/classes/ResourceImporterOBJ.xml b/doc/classes/ResourceImporterOBJ.xml index 55043a311c..a63dddb0e8 100644 --- a/doc/classes/ResourceImporterOBJ.xml +++ b/doc/classes/ResourceImporterOBJ.xml @@ -21,9 +21,6 @@ <member name="offset_mesh" type="Vector3" setter="" getter="" default="Vector3(0, 0, 0)"> Offsets the mesh's data by the specified value. This can be used to work around misaligned meshes without having to modify the source file. </member> - <member name="optimize_mesh" type="bool" setter="" getter="" default="true"> - Unused parameter. This currently has no effect. - </member> <member name="scale_mesh" type="Vector3" setter="" getter="" default="Vector3(1, 1, 1)"> Scales the mesh's data by the specified value. This can be used to work around misscaled meshes without having to modify the source file. </member> diff --git a/doc/classes/ShapeCast2D.xml b/doc/classes/ShapeCast2D.xml index 576bd62cc3..385e3a9285 100644 --- a/doc/classes/ShapeCast2D.xml +++ b/doc/classes/ShapeCast2D.xml @@ -139,7 +139,7 @@ <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="1"> The shape's collision mask. Only objects in at least one collision layer enabled in the mask will be detected. </member> - <member name="collision_result" type="Array" setter="" getter="_get_collision_result" default="[]"> + <member name="collision_result" type="Array" setter="" getter="get_collision_result" default="[]"> Returns the complete collision information from the collision sweep. The data returned is the same as in the [method PhysicsDirectSpaceState2D.get_rest_info] method. </member> <member name="enabled" type="bool" setter="set_enabled" getter="is_enabled" default="true"> diff --git a/doc/classes/ShapeCast3D.xml b/doc/classes/ShapeCast3D.xml index 2c6efe2ebe..f70cf169df 100644 --- a/doc/classes/ShapeCast3D.xml +++ b/doc/classes/ShapeCast3D.xml @@ -146,7 +146,7 @@ <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="1"> The shape's collision mask. Only objects in at least one collision layer enabled in the mask will be detected. See [url=$DOCS_URL/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information. </member> - <member name="collision_result" type="Array" setter="" getter="_get_collision_result" default="[]"> + <member name="collision_result" type="Array" setter="" getter="get_collision_result" default="[]"> Returns the complete collision information from the collision sweep. The data returned is the same as in the [method PhysicsDirectSpaceState3D.get_rest_info] method. </member> <member name="debug_shape_custom_color" type="Color" setter="set_debug_shape_custom_color" getter="get_debug_shape_custom_color" default="Color(0, 0, 0, 1)"> diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml index 4fa9700f9c..9d476691bf 100644 --- a/doc/classes/TextServer.xml +++ b/doc/classes/TextServer.xml @@ -459,6 +459,13 @@ Returns a string containing all the characters available in the font. </description> </method> + <method name="font_get_supported_glyphs" qualifiers="const"> + <return type="PackedInt32Array" /> + <param index="0" name="font_rid" type="RID" /> + <description> + Returns an array containing all glyph indices in the font. + </description> + </method> <method name="font_get_texture_count" qualifiers="const"> <return type="int" /> <param index="0" name="font_rid" type="RID" /> diff --git a/doc/classes/TextServerExtension.xml b/doc/classes/TextServerExtension.xml index c148cdad52..3c27404f8e 100644 --- a/doc/classes/TextServerExtension.xml +++ b/doc/classes/TextServerExtension.xml @@ -496,6 +496,14 @@ Returns a string containing all the characters available in the font. </description> </method> + <method name="_font_get_supported_glyphs" qualifiers="virtual const"> + <return type="PackedInt32Array" /> + <param index="0" name="font_rid" type="RID" /> + <description> + [b]Required.[/b] + Returns an array containing all glyph indices in the font. + </description> + </method> <method name="_font_get_texture_count" qualifiers="virtual const"> <return type="int" /> <param index="0" name="font_rid" type="RID" /> diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml index d3197efc6b..0995a5a672 100644 --- a/doc/classes/TileMap.xml +++ b/doc/classes/TileMap.xml @@ -256,6 +256,33 @@ Returns a rectangle enclosing the used (non-empty) tiles of the map, including all layers. </description> </method> + <method name="is_cell_flipped_h" qualifiers="const"> + <return type="bool" /> + <param index="0" name="layer" type="int" /> + <param index="1" name="coords" type="Vector2i" /> + <param index="2" name="use_proxies" type="bool" default="false" /> + <description> + Returns [code]true[/code] if the cell on layer [param layer] at coordinates [param coords] is flipped horizontally. The result is valid only for atlas sources. + </description> + </method> + <method name="is_cell_flipped_v" qualifiers="const"> + <return type="bool" /> + <param index="0" name="layer" type="int" /> + <param index="1" name="coords" type="Vector2i" /> + <param index="2" name="use_proxies" type="bool" default="false" /> + <description> + Returns [code]true[/code] if the cell on layer [param layer] at coordinates [param coords] is flipped vertically. The result is valid only for atlas sources. + </description> + </method> + <method name="is_cell_transposed" qualifiers="const"> + <return type="bool" /> + <param index="0" name="layer" type="int" /> + <param index="1" name="coords" type="Vector2i" /> + <param index="2" name="use_proxies" type="bool" default="false" /> + <description> + Returns [code]true[/code] if the cell on layer [param layer] at coordinates [param coords] is transposed. The result is valid only for atlas sources. + </description> + </method> <method name="is_layer_enabled" qualifiers="const"> <return type="bool" /> <param index="0" name="layer" type="int" /> diff --git a/doc/classes/TileMapLayer.xml b/doc/classes/TileMapLayer.xml index b9acef2095..bead1c32c0 100644 --- a/doc/classes/TileMapLayer.xml +++ b/doc/classes/TileMapLayer.xml @@ -153,6 +153,27 @@ Returns whether the provided [param body] [RID] belongs to one of this [TileMapLayer]'s cells. </description> </method> + <method name="is_cell_flipped_h" qualifiers="const"> + <return type="bool" /> + <param index="0" name="coords" type="Vector2i" /> + <description> + Returns [code]true[/code] if the cell at coordinates [param coords] is flipped horizontally. The result is valid only for atlas sources. + </description> + </method> + <method name="is_cell_flipped_v" qualifiers="const"> + <return type="bool" /> + <param index="0" name="coords" type="Vector2i" /> + <description> + Returns [code]true[/code] if the cell at coordinates [param coords] is flipped vertically. The result is valid only for atlas sources. + </description> + </method> + <method name="is_cell_transposed" qualifiers="const"> + <return type="bool" /> + <param index="0" name="coords" type="Vector2i" /> + <description> + Returns [code]true[/code] if the cell at coordinates [param coords] is transposed. The result is valid only for atlas sources. + </description> + </method> <method name="local_to_map" qualifiers="const"> <return type="Vector2i" /> <param index="0" name="local_position" type="Vector2" /> diff --git a/doc/classes/Timer.xml b/doc/classes/Timer.xml index 9de1e09273..f8f9393847 100644 --- a/doc/classes/Timer.xml +++ b/doc/classes/Timer.xml @@ -28,7 +28,7 @@ <return type="void" /> <param index="0" name="time_sec" type="float" default="-1" /> <description> - Starts the timer, if it was not started already. Fails if the timer is not inside the tree. If [param time_sec] is greater than [code]0[/code], this value is used for the [member wait_time]. + Starts the timer, or resets the timer if it was started already. Fails if the timer is not inside the tree. If [param time_sec] is greater than [code]0[/code], this value is used for the [member wait_time]. [b]Note:[/b] This method does not resume a paused timer. See [member paused]. </description> </method> diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index b288ee7ff6..b24f26a764 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -102,6 +102,7 @@ await RenderingServer.frame_post_draw $Viewport.get_texture().get_image().save_png("user://Screenshot.png") [/codeblock] + [b]Note:[/b] When [member use_hdr_2d] is [code]true[/code] the returned texture will be an HDR image encoded in linear space. </description> </method> <method name="get_viewport_rid" qualifiers="const"> @@ -301,6 +302,7 @@ <member name="own_world_3d" type="bool" setter="set_use_own_world_3d" getter="is_using_own_world_3d" default="false"> If [code]true[/code], the viewport will use a unique copy of the [World3D] defined in [member world_3d]. </member> + <member name="physics_interpolation_mode" type="int" setter="set_physics_interpolation_mode" getter="get_physics_interpolation_mode" overrides="Node" enum="Node.PhysicsInterpolationMode" default="1" /> <member name="physics_object_picking" type="bool" setter="set_physics_object_picking" getter="get_physics_object_picking" default="false"> If [code]true[/code], the objects rendered by viewport become subjects of mouse picking process. [b]Note:[/b] The number of simultaneously pickable objects is limited to 64 and they are selected in a non-deterministic order, which can be different in each picking process. diff --git a/doc/classes/ViewportTexture.xml b/doc/classes/ViewportTexture.xml index ba2352ab61..f6840d6b09 100644 --- a/doc/classes/ViewportTexture.xml +++ b/doc/classes/ViewportTexture.xml @@ -8,6 +8,11 @@ To get a [ViewportTexture] in code, use the [method Viewport.get_texture] method on the target viewport. [b]Note:[/b] A [ViewportTexture] is always local to its scene (see [member Resource.resource_local_to_scene]). If the scene root is not ready, it may return incorrect data (see [signal Node.ready]). [b]Note:[/b] Instantiating scenes containing a high-resolution [ViewportTexture] may cause noticeable stutter. + [b]Note:[/b] When using a [Viewport] with [member Viewport.use_hdr_2d] set to [code]true[/code] the returned texture will be an HDR image encoded in linear space. This may look darker than normal when displayed directly on screen. To convert to gamma space, you can do the following: + [codeblock] + img.convert(Image.FORMAT_RGBA8) + imb.linear_to_srgb() + [/codeblock] </description> <tutorials> <link title="GUI in 3D Viewport Demo">https://godotengine.org/asset-library/asset/2807</link> diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp index a33fc977c6..38caff648e 100644 --- a/drivers/d3d12/rendering_device_driver_d3d12.cpp +++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp @@ -2143,33 +2143,59 @@ void RenderingDeviceDriverD3D12::command_pipeline_barrier(CommandBufferID p_cmd_ for (uint32_t i = 0; i < p_texture_barriers.size(); i++) { const TextureBarrier &texture_barrier_rd = p_texture_barriers[i]; const TextureInfo *texture_info = (const TextureInfo *)(texture_barrier_rd.texture.id); + if (texture_info->main_texture) { + texture_info = texture_info->main_texture; + } _rd_stages_and_access_to_d3d12(p_src_stages, texture_barrier_rd.prev_layout, texture_barrier_rd.src_access, texture_barrier_d3d12.SyncBefore, texture_barrier_d3d12.AccessBefore); _rd_stages_and_access_to_d3d12(p_dst_stages, texture_barrier_rd.next_layout, texture_barrier_rd.dst_access, texture_barrier_d3d12.SyncAfter, texture_barrier_d3d12.AccessAfter); texture_barrier_d3d12.LayoutBefore = _rd_texture_layout_to_d3d12_barrier_layout(texture_barrier_rd.prev_layout); texture_barrier_d3d12.LayoutAfter = _rd_texture_layout_to_d3d12_barrier_layout(texture_barrier_rd.next_layout); texture_barrier_d3d12.pResource = texture_info->resource; - texture_barrier_d3d12.Subresources.IndexOrFirstMipLevel = texture_barrier_rd.subresources.base_mipmap; - texture_barrier_d3d12.Subresources.NumMipLevels = texture_barrier_rd.subresources.mipmap_count; - texture_barrier_d3d12.Subresources.FirstArraySlice = texture_barrier_rd.subresources.base_layer; - texture_barrier_d3d12.Subresources.NumArraySlices = texture_barrier_rd.subresources.layer_count; - texture_barrier_d3d12.Subresources.FirstPlane = _compute_plane_slice(texture_info->format, texture_barrier_rd.subresources.aspect); - texture_barrier_d3d12.Subresources.NumPlanes = format_get_plane_count(texture_info->format); + if (texture_barrier_rd.subresources.mipmap_count == texture_info->mipmaps && texture_barrier_rd.subresources.layer_count == texture_info->layers) { + // So, all resources. Then, let's be explicit about it so D3D12 doesn't think + // we are dealing with a subset of subresources. + texture_barrier_d3d12.Subresources.IndexOrFirstMipLevel = 0xffffffff; + texture_barrier_d3d12.Subresources.NumMipLevels = 0; + // Because NumMipLevels == 0, all the other fields are ignored by D3D12. + } else { + texture_barrier_d3d12.Subresources.IndexOrFirstMipLevel = texture_barrier_rd.subresources.base_mipmap; + texture_barrier_d3d12.Subresources.NumMipLevels = texture_barrier_rd.subresources.mipmap_count; + texture_barrier_d3d12.Subresources.FirstArraySlice = texture_barrier_rd.subresources.base_layer; + texture_barrier_d3d12.Subresources.NumArraySlices = texture_barrier_rd.subresources.layer_count; + texture_barrier_d3d12.Subresources.FirstPlane = _compute_plane_slice(texture_info->format, texture_barrier_rd.subresources.aspect); + texture_barrier_d3d12.Subresources.NumPlanes = format_get_plane_count(texture_info->format); + } texture_barrier_d3d12.Flags = (texture_barrier_rd.prev_layout == RDD::TEXTURE_LAYOUT_UNDEFINED) ? D3D12_TEXTURE_BARRIER_FLAG_DISCARD : D3D12_TEXTURE_BARRIER_FLAG_NONE; texture_barriers.push_back(texture_barrier_d3d12); } // Define the barrier groups and execute. + D3D12_BARRIER_GROUP barrier_groups[3] = {}; - barrier_groups[0].Type = D3D12_BARRIER_TYPE_GLOBAL; - barrier_groups[1].Type = D3D12_BARRIER_TYPE_BUFFER; - barrier_groups[2].Type = D3D12_BARRIER_TYPE_TEXTURE; - barrier_groups[0].NumBarriers = global_barriers.size(); - barrier_groups[1].NumBarriers = buffer_barriers.size(); - barrier_groups[2].NumBarriers = texture_barriers.size(); - barrier_groups[0].pGlobalBarriers = global_barriers.ptr(); - barrier_groups[1].pBufferBarriers = buffer_barriers.ptr(); - barrier_groups[2].pTextureBarriers = texture_barriers.ptr(); - cmd_list_7->Barrier(ARRAY_SIZE(barrier_groups), barrier_groups); + uint32_t barrier_groups_count = 0; + + if (!global_barriers.is_empty()) { + D3D12_BARRIER_GROUP &barrier_group = barrier_groups[barrier_groups_count++]; + barrier_group.Type = D3D12_BARRIER_TYPE_GLOBAL; + barrier_group.NumBarriers = global_barriers.size(); + barrier_group.pGlobalBarriers = global_barriers.ptr(); + } + + if (!buffer_barriers.is_empty()) { + D3D12_BARRIER_GROUP &barrier_group = barrier_groups[barrier_groups_count++]; + barrier_group.Type = D3D12_BARRIER_TYPE_BUFFER; + barrier_group.NumBarriers = buffer_barriers.size(); + barrier_group.pBufferBarriers = buffer_barriers.ptr(); + } + + if (!texture_barriers.is_empty()) { + D3D12_BARRIER_GROUP &barrier_group = barrier_groups[barrier_groups_count++]; + barrier_group.Type = D3D12_BARRIER_TYPE_TEXTURE; + barrier_group.NumBarriers = texture_barriers.size(); + barrier_group.pTextureBarriers = texture_barriers.ptr(); + } + + cmd_list_7->Barrier(barrier_groups_count, barrier_groups); } /****************/ diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 37e7256d76..19ef3d416c 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -62,6 +62,10 @@ #define _EXT_DEBUG_SEVERITY_LOW_ARB 0x9148 #define _EXT_DEBUG_OUTPUT 0x92E0 +#ifndef GL_FRAMEBUFFER_SRGB +#define GL_FRAMEBUFFER_SRGB 0x8DB9 +#endif + #ifndef GLAPIENTRY #if defined(WINDOWS_ENABLED) #define GLAPIENTRY APIENTRY @@ -345,6 +349,9 @@ RasterizerGLES3::RasterizerGLES3() { } } + // Disable OpenGL linear to sRGB conversion, because Godot will always do this conversion itself. + glDisable(GL_FRAMEBUFFER_SRGB); + // OpenGL needs to be initialized before initializing the Rasterizers config = memnew(GLES3::Config); utilities = memnew(GLES3::Utilities); diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp index 4a15ed827a..5a0f394db0 100644 --- a/drivers/gles3/shader_gles3.cpp +++ b/drivers/gles3/shader_gles3.cpp @@ -698,7 +698,8 @@ void ShaderGLES3::_clear_version(Version *p_version) { void ShaderGLES3::_initialize_version(Version *p_version) { ERR_FAIL_COND(p_version->variants.size() > 0); - if (shader_cache_dir_valid && _load_from_cache(p_version)) { + bool use_cache = shader_cache_dir_valid && !(feedback_count > 0 && GLES3::Config::get_singleton()->disable_transform_feedback_shader_cache); + if (use_cache && _load_from_cache(p_version)) { return; } p_version->variants.reserve(variant_count); @@ -709,7 +710,7 @@ void ShaderGLES3::_initialize_version(Version *p_version) { _compile_specialization(spec, i, p_version, specialization_default_mask); p_version->variants[i].insert(specialization_default_mask, spec); } - if (shader_cache_dir_valid) { + if (use_cache) { _save_to_cache(p_version); } } diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index 6dd04af6b6..04ba98a420 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -1609,6 +1609,7 @@ void main() { #ifdef BASE_PASS /////////////////////// LIGHTING ////////////////////////////// +#ifndef AMBIENT_LIGHT_DISABLED // IBL precalculations float ndotv = clamp(dot(normal, view), 0.0, 1.0); vec3 F = f0 + (max(vec3(1.0 - roughness), f0) - f0) * pow(1.0 - ndotv, 5.0); @@ -1746,18 +1747,14 @@ void main() { #endif // USE_LIGHTMAP_CAPTURE #endif // !DISABLE_LIGHTMAP - { -#if defined(AMBIENT_LIGHT_DISABLED) - ambient_light = vec3(0.0, 0.0, 0.0); -#else - ambient_light *= albedo.rgb; - ambient_light *= ao; -#endif // AMBIENT_LIGHT_DISABLED - } + ambient_light *= albedo.rgb; + ambient_light *= ao; + +#endif // !AMBIENT_LIGHT_DISABLED // convert ao to direct light ao ao = mix(1.0, ao, ao_light_affect); - +#ifndef AMBIENT_LIGHT_DISABLED { #if defined(DIFFUSE_TOON) //simplify for toon, as @@ -1779,6 +1776,8 @@ void main() { #endif } +#endif // !AMBIENT_LIGHT_DISABLED + #ifndef DISABLE_LIGHT_DIRECTIONAL for (uint i = uint(0); i < scene_data.directional_light_count; i++) { #if defined(USE_LIGHTMAP) && !defined(DISABLE_LIGHTMAP) @@ -1871,7 +1870,7 @@ void main() { alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0)); #if defined(ALPHA_SCISSOR_USED) - if (alpha < alpha_scissor) { + if (alpha < alpha_scissor_threshold) { discard; } #endif // !ALPHA_SCISSOR_USED diff --git a/drivers/gles3/shaders/skeleton.glsl b/drivers/gles3/shaders/skeleton.glsl index aad856a5a2..66befbc3b2 100644 --- a/drivers/gles3/shaders/skeleton.glsl +++ b/drivers/gles3/shaders/skeleton.glsl @@ -59,7 +59,7 @@ layout(location = 10) in highp uvec4 in_bone_attrib; layout(location = 11) in mediump vec4 in_weight_attrib; #endif -uniform mediump sampler2D skeleton_texture; // texunit:0 +uniform highp sampler2D skeleton_texture; // texunit:0 #endif /* clang-format on */ diff --git a/drivers/gles3/storage/config.cpp b/drivers/gles3/storage/config.cpp index a28b050bf8..2b3c19dbb8 100644 --- a/drivers/gles3/storage/config.cpp +++ b/drivers/gles3/storage/config.cpp @@ -218,6 +218,8 @@ Config::Config() { //https://github.com/godotengine/godot/issues/92662#issuecomment-2161199477 //disable_particles_workaround = false; } + } else if (rendering_device_name == "PowerVR Rogue GE8320") { + disable_transform_feedback_shader_cache = true; } } diff --git a/drivers/gles3/storage/config.h b/drivers/gles3/storage/config.h index 0c9f9bc275..ff72fc5b58 100644 --- a/drivers/gles3/storage/config.h +++ b/drivers/gles3/storage/config.h @@ -96,6 +96,9 @@ public: bool disable_particles_workaround = false; // set to 'true' to disable 'GPUParticles' bool flip_xy_workaround = false; + // PowerVR GE 8320 workaround + bool disable_transform_feedback_shader_cache = false; + #ifdef ANDROID_ENABLED PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC eglFramebufferTextureMultiviewOVR = nullptr; PFNGLTEXSTORAGE3DMULTISAMPLEPROC eglTexStorage3DMultisample = nullptr; diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp index 210507c2c6..ea8b42b2e4 100644 --- a/drivers/unix/file_access_unix.cpp +++ b/drivers/unix/file_access_unix.cpp @@ -383,7 +383,7 @@ uint64_t FileAccessUnix::_get_modified_time(const String &p_file) { if (!err) { return status.st_mtime; } else { - print_verbose("Failed to get modified time for: " + p_file + ""); + WARN_PRINT("Failed to get modified time for: " + p_file); return 0; } } diff --git a/editor/animation_track_editor_plugins.cpp b/editor/animation_track_editor_plugins.cpp index 0297aaaf7f..d36e152ed9 100644 --- a/editor/animation_track_editor_plugins.cpp +++ b/editor/animation_track_editor_plugins.cpp @@ -353,9 +353,6 @@ void AnimationTrackEditAudio::set_node(Object *p_object) { id = p_object->get_instance_id(); } -void AnimationTrackEditAudio::_bind_methods() { -} - AnimationTrackEditAudio::AnimationTrackEditAudio() { AudioStreamPreviewGenerator::get_singleton()->connect("preview_updated", callable_mp(this, &AnimationTrackEditAudio::_preview_changed)); } @@ -952,9 +949,6 @@ void AnimationTrackEditTypeAudio::draw_key(int p_index, float p_pixels_sec, int } } -void AnimationTrackEditTypeAudio::_bind_methods() { -} - AnimationTrackEditTypeAudio::AnimationTrackEditTypeAudio() { AudioStreamPreviewGenerator::get_singleton()->connect("preview_updated", callable_mp(this, &AnimationTrackEditTypeAudio::_preview_changed)); } diff --git a/editor/animation_track_editor_plugins.h b/editor/animation_track_editor_plugins.h index 0e9ad44229..7ddbae1bbc 100644 --- a/editor/animation_track_editor_plugins.h +++ b/editor/animation_track_editor_plugins.h @@ -63,9 +63,6 @@ class AnimationTrackEditAudio : public AnimationTrackEdit { void _preview_changed(ObjectID p_which); -protected: - static void _bind_methods(); - public: virtual int get_key_height() const override; virtual Rect2 get_key_rect(int p_index, float p_pixels_sec) override; @@ -121,9 +118,6 @@ class AnimationTrackEditTypeAudio : public AnimationTrackEdit { float len_resizing_rel = 0.0f; bool over_drag_position = false; -protected: - static void _bind_methods(); - public: virtual void gui_input(const Ref<InputEvent> &p_event) override; diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp index c83e677b37..8ba5811ffa 100644 --- a/editor/dependency_editor.cpp +++ b/editor/dependency_editor.cpp @@ -237,9 +237,6 @@ void DependencyEditor::edit(const String &p_path) { } } -void DependencyEditor::_bind_methods() { -} - DependencyEditor::DependencyEditor() { VBoxContainer *vb = memnew(VBoxContainer); vb->set_name(TTR("Dependencies")); @@ -353,9 +350,6 @@ void DependencyEditorOwners::_file_option(int p_option) { } } -void DependencyEditorOwners::_bind_methods() { -} - void DependencyEditorOwners::_fill_owners(EditorFileSystemDirectory *efsd) { if (!efsd) { return; @@ -578,32 +572,34 @@ void DependencyRemoveDialog::ok_pressed() { } } + bool project_settings_modified = false; for (int i = 0; i < files_to_delete.size(); ++i) { // If the file we are deleting for e.g. the main scene, default environment, // or audio bus layout, we must clear its definition in Project Settings. if (files_to_delete[i] == String(GLOBAL_GET("application/config/icon"))) { ProjectSettings::get_singleton()->set("application/config/icon", ""); - } - if (files_to_delete[i] == String(GLOBAL_GET("application/run/main_scene"))) { + project_settings_modified = true; + } else if (files_to_delete[i] == String(GLOBAL_GET("application/run/main_scene"))) { ProjectSettings::get_singleton()->set("application/run/main_scene", ""); - } - if (files_to_delete[i] == String(GLOBAL_GET("application/boot_splash/image"))) { + project_settings_modified = true; + } else if (files_to_delete[i] == String(GLOBAL_GET("application/boot_splash/image"))) { ProjectSettings::get_singleton()->set("application/boot_splash/image", ""); - } - if (files_to_delete[i] == String(GLOBAL_GET("rendering/environment/defaults/default_environment"))) { + project_settings_modified = true; + } else if (files_to_delete[i] == String(GLOBAL_GET("rendering/environment/defaults/default_environment"))) { ProjectSettings::get_singleton()->set("rendering/environment/defaults/default_environment", ""); - } - if (files_to_delete[i] == String(GLOBAL_GET("display/mouse_cursor/custom_image"))) { + project_settings_modified = true; + } else if (files_to_delete[i] == String(GLOBAL_GET("display/mouse_cursor/custom_image"))) { ProjectSettings::get_singleton()->set("display/mouse_cursor/custom_image", ""); - } - if (files_to_delete[i] == String(GLOBAL_GET("gui/theme/custom"))) { + project_settings_modified = true; + } else if (files_to_delete[i] == String(GLOBAL_GET("gui/theme/custom"))) { ProjectSettings::get_singleton()->set("gui/theme/custom", ""); - } - if (files_to_delete[i] == String(GLOBAL_GET("gui/theme/custom_font"))) { + project_settings_modified = true; + } else if (files_to_delete[i] == String(GLOBAL_GET("gui/theme/custom_font"))) { ProjectSettings::get_singleton()->set("gui/theme/custom_font", ""); - } - if (files_to_delete[i] == String(GLOBAL_GET("audio/buses/default_bus_layout"))) { + project_settings_modified = true; + } else if (files_to_delete[i] == String(GLOBAL_GET("audio/buses/default_bus_layout"))) { ProjectSettings::get_singleton()->set("audio/buses/default_bus_layout", ""); + project_settings_modified = true; } String path = OS::get_singleton()->get_resource_dir() + files_to_delete[i].replace_first("res://", "/"); @@ -615,6 +611,9 @@ void DependencyRemoveDialog::ok_pressed() { emit_signal(SNAME("file_removed"), files_to_delete[i]); } } + if (project_settings_modified) { + ProjectSettings::get_singleton()->save(); + } if (dirs_to_delete.size() == 0) { // If we only deleted files we should only need to tell the file system about the files we touched. @@ -865,9 +864,6 @@ void OrphanResourcesDialog::_button_pressed(Object *p_item, int p_column, int p_ dep_edit->edit(path); } -void OrphanResourcesDialog::_bind_methods() { -} - OrphanResourcesDialog::OrphanResourcesDialog() { set_title(TTR("Orphan Resource Explorer")); delete_confirm = memnew(ConfirmationDialog); diff --git a/editor/dependency_editor.h b/editor/dependency_editor.h index d43b12f1d2..0256f39979 100644 --- a/editor/dependency_editor.h +++ b/editor/dependency_editor.h @@ -60,9 +60,6 @@ class DependencyEditor : public AcceptDialog { void _update_file(); -protected: - static void _bind_methods(); - public: void edit(const String &p_path); DependencyEditor(); @@ -81,7 +78,6 @@ class DependencyEditorOwners : public AcceptDialog { void _fill_owners(EditorFileSystemDirectory *efsd); - static void _bind_methods(); void _list_rmb_clicked(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index); void _select_file(int p_idx); void _empty_clicked(const Vector2 &p_pos, MouseButton p_mouse_button_index); @@ -175,7 +171,6 @@ class OrphanResourcesDialog : public ConfirmationDialog { void _button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button); void refresh(); - static void _bind_methods(); public: void show(); diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp index 331dacf6ad..bf5b717c19 100644 --- a/editor/doc_tools.cpp +++ b/editor/doc_tools.cpp @@ -38,7 +38,7 @@ #include "core/io/marshalls.h" #include "core/io/resource_importer.h" #include "core/object/script_language.h" -#include "core/string/translation.h" +#include "core/string/translation_server.h" #include "editor/editor_settings.h" #include "editor/export/editor_export.h" #include "scene/resources/theme.h" diff --git a/editor/editor_asset_installer.cpp b/editor/editor_asset_installer.cpp index 33049b6bc2..b415c72c15 100644 --- a/editor/editor_asset_installer.cpp +++ b/editor/editor_asset_installer.cpp @@ -659,9 +659,6 @@ void EditorAssetInstaller::_notification(int p_what) { } } -void EditorAssetInstaller::_bind_methods() { -} - EditorAssetInstaller::EditorAssetInstaller() { VBoxContainer *vb = memnew(VBoxContainer); add_child(vb); diff --git a/editor/editor_asset_installer.h b/editor/editor_asset_installer.h index bc63bc9916..41f092f304 100644 --- a/editor/editor_asset_installer.h +++ b/editor/editor_asset_installer.h @@ -96,7 +96,6 @@ class EditorAssetInstaller : public ConfirmationDialog { protected: void _notification(int p_what); - static void _bind_methods(); public: void open_asset(const String &p_path, bool p_autoskip_toplevel = false); diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp index b62351256e..32b2133f23 100644 --- a/editor/editor_autoload_settings.cpp +++ b/editor/editor_autoload_settings.cpp @@ -457,7 +457,9 @@ void EditorAutoloadSettings::init_autoloads() { for (const AutoloadInfo &info : autoload_cache) { if (info.node && info.in_editor) { - callable_mp((Node *)get_tree()->get_root(), &Node::add_child).call_deferred(info.node, false, Node::INTERNAL_MODE_DISABLED); + // It's important to add the node without deferring because code in plugins or tool scripts + // could use the autoload node when they are enabled. + get_tree()->get_root()->add_child(info.node); } } } diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index feca12b409..02a95fd836 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -1779,7 +1779,9 @@ String EditorFileSystem::_get_global_script_class(const String &p_type, const St void EditorFileSystem::_update_file_icon_path(EditorFileSystemDirectory::FileInfo *file_info) { String icon_path; - if (file_info->script_class_icon_path.is_empty() && !file_info->deps.is_empty()) { + if (file_info->resource_script_class != StringName()) { + icon_path = EditorNode::get_editor_data().script_class_get_icon_path(file_info->resource_script_class); + } else if (file_info->script_class_icon_path.is_empty() && !file_info->deps.is_empty()) { const String &script_dep = file_info->deps[0]; // Assuming the first dependency is a script. const String &script_path = script_dep.contains("::") ? script_dep.get_slice("::", 2) : script_dep; if (!script_path.is_empty()) { diff --git a/editor/editor_locale_dialog.cpp b/editor/editor_locale_dialog.cpp index f8fd05bf1e..83f1c70c69 100644 --- a/editor/editor_locale_dialog.cpp +++ b/editor/editor_locale_dialog.cpp @@ -31,6 +31,7 @@ #include "editor_locale_dialog.h" #include "core/config/project_settings.h" +#include "core/string/translation_server.h" #include "editor/editor_undo_redo_manager.h" #include "editor/themes/editor_scale.h" #include "scene/gui/check_button.h" diff --git a/editor/editor_locale_dialog.h b/editor/editor_locale_dialog.h index 467861c36b..bc75e1df59 100644 --- a/editor/editor_locale_dialog.h +++ b/editor/editor_locale_dialog.h @@ -31,7 +31,6 @@ #ifndef EDITOR_LOCALE_DIALOG_H #define EDITOR_LOCALE_DIALOG_H -#include "core/string/translation.h" #include "scene/gui/dialogs.h" class Button; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index f24fa344ae..d1dffba2ab 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -42,7 +42,7 @@ #include "core/os/os.h" #include "core/os/time.h" #include "core/string/print_string.h" -#include "core/string/translation.h" +#include "core/string/translation_server.h" #include "core/version.h" #include "editor/editor_string_names.h" #include "main/main.h" diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index fdb4ec170b..19a4165041 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -129,9 +129,6 @@ void EditorPropertyText::set_placeholder(const String &p_string) { text->set_placeholder(p_string); } -void EditorPropertyText::_bind_methods() { -} - EditorPropertyText::EditorPropertyText() { HBoxContainer *hb = memnew(HBoxContainer); add_child(hb); @@ -219,9 +216,6 @@ void EditorPropertyMultilineText::_notification(int p_what) { } } -void EditorPropertyMultilineText::_bind_methods() { -} - EditorPropertyMultilineText::EditorPropertyMultilineText(bool p_expression) { HBoxContainer *hb = memnew(HBoxContainer); hb->add_theme_constant_override("separation", 0); @@ -343,9 +337,6 @@ void EditorPropertyTextEnum::setup(const Vector<String> &p_options, bool p_strin } } -void EditorPropertyTextEnum::_bind_methods() { -} - void EditorPropertyTextEnum::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: @@ -447,9 +438,6 @@ void EditorPropertyLocale::_locale_focus_exited() { _locale_selected(locale->get_text()); } -void EditorPropertyLocale::_bind_methods() { -} - EditorPropertyLocale::EditorPropertyLocale() { HBoxContainer *locale_hb = memnew(HBoxContainer); add_child(locale_hb); @@ -586,9 +574,6 @@ bool EditorPropertyPath::_can_drop_data_fw(const Point2 &p_point, const Variant return false; } -void EditorPropertyPath::_bind_methods() { -} - EditorPropertyPath::EditorPropertyPath() { HBoxContainer *path_hb = memnew(HBoxContainer); add_child(path_hb); @@ -637,9 +622,6 @@ void EditorPropertyClassName::_dialog_created() { update_property(); } -void EditorPropertyClassName::_bind_methods() { -} - EditorPropertyClassName::EditorPropertyClassName() { property = memnew(Button); property->set_clip_text(true); @@ -669,9 +651,6 @@ void EditorPropertyCheck::update_property() { checkbox->set_disabled(is_read_only()); } -void EditorPropertyCheck::_bind_methods() { -} - EditorPropertyCheck::EditorPropertyCheck() { checkbox = memnew(CheckBox); checkbox->set_text(TTR("On")); @@ -725,9 +704,6 @@ void EditorPropertyEnum::set_option_button_clip(bool p_enable) { options->set_clip_text(p_enable); } -void EditorPropertyEnum::_bind_methods() { -} - EditorPropertyEnum::EditorPropertyEnum() { options = memnew(OptionButton); options->set_clip_text(true); @@ -804,9 +780,6 @@ void EditorPropertyFlags::setup(const Vector<String> &p_options) { } } -void EditorPropertyFlags::_bind_methods() { -} - EditorPropertyFlags::EditorPropertyFlags() { vbox = memnew(VBoxContainer); add_child(vbox); @@ -1280,9 +1253,6 @@ void EditorPropertyLayers::_refresh_names() { setup(layer_type); } -void EditorPropertyLayers::_bind_methods() { -} - EditorPropertyLayers::EditorPropertyLayers() { HBoxContainer *hb = memnew(HBoxContainer); hb->set_clip_contents(true); @@ -1330,9 +1300,6 @@ void EditorPropertyInteger::update_property() { #endif } -void EditorPropertyInteger::_bind_methods() { -} - void EditorPropertyInteger::setup(int64_t p_min, int64_t p_max, int64_t p_step, bool p_hide_slider, bool p_allow_greater, bool p_allow_lesser, const String &p_suffix) { spin->set_min(p_min); spin->set_max(p_max); @@ -1385,9 +1352,6 @@ void EditorPropertyObjectID::setup(const String &p_base_type) { base_type = p_base_type; } -void EditorPropertyObjectID::_bind_methods() { -} - EditorPropertyObjectID::EditorPropertyObjectID() { edit = memnew(Button); add_child(edit); @@ -1413,9 +1377,6 @@ void EditorPropertySignal::update_property() { edit->set_icon(get_editor_theme_icon(SNAME("Signals"))); } -void EditorPropertySignal::_bind_methods() { -} - EditorPropertySignal::EditorPropertySignal() { edit = memnew(Button); add_child(edit); @@ -1435,9 +1396,6 @@ void EditorPropertyCallable::update_property() { edit->set_icon(get_editor_theme_icon(SNAME("Callable"))); } -void EditorPropertyCallable::_bind_methods() { -} - EditorPropertyCallable::EditorPropertyCallable() { edit = memnew(Button); add_child(edit); @@ -1465,9 +1423,6 @@ void EditorPropertyFloat::update_property() { spin->set_value_no_signal(val); } -void EditorPropertyFloat::_bind_methods() { -} - void EditorPropertyFloat::setup(double p_min, double p_max, double p_step, bool p_hide_slider, bool p_exp_range, bool p_greater, bool p_lesser, const String &p_suffix, bool p_radians_as_degrees) { radians_as_degrees = p_radians_as_degrees; spin->set_min(p_min); @@ -1675,9 +1630,6 @@ void EditorPropertyEasing::_notification(int p_what) { } } -void EditorPropertyEasing::_bind_methods() { -} - EditorPropertyEasing::EditorPropertyEasing() { easing_draw = memnew(Control); easing_draw->connect(SceneStringName(draw), callable_mp(this, &EditorPropertyEasing::_draw_easing)); @@ -1740,9 +1692,6 @@ void EditorPropertyRect2::_notification(int p_what) { } } -void EditorPropertyRect2::_bind_methods() { -} - void EditorPropertyRect2::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) { for (int i = 0; i < 4; i++) { spin[i]->set_min(p_min); @@ -1837,9 +1786,6 @@ void EditorPropertyRect2i::_notification(int p_what) { } } -void EditorPropertyRect2i::_bind_methods() { -} - void EditorPropertyRect2i::setup(int p_min, int p_max, const String &p_suffix) { for (int i = 0; i < 4; i++) { spin[i]->set_min(p_min); @@ -1933,9 +1879,6 @@ void EditorPropertyPlane::_notification(int p_what) { } } -void EditorPropertyPlane::_bind_methods() { -} - void EditorPropertyPlane::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) { for (int i = 0; i < 4; i++) { spin[i]->set_min(p_min); @@ -2085,9 +2028,6 @@ void EditorPropertyQuaternion::_notification(int p_what) { } } -void EditorPropertyQuaternion::_bind_methods() { -} - void EditorPropertyQuaternion::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix, bool p_hide_editor) { for (int i = 0; i < 4; i++) { spin[i]->set_min(p_min); @@ -2232,9 +2172,6 @@ void EditorPropertyAABB::_notification(int p_what) { } } -void EditorPropertyAABB::_bind_methods() { -} - void EditorPropertyAABB::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) { for (int i = 0; i < 6; i++) { spin[i]->set_min(p_min); @@ -2313,9 +2250,6 @@ void EditorPropertyTransform2D::_notification(int p_what) { } } -void EditorPropertyTransform2D::_bind_methods() { -} - void EditorPropertyTransform2D::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) { for (int i = 0; i < 6; i++) { spin[i]->set_min(p_min); @@ -2398,9 +2332,6 @@ void EditorPropertyBasis::_notification(int p_what) { } } -void EditorPropertyBasis::_bind_methods() { -} - void EditorPropertyBasis::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) { for (int i = 0; i < 9; i++) { spin[i]->set_min(p_min); @@ -2490,9 +2421,6 @@ void EditorPropertyTransform3D::_notification(int p_what) { } } -void EditorPropertyTransform3D::_bind_methods() { -} - void EditorPropertyTransform3D::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) { for (int i = 0; i < 12; i++) { spin[i]->set_min(p_min); @@ -2590,9 +2518,6 @@ void EditorPropertyProjection::_notification(int p_what) { } } -void EditorPropertyProjection::_bind_methods() { -} - void EditorPropertyProjection::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) { for (int i = 0; i < 16; i++) { spin[i]->set_min(p_min); diff --git a/editor/editor_properties.h b/editor/editor_properties.h index e9e788ab7b..2ec78cdb44 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -66,7 +66,6 @@ class EditorPropertyText : public EditorProperty { protected: virtual void _set_read_only(bool p_read_only) override; - static void _bind_methods(); public: void set_string_name(bool p_enabled); @@ -92,7 +91,6 @@ class EditorPropertyMultilineText : public EditorProperty { protected: virtual void _set_read_only(bool p_read_only) override; void _notification(int p_what); - static void _bind_methods(); public: virtual void update_property() override; @@ -126,7 +124,6 @@ class EditorPropertyTextEnum : public EditorProperty { protected: virtual void _set_read_only(bool p_read_only) override; - static void _bind_methods(); void _notification(int p_what); public: @@ -153,7 +150,6 @@ class EditorPropertyPath : public EditorProperty { protected: virtual void _set_read_only(bool p_read_only) override; - static void _bind_methods(); void _notification(int p_what); public: @@ -174,7 +170,6 @@ class EditorPropertyLocale : public EditorProperty { void _locale_focus_exited(); protected: - static void _bind_methods(); void _notification(int p_what); public: @@ -196,7 +191,6 @@ private: protected: virtual void _set_read_only(bool p_read_only) override; - static void _bind_methods(); public: void setup(const String &p_base_type, const String &p_selected_type); @@ -212,7 +206,6 @@ class EditorPropertyCheck : public EditorProperty { protected: virtual void _set_read_only(bool p_read_only) override; - static void _bind_methods(); public: virtual void update_property() override; @@ -227,7 +220,6 @@ class EditorPropertyEnum : public EditorProperty { protected: virtual void _set_read_only(bool p_read_only) override; - static void _bind_methods(); public: void setup(const Vector<String> &p_options); @@ -246,7 +238,6 @@ class EditorPropertyFlags : public EditorProperty { protected: virtual void _set_read_only(bool p_read_only) override; - static void _bind_methods(); public: void setup(const Vector<String> &p_options); @@ -327,7 +318,6 @@ private: protected: void _notification(int p_what); virtual void _set_read_only(bool p_read_only) override; - static void _bind_methods(); public: void setup(LayerType p_layer_type); @@ -344,7 +334,6 @@ class EditorPropertyInteger : public EditorProperty { protected: virtual void _set_read_only(bool p_read_only) override; - static void _bind_methods(); public: virtual void update_property() override; @@ -360,7 +349,6 @@ class EditorPropertyObjectID : public EditorProperty { protected: virtual void _set_read_only(bool p_read_only) override; - static void _bind_methods(); public: virtual void update_property() override; @@ -374,9 +362,6 @@ class EditorPropertySignal : public EditorProperty { String base_type; void _edit_pressed(); -protected: - static void _bind_methods(); - public: virtual void update_property() override; EditorPropertySignal(); @@ -387,9 +372,6 @@ class EditorPropertyCallable : public EditorProperty { Button *edit = nullptr; String base_type; -protected: - static void _bind_methods(); - public: virtual void update_property() override; EditorPropertyCallable(); @@ -403,7 +385,6 @@ class EditorPropertyFloat : public EditorProperty { protected: virtual void _set_read_only(bool p_read_only) override; - static void _bind_methods(); public: virtual void update_property() override; @@ -445,7 +426,6 @@ class EditorPropertyEasing : public EditorProperty { protected: virtual void _set_read_only(bool p_read_only) override; - static void _bind_methods(); public: virtual void update_property() override; @@ -461,7 +441,6 @@ class EditorPropertyRect2 : public EditorProperty { protected: virtual void _set_read_only(bool p_read_only) override; void _notification(int p_what); - static void _bind_methods(); public: virtual void update_property() override; @@ -477,7 +456,6 @@ class EditorPropertyRect2i : public EditorProperty { protected: virtual void _set_read_only(bool p_read_only) override; void _notification(int p_what); - static void _bind_methods(); public: virtual void update_property() override; @@ -493,7 +471,6 @@ class EditorPropertyPlane : public EditorProperty { protected: virtual void _set_read_only(bool p_read_only) override; void _notification(int p_what); - static void _bind_methods(); public: virtual void update_property() override; @@ -526,7 +503,6 @@ class EditorPropertyQuaternion : public EditorProperty { protected: virtual void _set_read_only(bool p_read_only) override; void _notification(int p_what); - static void _bind_methods(); public: virtual void update_property() override; @@ -542,7 +518,6 @@ class EditorPropertyAABB : public EditorProperty { protected: virtual void _set_read_only(bool p_read_only) override; void _notification(int p_what); - static void _bind_methods(); public: virtual void update_property() override; @@ -558,7 +533,6 @@ class EditorPropertyTransform2D : public EditorProperty { protected: virtual void _set_read_only(bool p_read_only) override; void _notification(int p_what); - static void _bind_methods(); public: virtual void update_property() override; @@ -574,7 +548,6 @@ class EditorPropertyBasis : public EditorProperty { protected: virtual void _set_read_only(bool p_read_only) override; void _notification(int p_what); - static void _bind_methods(); public: virtual void update_property() override; @@ -590,7 +563,6 @@ class EditorPropertyTransform3D : public EditorProperty { protected: virtual void _set_read_only(bool p_read_only) override; void _notification(int p_what); - static void _bind_methods(); public: virtual void update_property() override; @@ -607,7 +579,6 @@ class EditorPropertyProjection : public EditorProperty { protected: virtual void _set_read_only(bool p_read_only) override; void _notification(int p_what); - static void _bind_methods(); public: virtual void update_property() override; diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index 9aa2390230..d58d0520cc 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -836,9 +836,6 @@ bool EditorPropertyArray::is_colored(ColorationMode p_mode) { return p_mode == COLORATION_CONTAINER_RESOURCE; } -void EditorPropertyArray::_bind_methods() { -} - EditorPropertyArray::EditorPropertyArray() { object.instantiate(); page_length = int(EDITOR_GET("interface/inspector/max_array_dictionary_items_per_page")); @@ -1166,9 +1163,6 @@ void EditorPropertyDictionary::_page_changed(int p_page) { update_property(); } -void EditorPropertyDictionary::_bind_methods() { -} - bool EditorPropertyDictionary::is_colored(ColorationMode p_mode) { return p_mode == COLORATION_CONTAINER_RESOURCE; } @@ -1385,9 +1379,6 @@ void EditorPropertyLocalizableString::_page_changed(int p_page) { update_property(); } -void EditorPropertyLocalizableString::_bind_methods() { -} - EditorPropertyLocalizableString::EditorPropertyLocalizableString() { object.instantiate(); page_length = int(EDITOR_GET("interface/inspector/max_array_dictionary_items_per_page")); diff --git a/editor/editor_properties_array_dict.h b/editor/editor_properties_array_dict.h index acde766754..024c04956f 100644 --- a/editor/editor_properties_array_dict.h +++ b/editor/editor_properties_array_dict.h @@ -150,7 +150,6 @@ protected: bool updating = false; bool dropping = false; - static void _bind_methods(); void _notification(int p_what); virtual void _add_element(); @@ -234,7 +233,6 @@ class EditorPropertyDictionary : public EditorProperty { void _object_id_selected(const StringName &p_property, ObjectID p_id); protected: - static void _bind_methods(); void _notification(int p_what); public: @@ -271,7 +269,6 @@ class EditorPropertyLocalizableString : public EditorProperty { void _object_id_selected(const StringName &p_property, ObjectID p_id); protected: - static void _bind_methods(); void _notification(int p_what); public: diff --git a/editor/editor_property_name_processor.cpp b/editor/editor_property_name_processor.cpp index f23cab676c..7b45ba6a9f 100644 --- a/editor/editor_property_name_processor.cpp +++ b/editor/editor_property_name_processor.cpp @@ -30,7 +30,7 @@ #include "editor_property_name_processor.h" -#include "core/string/translation.h" +#include "core/string/translation_server.h" #include "editor_settings.h" EditorPropertyNameProcessor *EditorPropertyNameProcessor::singleton = nullptr; diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 0956d12236..c3a939c007 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -43,7 +43,7 @@ #include "core/object/class_db.h" #include "core/os/keyboard.h" #include "core/os/os.h" -#include "core/string/translation.h" +#include "core/string/translation_server.h" #include "core/version.h" #include "editor/editor_node.h" #include "editor/editor_paths.h" @@ -440,6 +440,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { #endif EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/font_subpixel_positioning", 1, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel") EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/font_disable_embedded_bitmaps", true, ""); + EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/font_allow_msdf", true, "") EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "interface/editor/main_font", "", "*.ttf,*.otf,*.woff,*.woff2,*.pfb,*.pfm") EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "interface/editor/main_font_bold", "", "*.ttf,*.otf,*.woff,*.woff2,*.pfb,*.pfm") diff --git a/editor/editor_translation.cpp b/editor/editor_translation.cpp index 77154ec344..4654d41082 100644 --- a/editor/editor_translation.cpp +++ b/editor/editor_translation.cpp @@ -33,6 +33,7 @@ #include "core/io/compression.h" #include "core/io/file_access_memory.h" #include "core/io/translation_loader_po.h" +#include "core/string/translation_server.h" #include "editor/doc_translations.gen.h" #include "editor/editor_translations.gen.h" #include "editor/extractable_translations.gen.h" diff --git a/editor/export/export_template_manager.cpp b/editor/export/export_template_manager.cpp index a6f6c87ee1..0caf0ee066 100644 --- a/editor/export/export_template_manager.cpp +++ b/editor/export/export_template_manager.cpp @@ -859,9 +859,6 @@ void ExportTemplateManager::_notification(int p_what) { } } -void ExportTemplateManager::_bind_methods() { -} - ExportTemplateManager::ExportTemplateManager() { set_title(TTR("Export Template Manager")); set_hide_on_ok(false); diff --git a/editor/export/export_template_manager.h b/editor/export/export_template_manager.h index a00d874580..b1c5855878 100644 --- a/editor/export/export_template_manager.h +++ b/editor/export/export_template_manager.h @@ -119,7 +119,6 @@ class ExportTemplateManager : public AcceptDialog { protected: void _notification(int p_what); - static void _bind_methods(); public: static String get_android_build_directory(const Ref<EditorExportPreset> &p_preset); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index d4bd97a393..b780d42119 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -953,7 +953,8 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) { files->set_max_columns(1); files->set_max_text_lines(1); files->set_fixed_column_width(0); - files->set_fixed_icon_size(Size2()); + const int icon_size = get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor)); + files->set_fixed_icon_size(Size2(icon_size, icon_size)); } Ref<Texture2D> folder_icon = (use_thumbnails) ? folder_thumbnail : get_theme_icon(SNAME("folder"), SNAME("FileDialog")); diff --git a/editor/gui/editor_object_selector.cpp b/editor/gui/editor_object_selector.cpp index 5b303760b0..bfd7e18de1 100644 --- a/editor/gui/editor_object_selector.cpp +++ b/editor/gui/editor_object_selector.cpp @@ -216,9 +216,6 @@ void EditorObjectSelector::_notification(int p_what) { } } -void EditorObjectSelector::_bind_methods() { -} - EditorObjectSelector::EditorObjectSelector(EditorSelectionHistory *p_history) { history = p_history; diff --git a/editor/gui/editor_object_selector.h b/editor/gui/editor_object_selector.h index 72ff285cf6..60d6df488b 100644 --- a/editor/gui/editor_object_selector.h +++ b/editor/gui/editor_object_selector.h @@ -58,7 +58,6 @@ class EditorObjectSelector : public Button { protected: void _notification(int p_what); - static void _bind_methods(); public: virtual Size2 get_minimum_size() const override; diff --git a/editor/gui/editor_spin_slider.cpp b/editor/gui/editor_spin_slider.cpp index 4e8d6d63bf..9f9bdb37b3 100644 --- a/editor/gui/editor_spin_slider.cpp +++ b/editor/gui/editor_spin_slider.cpp @@ -614,13 +614,13 @@ void EditorSpinSlider::_value_focus_exited() { // -> TAB was pressed // -> modal_close was not called // -> need to close/hide manually - if (value_input_closed_frame != Engine::get_singleton()->get_frames_drawn()) { + if (!is_visible_in_tree() || value_input_closed_frame != Engine::get_singleton()->get_frames_drawn()) { + // Hidden or something else took focus. if (value_input_popup) { value_input_popup->hide(); } - //tab was pressed } else { - //enter, click, esc + // Enter or Esc was pressed. grab_focus(); } diff --git a/editor/icons/Panels1.svg b/editor/icons/Panels1.svg index 33a511a22b..c37d974058 100644 --- a/editor/icons/Panels1.svg +++ b/editor/icons/Panels1.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="m0 0h16v16h-16z"/></svg>
\ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M2 0a2 2 0 0 0-2 2v12.002a2 2 0 0 0 2 2h12.002a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm0 4h12v10H2z"/></svg>
\ No newline at end of file diff --git a/editor/icons/Panels2.svg b/editor/icons/Panels2.svg index 2621c36c57..673ae62aec 100644 --- a/editor/icons/Panels2.svg +++ b/editor/icons/Panels2.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="m0 0v7h16v-7zm0 9v7h16v-7z"/></svg>
\ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm0 4h12v4H2zm12 6v4H2v-4z"/></svg>
\ No newline at end of file diff --git a/editor/icons/Panels2Alt.svg b/editor/icons/Panels2Alt.svg index 0a6c6b6c04..e2f27e415e 100644 --- a/editor/icons/Panels2Alt.svg +++ b/editor/icons/Panels2Alt.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="m0 0v16h7v-16zm9 0v16h7v-16z"/></svg>
\ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm0 4h5v10H2zm7 0h5v10H9z"/></svg>
\ No newline at end of file diff --git a/editor/icons/Panels3.svg b/editor/icons/Panels3.svg index 5ee8b7f120..d5cf987c41 100644 --- a/editor/icons/Panels3.svg +++ b/editor/icons/Panels3.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="m0 0v7h16v-7zm0 9v7h7v-7zm9 0v7h7v-7z"/></svg>
\ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm0 4h12v4H2zm0 6h5v4H2zm7 0h5v4H9z"/></svg>
\ No newline at end of file diff --git a/editor/icons/Panels3Alt.svg b/editor/icons/Panels3Alt.svg index dc373c6fc5..ea6745df6f 100644 --- a/editor/icons/Panels3Alt.svg +++ b/editor/icons/Panels3Alt.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="m0 0v7h7v-7zm9 0v16h7v-16zm-9 9v7h7v-7z"/></svg>
\ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm0 4h5v4H2zm7 0h5v10H9zm-7 6h5v4H2z"/></svg>
\ No newline at end of file diff --git a/editor/icons/Panels4.svg b/editor/icons/Panels4.svg index e0ff57de4d..407dfba1b6 100644 --- a/editor/icons/Panels4.svg +++ b/editor/icons/Panels4.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="m0 0v7h7v-7zm9 0v7h7v-7zm-9 9v7h7v-7zm9 0v7h7v-7z"/></svg>
\ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm0 4h5v4H2zm7 0h5v4H9zm-7 6h5v4H2zm7 0h5v4H9z"/></svg>
\ No newline at end of file diff --git a/editor/import/3d/resource_importer_obj.cpp b/editor/import/3d/resource_importer_obj.cpp index 2fbf564ae8..f3770adcb7 100644 --- a/editor/import/3d/resource_importer_obj.cpp +++ b/editor/import/3d/resource_importer_obj.cpp @@ -202,7 +202,7 @@ static Error _parse_material_library(const String &p_path, HashMap<String, Ref<S return OK; } -static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes, bool p_single_mesh, bool p_generate_tangents, bool p_optimize, Vector3 p_scale_mesh, Vector3 p_offset_mesh, bool p_disable_compression, List<String> *r_missing_deps) { +static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes, bool p_single_mesh, bool p_generate_tangents, Vector3 p_scale_mesh, Vector3 p_offset_mesh, bool p_disable_compression, List<String> *r_missing_deps) { Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Couldn't open OBJ file '%s', it may not exist or not be readable.", p_path)); @@ -512,7 +512,7 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes, Node *EditorOBJImporter::import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err) { List<Ref<ImporterMesh>> meshes; - Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, false, Vector3(1, 1, 1), Vector3(0, 0, 0), p_flags & IMPORT_FORCE_DISABLE_MESH_COMPRESSION, r_missing_deps); + Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, Vector3(1, 1, 1), Vector3(0, 0, 0), p_flags & IMPORT_FORCE_DISABLE_MESH_COMPRESSION, r_missing_deps); if (err != OK) { if (r_err) { @@ -583,7 +583,6 @@ void ResourceImporterOBJ::get_import_options(const String &p_path, List<ImportOp r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate_tangents"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "scale_mesh"), Vector3(1, 1, 1))); r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "offset_mesh"), Vector3(0, 0, 0))); - r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "optimize_mesh"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force_disable_mesh_compression"), false)); } @@ -594,7 +593,7 @@ bool ResourceImporterOBJ::get_option_visibility(const String &p_path, const Stri Error ResourceImporterOBJ::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { List<Ref<ImporterMesh>> meshes; - Error err = _parse_obj(p_source_file, meshes, true, p_options["generate_tangents"], p_options["optimize_mesh"], p_options["scale_mesh"], p_options["offset_mesh"], p_options["force_disable_mesh_compression"], nullptr); + Error err = _parse_obj(p_source_file, meshes, true, p_options["generate_tangents"], p_options["scale_mesh"], p_options["offset_mesh"], p_options["force_disable_mesh_compression"], nullptr); ERR_FAIL_COND_V(err != OK, err); ERR_FAIL_COND_V(meshes.size() != 1, ERR_BUG); diff --git a/editor/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp index df4026195c..8ad8e6201e 100644 --- a/editor/import/3d/resource_importer_scene.cpp +++ b/editor/import/3d/resource_importer_scene.cpp @@ -2013,6 +2013,7 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/lods", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "lods/normal_split_angle", PROPERTY_HINT_RANGE, "0,180,0.1,degrees"), 25.0f)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "lods/normal_merge_angle", PROPERTY_HINT_RANGE, "0,180,0.1,degrees"), 60.0f)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "lods/raycast_normals", PROPERTY_HINT_NONE, ""), false)); } break; case INTERNAL_IMPORT_CATEGORY_MATERIAL: { r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "use_external/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false)); @@ -2440,6 +2441,7 @@ Node *ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_ bool generate_lods = p_generate_lods; float split_angle = 25.0f; float merge_angle = 60.0f; + bool raycast_normals = false; bool create_shadow_meshes = p_create_shadow_meshes; bool bake_lightmaps = p_light_bake_mode == LIGHT_BAKE_STATIC_LIGHTMAPS; String save_to_file; @@ -2494,6 +2496,10 @@ Node *ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_ merge_angle = mesh_settings["lods/normal_merge_angle"]; } + if (mesh_settings.has("lods/raycast_normals")) { + raycast_normals = mesh_settings["lods/raycast_normals"]; + } + if (bool(mesh_settings.get("save_to_file/enabled", false))) { save_to_file = mesh_settings.get("save_to_file/path", String()); if (!save_to_file.is_resource_file()) { @@ -2538,9 +2544,11 @@ Node *ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_ } } + src_mesh_node->get_mesh()->optimize_indices_for_cache(); + if (generate_lods) { Array skin_pose_transform_array = _get_skinned_pose_transforms(src_mesh_node); - src_mesh_node->get_mesh()->generate_lods(merge_angle, split_angle, skin_pose_transform_array); + src_mesh_node->get_mesh()->generate_lods(merge_angle, split_angle, skin_pose_transform_array, raycast_normals); } if (create_shadow_meshes) { diff --git a/editor/import/3d/scene_import_settings.cpp b/editor/import/3d/scene_import_settings.cpp index 6b741b7dd7..ed3eaa94c1 100644 --- a/editor/import/3d/scene_import_settings.cpp +++ b/editor/import/3d/scene_import_settings.cpp @@ -117,7 +117,9 @@ class SceneImportSettingsData : public Object { ERR_FAIL_NULL(settings); if (r_option.name == "rest_pose/load_pose") { if (!settings->has("rest_pose/load_pose") || int((*settings)["rest_pose/load_pose"]) != 2) { - (*settings)["rest_pose/external_animation_library"] = Variant(); + if (settings->has("rest_pose/external_animation_library")) { + (*settings)["rest_pose/external_animation_library"] = Variant(); + } } } if (r_option.name == "rest_pose/selected_animation") { @@ -134,7 +136,10 @@ class SceneImportSettingsData : public Object { } } break; case 2: { - Object *res = (*settings)["rest_pose/external_animation_library"]; + Object *res = nullptr; + if (settings->has("rest_pose/external_animation_library")) { + res = (*settings)["rest_pose/external_animation_library"]; + } Ref<Animation> anim(res); Ref<AnimationLibrary> library(res); if (anim.is_valid()) { diff --git a/editor/import/dynamic_font_import_settings.cpp b/editor/import/dynamic_font_import_settings.cpp index 00ce3d6a7a..590e3a9ede 100644 --- a/editor/import/dynamic_font_import_settings.cpp +++ b/editor/import/dynamic_font_import_settings.cpp @@ -31,6 +31,7 @@ #include "dynamic_font_import_settings.h" #include "core/config/project_settings.h" +#include "core/string/translation_server.h" #include "editor/editor_file_system.h" #include "editor/editor_inspector.h" #include "editor/editor_locale_dialog.h" diff --git a/editor/import/resource_importer_csv_translation.cpp b/editor/import/resource_importer_csv_translation.cpp index d2705ac98a..c181011402 100644 --- a/editor/import/resource_importer_csv_translation.cpp +++ b/editor/import/resource_importer_csv_translation.cpp @@ -33,7 +33,7 @@ #include "core/io/file_access.h" #include "core/io/resource_saver.h" #include "core/string/optimized_translation.h" -#include "core/string/translation.h" +#include "core/string/translation_server.h" String ResourceImporterCSVTranslation::get_importer_name() const { return "csv_translation"; diff --git a/editor/import/resource_importer_dynamic_font.cpp b/editor/import/resource_importer_dynamic_font.cpp index c52f53146e..fa222b2790 100644 --- a/editor/import/resource_importer_dynamic_font.cpp +++ b/editor/import/resource_importer_dynamic_font.cpp @@ -118,7 +118,7 @@ void ResourceImporterDynamicFont::get_import_options(const String &p_path, List< r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "allow_system_fallback"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force_autohinter"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), 1)); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel"), 1)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel,Auto (Except Pixel Fonts)"), 4)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), 0.0)); r_options->push_back(ImportOption(PropertyInfo(Variant::NIL, "Fallbacks", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP), Variant())); @@ -176,11 +176,44 @@ Error ResourceImporterDynamicFont::import(const String &p_source_file, const Str font->set_fixed_size(0); font->set_force_autohinter(autohinter); font->set_allow_system_fallback(allow_system_fallback); - font->set_subpixel_positioning((TextServer::SubpixelPositioning)subpixel_positioning); font->set_hinting((TextServer::Hinting)hinting); font->set_oversampling(oversampling); font->set_fallbacks(fallbacks); + if (subpixel_positioning == 4 /* Auto (Except Pixel Fonts) */) { + PackedInt32Array glyphs = TS->font_get_supported_glyphs(font->get_rids()[0]); + bool is_pixel = true; + for (int32_t gl : glyphs) { + Dictionary ct = TS->font_get_glyph_contours(font->get_rids()[0], 16, gl); + PackedInt32Array contours = ct["contours"]; + PackedVector3Array points = ct["points"]; + int prev_start = 0; + for (int i = 0; i < contours.size(); i++) { + for (int j = prev_start; j <= contours[i]; j++) { + int next_point = (j < contours[i]) ? (j + 1) : prev_start; + if ((points[j].z != TextServer::CONTOUR_CURVE_TAG_ON) || (!Math::is_equal_approx(points[j].x, points[next_point].x) && !Math::is_equal_approx(points[j].y, points[next_point].y))) { + is_pixel = false; + break; + } + } + prev_start = contours[i] + 1; + if (!is_pixel) { + break; + } + } + if (!is_pixel) { + break; + } + } + if (is_pixel && !glyphs.is_empty()) { + print_line(vformat("%s: Pixel font detected, disabling subpixel positioning.", p_source_file)); + subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_DISABLED; + } else { + subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO; + } + } + font->set_subpixel_positioning((TextServer::SubpixelPositioning)subpixel_positioning); + Dictionary langs = p_options["language_support"]; for (int i = 0; i < langs.size(); i++) { String key = langs.get_key_at_index(i); diff --git a/editor/import/resource_importer_imagefont.cpp b/editor/import/resource_importer_imagefont.cpp index f1b6d3a6ac..f01381904d 100644 --- a/editor/import/resource_importer_imagefont.cpp +++ b/editor/import/resource_importer_imagefont.cpp @@ -293,18 +293,20 @@ Error ResourceImporterImageFont::import(const String &p_source_file, const Strin } String from_tokens; for (int i = 0; i < kp_tokens[0].length(); i++) { - if (i <= kp_tokens[0].length() - 6 && kp_tokens[0][i] == '\\' && kp_tokens[0][i + 1] == 'u') { + if (i <= kp_tokens[0].length() - 6 && kp_tokens[0][i] == '\\' && kp_tokens[0][i + 1] == 'u' && is_hex_digit(kp_tokens[0][i + 2]) && is_hex_digit(kp_tokens[0][i + 3]) && is_hex_digit(kp_tokens[0][i + 4]) && is_hex_digit(kp_tokens[0][i + 5])) { char32_t charcode = kp_tokens[0].substr(i + 2, 4).hex_to_int(); from_tokens += charcode; + i += 5; } else { from_tokens += kp_tokens[0][i]; } } String to_tokens; for (int i = 0; i < kp_tokens[1].length(); i++) { - if (i <= kp_tokens[1].length() - 6 && kp_tokens[1][i] == '\\' && kp_tokens[1][i + 1] == 'u') { + if (i <= kp_tokens[1].length() - 6 && kp_tokens[1][i] == '\\' && kp_tokens[1][i + 1] == 'u' && is_hex_digit(kp_tokens[1][i + 2]) && is_hex_digit(kp_tokens[1][i + 3]) && is_hex_digit(kp_tokens[1][i + 4]) && is_hex_digit(kp_tokens[1][i + 5])) { char32_t charcode = kp_tokens[1].substr(i + 2, 4).hex_to_int(); to_tokens += charcode; + i += 5; } else { to_tokens += kp_tokens[1][i]; } diff --git a/editor/localization_editor.cpp b/editor/localization_editor.cpp index 11d1b3e089..6bc3a27a95 100644 --- a/editor/localization_editor.cpp +++ b/editor/localization_editor.cpp @@ -31,7 +31,7 @@ #include "localization_editor.h" #include "core/config/project_settings.h" -#include "core/string/translation.h" +#include "core/string/translation_server.h" #include "editor/editor_translation_parser.h" #include "editor/editor_undo_redo_manager.h" #include "editor/filesystem_dock.h" diff --git a/editor/node_dock.cpp b/editor/node_dock.cpp index 0c79e5d747..0da8d8291f 100644 --- a/editor/node_dock.cpp +++ b/editor/node_dock.cpp @@ -48,9 +48,6 @@ void NodeDock::show_connections() { connections->show(); } -void NodeDock::_bind_methods() { -} - void NodeDock::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: diff --git a/editor/node_dock.h b/editor/node_dock.h index e9dcc41d48..adf378bd06 100644 --- a/editor/node_dock.h +++ b/editor/node_dock.h @@ -55,7 +55,6 @@ public: static NodeDock *get_singleton() { return singleton; } protected: - static void _bind_methods(); void _notification(int p_what); public: diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp index f73c494b25..60d808952e 100644 --- a/editor/plugins/abstract_polygon_2d_editor.cpp +++ b/editor/plugins/abstract_polygon_2d_editor.cpp @@ -620,9 +620,6 @@ void AbstractPolygon2DEditor::edit(Node *p_polygon) { canvas_item_editor->update_viewport(); } -void AbstractPolygon2DEditor::_bind_methods() { -} - void AbstractPolygon2DEditor::remove_point(const Vertex &p_vertex) { EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); Vector<Vector2> vertices = _get_polygon(p_vertex.polygon); diff --git a/editor/plugins/abstract_polygon_2d_editor.h b/editor/plugins/abstract_polygon_2d_editor.h index 42170d9ffd..66d4e1b7ef 100644 --- a/editor/plugins/abstract_polygon_2d_editor.h +++ b/editor/plugins/abstract_polygon_2d_editor.h @@ -109,7 +109,6 @@ protected: void _notification(int p_what); void _node_removed(Node *p_node); - static void _bind_methods(); void remove_point(const Vertex &p_vertex); Vertex get_active_point() const; diff --git a/editor/plugins/animation_tree_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp index 757d410b78..7906b50c2c 100644 --- a/editor/plugins/animation_tree_editor_plugin.cpp +++ b/editor/plugins/animation_tree_editor_plugin.cpp @@ -199,9 +199,6 @@ void AnimationTreeEditor::_notification(int p_what) { } } -void AnimationTreeEditor::_bind_methods() { -} - AnimationTreeEditor *AnimationTreeEditor::singleton = nullptr; void AnimationTreeEditor::add_plugin(AnimationTreeNodeEditorPlugin *p_editor) { diff --git a/editor/plugins/animation_tree_editor_plugin.h b/editor/plugins/animation_tree_editor_plugin.h index 8dc820695a..a234af6874 100644 --- a/editor/plugins/animation_tree_editor_plugin.h +++ b/editor/plugins/animation_tree_editor_plugin.h @@ -72,7 +72,6 @@ class AnimationTreeEditor : public VBoxContainer { protected: void _notification(int p_what); void _node_removed(Node *p_node); - static void _bind_methods(); static AnimationTreeEditor *singleton; diff --git a/editor/plugins/bone_map_editor_plugin.cpp b/editor/plugins/bone_map_editor_plugin.cpp index c00436b01f..015dfdbca5 100644 --- a/editor/plugins/bone_map_editor_plugin.cpp +++ b/editor/plugins/bone_map_editor_plugin.cpp @@ -252,9 +252,6 @@ StringName BonePicker::get_selected_bone() { return selected->get_text(0); } -void BonePicker::_bind_methods() { -} - void BonePicker::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { diff --git a/editor/plugins/bone_map_editor_plugin.h b/editor/plugins/bone_map_editor_plugin.h index f3aa2fc84d..988ad40ec4 100644 --- a/editor/plugins/bone_map_editor_plugin.h +++ b/editor/plugins/bone_map_editor_plugin.h @@ -122,8 +122,6 @@ public: protected: void _notification(int p_what); - static void _bind_methods(); - void _confirm(); private: diff --git a/editor/plugins/camera_3d_editor_plugin.cpp b/editor/plugins/camera_3d_editor_plugin.cpp index 62b40043c1..f4116ed364 100644 --- a/editor/plugins/camera_3d_editor_plugin.cpp +++ b/editor/plugins/camera_3d_editor_plugin.cpp @@ -46,9 +46,6 @@ void Camera3DEditor::_pressed() { Node3DEditor::get_singleton()->set_custom_camera(sn); } -void Camera3DEditor::_bind_methods() { -} - void Camera3DEditor::edit(Node *p_camera) { node = p_camera; diff --git a/editor/plugins/camera_3d_editor_plugin.h b/editor/plugins/camera_3d_editor_plugin.h index 2e4d8a1ee3..1c6838aa02 100644 --- a/editor/plugins/camera_3d_editor_plugin.h +++ b/editor/plugins/camera_3d_editor_plugin.h @@ -45,7 +45,6 @@ class Camera3DEditor : public Control { protected: void _node_removed(Node *p_node); - static void _bind_methods(); public: void edit(Node *p_camera); diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.cpp b/editor/plugins/cpu_particles_2d_editor_plugin.cpp index 92c0fd847b..4869a202d7 100644 --- a/editor/plugins/cpu_particles_2d_editor_plugin.cpp +++ b/editor/plugins/cpu_particles_2d_editor_plugin.cpp @@ -258,9 +258,6 @@ void CPUParticles2DEditorPlugin::_notification(int p_what) { } } -void CPUParticles2DEditorPlugin::_bind_methods() { -} - CPUParticles2DEditorPlugin::CPUParticles2DEditorPlugin() { particles = nullptr; diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.h b/editor/plugins/cpu_particles_2d_editor_plugin.h index 4d59c9981e..645e6d8345 100644 --- a/editor/plugins/cpu_particles_2d_editor_plugin.h +++ b/editor/plugins/cpu_particles_2d_editor_plugin.h @@ -79,7 +79,6 @@ class CPUParticles2DEditorPlugin : public EditorPlugin { protected: void _notification(int p_what); - static void _bind_methods(); public: virtual String get_name() const override { return "CPUParticles2D"; } diff --git a/editor/plugins/cpu_particles_3d_editor_plugin.cpp b/editor/plugins/cpu_particles_3d_editor_plugin.cpp index be2847d1d7..69f287c134 100644 --- a/editor/plugins/cpu_particles_3d_editor_plugin.cpp +++ b/editor/plugins/cpu_particles_3d_editor_plugin.cpp @@ -157,9 +157,6 @@ void CPUParticles3DEditor::_generate_emission_points() { } } -void CPUParticles3DEditor::_bind_methods() { -} - CPUParticles3DEditor::CPUParticles3DEditor() { particles_editor_hb = memnew(HBoxContainer); Node3DEditor::get_singleton()->add_control_to_menu_panel(particles_editor_hb); diff --git a/editor/plugins/cpu_particles_3d_editor_plugin.h b/editor/plugins/cpu_particles_3d_editor_plugin.h index 99178b7fde..2a1ea93ac8 100644 --- a/editor/plugins/cpu_particles_3d_editor_plugin.h +++ b/editor/plugins/cpu_particles_3d_editor_plugin.h @@ -60,7 +60,6 @@ class CPUParticles3DEditor : public GPUParticles3DEditorBase { protected: void _notification(int p_notification); void _node_removed(Node *p_node); - static void _bind_methods(); public: void edit(CPUParticles3D *p_particles); diff --git a/editor/plugins/editor_plugin_settings.cpp b/editor/plugins/editor_plugin_settings.cpp index afc60b09d6..5949bc141f 100644 --- a/editor/plugins/editor_plugin_settings.cpp +++ b/editor/plugins/editor_plugin_settings.cpp @@ -199,9 +199,6 @@ Vector<String> EditorPluginSettings::_get_plugins(const String &p_dir) { return plugins; } -void EditorPluginSettings::_bind_methods() { -} - EditorPluginSettings::EditorPluginSettings() { ProjectSettings::get_singleton()->add_hidden_prefix("editor_plugins/"); diff --git a/editor/plugins/editor_plugin_settings.h b/editor/plugins/editor_plugin_settings.h index 5b470b3e58..ea7d0ea8b3 100644 --- a/editor/plugins/editor_plugin_settings.h +++ b/editor/plugins/editor_plugin_settings.h @@ -67,8 +67,6 @@ class EditorPluginSettings : public VBoxContainer { protected: void _notification(int p_what); - static void _bind_methods(); - public: void update_plugins(); diff --git a/editor/plugins/font_config_plugin.cpp b/editor/plugins/font_config_plugin.cpp index e6ce63fe36..d712c14861 100644 --- a/editor/plugins/font_config_plugin.cpp +++ b/editor/plugins/font_config_plugin.cpp @@ -30,6 +30,7 @@ #include "font_config_plugin.h" +#include "core/string/translation_server.h" #include "editor/editor_settings.h" #include "editor/import/dynamic_font_import_settings.h" #include "editor/themes/editor_scale.h" @@ -63,9 +64,6 @@ bool EditorPropertyFontMetaObject::_get(const StringName &p_name, Variant &r_ret return false; } -void EditorPropertyFontMetaObject::_bind_methods() { -} - void EditorPropertyFontMetaObject::set_dict(const Dictionary &p_dict) { dict = p_dict; } diff --git a/editor/plugins/font_config_plugin.h b/editor/plugins/font_config_plugin.h index 4e798fc3e8..e83f29a77b 100644 --- a/editor/plugins/font_config_plugin.h +++ b/editor/plugins/font_config_plugin.h @@ -46,7 +46,6 @@ class EditorPropertyFontMetaObject : public RefCounted { protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; - static void _bind_methods(); public: void set_dict(const Dictionary &p_dict); diff --git a/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp index 19dd45a3ea..21ad8f021c 100644 --- a/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp @@ -112,9 +112,12 @@ void Camera3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, float a = _find_closest_angle_to_half_pi_arc(s[0], s[1], 1.0, gt2); camera->set("fov", CLAMP(a * 2.0, 1, 179)); } else { + Camera3D::KeepAspect aspect = camera->get_keep_aspect_mode(); + Vector3 far = aspect == Camera3D::KeepAspect::KEEP_WIDTH ? Vector3(4096, 0, -1) : Vector3(0, 4096, -1); + Vector3 ra, rb; - Geometry3D::get_closest_points_between_segments(Vector3(0, 0, -1), Vector3(4096, 0, -1), s[0], s[1], ra, rb); - float d = ra.x * 2; + Geometry3D::get_closest_points_between_segments(Vector3(0, 0, -1), far, s[0], s[1], ra, rb); + float d = aspect == Camera3D::KeepAspect::KEEP_WIDTH ? ra.x * 2 : ra.y * 2; if (Node3DEditor::get_singleton()->is_snap_enabled()) { d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); } @@ -213,25 +216,33 @@ void Camera3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { } break; case Camera3D::PROJECTION_ORTHOGONAL: { + Camera3D::KeepAspect aspect = camera->get_keep_aspect_mode(); + float size = camera->get_size(); + float keep_size = size * 0.5; - float hsize = size * 0.5; - Vector3 right(hsize * size_factor.x, 0, 0); - Vector3 up(0, hsize * size_factor.y, 0); + Vector3 right, up; Vector3 back(0, 0, -1.0); Vector3 front(0, 0, 0); + if (aspect == Camera3D::KeepAspect::KEEP_WIDTH) { + right = Vector3(keep_size, 0, 0); + up = Vector3(0, keep_size / viewport_aspect, 0); + handles.push_back(right + back); + } else { + right = Vector3(keep_size * viewport_aspect, 0, 0); + up = Vector3(0, keep_size, 0); + handles.push_back(up + back); + } + ADD_QUAD(-up - right, -up + right, up + right, up - right); ADD_QUAD(-up - right + back, -up + right + back, up + right + back, up - right + back); ADD_QUAD(up + right, up + right + back, up - right + back, up - right); ADD_QUAD(-up + right, -up + right + back, -up - right + back, -up - right); - handles.push_back(right + back); - - right.x = MIN(right.x, hsize * 0.25); - Vector3 tup(0, up.y + hsize / 2, back.z); + right.x = MIN(right.x, keep_size * 0.25); + Vector3 tup(0, up.y + keep_size / 2, back.z); ADD_TRIANGLE(tup, right + up + back, -right + up + back); - } break; case Camera3D::PROJECTION_FRUSTUM: { diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.cpp b/editor/plugins/gpu_particles_2d_editor_plugin.cpp index b70c34b785..1b68b55ff6 100644 --- a/editor/plugins/gpu_particles_2d_editor_plugin.cpp +++ b/editor/plugins/gpu_particles_2d_editor_plugin.cpp @@ -360,9 +360,6 @@ void GPUParticles2DEditorPlugin::_notification(int p_what) { } } -void GPUParticles2DEditorPlugin::_bind_methods() { -} - GPUParticles2DEditorPlugin::GPUParticles2DEditorPlugin() { particles = nullptr; diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.h b/editor/plugins/gpu_particles_2d_editor_plugin.h index bb0ca5de3a..658e4d87e5 100644 --- a/editor/plugins/gpu_particles_2d_editor_plugin.h +++ b/editor/plugins/gpu_particles_2d_editor_plugin.h @@ -86,7 +86,6 @@ class GPUParticles2DEditorPlugin : public EditorPlugin { protected: void _notification(int p_what); - static void _bind_methods(); public: virtual String get_name() const override { return "GPUParticles2D"; } diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.cpp b/editor/plugins/gpu_particles_3d_editor_plugin.cpp index 00c5332464..4e9be0aa53 100644 --- a/editor/plugins/gpu_particles_3d_editor_plugin.cpp +++ b/editor/plugins/gpu_particles_3d_editor_plugin.cpp @@ -198,9 +198,6 @@ void GPUParticles3DEditorBase::_node_selected(const NodePath &p_path) { emission_dialog->popup_centered(Size2(300, 130)); } -void GPUParticles3DEditorBase::_bind_methods() { -} - GPUParticles3DEditorBase::GPUParticles3DEditorBase() { emission_dialog = memnew(ConfirmationDialog); emission_dialog->set_title(TTR("Create Emitter")); @@ -402,9 +399,6 @@ void GPUParticles3DEditor::_generate_emission_points() { } } -void GPUParticles3DEditor::_bind_methods() { -} - GPUParticles3DEditor::GPUParticles3DEditor() { node = nullptr; particles_editor_hb = memnew(HBoxContainer); diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.h b/editor/plugins/gpu_particles_3d_editor_plugin.h index 3b2ab2f8ca..5f59f6dca7 100644 --- a/editor/plugins/gpu_particles_3d_editor_plugin.h +++ b/editor/plugins/gpu_particles_3d_editor_plugin.h @@ -62,8 +62,6 @@ protected: virtual void _generate_emission_points(){}; void _node_selected(const NodePath &p_path); - static void _bind_methods(); - public: GPUParticles3DEditorBase(); }; @@ -95,7 +93,6 @@ class GPUParticles3DEditor : public GPUParticles3DEditorBase { protected: void _notification(int p_notification); void _node_removed(Node *p_node); - static void _bind_methods(); public: void edit(GPUParticles3D *p_particles); diff --git a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp index 25b076d0e5..c21a1b5dd6 100644 --- a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp +++ b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp @@ -175,9 +175,6 @@ void GPUParticlesCollisionSDF3DEditorPlugin::_sdf_save_path_and_bake(const Strin } } -void GPUParticlesCollisionSDF3DEditorPlugin::_bind_methods() { -} - GPUParticlesCollisionSDF3DEditorPlugin::GPUParticlesCollisionSDF3DEditorPlugin() { bake_hb = memnew(HBoxContainer); bake_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL); diff --git a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h index bba8bd2584..3e1dae77ea 100644 --- a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h +++ b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h @@ -58,7 +58,6 @@ class GPUParticlesCollisionSDF3DEditorPlugin : public EditorPlugin { void _sdf_save_path_and_bake(const String &p_path); protected: - static void _bind_methods(); void _notification(int p_what); public: diff --git a/editor/plugins/input_event_editor_plugin.cpp b/editor/plugins/input_event_editor_plugin.cpp index 5a90d2de61..30debfc14f 100644 --- a/editor/plugins/input_event_editor_plugin.cpp +++ b/editor/plugins/input_event_editor_plugin.cpp @@ -33,9 +33,6 @@ #include "editor/event_listener_line_edit.h" #include "editor/input_event_configuration_dialog.h" -void InputEventConfigContainer::_bind_methods() { -} - void InputEventConfigContainer::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: diff --git a/editor/plugins/input_event_editor_plugin.h b/editor/plugins/input_event_editor_plugin.h index 48c16a4807..276bb74f6b 100644 --- a/editor/plugins/input_event_editor_plugin.h +++ b/editor/plugins/input_event_editor_plugin.h @@ -51,7 +51,6 @@ class InputEventConfigContainer : public VBoxContainer { protected: void _notification(int p_what); - static void _bind_methods(); public: void set_event(const Ref<InputEvent> &p_event); diff --git a/editor/plugins/mesh_library_editor_plugin.cpp b/editor/plugins/mesh_library_editor_plugin.cpp index 0b2549986c..58cc670475 100644 --- a/editor/plugins/mesh_library_editor_plugin.cpp +++ b/editor/plugins/mesh_library_editor_plugin.cpp @@ -242,9 +242,6 @@ void MeshLibraryEditor::_menu_cbk(int p_option) { } } -void MeshLibraryEditor::_bind_methods() { -} - MeshLibraryEditor::MeshLibraryEditor() { file = memnew(EditorFileDialog); file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE); diff --git a/editor/plugins/mesh_library_editor_plugin.h b/editor/plugins/mesh_library_editor_plugin.h index 94f46beea1..5a9e32178d 100644 --- a/editor/plugins/mesh_library_editor_plugin.h +++ b/editor/plugins/mesh_library_editor_plugin.h @@ -68,9 +68,6 @@ class MeshLibraryEditor : public Control { static void _import_scene(Node *p_scene, Ref<MeshLibrary> p_library, bool p_merge, bool p_apply_xforms); static void _import_scene_parse_node(Ref<MeshLibrary> p_library, HashMap<int, MeshInstance3D *> &p_mesh_instances, Node *p_node, bool p_merge, bool p_apply_xforms); -protected: - static void _bind_methods(); - public: MenuButton *get_menu_button() const { return menu; } diff --git a/editor/plugins/multimesh_editor_plugin.cpp b/editor/plugins/multimesh_editor_plugin.cpp index 3980f23c8a..76ffdb02e1 100644 --- a/editor/plugins/multimesh_editor_plugin.cpp +++ b/editor/plugins/multimesh_editor_plugin.cpp @@ -266,9 +266,6 @@ void MultiMeshEditor::_browse(bool p_source) { std->popup_scenetree_dialog(browsed_node); } -void MultiMeshEditor::_bind_methods() { -} - MultiMeshEditor::MultiMeshEditor() { options = memnew(MenuButton); options->set_switch_on_hover(true); diff --git a/editor/plugins/multimesh_editor_plugin.h b/editor/plugins/multimesh_editor_plugin.h index 5051926c64..3fe63bfa92 100644 --- a/editor/plugins/multimesh_editor_plugin.h +++ b/editor/plugins/multimesh_editor_plugin.h @@ -79,7 +79,6 @@ class MultiMeshEditor : public Control { protected: void _node_removed(Node *p_node); - static void _bind_methods(); public: void edit(MultiMeshInstance3D *p_multimesh); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 59a4ac8075..22300fe851 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -1064,21 +1064,23 @@ void Node3DEditorViewport::_select_region() { if (found_nodes.has(sp)) { continue; } - found_nodes.insert(sp); Node *node = Object::cast_to<Node>(sp); + + // Selection requires that the node is the edited scene or its descendant, and has an owner. if (node != edited_scene) { + if (!node->get_owner() || !edited_scene->is_ancestor_of(node)) { + continue; + } node = edited_scene->get_deepest_editable_node(node); - } - - // Prevent selection of nodes not owned by the edited scene. - while (node && node != edited_scene->get_parent()) { - Node *node_owner = node->get_owner(); - if (node_owner == edited_scene || node == edited_scene || (node_owner != nullptr && edited_scene->is_editable_instance(node_owner))) { - break; + while (node != edited_scene) { + Node *node_owner = node->get_owner(); + if (node_owner == edited_scene || (node_owner != nullptr && edited_scene->is_editable_instance(node_owner))) { + break; + } + node = node->get_parent(); } - node = node->get_parent(); } // Replace the node by the group if grouped @@ -2252,6 +2254,11 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { } if (_edit.mode == TRANSFORM_NONE) { + if (_edit.gizmo.is_null() && is_freelook_active() && k->get_keycode() == Key::ESCAPE) { + set_freelook_active(false); + return; + } + if (_edit.gizmo.is_valid() && (k->get_keycode() == Key::ESCAPE || k->get_keycode() == Key::BACKSPACE)) { // Restore. _edit.gizmo->commit_handle(_edit.gizmo_handle, _edit.gizmo_handle_secondary, _edit.gizmo_initial_value, true); @@ -5001,14 +5008,24 @@ void Node3DEditorViewport::update_transform(bool p_shift) { } break; case TRANSFORM_ROTATE: { - Plane plane = Plane(_get_camera_normal(), _edit.center); + Plane plane; + if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) { + Vector3 cam_to_obj = _edit.center - _get_camera_position(); + if (!cam_to_obj.is_zero_approx()) { + plane = Plane(cam_to_obj.normalized(), _edit.center); + } else { + plane = Plane(_get_camera_normal(), _edit.center); + } + } else { + plane = Plane(_get_camera_normal(), _edit.center); + } Vector3 local_axis; Vector3 global_axis; switch (_edit.plane) { case TRANSFORM_VIEW: // local_axis unused - global_axis = _get_camera_normal(); + global_axis = plane.normal; break; case TRANSFORM_X_AXIS: local_axis = Vector3(1, 0, 0); @@ -5039,7 +5056,7 @@ void Node3DEditorViewport::update_transform(bool p_shift) { break; } - static const float orthogonal_threshold = Math::cos(Math::deg_to_rad(87.0f)); + static const float orthogonal_threshold = Math::cos(Math::deg_to_rad(85.0f)); bool axis_is_orthogonal = ABS(plane.normal.dot(global_axis)) < orthogonal_threshold; double angle = 0.0f; @@ -7289,9 +7306,15 @@ void Node3DEditor::_init_grid() { bool orthogonal = camera->get_projection() == Camera3D::PROJECTION_ORTHOGONAL; - Vector<Color> grid_colors[3]; - Vector<Vector3> grid_points[3]; - Vector<Vector3> grid_normals[3]; + static LocalVector<Color> grid_colors[3]; + static LocalVector<Vector3> grid_points[3]; + static LocalVector<Vector3> grid_normals[3]; + + for (uint32_t n = 0; n < 3; n++) { + grid_colors[n].clear(); + grid_points[n].clear(); + grid_normals[n].clear(); + } Color primary_grid_color = EDITOR_GET("editors/3d/primary_grid_color"); Color secondary_grid_color = EDITOR_GET("editors/3d/secondary_grid_color"); @@ -7299,9 +7322,9 @@ void Node3DEditor::_init_grid() { int primary_grid_steps = EDITOR_GET("editors/3d/primary_grid_steps"); // Which grid planes are enabled? Which should we generate? - grid_enable[0] = grid_visible[0] = EDITOR_GET("editors/3d/grid_xy_plane"); - grid_enable[1] = grid_visible[1] = EDITOR_GET("editors/3d/grid_yz_plane"); - grid_enable[2] = grid_visible[2] = EDITOR_GET("editors/3d/grid_xz_plane"); + grid_enable[0] = grid_visible[0] = orthogonal || EDITOR_GET("editors/3d/grid_xy_plane"); + grid_enable[1] = grid_visible[1] = orthogonal || EDITOR_GET("editors/3d/grid_yz_plane"); + grid_enable[2] = grid_visible[2] = orthogonal || EDITOR_GET("editors/3d/grid_xz_plane"); // Offsets division_level for bigger or smaller grids. // Default value is -0.2. -1.0 gives Blender-like behavior, 0.5 gives huge grids. @@ -7367,10 +7390,9 @@ void Node3DEditor::_init_grid() { grid_mat[c]->set_shader_parameter("grid_size", grid_fade_size); grid_mat[c]->set_shader_parameter("orthogonal", orthogonal); - // Cache these so we don't have to re-access memory. - Vector<Vector3> &ref_grid = grid_points[c]; - Vector<Vector3> &ref_grid_normals = grid_normals[c]; - Vector<Color> &ref_grid_colors = grid_colors[c]; + LocalVector<Vector3> &ref_grid = grid_points[c]; + LocalVector<Vector3> &ref_grid_normals = grid_normals[c]; + LocalVector<Color> &ref_grid_colors = grid_colors[c]; // Count our elements same as code below it. int expected_size = 0; @@ -7415,12 +7437,12 @@ void Node3DEditor::_init_grid() { line_end[a] = position_a; line_bgn[b] = bgn_b; line_end[b] = end_b; - ref_grid.set(idx, line_bgn); - ref_grid.set(idx + 1, line_end); - ref_grid_colors.set(idx, line_color); - ref_grid_colors.set(idx + 1, line_color); - ref_grid_normals.set(idx, normal); - ref_grid_normals.set(idx + 1, normal); + ref_grid[idx] = line_bgn; + ref_grid[idx + 1] = line_end; + ref_grid_colors[idx] = line_color; + ref_grid_colors[idx + 1] = line_color; + ref_grid_normals[idx] = normal; + ref_grid_normals[idx + 1] = normal; idx += 2; } @@ -7431,12 +7453,12 @@ void Node3DEditor::_init_grid() { line_end[b] = position_b; line_bgn[a] = bgn_a; line_end[a] = end_a; - ref_grid.set(idx, line_bgn); - ref_grid.set(idx + 1, line_end); - ref_grid_colors.set(idx, line_color); - ref_grid_colors.set(idx + 1, line_color); - ref_grid_normals.set(idx, normal); - ref_grid_normals.set(idx + 1, normal); + ref_grid[idx] = line_bgn; + ref_grid[idx + 1] = line_end; + ref_grid_colors[idx] = line_color; + ref_grid_colors[idx + 1] = line_color; + ref_grid_normals[idx] = normal; + ref_grid_normals[idx + 1] = normal; idx += 2; } } @@ -7445,9 +7467,9 @@ void Node3DEditor::_init_grid() { grid[c] = RenderingServer::get_singleton()->mesh_create(); Array d; d.resize(RS::ARRAY_MAX); - d[RenderingServer::ARRAY_VERTEX] = grid_points[c]; - d[RenderingServer::ARRAY_COLOR] = grid_colors[c]; - d[RenderingServer::ARRAY_NORMAL] = grid_normals[c]; + d[RenderingServer::ARRAY_VERTEX] = (Vector<Vector3>)grid_points[c]; + d[RenderingServer::ARRAY_COLOR] = (Vector<Color>)grid_colors[c]; + d[RenderingServer::ARRAY_NORMAL] = (Vector<Vector3>)grid_normals[c]; RenderingServer::get_singleton()->mesh_add_surface_from_arrays(grid[c], RenderingServer::PRIMITIVE_LINES, d); RenderingServer::get_singleton()->mesh_surface_set_material(grid[c], 0, grid_mat[c]->get_rid()); grid_instance[c] = RenderingServer::get_singleton()->instance_create2(grid[c], get_tree()->get_root()->get_world_3d()->get_scenario()); diff --git a/editor/plugins/packed_scene_translation_parser_plugin.cpp b/editor/plugins/packed_scene_translation_parser_plugin.cpp index e9ddaeb3fe..b38965753e 100644 --- a/editor/plugins/packed_scene_translation_parser_plugin.cpp +++ b/editor/plugins/packed_scene_translation_parser_plugin.cpp @@ -83,9 +83,8 @@ Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path, int idx_last = atr_owners.size() - 1; if (idx_last > 0 && !parent_path.begins_with(atr_owners[idx_last].first)) { - // Switch to the previous auto translation owner this was nested in, if that was the case. + // Exit from the current owner nesting into the previous one. atr_owners.remove_at(idx_last); - idx_last -= 1; } if (property == "auto_translate_mode") { @@ -106,7 +105,7 @@ Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path, // If `auto_translate_mode` wasn't found, that means it is set to its default value (`AUTO_TRANSLATE_MODE_INHERIT`). if (!auto_translate_mode_found) { int idx_last = atr_owners.size() - 1; - if (idx_last > 0 && atr_owners[idx_last].first == parent_path) { + if (idx_last > 0 && parent_path.begins_with(atr_owners[idx_last].first)) { auto_translating = atr_owners[idx_last].second; } else { atr_owners.push_back(Pair(state->get_node_path(i), true)); diff --git a/editor/plugins/physical_bone_3d_editor_plugin.cpp b/editor/plugins/physical_bone_3d_editor_plugin.cpp index b7c12ab5c0..d7e9701452 100644 --- a/editor/plugins/physical_bone_3d_editor_plugin.cpp +++ b/editor/plugins/physical_bone_3d_editor_plugin.cpp @@ -36,9 +36,6 @@ #include "scene/3d/physics/physical_bone_3d.h" #include "scene/gui/separator.h" -void PhysicalBone3DEditor::_bind_methods() { -} - void PhysicalBone3DEditor::_on_toggle_button_transform_joint(bool p_is_pressed) { _set_move_joint(); } diff --git a/editor/plugins/physical_bone_3d_editor_plugin.h b/editor/plugins/physical_bone_3d_editor_plugin.h index fb6f30cc57..b057644bb1 100644 --- a/editor/plugins/physical_bone_3d_editor_plugin.h +++ b/editor/plugins/physical_bone_3d_editor_plugin.h @@ -45,9 +45,6 @@ class PhysicalBone3DEditor : public Object { PhysicalBone3D *selected = nullptr; -protected: - static void _bind_methods(); - private: void _on_toggle_button_transform_joint(bool p_is_pressed); void _set_move_joint(); diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index e442c37edd..f0ea322504 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -1255,44 +1255,43 @@ void Polygon2DEditor::_uv_draw() { //draw skeleton NodePath skeleton_path = node->get_skeleton(); - if (node->has_node(skeleton_path)) { - Skeleton2D *skeleton = Object::cast_to<Skeleton2D>(node->get_node(skeleton_path)); - if (skeleton) { - for (int i = 0; i < skeleton->get_bone_count(); i++) { - Bone2D *bone = skeleton->get_bone(i); - if (bone->get_rest() == Transform2D(0, 0, 0, 0, 0, 0)) { - continue; //not set - } + Skeleton2D *skeleton = Object::cast_to<Skeleton2D>(node->get_node_or_null(skeleton_path)); + if (skeleton) { + Transform2D skeleton_xform = node->get_global_transform().affine_inverse().translated(-node->get_offset()) * skeleton->get_global_transform(); + for (int i = 0; i < skeleton->get_bone_count(); i++) { + Bone2D *bone = skeleton->get_bone(i); + if (bone->get_rest() == Transform2D(0, 0, 0, 0, 0, 0)) { + continue; //not set + } - bool current = bone_path == skeleton->get_path_to(bone); + bool current = bone_path == skeleton->get_path_to(bone); - bool found_child = false; + bool found_child = false; - for (int j = 0; j < bone->get_child_count(); j++) { - Bone2D *n = Object::cast_to<Bone2D>(bone->get_child(j)); - if (!n) { - continue; - } + for (int j = 0; j < bone->get_child_count(); j++) { + Bone2D *n = Object::cast_to<Bone2D>(bone->get_child(j)); + if (!n) { + continue; + } - found_child = true; + found_child = true; - Transform2D bone_xform = node->get_global_transform().affine_inverse().translated(-node->get_offset()) * (skeleton->get_global_transform() * bone->get_skeleton_rest()); - Transform2D endpoint_xform = bone_xform * n->get_transform(); + Transform2D bone_xform = skeleton_xform * bone->get_skeleton_rest(); + Transform2D endpoint_xform = bone_xform * n->get_transform(); - Color color = current ? Color(1, 1, 1) : Color(0.5, 0.5, 0.5); - uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), Color(0, 0, 0), Math::round((current ? 5 : 4) * EDSCALE)); - uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), color, Math::round((current ? 3 : 2) * EDSCALE)); - } + Color color = current ? Color(1, 1, 1) : Color(0.5, 0.5, 0.5); + uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), Color(0, 0, 0), Math::round((current ? 5 : 4) * EDSCALE)); + uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), color, Math::round((current ? 3 : 2) * EDSCALE)); + } - if (!found_child) { - //draw normally - Transform2D bone_xform = node->get_global_transform().affine_inverse().translated(-node->get_offset()) * (skeleton->get_global_transform() * bone->get_skeleton_rest()); - Transform2D endpoint_xform = bone_xform * Transform2D(0, Vector2(bone->get_length(), 0)); + if (!found_child) { + //draw normally + Transform2D bone_xform = skeleton_xform * bone->get_skeleton_rest(); + Transform2D endpoint_xform = bone_xform * Transform2D(0, Vector2(bone->get_length(), 0)).rotated(bone->get_bone_angle()); - Color color = current ? Color(1, 1, 1) : Color(0.5, 0.5, 0.5); - uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), Color(0, 0, 0), Math::round((current ? 5 : 4) * EDSCALE)); - uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), color, Math::round((current ? 3 : 2) * EDSCALE)); - } + Color color = current ? Color(1, 1, 1) : Color(0.5, 0.5, 0.5); + uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), Color(0, 0, 0), Math::round((current ? 5 : 4) * EDSCALE)); + uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), color, Math::round((current ? 3 : 2) * EDSCALE)); } } } diff --git a/editor/plugins/root_motion_editor_plugin.cpp b/editor/plugins/root_motion_editor_plugin.cpp index 8b31aa92b3..cd422fc291 100644 --- a/editor/plugins/root_motion_editor_plugin.cpp +++ b/editor/plugins/root_motion_editor_plugin.cpp @@ -193,9 +193,6 @@ void EditorPropertyRootMotion::_notification(int p_what) { } } -void EditorPropertyRootMotion::_bind_methods() { -} - EditorPropertyRootMotion::EditorPropertyRootMotion() { HBoxContainer *hbc = memnew(HBoxContainer); add_child(hbc); diff --git a/editor/plugins/root_motion_editor_plugin.h b/editor/plugins/root_motion_editor_plugin.h index 5befdb6006..a3e90360ae 100644 --- a/editor/plugins/root_motion_editor_plugin.h +++ b/editor/plugins/root_motion_editor_plugin.h @@ -52,7 +52,6 @@ class EditorPropertyRootMotion : public EditorProperty { void _node_clear(); protected: - static void _bind_methods(); void _notification(int p_what); public: diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index c51eb44aee..9da66a0862 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -3799,7 +3799,10 @@ void ScriptEditor::_on_find_in_files_result_selected(const String &fpath, int li ShaderEditorPlugin *shader_editor = Object::cast_to<ShaderEditorPlugin>(EditorNode::get_editor_data().get_editor_by_name("Shader")); shader_editor->edit(res.ptr()); shader_editor->make_visible(true); - shader_editor->get_shader_editor(res)->goto_line_selection(line_number - 1, begin, end); + TextShaderEditor *text_shader_editor = Object::cast_to<TextShaderEditor>(shader_editor->get_shader_editor(res)); + if (text_shader_editor) { + text_shader_editor->goto_line_selection(line_number - 1, begin, end); + } return; } else if (fpath.get_extension() == "tscn") { Ref<FileAccess> f = FileAccess::open(fpath, FileAccess::READ); diff --git a/editor/plugins/shader/shader_editor.h b/editor/plugins/shader/shader_editor.h new file mode 100644 index 0000000000..32e690015b --- /dev/null +++ b/editor/plugins/shader/shader_editor.h @@ -0,0 +1,50 @@ +/**************************************************************************/ +/* shader_editor.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 SHADER_EDITOR_H +#define SHADER_EDITOR_H + +#include "scene/gui/control.h" +#include "scene/resources/shader.h" + +class ShaderEditor : public Control { + GDCLASS(ShaderEditor, Control); + +public: + virtual void edit_shader(const Ref<Shader> &p_shader) = 0; + virtual void edit_shader_include(const Ref<ShaderInclude> &p_shader_inc) {} + + virtual void apply_shaders() = 0; + virtual bool is_unsaved() const = 0; + virtual void save_external_data(const String &p_str = "") = 0; + virtual void validate_script() = 0; +}; + +#endif // SHADER_EDITOR_H diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 9d1576cdf2..82c436d3a5 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -142,7 +142,7 @@ void ShaderEditorPlugin::edit(Object *p_object) { } es.shader_inc = Ref<ShaderInclude>(si); es.shader_editor = memnew(TextShaderEditor); - es.shader_editor->edit(si); + es.shader_editor->edit_shader_include(si); shader_tabs->add_child(es.shader_editor); } else { Shader *s = Object::cast_to<Shader>(p_object); @@ -156,20 +156,18 @@ void ShaderEditorPlugin::edit(Object *p_object) { es.shader = Ref<Shader>(s); Ref<VisualShader> vs = es.shader; if (vs.is_valid()) { - es.visual_shader_editor = memnew(VisualShaderEditor); - shader_tabs->add_child(es.visual_shader_editor); - es.visual_shader_editor->edit(vs.ptr()); + es.shader_editor = memnew(VisualShaderEditor); } else { es.shader_editor = memnew(TextShaderEditor); - shader_tabs->add_child(es.shader_editor); - es.shader_editor->edit(s); } + shader_tabs->add_child(es.shader_editor); + es.shader_editor->edit_shader(es.shader); } - if (es.shader_editor) { - es.shader_editor->connect("validation_changed", callable_mp(this, &ShaderEditorPlugin::_update_shader_list)); - - CodeTextEditor *cte = es.shader_editor->get_code_editor(); + TextShaderEditor *text_shader_editor = Object::cast_to<TextShaderEditor>(es.shader_editor); + if (text_shader_editor) { + text_shader_editor->connect("validation_changed", callable_mp(this, &ShaderEditorPlugin::_update_shader_list)); + CodeTextEditor *cte = text_shader_editor->get_code_editor(); if (cte) { cte->set_zoom_factor(text_shader_zoom_factor); cte->connect("zoomed", callable_mp(this, &ShaderEditorPlugin::_set_text_shader_zoom_factor)); @@ -194,7 +192,7 @@ void ShaderEditorPlugin::make_visible(bool p_visible) { void ShaderEditorPlugin::selected_notify() { } -TextShaderEditor *ShaderEditorPlugin::get_shader_editor(const Ref<Shader> &p_for_shader) { +ShaderEditor *ShaderEditorPlugin::get_shader_editor(const Ref<Shader> &p_for_shader) { for (EditedShader &edited_shader : edited_shaders) { if (edited_shader.shader == p_for_shader) { return edited_shader.shader_editor; @@ -203,15 +201,6 @@ TextShaderEditor *ShaderEditorPlugin::get_shader_editor(const Ref<Shader> &p_for return nullptr; } -VisualShaderEditor *ShaderEditorPlugin::get_visual_shader_editor(const Ref<Shader> &p_for_shader) { - for (EditedShader &edited_shader : edited_shaders) { - if (edited_shader.shader == p_for_shader) { - return edited_shader.visual_shader_editor; - } - } - return nullptr; -} - void ShaderEditorPlugin::set_window_layout(Ref<ConfigFile> p_layout) { if (EDITOR_GET("interface/multi_window/restore_windows_on_load") && window_wrapper->is_window_available() && p_layout->has_section_key("ShaderEditor", "window_rect")) { window_wrapper->restore_window_from_saved_position( @@ -280,7 +269,7 @@ void ShaderEditorPlugin::get_window_layout(Ref<ConfigFile> p_layout) { String selected_shader; for (int i = 0; i < shader_tabs->get_tab_count(); i++) { EditedShader edited_shader = edited_shaders[i]; - if (edited_shader.shader_editor || edited_shader.visual_shader_editor) { + if (edited_shader.shader_editor) { String shader_path; if (edited_shader.shader.is_valid()) { shader_path = edited_shader.shader->get_path(); @@ -290,10 +279,9 @@ void ShaderEditorPlugin::get_window_layout(Ref<ConfigFile> p_layout) { } shaders.push_back(shader_path); - TextShaderEditor *shader_editor = Object::cast_to<TextShaderEditor>(shader_tabs->get_current_tab_control()); - VisualShaderEditor *visual_shader_editor = Object::cast_to<VisualShaderEditor>(shader_tabs->get_current_tab_control()); + ShaderEditor *shader_editor = Object::cast_to<ShaderEditor>(shader_tabs->get_current_tab_control()); - if ((shader_editor && edited_shader.shader_editor == shader_editor) || (visual_shader_editor && edited_shader.visual_shader_editor == visual_shader_editor)) { + if (shader_editor && edited_shader.shader_editor == shader_editor) { selected_shader = shader_path; } } @@ -366,10 +354,6 @@ void ShaderEditorPlugin::_shader_selected(int p_index) { edited_shaders[p_index].shader_editor->validate_script(); } - if (edited_shaders[p_index].visual_shader_editor) { - edited_shaders[p_index].visual_shader_editor->validate_script(); - } - shader_tabs->set_current_tab(p_index); shader_list->select(p_index); } @@ -440,7 +424,7 @@ void ShaderEditorPlugin::_menu_item_pressed(int p_index) { case FILE_SAVE: { int index = shader_tabs->get_current_tab(); ERR_FAIL_INDEX(index, shader_tabs->get_tab_count()); - TextShaderEditor *editor = edited_shaders[index].shader_editor; + TextShaderEditor *editor = Object::cast_to<TextShaderEditor>(edited_shaders[index].shader_editor); if (editor) { if (editor->get_trim_trailing_whitespace_on_save()) { editor->trim_trailing_whitespace(); @@ -462,7 +446,7 @@ void ShaderEditorPlugin::_menu_item_pressed(int p_index) { case FILE_SAVE_AS: { int index = shader_tabs->get_current_tab(); ERR_FAIL_INDEX(index, shader_tabs->get_tab_count()); - TextShaderEditor *editor = edited_shaders[index].shader_editor; + TextShaderEditor *editor = Object::cast_to<TextShaderEditor>(edited_shaders[index].shader_editor); if (editor) { if (editor->get_trim_trailing_whitespace_on_save()) { editor->trim_trailing_whitespace(); @@ -623,8 +607,9 @@ void ShaderEditorPlugin::_set_text_shader_zoom_factor(float p_zoom_factor) { if (text_shader_zoom_factor != p_zoom_factor) { text_shader_zoom_factor = p_zoom_factor; for (const EditedShader &edited_shader : edited_shaders) { - if (edited_shader.shader_editor) { - CodeTextEditor *cte = edited_shader.shader_editor->get_code_editor(); + TextShaderEditor *text_shader_editor = Object::cast_to<TextShaderEditor>(edited_shader.shader_editor); + if (text_shader_editor) { + CodeTextEditor *cte = text_shader_editor->get_code_editor(); if (cte && cte->get_zoom_factor() != text_shader_zoom_factor) { cte->set_zoom_factor(text_shader_zoom_factor); } @@ -655,12 +640,13 @@ void ShaderEditorPlugin::_res_saved_callback(const Ref<Resource> &p_res) { } ERR_FAIL_COND(shader_res.is_null()); - if (!edited.shader_editor || !shader_res->is_built_in()) { + TextShaderEditor *text_shader_editor = Object::cast_to<TextShaderEditor>(edited.shader_editor); + if (!text_shader_editor || !shader_res->is_built_in()) { continue; } if (shader_res->get_path().get_slice("::", 0) == path) { - edited.shader_editor->tag_saved_version(); + text_shader_editor->tag_saved_version(); _update_shader_list(); } } diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h index 386261d844..f9b9405e45 100644 --- a/editor/plugins/shader_editor_plugin.h +++ b/editor/plugins/shader_editor_plugin.h @@ -37,6 +37,7 @@ class HSplitContainer; class ItemList; class MenuButton; class ShaderCreateDialog; +class ShaderEditor; class TabContainer; class TextShaderEditor; class VisualShaderEditor; @@ -52,8 +53,7 @@ class ShaderEditorPlugin : public EditorPlugin { struct EditedShader { Ref<Shader> shader; Ref<ShaderInclude> shader_inc; - TextShaderEditor *shader_editor = nullptr; - VisualShaderEditor *visual_shader_editor = nullptr; + ShaderEditor *shader_editor = nullptr; String path; String name; }; @@ -121,8 +121,7 @@ public: virtual void make_visible(bool p_visible) override; virtual void selected_notify() override; - TextShaderEditor *get_shader_editor(const Ref<Shader> &p_for_shader); - VisualShaderEditor *get_visual_shader_editor(const Ref<Shader> &p_for_shader); + ShaderEditor *get_shader_editor(const Ref<Shader> &p_for_shader); virtual void set_window_layout(Ref<ConfigFile> p_layout) override; virtual void get_window_layout(Ref<ConfigFile> p_layout) override; diff --git a/editor/plugins/shader_file_editor_plugin.cpp b/editor/plugins/shader_file_editor_plugin.cpp index 05919fb0f6..d732d51f69 100644 --- a/editor/plugins/shader_file_editor_plugin.cpp +++ b/editor/plugins/shader_file_editor_plugin.cpp @@ -218,9 +218,6 @@ void ShaderFileEditor::_editor_settings_changed() { } } -void ShaderFileEditor::_bind_methods() { -} - void ShaderFileEditor::edit(const Ref<RDShaderFile> &p_shader) { if (p_shader.is_null()) { if (shader_file.is_valid()) { diff --git a/editor/plugins/shader_file_editor_plugin.h b/editor/plugins/shader_file_editor_plugin.h index 9a915513ef..fea770b7e0 100644 --- a/editor/plugins/shader_file_editor_plugin.h +++ b/editor/plugins/shader_file_editor_plugin.h @@ -62,7 +62,6 @@ class ShaderFileEditor : public PanelContainer { protected: void _notification(int p_what); - static void _bind_methods(); public: static ShaderFileEditor *singleton; diff --git a/editor/plugins/skeleton_2d_editor_plugin.cpp b/editor/plugins/skeleton_2d_editor_plugin.cpp index 8308fe6d6e..97c5c0c7dd 100644 --- a/editor/plugins/skeleton_2d_editor_plugin.cpp +++ b/editor/plugins/skeleton_2d_editor_plugin.cpp @@ -90,9 +90,6 @@ void Skeleton2DEditor::_menu_option(int p_option) { } } -void Skeleton2DEditor::_bind_methods() { -} - Skeleton2DEditor::Skeleton2DEditor() { options = memnew(MenuButton); diff --git a/editor/plugins/skeleton_2d_editor_plugin.h b/editor/plugins/skeleton_2d_editor_plugin.h index 74fd59f1c4..89e2e5d0f2 100644 --- a/editor/plugins/skeleton_2d_editor_plugin.h +++ b/editor/plugins/skeleton_2d_editor_plugin.h @@ -57,7 +57,6 @@ class Skeleton2DEditor : public Control { protected: void _node_removed(Node *p_node); - static void _bind_methods(); public: void edit(Skeleton2D *p_sprite); diff --git a/editor/plugins/skeleton_ik_3d_editor_plugin.cpp b/editor/plugins/skeleton_ik_3d_editor_plugin.cpp index 1753df5cb7..9b98b6ffa2 100644 --- a/editor/plugins/skeleton_ik_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_ik_3d_editor_plugin.cpp @@ -73,9 +73,6 @@ void SkeletonIK3DEditorPlugin::make_visible(bool p_visible) { } } -void SkeletonIK3DEditorPlugin::_bind_methods() { -} - SkeletonIK3DEditorPlugin::SkeletonIK3DEditorPlugin() { play_btn = memnew(Button); play_btn->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Play"), EditorStringName(EditorIcons))); diff --git a/editor/plugins/skeleton_ik_3d_editor_plugin.h b/editor/plugins/skeleton_ik_3d_editor_plugin.h index 2ef5467263..95ab3696be 100644 --- a/editor/plugins/skeleton_ik_3d_editor_plugin.h +++ b/editor/plugins/skeleton_ik_3d_editor_plugin.h @@ -45,9 +45,6 @@ class SkeletonIK3DEditorPlugin : public EditorPlugin { void _play(); -protected: - static void _bind_methods(); - public: virtual String get_name() const override { return "SkeletonIK3D"; } bool has_main_screen() const override { return false; } diff --git a/editor/plugins/text_shader_editor.cpp b/editor/plugins/text_shader_editor.cpp index 276c57533f..6cd92bdf57 100644 --- a/editor/plugins/text_shader_editor.cpp +++ b/editor/plugins/text_shader_editor.cpp @@ -895,7 +895,7 @@ void TextShaderEditor::_reload() { } } -void TextShaderEditor::edit(const Ref<Shader> &p_shader) { +void TextShaderEditor::edit_shader(const Ref<Shader> &p_shader) { if (p_shader.is_null() || !p_shader->is_text_shader()) { return; } @@ -910,7 +910,7 @@ void TextShaderEditor::edit(const Ref<Shader> &p_shader) { code_editor->set_edited_shader(shader); } -void TextShaderEditor::edit(const Ref<ShaderInclude> &p_shader_inc) { +void TextShaderEditor::edit_shader_include(const Ref<ShaderInclude> &p_shader_inc) { if (p_shader_inc.is_null()) { return; } @@ -1141,6 +1141,7 @@ TextShaderEditor::TextShaderEditor() { context_menu->connect(SceneStringName(id_pressed), callable_mp(this, &TextShaderEditor::_menu_option)); VBoxContainer *main_container = memnew(VBoxContainer); + main_container->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); HBoxContainer *hbc = memnew(HBoxContainer); edit_menu = memnew(MenuButton); diff --git a/editor/plugins/text_shader_editor.h b/editor/plugins/text_shader_editor.h index 61066ed7c6..55efb4e30e 100644 --- a/editor/plugins/text_shader_editor.h +++ b/editor/plugins/text_shader_editor.h @@ -32,6 +32,7 @@ #define TEXT_SHADER_EDITOR_H #include "editor/code_editor.h" +#include "editor/plugins/shader/shader_editor.h" #include "scene/gui/margin_container.h" #include "scene/gui/menu_button.h" #include "scene/gui/rich_text_label.h" @@ -104,8 +105,8 @@ public: ShaderTextEditor(); }; -class TextShaderEditor : public MarginContainer { - GDCLASS(TextShaderEditor, MarginContainer); +class TextShaderEditor : public ShaderEditor { + GDCLASS(TextShaderEditor, ShaderEditor); enum { EDIT_UNDO, @@ -188,19 +189,21 @@ protected: void _bookmark_item_pressed(int p_idx); public: + virtual void edit_shader(const Ref<Shader> &p_shader) override; + virtual void edit_shader_include(const Ref<ShaderInclude> &p_shader_inc) override; + + virtual void apply_shaders() override; + virtual bool is_unsaved() const override; + virtual void save_external_data(const String &p_str = "") override; + virtual void validate_script() override; + bool was_compilation_successful() const { return compilation_success; } bool get_trim_trailing_whitespace_on_save() const { return trim_trailing_whitespace_on_save; } bool get_trim_final_newlines_on_save() const { return trim_final_newlines_on_save; } - void apply_shaders(); void ensure_select_current(); - void edit(const Ref<Shader> &p_shader); - void edit(const Ref<ShaderInclude> &p_shader_inc); void goto_line_selection(int p_line, int p_begin, int p_end); - void save_external_data(const String &p_str = ""); void trim_trailing_whitespace(); void trim_final_newlines(); - void validate_script(); - bool is_unsaved() const; void tag_saved_version(); ShaderTextEditor *get_code_editor() { return code_editor; } diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp index 3942baed8a..b806d1e042 100644 --- a/editor/plugins/tiles/tile_atlas_view.cpp +++ b/editor/plugins/tiles/tile_atlas_view.cpp @@ -372,7 +372,7 @@ void TileAtlasView::_draw_base_tiles_shape_grid() { for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) { Vector2i tile_id = tile_set_atlas_source->get_tile_id(i); Vector2 in_tile_base_offset = tile_set_atlas_source->get_tile_data(tile_id, 0)->get_texture_origin(); - if (tile_set_atlas_source->is_position_in_tile_texture_region(tile_id, 0, -tile_shape_size / 2) && tile_set_atlas_source->is_position_in_tile_texture_region(tile_id, 0, tile_shape_size / 2 - Vector2(1, 1))) { + if (tile_set_atlas_source->is_rect_in_tile_texture_region(tile_id, 0, Rect2(Vector2(-tile_shape_size) / 2, tile_shape_size))) { for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(tile_id); frame++) { Color color = grid_color; if (frame > 0) { diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index f985bbc629..af52243c41 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -165,10 +165,14 @@ void GenericTilePolygonEditor::_base_control_draw() { base_control->draw_set_transform_matrix(xform); // Draw fill rect under texture region. - Rect2 texture_rect(-background_region.size / 2, background_region.size); + Rect2 texture_rect(Vector2(), background_region.size); if (tile_data) { texture_rect.position -= tile_data->get_texture_origin(); + if (tile_data->get_transpose()) { + texture_rect.size = Size2(texture_rect.size.y, texture_rect.size.x); + } } + texture_rect.position -= texture_rect.size / 2; // Half-size offset must be applied after transposing. base_control->draw_rect(texture_rect, Color(1, 1, 1, 0.3)); // Draw the background. @@ -180,18 +184,14 @@ void GenericTilePolygonEditor::_base_control_draw() { if (tile_data->get_flip_v()) { region_size.y = -region_size.y; } - base_control->draw_texture_rect_region(background_atlas_source->get_texture(), Rect2(-background_region.size / 2 - tile_data->get_texture_origin(), region_size), background_region, tile_data->get_modulate(), tile_data->get_transpose()); + // Destination rect position must account for transposing, size must not. + base_control->draw_texture_rect_region(background_atlas_source->get_texture(), Rect2(texture_rect.position, region_size), background_region, tile_data->get_modulate(), tile_data->get_transpose()); } // Compute and draw the grid area. Rect2 grid_area = Rect2(-base_tile_size / 2, base_tile_size); - if (tile_data) { - grid_area.expand_to(-background_region.get_size() / 2 - tile_data->get_texture_origin()); - grid_area.expand_to(background_region.get_size() / 2 - tile_data->get_texture_origin()); - } else { - grid_area.expand_to(-background_region.get_size() / 2); - grid_area.expand_to(background_region.get_size() / 2); - } + grid_area.expand_to(texture_rect.position); + grid_area.expand_to(texture_rect.get_end()); base_control->draw_rect(grid_area, Color(1, 1, 1, 0.3), false); // Draw grid. @@ -523,6 +523,21 @@ void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event) xform.set_origin(base_control->get_size() / 2 + panning); xform.set_scale(Vector2(editor_zoom_widget->get_zoom(), editor_zoom_widget->get_zoom())); + Ref<InputEventPanGesture> pan_gesture = p_event; + if (pan_gesture.is_valid()) { + panning += pan_gesture->get_delta() * 8; + drag_last_pos = Vector2(); + button_center_view->set_disabled(panning.is_zero_approx()); + accept_event(); + } + + Ref<InputEventMagnifyGesture> magnify_gesture = p_event; + if (magnify_gesture.is_valid()) { + editor_zoom_widget->set_zoom(editor_zoom_widget->get_zoom() * magnify_gesture->get_factor()); + _zoom_changed(); + accept_event(); + } + Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid()) { if (drag_type == DRAG_TYPE_DRAG_POINT) { @@ -1385,10 +1400,8 @@ void TileDataTextureOriginEditor::draw_over_tile(CanvasItem *p_canvas_item, Tran TileSetSource *source = *(tile_set->get_source(p_cell.source_id)); TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source->is_position_in_tile_texture_region(p_cell.get_atlas_coords(), p_cell.alternative_tile, -tile_set_tile_size / 2) && atlas_source->is_position_in_tile_texture_region(p_cell.get_atlas_coords(), p_cell.alternative_tile, tile_set_tile_size / 2 - Vector2(1, 1))) { - Transform2D tile_xform; - tile_xform.set_scale(tile_set_tile_size); - tile_set->draw_tile_shape(p_canvas_item, p_transform * tile_xform, color); + if (atlas_source->is_rect_in_tile_texture_region(p_cell.get_atlas_coords(), p_cell.alternative_tile, Rect2(Vector2(-tile_set_tile_size) / 2, tile_set_tile_size))) { + tile_set->draw_tile_shape(p_canvas_item, p_transform.scaled_local(tile_set_tile_size), color); } if (atlas_source->is_position_in_tile_texture_region(p_cell.get_atlas_coords(), p_cell.alternative_tile, Vector2())) { diff --git a/editor/plugins/tiles/tile_map_layer_editor.cpp b/editor/plugins/tiles/tile_map_layer_editor.cpp index b76e673414..63a54372b5 100644 --- a/editor/plugins/tiles/tile_map_layer_editor.cpp +++ b/editor/plugins/tiles/tile_map_layer_editor.cpp @@ -2368,7 +2368,7 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() { tiles_bottom_panel->set_name(TTR("Tiles")); missing_source_label = memnew(Label); - missing_source_label->set_text(TTR("This TileMap's TileSet has no source configured. Go to the TileSet bottom panel to add one.")); + missing_source_label->set_text(TTR("This TileMap's TileSet has no Tile Source configured. Go to the TileSet bottom panel to add one.")); missing_source_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); missing_source_label->set_v_size_flags(Control::SIZE_EXPAND_FILL); missing_source_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index 7e34a36a6e..224c4e434f 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -2612,7 +2612,6 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { atlas_source_inspector->set_v_size_flags(SIZE_EXPAND_FILL); atlas_source_inspector->set_show_categories(false, true); atlas_source_inspector->set_use_doc_hints(true); - atlas_source_inspector->add_inspector_plugin(memnew(TileSourceInspectorPlugin)); middle_vbox_container->add_child(atlas_source_inspector); // -- Right side -- @@ -2740,9 +2739,8 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { add_child(confirm_auto_create_tiles); // Inspector plugin. - Ref<EditorInspectorPluginTileData> tile_data_inspector_plugin; - tile_data_inspector_plugin.instantiate(); - EditorInspector::add_inspector_plugin(tile_data_inspector_plugin); + EditorInspector::add_inspector_plugin(memnew(EditorInspectorPluginTileData)); + EditorInspector::add_inspector_plugin(memnew(TileSourceInspectorPlugin)); } TileSetAtlasSourceEditor::~TileSetAtlasSourceEditor() { diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp index 263e9cfa3b..7e5336ce06 100644 --- a/editor/plugins/tiles/tile_set_editor.cpp +++ b/editor/plugins/tiles/tile_set_editor.cpp @@ -818,7 +818,7 @@ TileSetEditor::TileSetEditor() { tabs_bar = memnew(TabBar); tabs_bar->set_tab_alignment(TabBar::ALIGNMENT_CENTER); tabs_bar->set_clip_tabs(false); - tabs_bar->add_tab(TTR("Tiles")); + tabs_bar->add_tab(TTR("Tile Sources")); tabs_bar->add_tab(TTR("Patterns")); tabs_bar->connect("tab_changed", callable_mp(this, &TileSetEditor::_tab_changed)); diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp index 22ef779b8d..305407efdb 100644 --- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp @@ -534,7 +534,6 @@ TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() { scenes_collection_source_inspector = memnew(EditorInspector); scenes_collection_source_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED); scenes_collection_source_inspector->set_use_doc_hints(true); - scenes_collection_source_inspector->add_inspector_plugin(memnew(TileSourceInspectorPlugin)); middle_vbox_container->add_child(scenes_collection_source_inspector); // Tile inspector. @@ -581,6 +580,8 @@ TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() { scene_tile_delete_button->set_disabled(true); scene_tile_delete_button->connect(SceneStringName(pressed), callable_mp(this, &TileSetScenesCollectionSourceEditor::_source_delete_pressed)); scenes_bottom_actions->add_child(scene_tile_delete_button); + + EditorInspector::add_inspector_plugin(memnew(TileSourceInspectorPlugin)); } TileSetScenesCollectionSourceEditor::~TileSetScenesCollectionSourceEditor() { diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 2da9d66d9a..b2669cf1af 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -154,7 +154,6 @@ VSRerouteNode::VSRerouteNode() { title_lbl->hide(); const Size2 size = Size2(32, 32) * EDSCALE; - print_line("VSRerouteNode size: " + size); Control *slot_area = memnew(Control); slot_area->set_custom_minimum_size(size); @@ -1509,17 +1508,18 @@ Vector2 VisualShaderEditor::selection_center; List<VisualShaderEditor::CopyItem> VisualShaderEditor::copy_items_buffer; List<VisualShader::Connection> VisualShaderEditor::copy_connections_buffer; -void VisualShaderEditor::edit(VisualShader *p_visual_shader) { +void VisualShaderEditor::edit_shader(const Ref<Shader> &p_shader) { bool changed = false; - if (p_visual_shader) { + VisualShader *visual_shader_ptr = Object::cast_to<VisualShader>(p_shader.ptr()); + if (visual_shader_ptr) { if (visual_shader.is_null()) { changed = true; } else { - if (visual_shader.ptr() != p_visual_shader) { + if (visual_shader.ptr() != visual_shader_ptr) { changed = true; } } - visual_shader = Ref<VisualShader>(p_visual_shader); + visual_shader = p_shader; graph_plugin->register_shader(visual_shader.ptr()); visual_shader->connect_changed(callable_mp(this, &VisualShaderEditor::_update_preview)); @@ -1546,6 +1546,19 @@ void VisualShaderEditor::edit(VisualShader *p_visual_shader) { } } +void VisualShaderEditor::apply_shaders() { + // Stub. TODO: Implement apply_shaders in visual shaders for parity with text shaders. +} + +bool VisualShaderEditor::is_unsaved() const { + // Stub. TODO: Implement is_unsaved in visual shaders for parity with text shaders. + return false; +} + +void VisualShaderEditor::save_external_data(const String &p_str) { + ResourceSaver::save(visual_shader, visual_shader->get_path()); +} + void VisualShaderEditor::validate_script() { if (visual_shader.is_valid()) { _update_nodes(); @@ -6054,8 +6067,7 @@ VisualShaderEditor::VisualShaderEditor() { graph = memnew(GraphEdit); graph->get_menu_hbox()->set_h_size_flags(SIZE_EXPAND_FILL); - graph->set_v_size_flags(SIZE_EXPAND_FILL); - graph->set_h_size_flags(SIZE_EXPAND_FILL); + graph->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); graph->set_grid_pattern(GraphEdit::GridPattern::GRID_PATTERN_DOTS); int grid_pattern = EDITOR_GET("editors/visual_editors/grid_pattern"); graph->set_grid_pattern((GraphEdit::GridPattern)grid_pattern); @@ -7562,7 +7574,7 @@ void EditorPropertyVisualShaderMode::_option_selected(int p_which) { if (!shader_editor) { return; } - VisualShaderEditor *editor = shader_editor->get_visual_shader_editor(visual_shader); + VisualShaderEditor *editor = Object::cast_to<VisualShaderEditor>(shader_editor->get_shader_editor(visual_shader)); if (!editor) { return; } @@ -7653,9 +7665,6 @@ void EditorPropertyVisualShaderMode::set_option_button_clip(bool p_enable) { options->set_clip_text(p_enable); } -void EditorPropertyVisualShaderMode::_bind_methods() { -} - EditorPropertyVisualShaderMode::EditorPropertyVisualShaderMode() { options = memnew(OptionButton); options->set_clip_text(true); @@ -7787,9 +7796,6 @@ void VisualShaderNodePortPreview::_notification(int p_what) { } } -void VisualShaderNodePortPreview::_bind_methods() { -} - ////////////////////////////////// String VisualShaderConversionPlugin::converts_to() const { diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index a9826fd617..6ce096f821 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -34,6 +34,7 @@ #include "editor/editor_properties.h" #include "editor/plugins/editor_plugin.h" #include "editor/plugins/editor_resource_conversion_plugin.h" +#include "editor/plugins/shader/shader_editor.h" #include "scene/gui/graph_edit.h" #include "scene/resources/syntax_highlighter.h" #include "scene/resources/visual_shader.h" @@ -195,8 +196,8 @@ public: VisualShaderEditedProperty() {} }; -class VisualShaderEditor : public VBoxContainer { - GDCLASS(VisualShaderEditor, VBoxContainer); +class VisualShaderEditor : public ShaderEditor { + GDCLASS(VisualShaderEditor, ShaderEditor); friend class VisualShaderGraphPlugin; PopupPanel *property_editor_popup = nullptr; @@ -596,6 +597,12 @@ protected: static void _bind_methods(); public: + virtual void edit_shader(const Ref<Shader> &p_shader) override; + virtual void apply_shaders() override; + virtual bool is_unsaved() const override; + virtual void save_external_data(const String &p_str = "") override; + virtual void validate_script() override; + void add_plugin(const Ref<VisualShaderNodePlugin> &p_plugin); void remove_plugin(const Ref<VisualShaderNodePlugin> &p_plugin); @@ -609,11 +616,8 @@ public: virtual Size2 get_minimum_size() const override; - void edit(VisualShader *p_visual_shader); Ref<VisualShader> get_visual_shader() const { return visual_shader; } - void validate_script(); - VisualShaderEditor(); }; @@ -630,9 +634,6 @@ class EditorPropertyVisualShaderMode : public EditorProperty { void _option_selected(int p_which); -protected: - static void _bind_methods(); - public: void setup(const Vector<String> &p_options); virtual void update_property() override; @@ -658,7 +659,6 @@ class VisualShaderNodePortPreview : public Control { void _shader_changed(); //must regen protected: void _notification(int p_what); - static void _bind_methods(); public: virtual Size2 get_minimum_size() const override; diff --git a/editor/plugins/voxel_gi_editor_plugin.cpp b/editor/plugins/voxel_gi_editor_plugin.cpp index e7b2435567..3e835d5cb6 100644 --- a/editor/plugins/voxel_gi_editor_plugin.cpp +++ b/editor/plugins/voxel_gi_editor_plugin.cpp @@ -177,9 +177,6 @@ void VoxelGIEditorPlugin::_voxel_gi_save_path_and_bake(const String &p_path) { } } -void VoxelGIEditorPlugin::_bind_methods() { -} - VoxelGIEditorPlugin::VoxelGIEditorPlugin() { bake_hb = memnew(HBoxContainer); bake_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL); diff --git a/editor/plugins/voxel_gi_editor_plugin.h b/editor/plugins/voxel_gi_editor_plugin.h index 58ef22ddc6..d09822dda6 100644 --- a/editor/plugins/voxel_gi_editor_plugin.h +++ b/editor/plugins/voxel_gi_editor_plugin.h @@ -58,7 +58,6 @@ class VoxelGIEditorPlugin : public EditorPlugin { void _voxel_gi_save_path_and_bake(const String &p_path); protected: - static void _bind_methods(); void _notification(int p_what); public: diff --git a/editor/project_manager/quick_settings_dialog.cpp b/editor/project_manager/quick_settings_dialog.cpp index a98d9073b0..4502b42fe6 100644 --- a/editor/project_manager/quick_settings_dialog.cpp +++ b/editor/project_manager/quick_settings_dialog.cpp @@ -31,7 +31,7 @@ #include "quick_settings_dialog.h" #include "core/config/project_settings.h" -#include "core/string/translation.h" +#include "core/string/translation_server.h" #include "editor/editor_settings.h" #include "editor/editor_string_names.h" #include "editor/themes/editor_scale.h" diff --git a/editor/property_selector.cpp b/editor/property_selector.cpp index 0394c9e249..d47270841d 100644 --- a/editor/property_selector.cpp +++ b/editor/property_selector.cpp @@ -123,50 +123,6 @@ void PropertySelector::_update_search() { TreeItem *category = nullptr; bool found = false; - - Ref<Texture2D> type_icons[] = { - search_options->get_editor_theme_icon(SNAME("Variant")), - search_options->get_editor_theme_icon(SNAME("bool")), - search_options->get_editor_theme_icon(SNAME("int")), - search_options->get_editor_theme_icon(SNAME("float")), - search_options->get_editor_theme_icon(SNAME("String")), - search_options->get_editor_theme_icon(SNAME("Vector2")), - search_options->get_editor_theme_icon(SNAME("Vector2i")), - search_options->get_editor_theme_icon(SNAME("Rect2")), - search_options->get_editor_theme_icon(SNAME("Rect2i")), - search_options->get_editor_theme_icon(SNAME("Vector3")), - search_options->get_editor_theme_icon(SNAME("Vector3i")), - search_options->get_editor_theme_icon(SNAME("Transform2D")), - search_options->get_editor_theme_icon(SNAME("Vector4")), - search_options->get_editor_theme_icon(SNAME("Vector4i")), - search_options->get_editor_theme_icon(SNAME("Plane")), - search_options->get_editor_theme_icon(SNAME("Quaternion")), - search_options->get_editor_theme_icon(SNAME("AABB")), - search_options->get_editor_theme_icon(SNAME("Basis")), - search_options->get_editor_theme_icon(SNAME("Transform3D")), - search_options->get_editor_theme_icon(SNAME("Projection")), - search_options->get_editor_theme_icon(SNAME("Color")), - search_options->get_editor_theme_icon(SNAME("StringName")), - search_options->get_editor_theme_icon(SNAME("NodePath")), - search_options->get_editor_theme_icon(SNAME("RID")), - search_options->get_editor_theme_icon(SNAME("MiniObject")), - search_options->get_editor_theme_icon(SNAME("Callable")), - search_options->get_editor_theme_icon(SNAME("Signal")), - search_options->get_editor_theme_icon(SNAME("Dictionary")), - search_options->get_editor_theme_icon(SNAME("Array")), - search_options->get_editor_theme_icon(SNAME("PackedByteArray")), - search_options->get_editor_theme_icon(SNAME("PackedInt32Array")), - search_options->get_editor_theme_icon(SNAME("PackedInt64Array")), - search_options->get_editor_theme_icon(SNAME("PackedFloat32Array")), - search_options->get_editor_theme_icon(SNAME("PackedFloat64Array")), - search_options->get_editor_theme_icon(SNAME("PackedStringArray")), - search_options->get_editor_theme_icon(SNAME("PackedVector2Array")), - search_options->get_editor_theme_icon(SNAME("PackedVector3Array")), - search_options->get_editor_theme_icon(SNAME("PackedColorArray")), - search_options->get_editor_theme_icon(SNAME("PackedVector4Array")), - }; - static_assert((sizeof(type_icons) / sizeof(type_icons[0])) == Variant::VARIANT_MAX, "Number of type icons doesn't match the number of Variant types."); - for (const PropertyInfo &E : props) { if (E.usage == PROPERTY_USAGE_CATEGORY) { if (category && category->get_first_child() == nullptr) { @@ -194,14 +150,14 @@ void PropertySelector::_update_search() { continue; } - if (type_filter.size() && !type_filter.has(E.type)) { + if (!type_filter.is_empty() && !type_filter.has(E.type)) { continue; } TreeItem *item = search_options->create_item(category ? category : root); item->set_text(0, E.name); item->set_metadata(0, E.name); - item->set_icon(0, type_icons[E.type]); + item->set_icon(0, search_options->get_editor_theme_icon(Variant::get_type_name(E.type))); if (!found && !search_box->get_text().is_empty() && E.name.containsn(search_text)) { item->select(0); @@ -209,6 +165,9 @@ void PropertySelector::_update_search() { } item->set_selectable(0, true); + + _create_subproperties(item, E.type); + item->set_collapsed(true); } if (category && category->get_first_child() == nullptr) { @@ -407,6 +366,133 @@ void PropertySelector::_hide_requested() { _cancel_pressed(); // From AcceptDialog. } +void PropertySelector::_create_subproperties(TreeItem *p_parent_item, Variant::Type p_type) { + switch (p_type) { + case Variant::VECTOR2: { + _create_subproperty(p_parent_item, "x", Variant::FLOAT); + _create_subproperty(p_parent_item, "y", Variant::FLOAT); + } break; + + case Variant::VECTOR2I: { + _create_subproperty(p_parent_item, "x", Variant::INT); + _create_subproperty(p_parent_item, "y", Variant::INT); + } break; + + case Variant::RECT2: { + _create_subproperty(p_parent_item, "position", Variant::VECTOR2); + _create_subproperty(p_parent_item, "size", Variant::VECTOR2); + _create_subproperty(p_parent_item, "end", Variant::VECTOR2); + } break; + + case Variant::RECT2I: { + _create_subproperty(p_parent_item, "position", Variant::VECTOR2I); + _create_subproperty(p_parent_item, "size", Variant::VECTOR2I); + _create_subproperty(p_parent_item, "end", Variant::VECTOR2I); + } break; + + case Variant::VECTOR3: { + _create_subproperty(p_parent_item, "x", Variant::FLOAT); + _create_subproperty(p_parent_item, "y", Variant::FLOAT); + _create_subproperty(p_parent_item, "z", Variant::FLOAT); + } break; + + case Variant::VECTOR3I: { + _create_subproperty(p_parent_item, "x", Variant::INT); + _create_subproperty(p_parent_item, "y", Variant::INT); + _create_subproperty(p_parent_item, "z", Variant::INT); + } break; + + case Variant::TRANSFORM2D: { + _create_subproperty(p_parent_item, "origin", Variant::VECTOR2); + _create_subproperty(p_parent_item, "x", Variant::VECTOR2); + _create_subproperty(p_parent_item, "y", Variant::VECTOR2); + } break; + + case Variant::VECTOR4: { + _create_subproperty(p_parent_item, "x", Variant::FLOAT); + _create_subproperty(p_parent_item, "y", Variant::FLOAT); + _create_subproperty(p_parent_item, "z", Variant::FLOAT); + _create_subproperty(p_parent_item, "w", Variant::FLOAT); + } break; + + case Variant::VECTOR4I: { + _create_subproperty(p_parent_item, "x", Variant::INT); + _create_subproperty(p_parent_item, "y", Variant::INT); + _create_subproperty(p_parent_item, "z", Variant::INT); + _create_subproperty(p_parent_item, "w", Variant::INT); + } break; + + case Variant::PLANE: { + _create_subproperty(p_parent_item, "x", Variant::FLOAT); + _create_subproperty(p_parent_item, "y", Variant::FLOAT); + _create_subproperty(p_parent_item, "z", Variant::FLOAT); + _create_subproperty(p_parent_item, "normal", Variant::VECTOR3); + _create_subproperty(p_parent_item, "d", Variant::FLOAT); + } break; + + case Variant::QUATERNION: { + _create_subproperty(p_parent_item, "x", Variant::FLOAT); + _create_subproperty(p_parent_item, "y", Variant::FLOAT); + _create_subproperty(p_parent_item, "z", Variant::FLOAT); + _create_subproperty(p_parent_item, "w", Variant::FLOAT); + } break; + + case Variant::AABB: { + _create_subproperty(p_parent_item, "position", Variant::VECTOR3); + _create_subproperty(p_parent_item, "size", Variant::VECTOR3); + _create_subproperty(p_parent_item, "end", Variant::VECTOR3); + } break; + + case Variant::BASIS: { + _create_subproperty(p_parent_item, "x", Variant::VECTOR3); + _create_subproperty(p_parent_item, "y", Variant::VECTOR3); + _create_subproperty(p_parent_item, "z", Variant::VECTOR3); + } break; + + case Variant::TRANSFORM3D: { + _create_subproperty(p_parent_item, "basis", Variant::BASIS); + _create_subproperty(p_parent_item, "origin", Variant::VECTOR3); + } break; + + case Variant::PROJECTION: { + _create_subproperty(p_parent_item, "x", Variant::VECTOR4); + _create_subproperty(p_parent_item, "y", Variant::VECTOR4); + _create_subproperty(p_parent_item, "z", Variant::VECTOR4); + _create_subproperty(p_parent_item, "w", Variant::VECTOR4); + } break; + + case Variant::COLOR: { + _create_subproperty(p_parent_item, "r", Variant::FLOAT); + _create_subproperty(p_parent_item, "g", Variant::FLOAT); + _create_subproperty(p_parent_item, "b", Variant::FLOAT); + _create_subproperty(p_parent_item, "a", Variant::FLOAT); + _create_subproperty(p_parent_item, "r8", Variant::INT); + _create_subproperty(p_parent_item, "g8", Variant::INT); + _create_subproperty(p_parent_item, "b8", Variant::INT); + _create_subproperty(p_parent_item, "a8", Variant::INT); + _create_subproperty(p_parent_item, "h", Variant::FLOAT); + _create_subproperty(p_parent_item, "s", Variant::FLOAT); + _create_subproperty(p_parent_item, "v", Variant::FLOAT); + } break; + + default: { + } + } +} + +void PropertySelector::_create_subproperty(TreeItem *p_parent_item, const String &p_name, Variant::Type p_type) { + if (!type_filter.is_empty() && !type_filter.has(p_type)) { + return; + } + + TreeItem *item = search_options->create_item(p_parent_item); + item->set_text(0, p_name); + item->set_metadata(0, String(p_parent_item->get_metadata(0)) + ":" + p_name); + item->set_icon(0, search_options->get_editor_theme_icon(Variant::get_type_name(p_type))); + + _create_subproperties(item, p_type); +} + void PropertySelector::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { @@ -576,7 +662,6 @@ PropertySelector::PropertySelector() { search_options->connect("item_activated", callable_mp(this, &PropertySelector::_confirmed)); search_options->connect("cell_selected", callable_mp(this, &PropertySelector::_item_selected)); search_options->set_hide_root(true); - search_options->set_hide_folding(true); help_bit = memnew(EditorHelpBit); help_bit->set_content_height_limits(80 * EDSCALE, 80 * EDSCALE); diff --git a/editor/property_selector.h b/editor/property_selector.h index b162ee6cfd..34cade9267 100644 --- a/editor/property_selector.h +++ b/editor/property_selector.h @@ -36,6 +36,7 @@ class EditorHelpBit; class LineEdit; class Tree; +class TreeItem; class PropertySelector : public ConfirmationDialog { GDCLASS(PropertySelector, ConfirmationDialog); @@ -62,6 +63,9 @@ class PropertySelector : public ConfirmationDialog { Vector<Variant::Type> type_filter; + void _create_subproperties(TreeItem *p_parent_item, Variant::Type p_type); + void _create_subproperty(TreeItem *p_parent_item, const String &p_name, Variant::Type p_type); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 56924fc1fe..30050901d9 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -1551,7 +1551,7 @@ void SceneTreeDock::_notification(int p_what) { node_shortcuts_toggle->set_tooltip_text(TTR("Toggle the display of favorite nodes.")); node_shortcuts_toggle->set_pressed(EDITOR_GET("_use_favorites_root_selection")); node_shortcuts_toggle->set_anchors_and_offsets_preset(Control::PRESET_CENTER_RIGHT); - node_shortcuts_toggle->connect(SceneStringName(pressed), callable_mp(this, &SceneTreeDock::_update_create_root_dialog)); + node_shortcuts_toggle->connect(SceneStringName(pressed), callable_mp(this, &SceneTreeDock::_update_create_root_dialog).bind(false)); top_row->add_child(node_shortcuts_toggle); create_root_dialog->add_child(top_row); @@ -1601,7 +1601,7 @@ void SceneTreeDock::_notification(int p_what) { button_clipboard->set_icon(get_editor_theme_icon(SNAME("ActionPaste"))); button_clipboard->connect(SceneStringName(pressed), callable_mp(this, &SceneTreeDock::_tool_selected).bind(TOOL_PASTE, false)); - _update_create_root_dialog(); + _update_create_root_dialog(true); } break; case NOTIFICATION_ENTER_TREE: { @@ -4116,9 +4116,12 @@ void SceneTreeDock::_local_tree_selected() { edit_local->set_pressed(true); } -void SceneTreeDock::_update_create_root_dialog() { - EditorSettings::get_singleton()->set_setting("_use_favorites_root_selection", node_shortcuts_toggle->is_pressed()); - EditorSettings::get_singleton()->save(); +void SceneTreeDock::_update_create_root_dialog(bool p_initializing) { + if (!p_initializing) { + EditorSettings::get_singleton()->set_setting("_use_favorites_root_selection", node_shortcuts_toggle->is_pressed()); + EditorSettings::get_singleton()->save(); + } + if (node_shortcuts_toggle->is_pressed()) { for (int i = 0; i < favorite_node_shortcuts->get_child_count(); i++) { favorite_node_shortcuts->get_child(i)->queue_free(); @@ -4542,7 +4545,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec create_dialog->set_base_type("Node"); add_child(create_dialog); create_dialog->connect("create", callable_mp(this, &SceneTreeDock::_create)); - create_dialog->connect("favorites_updated", callable_mp(this, &SceneTreeDock::_update_create_root_dialog)); + create_dialog->connect("favorites_updated", callable_mp(this, &SceneTreeDock::_update_create_root_dialog).bind(false)); #ifdef MODULE_REGEX_ENABLED rename_dialog = memnew(RenameDialog(scene_tree)); diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index 5028cd5cc9..f01e6598cf 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -282,7 +282,7 @@ class SceneTreeDock : public VBoxContainer { void _remote_tree_selected(); void _local_tree_selected(); - void _update_create_root_dialog(); + void _update_create_root_dialog(bool p_initializing = false); void _favorite_root_selected(const String &p_class); void _feature_profile_changed(); diff --git a/editor/themes/editor_fonts.cpp b/editor/themes/editor_fonts.cpp index fc994a17d1..c50d1237e0 100644 --- a/editor/themes/editor_fonts.cpp +++ b/editor/themes/editor_fonts.cpp @@ -117,6 +117,7 @@ void editor_register_fonts(const Ref<Theme> &p_theme) { int font_hinting_setting = (int)EDITOR_GET("interface/editor/font_hinting"); TextServer::SubpixelPositioning font_subpixel_positioning = (TextServer::SubpixelPositioning)(int)EDITOR_GET("interface/editor/font_subpixel_positioning"); bool font_disable_embedded_bitmaps = (bool)EDITOR_GET("interface/editor/font_disable_embedded_bitmaps"); + bool font_allow_msdf = (bool)EDITOR_GET("interface/editor/font_allow_msdf"); TextServer::Hinting font_hinting; TextServer::Hinting font_mono_hinting; @@ -153,7 +154,7 @@ void editor_register_fonts(const Ref<Theme> &p_theme) { const float embolden_strength = 0.6; Ref<Font> default_font = load_internal_font(_font_NotoSans_Regular, _font_NotoSans_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, false); - Ref<Font> default_font_msdf = load_internal_font(_font_NotoSans_Regular, _font_NotoSans_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, true); + Ref<Font> default_font_msdf = load_internal_font(_font_NotoSans_Regular, _font_NotoSans_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, font_allow_msdf); TypedArray<Font> fallbacks; Ref<FontFile> arabic_font = load_internal_font(_font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, false, &fallbacks); @@ -173,7 +174,7 @@ void editor_register_fonts(const Ref<Theme> &p_theme) { default_font_msdf->set_fallbacks(fallbacks); Ref<FontFile> default_font_bold = load_internal_font(_font_NotoSans_Bold, _font_NotoSans_Bold_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, false); - Ref<FontFile> default_font_bold_msdf = load_internal_font(_font_NotoSans_Bold, _font_NotoSans_Bold_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, true); + Ref<FontFile> default_font_bold_msdf = load_internal_font(_font_NotoSans_Bold, _font_NotoSans_Bold_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, font_allow_msdf); TypedArray<Font> fallbacks_bold; Ref<FontFile> arabic_font_bold = load_internal_font(_font_NotoNaskhArabicUI_Bold, _font_NotoNaskhArabicUI_Bold_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, false, &fallbacks_bold); @@ -234,7 +235,7 @@ void editor_register_fonts(const Ref<Theme> &p_theme) { Ref<FontVariation> default_fc_msdf; default_fc_msdf.instantiate(); if (custom_font_path.length() > 0 && dir->file_exists(custom_font_path)) { - Ref<FontFile> custom_font = load_external_font(custom_font_path, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps); + Ref<FontFile> custom_font = load_external_font(custom_font_path, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, font_allow_msdf); { TypedArray<Font> fallback_custom; fallback_custom.push_back(default_font_msdf); @@ -277,7 +278,7 @@ void editor_register_fonts(const Ref<Theme> &p_theme) { Ref<FontVariation> bold_fc_msdf; bold_fc_msdf.instantiate(); if (custom_font_path_bold.length() > 0 && dir->file_exists(custom_font_path_bold)) { - Ref<FontFile> custom_font = load_external_font(custom_font_path_bold, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps); + Ref<FontFile> custom_font = load_external_font(custom_font_path_bold, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, font_allow_msdf); { TypedArray<Font> fallback_custom; fallback_custom.push_back(default_font_bold_msdf); @@ -285,7 +286,7 @@ void editor_register_fonts(const Ref<Theme> &p_theme) { } bold_fc_msdf->set_base_font(custom_font); } else if (custom_font_path.length() > 0 && dir->file_exists(custom_font_path)) { - Ref<FontFile> custom_font = load_external_font(custom_font_path, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps); + Ref<FontFile> custom_font = load_external_font(custom_font_path, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, font_allow_msdf); { TypedArray<Font> fallback_custom; fallback_custom.push_back(default_font_bold_msdf); diff --git a/editor/themes/editor_theme_manager.cpp b/editor/themes/editor_theme_manager.cpp index 9d8cbb053d..b271970f21 100644 --- a/editor/themes/editor_theme_manager.cpp +++ b/editor/themes/editor_theme_manager.cpp @@ -865,7 +865,7 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the // CheckBox. { Ref<StyleBoxFlat> checkbox_style = p_config.panel_container_style->duplicate(); - checkbox_style->set_content_margin_all(p_config.base_margin * EDSCALE); + checkbox_style->set_content_margin_individual((p_config.increased_margin + 2) * EDSCALE, p_config.base_margin * EDSCALE, (p_config.increased_margin + 2) * EDSCALE, p_config.base_margin * EDSCALE); p_theme->set_stylebox(CoreStringName(normal), "CheckBox", checkbox_style); p_theme->set_stylebox(SceneStringName(pressed), "CheckBox", checkbox_style); diff --git a/main/main.cpp b/main/main.cpp index e42469b51b..bdae1bb1b0 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -49,7 +49,7 @@ #include "core/os/os.h" #include "core/os/time.h" #include "core/register_core_types.h" -#include "core/string/translation.h" +#include "core/string/translation_server.h" #include "core/version.h" #include "drivers/register_driver_types.h" #include "main/app_icon.gen.h" @@ -1511,6 +1511,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph main_args.push_back(arg); main_args.push_back(N->get()); N = N->next(); + // GDScript docgen requires Autoloads, but loading those also creates a main loop. + // This forces main loop to quit without adding more GDScript-specific exceptions to setup. + quit_after = 1; } else { OS::get_singleton()->print("Missing relative or absolute path to project for --gdscript-docs, aborting.\n"); goto error; @@ -2053,7 +2056,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph BLOCK_DEVICE("Intel", "Intel HD Graphics P3000"); BLOCK_DEVICE("Intel", "Intel(R) HD Graphics P3000"); BLOCK_DEVICE("0x8086", "0x0112"); // HD Graphics P3000, Gen6, Sandy Bridge - BLOCK_DEVICE("0x8086", "0x0122"); // HD Graphics P3000, Gen6, Sandy Bridge + BLOCK_DEVICE("0x8086", "0x0122"); BLOCK_DEVICE("0x8086", "0x015A"); // HD Graphics, Gen7, Ivy Bridge BLOCK_DEVICE("Intel", "Intel HD Graphics 2500"); BLOCK_DEVICE("Intel", "Intel(R) HD Graphics 2500"); @@ -2061,10 +2064,81 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph BLOCK_DEVICE("Intel", "Intel HD Graphics 4000"); BLOCK_DEVICE("Intel", "Intel(R) HD Graphics 4000"); BLOCK_DEVICE("0x8086", "0x0162"); // HD Graphics 4000, Gen7, Ivy Bridge - BLOCK_DEVICE("0x8086", "0x0166"); // HD Graphics 4000, Gen7, Ivy Bridge + BLOCK_DEVICE("0x8086", "0x0166"); BLOCK_DEVICE("Intel", "Intel HD Graphics P4000"); BLOCK_DEVICE("Intel", "Intel(R) HD Graphics P4000"); BLOCK_DEVICE("0x8086", "0x016A"); // HD Graphics P4000, Gen7, Ivy Bridge + BLOCK_DEVICE("Intel", "Intel(R) Vallyview Graphics"); + BLOCK_DEVICE("0x8086", "0x0F30"); // Intel(R) Vallyview Graphics, Gen7, Vallyview + BLOCK_DEVICE("0x8086", "0x0F31"); + BLOCK_DEVICE("Intel", "Intel(R) HD Graphics 4200"); + BLOCK_DEVICE("0x8086", "0x0A1E"); // Intel(R) HD Graphics 4200, Gen7.5, Haswell + BLOCK_DEVICE("Intel", "Intel(R) HD Graphics 4400"); + BLOCK_DEVICE("0x8086", "0x0A16"); // Intel(R) HD Graphics 4400, Gen7.5, Haswell + BLOCK_DEVICE("Intel", "Intel(R) HD Graphics 4600"); + BLOCK_DEVICE("0x8086", "0x0412"); // Intel(R) HD Graphics 4600, Gen7.5, Haswell + BLOCK_DEVICE("0x8086", "0x0416"); + BLOCK_DEVICE("0x8086", "0x0426"); + BLOCK_DEVICE("0x8086", "0x0D12"); + BLOCK_DEVICE("0x8086", "0x0D16"); + BLOCK_DEVICE("Intel", "Intel(R) HD Graphics P4600/P4700"); + BLOCK_DEVICE("0x8086", "0x041A"); // Intel(R) HD Graphics P4600/P4700, Gen7.5, Haswell + BLOCK_DEVICE("Intel", "Intel(R) HD Graphics 5000"); + BLOCK_DEVICE("0x8086", "0x0422"); // Intel(R) HD Graphics 5000, Gen7.5, Haswell + BLOCK_DEVICE("0x8086", "0x042A"); + BLOCK_DEVICE("0x8086", "0x0A26"); + BLOCK_DEVICE("Intel", "Intel(R) Iris(TM) Graphics 5100"); + BLOCK_DEVICE("0x8086", "0x0A22"); // Intel(R) Iris(TM) Graphics 5100, Gen7.5, Haswell + BLOCK_DEVICE("0x8086", "0x0A2A"); + BLOCK_DEVICE("0x8086", "0x0A2B"); + BLOCK_DEVICE("0x8086", "0x0A2E"); + BLOCK_DEVICE("Intel", "Intel(R) Iris(TM) Pro Graphics 5200"); + BLOCK_DEVICE("0x8086", "0x0D22"); // Intel(R) Iris(TM) Pro Graphics 5200, Gen7.5, Haswell + BLOCK_DEVICE("0x8086", "0x0D26"); + BLOCK_DEVICE("0x8086", "0x0D2A"); + BLOCK_DEVICE("0x8086", "0x0D2B"); + BLOCK_DEVICE("0x8086", "0x0D2E"); + BLOCK_DEVICE("Intel", "Intel(R) HD Graphics 400"); + BLOCK_DEVICE("Intel", "Intel(R) HD Graphics 405"); + BLOCK_DEVICE("0x8086", "0x22B0"); // Intel(R) HD Graphics, Gen8, Cherryview Braswell + BLOCK_DEVICE("0x8086", "0x22B1"); + BLOCK_DEVICE("0x8086", "0x22B2"); + BLOCK_DEVICE("0x8086", "0x22B3"); + BLOCK_DEVICE("Intel", "Intel(R) HD Graphics 5300"); + BLOCK_DEVICE("0x8086", "0x161E"); // Intel(R) HD Graphics 5300, Gen8, Broadwell + BLOCK_DEVICE("Intel", "Intel(R) HD Graphics 5500"); + BLOCK_DEVICE("0x8086", "0x1616"); // Intel(R) HD Graphics 5500, Gen8, Broadwell + BLOCK_DEVICE("Intel", "Intel(R) HD Graphics 5600"); + BLOCK_DEVICE("0x8086", "0x1612"); // Intel(R) HD Graphics 5600, Gen8, Broadwell + BLOCK_DEVICE("Intel", "Intel(R) HD Graphics 6000"); + BLOCK_DEVICE("0x8086", "0x1626"); // Intel(R) HD Graphics 6000, Gen8, Broadwell + BLOCK_DEVICE("Intel", "Intel(R) Iris(TM) Graphics 6100"); + BLOCK_DEVICE("0x8086", "0x162B"); // Intel(R) Iris(TM) Graphics 6100, Gen8, Broadwell + BLOCK_DEVICE("Intel", "Intel(R) Iris(TM) Pro Graphics 6200"); + BLOCK_DEVICE("0x8086", "0x1622"); // Intel(R) Iris(TM) Pro Graphics 6200, Gen8, Broadwell + BLOCK_DEVICE("Intel", "Intel(R) Iris(TM) Pro Graphics P6300"); + BLOCK_DEVICE("0x8086", "0x162A"); // Intel(R) Iris(TM) Pro Graphics P6300, Gen8, Broadwell + BLOCK_DEVICE("Intel", "Intel(R) HD Graphics 500"); + BLOCK_DEVICE("Intel", "Intel(R) HD Graphics 505"); + BLOCK_DEVICE("Intel", "Intel(R) HD Graphics 510"); + BLOCK_DEVICE("0x8086", "0x1902"); // Intel(R) HD Graphics 510, Gen9, Skylake + BLOCK_DEVICE("0x8086", "0x1906"); + BLOCK_DEVICE("Intel", "Intel(R) HD Graphics 520"); + BLOCK_DEVICE("0x8086", "0x1916"); // Intel(R) HD Graphics 520, Gen9, Skylake + BLOCK_DEVICE("Intel", "Intel(R) HD Graphics 530"); + BLOCK_DEVICE("0x8086", "0x1912"); // Intel(R) HD Graphics 530, Gen9, Skylake + BLOCK_DEVICE("0x8086", "0x191B"); + BLOCK_DEVICE("Intel", "Intel(R) HD Graphics P530"); + BLOCK_DEVICE("0x8086", "0x191D"); // Intel(R) HD Graphics P530, Gen9, Skylake + BLOCK_DEVICE("Intel", "Intel(R) HD Graphics 515"); + BLOCK_DEVICE("0x8086", "0x191E"); // Intel(R) HD Graphics 515, Gen9, Skylake + BLOCK_DEVICE("Intel", "Intel(R) Iris Graphics 540"); + BLOCK_DEVICE("0x8086", "0x1926"); // Intel(R) Iris Graphics 540, Gen9, Skylake + BLOCK_DEVICE("0x8086", "0x1927"); + BLOCK_DEVICE("Intel", "Intel(R) Iris Pro Graphics 580"); + BLOCK_DEVICE("0x8086", "0x193B"); // Intel(R) Iris Pro Graphics 580, Gen9, Skylake + BLOCK_DEVICE("Intel", "Intel(R) Iris Pro Graphics P580"); + BLOCK_DEVICE("0x8086", "0x193D"); // Intel(R) Iris Pro Graphics P580, Gen9, Skylake #undef BLOCK_DEVICE @@ -2385,6 +2459,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph GLOBAL_DEF("debug/settings/stdout/print_fps", false); GLOBAL_DEF("debug/settings/stdout/print_gpu_profile", false); GLOBAL_DEF("debug/settings/stdout/verbose_stdout", false); + GLOBAL_DEF("debug/settings/physics_interpolation/enable_warnings", true); if (!OS::get_singleton()->_verbose_stdout) { // Not manually overridden. OS::get_singleton()->_verbose_stdout = GLOBAL_GET("debug/settings/stdout/verbose_stdout"); @@ -2465,7 +2540,10 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph OS::get_singleton()->benchmark_end_measure("Startup", "Main::Setup"); if (p_second_phase) { - return setup2(); + exit_err = setup2(); + if (exit_err != OK) { + goto error; + } } return OK; @@ -2763,6 +2841,30 @@ Error Main::setup2(bool p_show_boot_logo) { if (err != OK || display_server == nullptr) { ERR_PRINT("Unable to create DisplayServer, all display drivers failed.\nUse \"--headless\" command line argument to run the engine in headless mode if this is desired (e.g. for continuous integration)."); + + if (display_server) { + memdelete(display_server); + } + + GDExtensionManager::get_singleton()->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_SERVERS); + uninitialize_modules(MODULE_INITIALIZATION_LEVEL_SERVERS); + unregister_server_types(); + + if (input) { + memdelete(input); + } + if (tsman) { + memdelete(tsman); + } +#ifndef _3D_DISABLED + if (physics_server_3d_manager) { + memdelete(physics_server_3d_manager); + } +#endif // _3D_DISABLED + if (physics_server_2d_manager) { + memdelete(physics_server_2d_manager); + } + return err; } @@ -2947,9 +3049,6 @@ Error Main::setup2(bool p_show_boot_logo) { id->set_emulate_mouse_from_touch(bool(GLOBAL_DEF_BASIC("input_devices/pointing/emulate_mouse_from_touch", true))); } - GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_long_press_as_right_click", false); - GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_pan_and_scale_gestures", false); - GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "input_devices/pointing/android/rotary_input_scroll_axis", PROPERTY_HINT_ENUM, "Horizontal,Vertical"), 1); OS::get_singleton()->benchmark_end_measure("Startup", "Setup Window and Boot"); } @@ -3062,6 +3161,14 @@ Error Main::setup2(bool p_show_boot_logo) { OS::get_singleton()->benchmark_end_measure("Scene", "Modules and Extensions"); } + PackedStringArray extensions; + extensions.push_back("gd"); + if (ClassDB::class_exists("CSharpScript")) { + extensions.push_back("cs"); + } + extensions.push_back("gdshader"); + GLOBAL_DEF_NOVAL(PropertyInfo(Variant::PACKED_STRING_ARRAY, "editor/script/search_in_file_extensions"), extensions); // Note: should be defined after Scene level modules init to see .NET. + OS::get_singleton()->benchmark_end_measure("Startup", "Scene"); #ifdef TOOLS_ENABLED @@ -4054,16 +4161,16 @@ bool Main::iteration() { uint64_t physics_begin = OS::get_singleton()->get_ticks_usec(); -#ifndef _3D_DISABLED - PhysicsServer3D::get_singleton()->sync(); - PhysicsServer3D::get_singleton()->flush_queries(); -#endif // _3D_DISABLED - // Prepare the fixed timestep interpolated nodes BEFORE they are updated // by the physics server, otherwise the current and previous transforms // may be the same, and no interpolation takes place. OS::get_singleton()->get_main_loop()->iteration_prepare(); +#ifndef _3D_DISABLED + PhysicsServer3D::get_singleton()->sync(); + PhysicsServer3D::get_singleton()->flush_queries(); +#endif // _3D_DISABLED + PhysicsServer2D::get_singleton()->sync(); PhysicsServer2D::get_singleton()->flush_queries(); @@ -4073,6 +4180,7 @@ bool Main::iteration() { #endif // _3D_DISABLED PhysicsServer2D::get_singleton()->end_sync(); + Engine::get_singleton()->_in_physics = false; exit = true; break; } @@ -4096,6 +4204,8 @@ bool Main::iteration() { message_queue->flush(); + OS::get_singleton()->get_main_loop()->iteration_end(); + physics_process_ticks = MAX(physics_process_ticks, OS::get_singleton()->get_ticks_usec() - physics_begin); // keep the largest one for reference physics_process_max = MAX(OS::get_singleton()->get_ticks_usec() - physics_begin, physics_process_max); diff --git a/main/main_timer_sync.cpp b/main/main_timer_sync.cpp index d358d9fa93..569930d427 100644 --- a/main/main_timer_sync.cpp +++ b/main/main_timer_sync.cpp @@ -299,17 +299,6 @@ int64_t MainTimerSync::DeltaSmoother::smooth_delta(int64_t p_delta) { // before advance_core considers changing the physics_steps return from // the typical values as defined by typical_physics_steps double MainTimerSync::get_physics_jitter_fix() { - // Turn off jitter fix when using fixed timestep interpolation. - // Note this shouldn't be on UNTIL 3d interpolation is implemented, - // otherwise we will get people making 3d games with the physics_interpolation - // set to on getting jitter fix disabled unexpectedly. -#if 0 - if (Engine::get_singleton()->is_physics_interpolation_enabled()) { - // Would be better to write a simple bypass for jitter fix but this will do to get started. - return 0.0; - } -#endif - return Engine::get_singleton()->get_physics_jitter_fix(); } diff --git a/main/performance.cpp b/main/performance.cpp index 8eda697b16..0547b3bff0 100644 --- a/main/performance.cpp +++ b/main/performance.cpp @@ -91,6 +91,7 @@ void Performance::_bind_methods() { BIND_ENUM_CONSTANT(NAVIGATION_EDGE_MERGE_COUNT); BIND_ENUM_CONSTANT(NAVIGATION_EDGE_CONNECTION_COUNT); BIND_ENUM_CONSTANT(NAVIGATION_EDGE_FREE_COUNT); + BIND_ENUM_CONSTANT(NAVIGATION_OBSTACLE_COUNT); BIND_ENUM_CONSTANT(MONITOR_MAX); } @@ -141,6 +142,7 @@ String Performance::get_monitor_name(Monitor p_monitor) const { PNAME("navigation/edges_merged"), PNAME("navigation/edges_connected"), PNAME("navigation/edges_free"), + PNAME("navigation/obstacles"), }; @@ -225,6 +227,8 @@ double Performance::get_monitor(Monitor p_monitor) const { return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_EDGE_CONNECTION_COUNT); case NAVIGATION_EDGE_FREE_COUNT: return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_EDGE_FREE_COUNT); + case NAVIGATION_OBSTACLE_COUNT: + return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_OBSTACLE_COUNT); default: { } @@ -272,6 +276,7 @@ Performance::MonitorType Performance::get_monitor_type(Monitor p_monitor) const MONITOR_TYPE_QUANTITY, MONITOR_TYPE_QUANTITY, MONITOR_TYPE_QUANTITY, + MONITOR_TYPE_QUANTITY, }; diff --git a/main/performance.h b/main/performance.h index 34162b2da9..05d678fe55 100644 --- a/main/performance.h +++ b/main/performance.h @@ -100,6 +100,7 @@ public: NAVIGATION_EDGE_MERGE_COUNT, NAVIGATION_EDGE_CONNECTION_COUNT, NAVIGATION_EDGE_FREE_COUNT, + NAVIGATION_OBSTACLE_COUNT, MONITOR_MAX }; diff --git a/misc/extension_api_validation/4.3-stable.expected b/misc/extension_api_validation/4.3-stable.expected index a2112d71f2..24c7702090 100644 --- a/misc/extension_api_validation/4.3-stable.expected +++ b/misc/extension_api_validation/4.3-stable.expected @@ -7,3 +7,10 @@ should instead be used to justify these changes and describe how users should wo Add new entries at the end of the file. ## Changes between 4.3-stable and 4.4-stable + +GH-95374 +-------- +Validate extension JSON: Error: Field 'classes/ShapeCast2D/properties/collision_result': getter changed value in new API, from "_get_collision_result" to &"get_collision_result". +Validate extension JSON: Error: Field 'classes/ShapeCast3D/properties/collision_result': getter changed value in new API, from "_get_collision_result" to &"get_collision_result". + +These getters have been renamed to expose them. GDExtension language bindings couldn't have exposed these properties before. diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp index b2de6b656e..6ea44c5fc3 100644 --- a/modules/dds/texture_loader_dds.cpp +++ b/modules/dds/texture_loader_dds.cpp @@ -42,14 +42,18 @@ enum { DDSD_PITCH = 0x00000008, DDSD_LINEARSIZE = 0x00080000, DDSD_MIPMAPCOUNT = 0x00020000, - DDPF_FOURCC = 0x00000004, DDPF_ALPHAPIXELS = 0x00000001, - DDPF_RGB = 0x00000040 + DDPF_ALPHAONLY = 0x00000002, + DDPF_FOURCC = 0x00000004, + DDPF_RGB = 0x00000040, + DDPF_RG_SNORM = 0x00080000 }; enum DDSFourCC { DDFCC_DXT1 = PF_FOURCC("DXT1"), + DDFCC_DXT2 = PF_FOURCC("DXT2"), DDFCC_DXT3 = PF_FOURCC("DXT3"), + DDFCC_DXT4 = PF_FOURCC("DXT4"), DDFCC_DXT5 = PF_FOURCC("DXT5"), DDFCC_ATI1 = PF_FOURCC("ATI1"), DDFCC_BC4U = PF_FOURCC("BC4U"), @@ -68,17 +72,25 @@ enum DDSFourCC { // Reference: https://learn.microsoft.com/en-us/windows/win32/api/dxgiformat/ne-dxgiformat-dxgi_format enum DXGIFormat { DXGI_R32G32B32A32_FLOAT = 2, + DXGI_R32G32B32_FLOAT = 6, DXGI_R16G16B16A16_FLOAT = 10, DXGI_R32G32_FLOAT = 16, DXGI_R10G10B10A2_UNORM = 24, DXGI_R8G8B8A8_UNORM = 28, + DXGI_R8G8B8A8_UNORM_SRGB = 29, DXGI_R16G16_FLOAT = 34, DXGI_R32_FLOAT = 41, + DXGI_R8G8_UNORM = 49, DXGI_R16_FLOAT = 54, + DXGI_R8_UNORM = 61, + DXGI_A8_UNORM = 65, DXGI_R9G9B9E5 = 67, DXGI_BC1_UNORM = 71, + DXGI_BC1_UNORM_SRGB = 72, DXGI_BC2_UNORM = 74, + DXGI_BC2_UNORM_SRGB = 75, DXGI_BC3_UNORM = 77, + DXGI_BC3_UNORM_SRGB = 78, DXGI_BC4_UNORM = 80, DXGI_BC5_UNORM = 83, DXGI_B5G6R5_UNORM = 85, @@ -87,6 +99,7 @@ enum DXGIFormat { DXGI_BC6H_UF16 = 95, DXGI_BC6H_SF16 = 96, DXGI_BC7_UNORM = 98, + DXGI_BC7_UNORM_SRGB = 99, DXGI_B4G4R4A4_UNORM = 115 }; @@ -100,25 +113,29 @@ enum DDSFormat { DDS_ATI2, DDS_BC6U, DDS_BC6S, - DDS_BC7U, + DDS_BC7, DDS_R16F, DDS_RG16F, DDS_RGBA16F, DDS_R32F, DDS_RG32F, + DDS_RGB32F, DDS_RGBA32F, DDS_RGB9E5, - DDS_BGRA8, - DDS_BGR8, - DDS_RGBA8, DDS_RGB8, + DDS_RGBA8, + DDS_BGR8, + DDS_BGRA8, DDS_BGR5A1, DDS_BGR565, + DDS_B2GR3, + DDS_B2GR3A8, DDS_BGR10A2, DDS_RGB10A2, DDS_BGRA4, DDS_LUMINANCE, DDS_LUMINANCE_ALPHA, + DDS_LUMINANCE_ALPHA_4, DDS_MAX }; @@ -132,31 +149,35 @@ struct DDSFormatInfo { static const DDSFormatInfo dds_format_info[DDS_MAX] = { { "DXT1/BC1", true, 4, 8, Image::FORMAT_DXT1 }, - { "DXT3/BC2", true, 4, 16, Image::FORMAT_DXT3 }, - { "DXT5/BC3", true, 4, 16, Image::FORMAT_DXT5 }, + { "DXT2/DXT3/BC2", true, 4, 16, Image::FORMAT_DXT3 }, + { "DXT4/DXT5/BC3", true, 4, 16, Image::FORMAT_DXT5 }, { "ATI1/BC4", true, 4, 8, Image::FORMAT_RGTC_R }, { "ATI2/A2XY/BC5", true, 4, 16, Image::FORMAT_RGTC_RG }, - { "BC6U", true, 4, 16, Image::FORMAT_BPTC_RGBFU }, - { "BC6S", true, 4, 16, Image::FORMAT_BPTC_RGBF }, - { "BC7U", true, 4, 16, Image::FORMAT_BPTC_RGBA }, + { "BC6UF", true, 4, 16, Image::FORMAT_BPTC_RGBFU }, + { "BC6SF", true, 4, 16, Image::FORMAT_BPTC_RGBF }, + { "BC7", true, 4, 16, Image::FORMAT_BPTC_RGBA }, { "R16F", false, 1, 2, Image::FORMAT_RH }, { "RG16F", false, 1, 4, Image::FORMAT_RGH }, { "RGBA16F", false, 1, 8, Image::FORMAT_RGBAH }, { "R32F", false, 1, 4, Image::FORMAT_RF }, { "RG32F", false, 1, 8, Image::FORMAT_RGF }, + { "RGB32F", false, 1, 12, Image::FORMAT_RGBF }, { "RGBA32F", false, 1, 16, Image::FORMAT_RGBAF }, { "RGB9E5", false, 1, 4, Image::FORMAT_RGBE9995 }, - { "BGRA8", false, 1, 4, Image::FORMAT_RGBA8 }, - { "BGR8", false, 1, 3, Image::FORMAT_RGB8 }, - { "RGBA8", false, 1, 4, Image::FORMAT_RGBA8 }, { "RGB8", false, 1, 3, Image::FORMAT_RGB8 }, + { "RGBA8", false, 1, 4, Image::FORMAT_RGBA8 }, + { "BGR8", false, 1, 3, Image::FORMAT_RGB8 }, + { "BGRA8", false, 1, 4, Image::FORMAT_RGBA8 }, { "BGR5A1", false, 1, 2, Image::FORMAT_RGBA8 }, { "BGR565", false, 1, 2, Image::FORMAT_RGB8 }, + { "B2GR3", false, 1, 1, Image::FORMAT_RGB8 }, + { "B2GR3A8", false, 1, 2, Image::FORMAT_RGBA8 }, { "BGR10A2", false, 1, 4, Image::FORMAT_RGBA8 }, { "RGB10A2", false, 1, 4, Image::FORMAT_RGBA8 }, { "BGRA4", false, 1, 2, Image::FORMAT_RGBA8 }, { "GRAYSCALE", false, 1, 1, Image::FORMAT_L8 }, - { "GRAYSCALE_ALPHA", false, 1, 2, Image::FORMAT_LA8 } + { "GRAYSCALE_ALPHA", false, 1, 2, Image::FORMAT_LA8 }, + { "GRAYSCALE_ALPHA_4", false, 1, 1, Image::FORMAT_LA8 } }; static DDSFormat dxgi_to_dds_format(uint32_t p_dxgi_format) { @@ -164,6 +185,9 @@ static DDSFormat dxgi_to_dds_format(uint32_t p_dxgi_format) { case DXGI_R32G32B32A32_FLOAT: { return DDS_RGBA32F; } + case DXGI_R32G32B32_FLOAT: { + return DDS_RGB32F; + } case DXGI_R16G16B16A16_FLOAT: { return DDS_RGBA16F; } @@ -173,7 +197,8 @@ static DDSFormat dxgi_to_dds_format(uint32_t p_dxgi_format) { case DXGI_R10G10B10A2_UNORM: { return DDS_RGB10A2; } - case DXGI_R8G8B8A8_UNORM: { + case DXGI_R8G8B8A8_UNORM: + case DXGI_R8G8B8A8_UNORM_SRGB: { return DDS_RGBA8; } case DXGI_R16G16_FLOAT: { @@ -182,19 +207,29 @@ static DDSFormat dxgi_to_dds_format(uint32_t p_dxgi_format) { case DXGI_R32_FLOAT: { return DDS_R32F; } + case DXGI_R8_UNORM: + case DXGI_A8_UNORM: { + return DDS_LUMINANCE; + } case DXGI_R16_FLOAT: { return DDS_R16F; } + case DXGI_R8G8_UNORM: { + return DDS_LUMINANCE_ALPHA; + } case DXGI_R9G9B9E5: { return DDS_RGB9E5; } - case DXGI_BC1_UNORM: { + case DXGI_BC1_UNORM: + case DXGI_BC1_UNORM_SRGB: { return DDS_DXT1; } - case DXGI_BC2_UNORM: { + case DXGI_BC2_UNORM: + case DXGI_BC2_UNORM_SRGB: { return DDS_DXT3; } - case DXGI_BC3_UNORM: { + case DXGI_BC3_UNORM: + case DXGI_BC3_UNORM_SRGB: { return DDS_DXT5; } case DXGI_BC4_UNORM: { @@ -218,8 +253,9 @@ static DDSFormat dxgi_to_dds_format(uint32_t p_dxgi_format) { case DXGI_BC6H_SF16: { return DDS_BC6S; } - case DXGI_BC7_UNORM: { - return DDS_BC7U; + case DXGI_BC7_UNORM: + case DXGI_BC7_UNORM_SRGB: { + return DDS_BC7; } case DXGI_B4G4R4A4_UNORM: { return DDS_BGRA4; @@ -299,9 +335,11 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig case DDFCC_DXT1: { dds_format = DDS_DXT1; } break; + case DDFCC_DXT2: case DDFCC_DXT3: { dds_format = DDS_DXT3; } break; + case DDFCC_DXT4: case DDFCC_DXT5: { dds_format = DDS_DXT5; } break; @@ -363,6 +401,8 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig dds_format = DDS_RGB10A2; } else if (format_rgb_bits == 16 && format_red_mask == 0xf00 && format_green_mask == 0xf0 && format_blue_mask == 0xf && format_alpha_mask == 0xf000) { dds_format = DDS_BGRA4; + } else if (format_rgb_bits == 16 && format_red_mask == 0xe0 && format_green_mask == 0x1c && format_blue_mask == 0x3 && format_alpha_mask == 0xff00) { + dds_format = DDS_B2GR3A8; } } else { @@ -373,18 +413,38 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig dds_format = DDS_RGB8; } else if (format_rgb_bits == 16 && format_red_mask == 0x0000f800 && format_green_mask == 0x000007e0 && format_blue_mask == 0x0000001f) { dds_format = DDS_BGR565; + } else if (format_rgb_bits == 8 && format_red_mask == 0xe0 && format_green_mask == 0x1c && format_blue_mask == 0x3) { + dds_format = DDS_B2GR3; } } } else { // Other formats. - if (format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 16 && format_red_mask == 0xff && format_alpha_mask == 0xff00) { - dds_format = DDS_LUMINANCE_ALPHA; - } else if (!(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 8 && format_red_mask == 0xff) { + if (format_flags & DDPF_ALPHAONLY && format_rgb_bits == 8 && format_alpha_mask == 0xff) { + // Alpha only. dds_format = DDS_LUMINANCE; } } + // Depending on the writer, luminance formats may or may not have the DDPF_RGB or DDPF_LUMINANCE flags defined, + // so we check for these formats after everything else failed. + if (dds_format == DDS_MAX) { + if (format_flags & DDPF_ALPHAPIXELS) { + // With alpha. + if (format_rgb_bits == 16 && format_red_mask == 0xff && format_alpha_mask == 0xff00) { + dds_format = DDS_LUMINANCE_ALPHA; + } else if (format_rgb_bits == 8 && format_red_mask == 0xf && format_alpha_mask == 0xf0) { + dds_format = DDS_LUMINANCE_ALPHA_4; + } + + } else { + // Without alpha. + if (format_rgb_bits == 8 && format_red_mask == 0xff) { + dds_format = DDS_LUMINANCE; + } + } + } + // No format detected, error. if (dds_format == DDS_MAX) { ERR_FAIL_V_MSG(Ref<Resource>(), "Unrecognized or unsupported color layout in DDS '" + p_path + "'."); @@ -433,10 +493,24 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig } // Calculate the space these formats will take up after decoding. - if (dds_format == DDS_BGR565) { - size = size * 3 / 2; - } else if (dds_format == DDS_BGR5A1 || dds_format == DDS_BGRA4) { - size = size * 2; + switch (dds_format) { + case DDS_BGR565: + size = size * 3 / 2; + break; + + case DDS_BGR5A1: + case DDS_BGRA4: + case DDS_B2GR3A8: + case DDS_LUMINANCE_ALPHA_4: + size = size * 2; + break; + + case DDS_B2GR3: + size = size * 3; + break; + + default: + break; } src_data.resize(size); @@ -503,6 +577,44 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig } } break; + case DDS_B2GR3: { + // To RGB8. + int colcount = size / 3; + + for (int i = colcount - 1; i >= 0; i--) { + int src_ofs = i; + int dst_ofs = i * 3; + + uint8_t b = (wb[src_ofs] & 0x3) << 6; + uint8_t g = (wb[src_ofs] & 0x1C) << 3; + uint8_t r = (wb[src_ofs] & 0xE0); + + wb[dst_ofs] = r; + wb[dst_ofs + 1] = g; + wb[dst_ofs + 2] = b; + } + + } break; + case DDS_B2GR3A8: { + // To RGBA8. + int colcount = size / 4; + + for (int i = colcount - 1; i >= 0; i--) { + int src_ofs = i * 2; + int dst_ofs = i * 4; + + uint8_t b = (wb[src_ofs] & 0x3) << 6; + uint8_t g = (wb[src_ofs] & 0x1C) << 3; + uint8_t r = (wb[src_ofs] & 0xE0); + uint8_t a = wb[src_ofs + 1]; + + wb[dst_ofs] = r; + wb[dst_ofs + 1] = g; + wb[dst_ofs + 2] = b; + wb[dst_ofs + 3] = a; + } + + } break; case DDS_RGB10A2: { // To RGBA8. int colcount = size / 4; @@ -549,6 +661,8 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig } } break; + + // Channel-swapped. case DDS_BGRA8: { // To RGBA8. int colcount = size / 4; @@ -568,6 +682,24 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig } break; + // Grayscale. + case DDS_LUMINANCE_ALPHA_4: { + // To LA8. + int colcount = size / 2; + + for (int i = colcount - 1; i >= 0; i--) { + int src_ofs = i; + int dst_ofs = i * 2; + + uint8_t l = wb[src_ofs] & 0x0F; + uint8_t a = wb[src_ofs] & 0xF0; + + wb[dst_ofs] = (l << 4) | l; + wb[dst_ofs + 1] = a | (a >> 4); + } + + } break; + default: { } } diff --git a/modules/etcpak/image_compress_etcpak.cpp b/modules/etcpak/image_compress_etcpak.cpp index 95ed462070..14887ce469 100644 --- a/modules/etcpak/image_compress_etcpak.cpp +++ b/modules/etcpak/image_compress_etcpak.cpp @@ -50,6 +50,7 @@ EtcpakType _determine_etc_type(Image::UsedChannels p_channels) { return EtcpakType::ETCPAK_TYPE_ETC2; case Image::USED_CHANNELS_RGBA: return EtcpakType::ETCPAK_TYPE_ETC2_ALPHA; + default: return EtcpakType::ETCPAK_TYPE_ETC2_ALPHA; } @@ -69,6 +70,7 @@ EtcpakType _determine_dxt_type(Image::UsedChannels p_channels) { return EtcpakType::ETCPAK_TYPE_DXT1; case Image::USED_CHANNELS_RGBA: return EtcpakType::ETCPAK_TYPE_DXT5; + default: return EtcpakType::ETCPAK_TYPE_DXT5; } @@ -79,71 +81,86 @@ void _compress_etc1(Image *r_img) { } void _compress_etc2(Image *r_img, Image::UsedChannels p_channels) { - EtcpakType type = _determine_etc_type(p_channels); - _compress_etcpak(type, r_img); + _compress_etcpak(_determine_etc_type(p_channels), r_img); } void _compress_bc(Image *r_img, Image::UsedChannels p_channels) { - EtcpakType type = _determine_dxt_type(p_channels); - _compress_etcpak(type, r_img); + _compress_etcpak(_determine_dxt_type(p_channels), r_img); } -void _compress_etcpak(EtcpakType p_compresstype, Image *r_img) { +void _compress_etcpak(EtcpakType p_compress_type, Image *r_img) { uint64_t start_time = OS::get_singleton()->get_ticks_msec(); - Image::Format img_format = r_img->get_format(); - if (Image::is_format_compressed(img_format)) { - return; // Do not compress, already compressed. - } - if (img_format > Image::FORMAT_RGBA8) { - // TODO: we should be able to handle FORMAT_RGBA4444 and FORMAT_RGBA5551 eventually + // The image is already compressed, return. + if (r_img->is_compressed()) { return; } - // Use RGBA8 to convert. - if (img_format != Image::FORMAT_RGBA8) { - r_img->convert(Image::FORMAT_RGBA8); - } + // Convert to RGBA8 for compression. + r_img->convert(Image::FORMAT_RGBA8); // Determine output format based on Etcpak type. Image::Format target_format = Image::FORMAT_RGBA8; - if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC1) { - target_format = Image::FORMAT_ETC; - r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC. - } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2) { - target_format = Image::FORMAT_ETC2_RGB8; - r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC. - } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_R) { - target_format = Image::FORMAT_ETC2_R11; - r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC. - } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RG) { - target_format = Image::FORMAT_ETC2_RG11; - r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC. - } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG) { - target_format = Image::FORMAT_ETC2_RA_AS_RG; - r_img->convert_rg_to_ra_rgba8(); - r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC. - } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_ALPHA) { - target_format = Image::FORMAT_ETC2_RGBA8; - r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC. - } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT1) { - target_format = Image::FORMAT_DXT1; - } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5_RA_AS_RG) { - target_format = Image::FORMAT_DXT5_RA_AS_RG; - r_img->convert_rg_to_ra_rgba8(); - } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5) { - target_format = Image::FORMAT_DXT5; - } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_RGTC_R) { - target_format = Image::FORMAT_RGTC_R; - } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_RGTC_RG) { - target_format = Image::FORMAT_RGTC_RG; - } else { - ERR_FAIL_MSG("Invalid or unsupported etcpak compression format, not ETC or DXT."); + + switch (p_compress_type) { + case EtcpakType::ETCPAK_TYPE_ETC1: + target_format = Image::FORMAT_ETC; + break; + + case EtcpakType::ETCPAK_TYPE_ETC2: + target_format = Image::FORMAT_ETC2_RGB8; + break; + + case EtcpakType::ETCPAK_TYPE_ETC2_ALPHA: + target_format = Image::FORMAT_ETC2_RGBA8; + break; + + case EtcpakType::ETCPAK_TYPE_ETC2_R: + target_format = Image::FORMAT_ETC2_R11; + break; + + case EtcpakType::ETCPAK_TYPE_ETC2_RG: + target_format = Image::FORMAT_ETC2_RG11; + break; + + case EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG: + target_format = Image::FORMAT_ETC2_RA_AS_RG; + r_img->convert_rg_to_ra_rgba8(); + break; + + case EtcpakType::ETCPAK_TYPE_DXT1: + target_format = Image::FORMAT_DXT1; + break; + + case EtcpakType::ETCPAK_TYPE_DXT5: + target_format = Image::FORMAT_DXT5; + break; + + case EtcpakType::ETCPAK_TYPE_RGTC_R: + target_format = Image::FORMAT_RGTC_R; + break; + + case EtcpakType::ETCPAK_TYPE_RGTC_RG: + target_format = Image::FORMAT_RGTC_RG; + break; + + case EtcpakType::ETCPAK_TYPE_DXT5_RA_AS_RG: + target_format = Image::FORMAT_DXT5_RA_AS_RG; + r_img->convert_rg_to_ra_rgba8(); + break; + + default: + ERR_FAIL_MSG("Invalid or unsupported etcpak compression format, not ETC or DXT."); + break; } - // Compress image data and (if required) mipmaps. + // It's badly documented but ETCPAK seems to expect BGRA8 for ETC formats. + if (p_compress_type < EtcpakType::ETCPAK_TYPE_DXT1) { + r_img->convert_rgba8_to_bgra8(); + } - const bool mipmaps = r_img->has_mipmaps(); + // Compress image data and (if required) mipmaps. + const bool has_mipmaps = r_img->has_mipmaps(); int width = r_img->get_width(); int height = r_img->get_height(); @@ -164,109 +181,115 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img) { are used for a 2x2 map, and texel 'a' is used for 1x1. Note that this is similar to, but distinct from, the surface pitch, which can encompass additional padding beyond the physical surface size. */ - int next_width = width <= 2 ? width : (width + 3) & ~3; - int next_height = height <= 2 ? height : (height + 3) & ~3; - if (next_width != width || next_height != height) { - r_img->resize(next_width, next_height, Image::INTERPOLATE_LANCZOS); - width = r_img->get_width(); - height = r_img->get_height(); + + if (width % 4 != 0 || height % 4 != 0) { + width = width <= 2 ? width : (width + 3) & ~3; + height = height <= 2 ? height : (height + 3) & ~3; } - // ERR_FAIL_COND(width % 4 != 0 || height % 4 != 0); // FIXME: No longer guaranteed. + // Multiple-of-4 should be guaranteed by above. // However, power-of-two 3d textures will create Nx2 and Nx1 mipmap levels, // which are individually compressed Image objects that violate the above rule. // Hence, we allow Nx1 and Nx2 images through without forcing to multiple-of-4. - const uint8_t *src_read = r_img->get_data().ptr(); - - print_verbose(vformat("etcpak: Encoding image size %dx%d to format %s%s.", width, height, Image::get_format_name(target_format), mipmaps ? ", with mipmaps" : "")); - - int64_t dest_size = Image::get_image_data_size(width, height, target_format, mipmaps); + // Create the buffer for compressed image data. Vector<uint8_t> dest_data; - dest_data.resize(dest_size); + dest_data.resize(Image::get_image_data_size(width, height, target_format, has_mipmaps)); uint8_t *dest_write = dest_data.ptrw(); - int mip_count = mipmaps ? Image::get_image_required_mipmaps(width, height, target_format) : 0; + const uint8_t *src_read = r_img->get_data().ptr(); + + const int mip_count = has_mipmaps ? Image::get_image_required_mipmaps(width, height, target_format) : 0; Vector<uint32_t> padded_src; for (int i = 0; i < mip_count + 1; i++) { // Get write mip metrics for target image. - int orig_mip_w, orig_mip_h; - int64_t mip_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, target_format, i, orig_mip_w, orig_mip_h); + int dest_mip_w, dest_mip_h; + int64_t dest_mip_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, target_format, i, dest_mip_w, dest_mip_h); + // Ensure that mip offset is a multiple of 8 (etcpak expects uint64_t pointer). - ERR_FAIL_COND(mip_ofs % 8 != 0); - uint64_t *dest_mip_write = (uint64_t *)&dest_write[mip_ofs]; + ERR_FAIL_COND(dest_mip_ofs % 8 != 0); + uint64_t *dest_mip_write = reinterpret_cast<uint64_t *>(dest_write + dest_mip_ofs); - // Block size. Align stride to multiple of 4 (RGBA8). - int mip_w = (orig_mip_w + 3) & ~3; - int mip_h = (orig_mip_h + 3) & ~3; - const uint32_t blocks = mip_w * mip_h / 16; + // Block size. + dest_mip_w = (dest_mip_w + 3) & ~3; + dest_mip_h = (dest_mip_h + 3) & ~3; + const uint32_t blocks = dest_mip_w * dest_mip_h / 16; // Get mip data from source image for reading. - int64_t src_mip_ofs = r_img->get_mipmap_offset(i); - const uint32_t *src_mip_read = (const uint32_t *)&src_read[src_mip_ofs]; + int64_t src_mip_ofs, src_mip_size; + int src_mip_w, src_mip_h; + + r_img->get_mipmap_offset_size_and_dimensions(i, src_mip_ofs, src_mip_size, src_mip_w, src_mip_h); + + const uint32_t *src_mip_read = reinterpret_cast<const uint32_t *>(src_read + src_mip_ofs); // Pad textures to nearest block by smearing. - if (mip_w != orig_mip_w || mip_h != orig_mip_h) { - padded_src.resize(mip_w * mip_h); + if (dest_mip_w != src_mip_w || dest_mip_h != src_mip_h) { + // Reserve the buffer for padded image data. + padded_src.resize(dest_mip_w * dest_mip_h); uint32_t *ptrw = padded_src.ptrw(); + int x = 0, y = 0; - for (y = 0; y < orig_mip_h; y++) { - for (x = 0; x < orig_mip_w; x++) { - ptrw[mip_w * y + x] = src_mip_read[orig_mip_w * y + x]; + for (y = 0; y < src_mip_h; y++) { + for (x = 0; x < src_mip_w; x++) { + ptrw[dest_mip_w * y + x] = src_mip_read[src_mip_w * y + x]; } + // First, smear in x. - for (; x < mip_w; x++) { - ptrw[mip_w * y + x] = ptrw[mip_w * y + x - 1]; + for (; x < dest_mip_w; x++) { + ptrw[dest_mip_w * y + x] = ptrw[dest_mip_w * y + x - 1]; } } + // Then, smear in y. - for (; y < mip_h; y++) { - for (x = 0; x < mip_w; x++) { - ptrw[mip_w * y + x] = ptrw[mip_w * y + x - mip_w]; + for (; y < dest_mip_h; y++) { + for (x = 0; x < dest_mip_w; x++) { + ptrw[dest_mip_w * y + x] = ptrw[dest_mip_w * y + x - dest_mip_w]; } } + // Override the src_mip_read pointer to our temporary Vector. src_mip_read = padded_src.ptr(); } - switch (p_compresstype) { + switch (p_compress_type) { case EtcpakType::ETCPAK_TYPE_ETC1: - CompressEtc1RgbDither(src_mip_read, dest_mip_write, blocks, mip_w); + CompressEtc1RgbDither(src_mip_read, dest_mip_write, blocks, dest_mip_w); break; case EtcpakType::ETCPAK_TYPE_ETC2: - CompressEtc2Rgb(src_mip_read, dest_mip_write, blocks, mip_w, true); + CompressEtc2Rgb(src_mip_read, dest_mip_write, blocks, dest_mip_w, true); break; case EtcpakType::ETCPAK_TYPE_ETC2_ALPHA: case EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG: - CompressEtc2Rgba(src_mip_read, dest_mip_write, blocks, mip_w, true); + CompressEtc2Rgba(src_mip_read, dest_mip_write, blocks, dest_mip_w, true); break; case EtcpakType::ETCPAK_TYPE_ETC2_R: - CompressEacR(src_mip_read, dest_mip_write, blocks, mip_w); + CompressEacR(src_mip_read, dest_mip_write, blocks, dest_mip_w); break; case EtcpakType::ETCPAK_TYPE_ETC2_RG: - CompressEacRg(src_mip_read, dest_mip_write, blocks, mip_w); + CompressEacRg(src_mip_read, dest_mip_write, blocks, dest_mip_w); break; case EtcpakType::ETCPAK_TYPE_DXT1: - CompressDxt1Dither(src_mip_read, dest_mip_write, blocks, mip_w); + CompressDxt1Dither(src_mip_read, dest_mip_write, blocks, dest_mip_w); break; case EtcpakType::ETCPAK_TYPE_DXT5: case EtcpakType::ETCPAK_TYPE_DXT5_RA_AS_RG: - CompressDxt5(src_mip_read, dest_mip_write, blocks, mip_w); + CompressDxt5(src_mip_read, dest_mip_write, blocks, dest_mip_w); break; case EtcpakType::ETCPAK_TYPE_RGTC_R: - CompressBc4(src_mip_read, dest_mip_write, blocks, mip_w); + CompressBc4(src_mip_read, dest_mip_write, blocks, dest_mip_w); break; case EtcpakType::ETCPAK_TYPE_RGTC_RG: - CompressBc5(src_mip_read, dest_mip_write, blocks, mip_w); + CompressBc5(src_mip_read, dest_mip_write, blocks, dest_mip_w); break; default: @@ -276,7 +299,7 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img) { } // Replace original image with compressed one. - r_img->set_data(width, height, mipmaps, target_format, dest_data); + r_img->set_data(width, height, has_mipmaps, target_format, dest_data); print_verbose(vformat("etcpak: Encoding took %d ms.", OS::get_singleton()->get_ticks_msec() - start_time)); } diff --git a/modules/etcpak/image_compress_etcpak.h b/modules/etcpak/image_compress_etcpak.h index 9d5343740b..d50b322fe4 100644 --- a/modules/etcpak/image_compress_etcpak.h +++ b/modules/etcpak/image_compress_etcpak.h @@ -51,6 +51,6 @@ void _compress_etc1(Image *r_img); void _compress_etc2(Image *r_img, Image::UsedChannels p_channels); void _compress_bc(Image *r_img, Image::UsedChannels p_channels); -void _compress_etcpak(EtcpakType p_compresstype, Image *r_img); +void _compress_etcpak(EtcpakType p_compress_type, Image *r_img); #endif // IMAGE_COMPRESS_ETCPAK_H diff --git a/modules/fbx/fbx_document.cpp b/modules/fbx/fbx_document.cpp index b9d9ec7b6c..4d3f7554c0 100644 --- a/modules/fbx/fbx_document.cpp +++ b/modules/fbx/fbx_document.cpp @@ -2114,9 +2114,6 @@ Error FBXDocument::_parse(Ref<FBXState> p_state, String p_path, Ref<FileAccess> return OK; } -void FBXDocument::_bind_methods() { -} - Node *FBXDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, bool p_trimming, bool p_remove_immutable_tracks) { Ref<FBXState> state = p_state; ERR_FAIL_COND_V(state.is_null(), nullptr); diff --git a/modules/fbx/fbx_document.h b/modules/fbx/fbx_document.h index 4a3bb176c2..96f1905881 100644 --- a/modules/fbx/fbx_document.h +++ b/modules/fbx/fbx_document.h @@ -61,9 +61,6 @@ public: PackedByteArray generate_buffer(Ref<GLTFState> p_state) override; Error write_to_filesystem(Ref<GLTFState> p_state, const String &p_path) override; -protected: - static void _bind_methods(); - private: String _get_texture_path(const String &p_base_directory, const String &p_source_file_path) const; void _process_uv_set(PackedVector2Array &uv_array); diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index eaf2565e69..7bf5e946fb 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -2857,8 +2857,11 @@ GDScriptLanguage::GDScriptLanguage() { _debug_parse_err_line = -1; _debug_parse_err_file = ""; +#ifdef DEBUG_ENABLED profiling = false; + profile_native_calls = false; script_frame_time = 0; +#endif int dmcs = GLOBAL_DEF(PropertyInfo(Variant::INT, "debug/settings/gdscript/max_call_stack", PROPERTY_HINT_RANGE, "512," + itos(GDScriptFunction::MAX_CALL_DEPTH - 1) + ",1"), 1024); diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 4e78fbe302..6527a0ea4d 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -459,9 +459,11 @@ class GDScriptLanguage : public ScriptLanguage { friend class GDScriptFunction; SelfList<GDScriptFunction>::List function_list; +#ifdef DEBUG_ENABLED bool profiling; bool profile_native_calls; uint64_t script_frame_time; +#endif HashMap<String, ObjectID> orphan_subclasses; diff --git a/modules/gltf/README.md b/modules/gltf/README.md index 5d8966b201..2d1e92e070 100644 --- a/modules/gltf/README.md +++ b/modules/gltf/README.md @@ -1,11 +1,11 @@ -# Godot GLTF import and export module +# Godot glTF import and export module -In a nutshell, the GLTF module works like this: +In a nutshell, the glTF module works like this: -* The [`structures/`](structures/) folder contains GLTF structures, the - small pieces that make up a GLTF file, represented as C++ classes. -* The [`extensions/`](extensions/) folder contains GLTF extensions, which - are optional features that build on top of the base GLTF spec. +* The [`structures/`](structures/) folder contains glTF structures, the + small pieces that make up a glTF file, represented as C++ classes. +* The [`extensions/`](extensions/) folder contains glTF extensions, which + are optional features that build on top of the base glTF spec. * [`GLTFState`](gltf_state.h) holds collections of structures and extensions. * [`GLTFDocument`](gltf_document.h) operates on GLTFState and its elements. * The [`editor/`](editor/) folder uses GLTFDocument to import and export 3D models. diff --git a/modules/gltf/doc_classes/GLTFAccessor.xml b/modules/gltf/doc_classes/GLTFAccessor.xml index dd059e6b79..bc142797a3 100644 --- a/modules/gltf/doc_classes/GLTFAccessor.xml +++ b/modules/gltf/doc_classes/GLTFAccessor.xml @@ -1,11 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GLTFAccessor" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> - Represents a GLTF accessor. + Represents a glTF accessor. </brief_description> <description> - GLTFAccessor is a data structure representing GLTF a [code]accessor[/code] that would be found in the [code]"accessors"[/code] array. A buffer is a blob of binary data. A buffer view is a slice of a buffer. An accessor is a typed interpretation of the data in a buffer view. - Most custom data stored in GLTF does not need accessors, only buffer views (see [GLTFBufferView]). Accessors are for more advanced use cases such as interleaved mesh data encoded for the GPU. + GLTFAccessor is a data structure representing a glTF [code]accessor[/code] that would be found in the [code]"accessors"[/code] array. A buffer is a blob of binary data. A buffer view is a slice of a buffer. An accessor is a typed interpretation of the data in a buffer view. + Most custom data stored in glTF does not need accessors, only buffer views (see [GLTFBufferView]). Accessors are for more advanced use cases such as interleaved mesh data encoded for the GPU. </description> <tutorials> <link title="Buffers, BufferViews, and Accessors in Khronos glTF specification">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_005_BuffersBufferViewsAccessors.md</link> @@ -13,7 +13,7 @@ </tutorials> <members> <member name="accessor_type" type="int" setter="set_accessor_type" getter="get_accessor_type" enum="GLTFAccessor.GLTFAccessorType" default="0"> - The GLTF accessor type as an enum. Possible values are 0 for "SCALAR", 1 for "VEC2", 2 for "VEC3", 3 for "VEC4", 4 for "MAT2", 5 for "MAT3", and 6 for "MAT4". + The glTF accessor type as an enum. Possible values are 0 for "SCALAR", 1 for "VEC2", 2 for "VEC3", 3 for "VEC4", 4 for "MAT2", 5 for "MAT3", and 6 for "MAT4". </member> <member name="buffer_view" type="int" setter="set_buffer_view" getter="get_buffer_view" default="-1"> The index of the buffer view this accessor is referencing. If [code]-1[/code], this accessor is not referencing any buffer view. @@ -22,7 +22,7 @@ The offset relative to the start of the buffer view in bytes. </member> <member name="component_type" type="int" setter="set_component_type" getter="get_component_type" default="0"> - The GLTF component type as an enum. Possible values are 5120 for "BYTE", 5121 for "UNSIGNED_BYTE", 5122 for "SHORT", 5123 for "UNSIGNED_SHORT", 5125 for "UNSIGNED_INT", and 5126 for "FLOAT". A value of 5125 or "UNSIGNED_INT" must not be used for any accessor that is not referenced by mesh.primitive.indices. + The glTF component type as an enum. Possible values are 5120 for "BYTE", 5121 for "UNSIGNED_BYTE", 5122 for "SHORT", 5123 for "UNSIGNED_SHORT", 5125 for "UNSIGNED_INT", and 5126 for "FLOAT". A value of 5125 or "UNSIGNED_INT" must not be used for any accessor that is not referenced by mesh.primitive.indices. </member> <member name="count" type="int" setter="set_count" getter="get_count" default="0"> The number of elements referenced by this accessor. @@ -55,7 +55,7 @@ The offset relative to the start of the bufferView in bytes. </member> <member name="type" type="int" setter="set_type" getter="get_type" deprecated="Use [member accessor_type] instead."> - The GLTF accessor type as an enum. Use [member accessor_type] instead. + The glTF accessor type as an enum. Use [member accessor_type] instead. </member> </members> <constants> diff --git a/modules/gltf/doc_classes/GLTFAnimation.xml b/modules/gltf/doc_classes/GLTFAnimation.xml index 2c70f4461f..d269145bbd 100644 --- a/modules/gltf/doc_classes/GLTFAnimation.xml +++ b/modules/gltf/doc_classes/GLTFAnimation.xml @@ -13,7 +13,7 @@ <param index="0" name="extension_name" type="StringName" /> <description> Gets additional arbitrary data in this [GLTFAnimation] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless. - The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the return value can be anything you set. If nothing was set, the return value is null. + The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the return value can be anything you set. If nothing was set, the return value is null. </description> </method> <method name="set_additional_data"> @@ -22,7 +22,7 @@ <param index="1" name="additional_data" type="Variant" /> <description> Sets additional arbitrary data in this [GLTFAnimation] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless. - The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the second argument can be anything you want. + The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the second argument can be anything you want. </description> </method> </methods> diff --git a/modules/gltf/doc_classes/GLTFBufferView.xml b/modules/gltf/doc_classes/GLTFBufferView.xml index e191935fc9..b7f499ad72 100644 --- a/modules/gltf/doc_classes/GLTFBufferView.xml +++ b/modules/gltf/doc_classes/GLTFBufferView.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GLTFBufferView" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> - Represents a GLTF buffer view. + Represents a glTF buffer view. </brief_description> <description> - GLTFBufferView is a data structure representing GLTF a [code]bufferView[/code] that would be found in the [code]"bufferViews"[/code] array. A buffer is a blob of binary data. A buffer view is a slice of a buffer that can be used to identify and extract data from the buffer. + GLTFBufferView is a data structure representing a glTF [code]bufferView[/code] that would be found in the [code]"bufferViews"[/code] array. A buffer is a blob of binary data. A buffer view is a slice of a buffer that can be used to identify and extract data from the buffer. Most custom uses of buffers only need to use the [member buffer], [member byte_length], and [member byte_offset]. The [member byte_stride] and [member indices] properties are for more advanced use cases such as interleaved mesh data encoded for the GPU. </description> <tutorials> diff --git a/modules/gltf/doc_classes/GLTFCamera.xml b/modules/gltf/doc_classes/GLTFCamera.xml index b334bf2867..9fce21659c 100644 --- a/modules/gltf/doc_classes/GLTFCamera.xml +++ b/modules/gltf/doc_classes/GLTFCamera.xml @@ -1,15 +1,15 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GLTFCamera" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> - Represents a GLTF camera. + Represents a glTF camera. </brief_description> <description> - Represents a camera as defined by the base GLTF spec. + Represents a camera as defined by the base glTF spec. </description> <tutorials> <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> - <link title="GLTF camera detailed specification">https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-camera</link> - <link title="GLTF camera spec and example file">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_015_SimpleCameras.md</link> + <link title="glTF camera detailed specification">https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-camera</link> + <link title="glTF camera spec and example file">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_015_SimpleCameras.md</link> </tutorials> <methods> <method name="from_dictionary" qualifiers="static"> @@ -41,19 +41,19 @@ </methods> <members> <member name="depth_far" type="float" setter="set_depth_far" getter="get_depth_far" default="4000.0"> - The distance to the far culling boundary for this camera relative to its local Z axis, in meters. This maps to GLTF's [code]zfar[/code] property. + The distance to the far culling boundary for this camera relative to its local Z axis, in meters. This maps to glTF's [code]zfar[/code] property. </member> <member name="depth_near" type="float" setter="set_depth_near" getter="get_depth_near" default="0.05"> - The distance to the near culling boundary for this camera relative to its local Z axis, in meters. This maps to GLTF's [code]znear[/code] property. + The distance to the near culling boundary for this camera relative to its local Z axis, in meters. This maps to glTF's [code]znear[/code] property. </member> <member name="fov" type="float" setter="set_fov" getter="get_fov" default="1.309"> - The FOV of the camera. This class and GLTF define the camera FOV in radians, while Godot uses degrees. This maps to GLTF's [code]yfov[/code] property. This value is only used for perspective cameras, when [member perspective] is true. + The FOV of the camera. This class and glTF define the camera FOV in radians, while Godot uses degrees. This maps to glTF's [code]yfov[/code] property. This value is only used for perspective cameras, when [member perspective] is true. </member> <member name="perspective" type="bool" setter="set_perspective" getter="get_perspective" default="true"> - Whether or not the camera is in perspective mode. If false, the camera is in orthographic/orthogonal mode. This maps to GLTF's camera [code]type[/code] property. See [member Camera3D.projection] and the GLTF spec for more information. + Whether or not the camera is in perspective mode. If false, the camera is in orthographic/orthogonal mode. This maps to glTF's camera [code]type[/code] property. See [member Camera3D.projection] and the glTF spec for more information. </member> <member name="size_mag" type="float" setter="set_size_mag" getter="get_size_mag" default="0.5"> - The size of the camera. This class and GLTF define the camera size magnitude as a radius in meters, while Godot defines it as a diameter in meters. This maps to GLTF's [code]ymag[/code] property. This value is only used for orthographic/orthogonal cameras, when [member perspective] is false. + The size of the camera. This class and glTF define the camera size magnitude as a radius in meters, while Godot defines it as a diameter in meters. This maps to glTF's [code]ymag[/code] property. This value is only used for orthographic/orthogonal cameras, when [member perspective] is false. </member> </members> </class> diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml index 1b51def28e..ebeed015e9 100644 --- a/modules/gltf/doc_classes/GLTFDocument.xml +++ b/modules/gltf/doc_classes/GLTFDocument.xml @@ -5,7 +5,7 @@ </brief_description> <description> GLTFDocument supports reading data from a glTF file, buffer, or Godot scene. This data can then be written to the filesystem, buffer, or used to create a Godot scene. - All of the data in a GLTF scene is stored in the [GLTFState] class. GLTFDocument processes state objects, but does not contain any scene data itself. GLTFDocument has member variables to store export configuration settings such as the image format, but is otherwise stateless. Multiple scenes can be processed with the same settings using the same GLTFDocument object and different [GLTFState] objects. + All of the data in a glTF scene is stored in the [GLTFState] class. GLTFDocument processes state objects, but does not contain any scene data itself. GLTFDocument has member variables to store export configuration settings such as the image format, but is otherwise stateless. Multiple scenes can be processed with the same settings using the same GLTFDocument object and different [GLTFState] objects. GLTFDocument can be extended with arbitrary functionality by extending the [GLTFDocumentExtension] class and registering it with GLTFDocument via [method register_gltf_document_extension]. This allows for custom data to be imported and exported. </description> <tutorials> @@ -21,7 +21,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 imports the data to the given [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> @@ -32,7 +32,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 imports the data at that file path to the given [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> @@ -49,7 +49,7 @@ <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"> @@ -91,7 +91,7 @@ </methods> <members> <member name="image_format" type="String" setter="set_image_format" getter="get_image_format" default=""PNG""> - The user-friendly name of the export image format. This is used when exporting the GLTF file, including writing to a file and writing to a byte array. + The user-friendly name of the export image format. This is used when exporting the glTF file, including writing to a file and writing to a byte array. By default, Godot allows the following options: "None", "PNG", "JPEG", "Lossless WebP", and "Lossy WebP". Support for more image formats can be added in [GLTFDocumentExtension] classes. </member> <member name="lossy_quality" type="float" setter="set_lossy_quality" getter="get_lossy_quality" default="0.75"> diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml index 0eabcb5022..5c548c472f 100644 --- a/modules/gltf/doc_classes/GLTFDocumentExtension.xml +++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml @@ -4,7 +4,7 @@ [GLTFDocument] extension class. </brief_description> <description> - Extends the functionality of the [GLTFDocument] class by allowing you to run arbitrary code at various stages of GLTF import or export. + Extends the functionality of the [GLTFDocument] class by allowing you to run arbitrary code at various stages of glTF import or export. To use, make a new class extending GLTFDocumentExtension, override any methods you need, make an instance of your class, and register it using [method GLTFDocument.register_gltf_document_extension]. [b]Note:[/b] Like GLTFDocument itself, all GLTFDocumentExtension classes must be stateless in order to function properly. If you need to store data, use the [code]set_additional_data[/code] and [code]get_additional_data[/code] methods in [GLTFState] or [GLTFNode]. </description> @@ -30,7 +30,7 @@ <param index="3" name="node" type="Node" /> <description> Part of the export process. This method is run after [method _get_saveable_image_formats] and before [method _export_post]. If this [GLTFDocumentExtension] is used for exporting images, this runs after [method _serialize_texture_json]. - This method can be used to modify the final JSON of each node. Data should be primarily stored in [param gltf_node] prior to serializing the JSON, but the original Godot [param node] is also provided if available. The node may be null if not available, such as when exporting GLTF data not generated from a Godot scene. + This method can be used to modify the final JSON of each node. Data should be primarily stored in [param gltf_node] prior to serializing the JSON, but the original Godot [param node] is also provided if available. The node may be null if not available, such as when exporting glTF data not generated from a Godot scene. </description> </method> <method name="_export_post" qualifiers="virtual"> @@ -38,7 +38,7 @@ <param index="0" name="state" type="GLTFState" /> <description> Part of the export process. This method is run last, after all other parts of the export process. - This method can be used to modify the final JSON of the generated GLTF file. + This method can be used to modify the final JSON of the generated glTF file. </description> </method> <method name="_export_preflight" qualifiers="virtual"> @@ -47,7 +47,7 @@ <param index="1" name="root" type="Node" /> <description> Part of the export process. This method is run first, before all other parts of the export process. - The return value is used to determine if this [GLTFDocumentExtension] instance should be used for exporting a given GLTF file. If [constant OK], the export will use this [GLTFDocumentExtension] instance. If not overridden, [constant OK] is returned. + The return value is used to determine if this [GLTFDocumentExtension] instance should be used for exporting a given glTF file. If [constant OK], the export will use this [GLTFDocumentExtension] instance. If not overridden, [constant OK] is returned. </description> </method> <method name="_export_preserialize" qualifiers="virtual"> @@ -86,7 +86,7 @@ <return type="PackedStringArray" /> <description> Part of the import process. This method is run after [method _import_preflight] and before [method _parse_node_extensions]. - Returns an array of the GLTF extensions supported by this GLTFDocumentExtension class. This is used to validate if a GLTF file with required extensions can be loaded. + Returns an array of the glTF extensions supported by this GLTFDocumentExtension class. This is used to validate if a glTF file with required extensions can be loaded. </description> </method> <method name="_import_node" qualifiers="virtual"> @@ -123,7 +123,7 @@ <param index="1" name="extensions" type="PackedStringArray" /> <description> Part of the import process. This method is run first, before all other parts of the import process. - The return value is used to determine if this [GLTFDocumentExtension] instance should be used for importing a given GLTF file. If [constant OK], the import will use this [GLTFDocumentExtension] instance. If not overridden, [constant OK] is returned. + The return value is used to determine if this [GLTFDocumentExtension] instance should be used for importing a given glTF file. If [constant OK], the import will use this [GLTFDocumentExtension] instance. If not overridden, [constant OK] is returned. </description> </method> <method name="_parse_image_data" qualifiers="virtual"> @@ -134,7 +134,7 @@ <param index="3" name="ret_image" type="Image" /> <description> Part of the import process. This method is run after [method _parse_node_extensions] and before [method _parse_texture_json]. - Runs when parsing image data from a GLTF file. The data could be sourced from a separate file, a URI, or a buffer, and then is passed as a byte array. + Runs when parsing image data from a glTF file. The data could be sourced from a separate file, a URI, or a buffer, and then is passed as a byte array. </description> </method> <method name="_parse_node_extensions" qualifiers="virtual"> @@ -154,7 +154,7 @@ <param index="2" name="ret_gltf_texture" type="GLTFTexture" /> <description> Part of the import process. This method is run after [method _parse_image_data] and before [method _generate_scene_node]. - Runs when parsing the texture JSON from the GLTF textures array. This can be used to set the source image index to use as the texture. + Runs when parsing the texture JSON from the glTF textures array. This can be used to set the source image index to use as the texture. </description> </method> <method name="_save_image_at_path" qualifiers="virtual"> @@ -166,7 +166,7 @@ <param index="4" name="lossy_quality" type="float" /> <description> Part of the export process. This method is run after [method _get_saveable_image_formats] and before [method _serialize_texture_json]. - This method is run when saving images separately from the GLTF file. When images are embedded, [method _serialize_image_to_bytes] runs instead. Note that these methods only run when this [GLTFDocumentExtension] is selected as the image exporter. + This method is run when saving images separately from the glTF file. When images are embedded, [method _serialize_image_to_bytes] runs instead. Note that these methods only run when this [GLTFDocumentExtension] is selected as the image exporter. </description> </method> <method name="_serialize_image_to_bytes" qualifiers="virtual"> @@ -178,7 +178,7 @@ <param index="4" name="lossy_quality" type="float" /> <description> Part of the export process. This method is run after [method _get_saveable_image_formats] and before [method _serialize_texture_json]. - This method is run when embedding images in the GLTF file. When images are saved separately, [method _save_image_at_path] runs instead. Note that these methods only run when this [GLTFDocumentExtension] is selected as the image exporter. + This method is run when embedding images in the glTF file. When images are saved separately, [method _save_image_at_path] runs instead. Note that these methods only run when this [GLTFDocumentExtension] is selected as the image exporter. This method must set the image MIME type in the [param image_dict] with the [code]"mimeType"[/code] key. For example, for a PNG image, it would be set to [code]"image/png"[/code]. The return value must be a [PackedByteArray] containing the image data. </description> </method> diff --git a/modules/gltf/doc_classes/GLTFLight.xml b/modules/gltf/doc_classes/GLTFLight.xml index 87ea159e7c..e07d24a144 100644 --- a/modules/gltf/doc_classes/GLTFLight.xml +++ b/modules/gltf/doc_classes/GLTFLight.xml @@ -1,14 +1,14 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GLTFLight" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> - Represents a GLTF light. + Represents a glTF light. </brief_description> <description> - Represents a light as defined by the [code]KHR_lights_punctual[/code] GLTF extension. + Represents a light as defined by the [code]KHR_lights_punctual[/code] glTF extension. </description> <tutorials> <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> - <link title="KHR_lights_punctual GLTF extension spec">https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_lights_punctual</link> + <link title="KHR_lights_punctual glTF extension spec">https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_lights_punctual</link> </tutorials> <methods> <method name="from_dictionary" qualifiers="static"> @@ -70,7 +70,7 @@ At this angle, the light drops off to zero brightness. Between the inner and outer cone angles, there is a transition from full brightness to zero brightness. If this angle is a half turn, then the spotlight emits in all directions. When creating a Godot [SpotLight3D], the outer cone angle is used as the angle of the spotlight. </member> <member name="range" type="float" setter="set_range" getter="get_range" default="inf"> - The range of the light, beyond which the light has no effect. GLTF lights with no range defined behave like physical lights (which have infinite range). When creating a Godot light, the range is clamped to 4096. + The range of the light, beyond which the light has no effect. glTF lights with no range defined behave like physical lights (which have infinite range). When creating a Godot light, the range is clamped to 4096. </member> </members> </class> diff --git a/modules/gltf/doc_classes/GLTFMesh.xml b/modules/gltf/doc_classes/GLTFMesh.xml index b4c3db7618..da73c20c1d 100644 --- a/modules/gltf/doc_classes/GLTFMesh.xml +++ b/modules/gltf/doc_classes/GLTFMesh.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GLTFMesh" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> - GLTFMesh represents a GLTF mesh. + GLTFMesh represents a glTF mesh. </brief_description> <description> - GLTFMesh handles 3D mesh data imported from GLTF files. It includes properties for blend channels, blend weights, instance materials, and the mesh itself. + GLTFMesh handles 3D mesh data imported from glTF files. It includes properties for blend channels, blend weights, instance materials, and the mesh itself. </description> <tutorials> <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> @@ -15,7 +15,7 @@ <param index="0" name="extension_name" type="StringName" /> <description> Gets additional arbitrary data in this [GLTFMesh] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless. - The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the return value can be anything you set. If nothing was set, the return value is null. + The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the return value can be anything you set. If nothing was set, the return value is null. </description> </method> <method name="set_additional_data"> @@ -24,7 +24,7 @@ <param index="1" name="additional_data" type="Variant" /> <description> Sets additional arbitrary data in this [GLTFMesh] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless. - The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the second argument can be anything you want. + The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the second argument can be anything you want. </description> </method> </methods> diff --git a/modules/gltf/doc_classes/GLTFNode.xml b/modules/gltf/doc_classes/GLTFNode.xml index 4a7570e4bc..2786c25e9a 100644 --- a/modules/gltf/doc_classes/GLTFNode.xml +++ b/modules/gltf/doc_classes/GLTFNode.xml @@ -1,15 +1,15 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GLTFNode" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> - GLTF node class. + glTF node class. </brief_description> <description> - Represents a GLTF node. GLTF nodes may have names, transforms, children (other GLTF nodes), and more specialized properties (represented by their own classes). - GLTF nodes generally exist inside of [GLTFState] which represents all data of a GLTF file. Most of GLTFNode's properties are indices of other data in the GLTF file. You can extend a GLTF node with additional properties by using [method get_additional_data] and [method set_additional_data]. + Represents a glTF node. glTF nodes may have names, transforms, children (other glTF nodes), and more specialized properties (represented by their own classes). + glTF nodes generally exist inside of [GLTFState] which represents all data of a glTF file. Most of GLTFNode's properties are indices of other data in the glTF file. You can extend a glTF node with additional properties by using [method get_additional_data] and [method set_additional_data]. </description> <tutorials> <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> - <link title="GLTF scene and node spec">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_004_ScenesNodes.md"</link> + <link title="glTF scene and node spec">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_004_ScenesNodes.md"</link> </tutorials> <methods> <method name="get_additional_data"> @@ -17,7 +17,7 @@ <param index="0" name="extension_name" type="StringName" /> <description> Gets additional arbitrary data in this [GLTFNode] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless. - The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the return value can be anything you set. If nothing was set, the return value is null. + The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the return value can be anything you set. If nothing was set, the return value is null. </description> </method> <method name="set_additional_data"> @@ -26,25 +26,25 @@ <param index="1" name="additional_data" type="Variant" /> <description> Sets additional arbitrary data in this [GLTFNode] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless. - The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the second argument can be anything you want. + The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the second argument can be anything you want. </description> </method> </methods> <members> <member name="camera" type="int" setter="set_camera" getter="get_camera" default="-1"> - If this GLTF node is a camera, the index of the [GLTFCamera] in the [GLTFState] that describes the camera's properties. If -1, this node is not a camera. + If this glTF node is a camera, the index of the [GLTFCamera] in the [GLTFState] that describes the camera's properties. If -1, this node is not a camera. </member> <member name="children" type="PackedInt32Array" setter="set_children" getter="get_children" default="PackedInt32Array()"> - The indices of the child nodes in the [GLTFState]. If this GLTF node has no children, this will be an empty array. + The indices of the child nodes in the [GLTFState]. If this glTF node has no children, this will be an empty array. </member> <member name="height" type="int" setter="set_height" getter="get_height" default="-1"> How deep into the node hierarchy this node is. A root node will have a height of 0, its children will have a height of 1, and so on. If -1, the height has not been calculated. </member> <member name="light" type="int" setter="set_light" getter="get_light" default="-1"> - If this GLTF node is a light, the index of the [GLTFLight] in the [GLTFState] that describes the light's properties. If -1, this node is not a light. + If this glTF node is a light, the index of the [GLTFLight] in the [GLTFState] that describes the light's properties. If -1, this node is not a light. </member> <member name="mesh" type="int" setter="set_mesh" getter="get_mesh" default="-1"> - If this GLTF node is a mesh, the index of the [GLTFMesh] in the [GLTFState] that describes the mesh's properties. If -1, this node is not a mesh. + If this glTF node is a mesh, the index of the [GLTFMesh] in the [GLTFState] that describes the mesh's properties. If -1, this node is not a mesh. </member> <member name="original_name" type="String" setter="set_original_name" getter="get_original_name" default=""""> The original name of the node. @@ -53,22 +53,22 @@ The index of the parent node in the [GLTFState]. If -1, this node is a root node. </member> <member name="position" type="Vector3" setter="set_position" getter="get_position" default="Vector3(0, 0, 0)"> - The position of the GLTF node relative to its parent. + The position of the glTF node relative to its parent. </member> <member name="rotation" type="Quaternion" setter="set_rotation" getter="get_rotation" default="Quaternion(0, 0, 0, 1)"> - The rotation of the GLTF node relative to its parent. + The rotation of the glTF node relative to its parent. </member> <member name="scale" type="Vector3" setter="set_scale" getter="get_scale" default="Vector3(1, 1, 1)"> - The scale of the GLTF node relative to its parent. + The scale of the glTF node relative to its parent. </member> <member name="skeleton" type="int" setter="set_skeleton" getter="get_skeleton" default="-1"> - If this GLTF node has a skeleton, the index of the [GLTFSkeleton] in the [GLTFState] that describes the skeleton's properties. If -1, this node does not have a skeleton. + If this glTF node has a skeleton, the index of the [GLTFSkeleton] in the [GLTFState] that describes the skeleton's properties. If -1, this node does not have a skeleton. </member> <member name="skin" type="int" setter="set_skin" getter="get_skin" default="-1"> - If this GLTF node has a skin, the index of the [GLTFSkin] in the [GLTFState] that describes the skin's properties. If -1, this node does not have a skin. + If this glTF node has a skin, the index of the [GLTFSkin] in the [GLTFState] that describes the skin's properties. If -1, this node does not have a skin. </member> <member name="xform" type="Transform3D" setter="set_xform" getter="get_xform" default="Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)"> - The transform of the GLTF node relative to its parent. This property is usually unused since the position, rotation, and scale properties are preferred. + The transform of the glTF node relative to its parent. This property is usually unused since the position, rotation, and scale properties are preferred. </member> </members> </class> diff --git a/modules/gltf/doc_classes/GLTFPhysicsBody.xml b/modules/gltf/doc_classes/GLTFPhysicsBody.xml index cd701e2f2f..1a76b190ba 100644 --- a/modules/gltf/doc_classes/GLTFPhysicsBody.xml +++ b/modules/gltf/doc_classes/GLTFPhysicsBody.xml @@ -1,21 +1,21 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GLTFPhysicsBody" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> - Represents a GLTF physics body. + Represents a glTF physics body. </brief_description> <description> - Represents a physics body as an intermediary between the [code]OMI_physics_body[/code] GLTF data and Godot's nodes, and it's abstracted in a way that allows adding support for different GLTF physics extensions in the future. + Represents a physics body as an intermediary between the [code]OMI_physics_body[/code] glTF data and Godot's nodes, and it's abstracted in a way that allows adding support for different glTF physics extensions in the future. </description> <tutorials> <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> - <link title="OMI_physics_body GLTF extension">https://github.com/omigroup/gltf-extensions/tree/main/extensions/2.0/OMI_physics_body</link> + <link title="OMI_physics_body glTF extension">https://github.com/omigroup/gltf-extensions/tree/main/extensions/2.0/OMI_physics_body</link> </tutorials> <methods> <method name="from_dictionary" qualifiers="static"> <return type="GLTFPhysicsBody" /> <param index="0" name="dictionary" type="Dictionary" /> <description> - Creates a new GLTFPhysicsBody instance by parsing the given [Dictionary] in the [code]OMI_physics_body[/code] GLTF extension format. + Creates a new GLTFPhysicsBody instance by parsing the given [Dictionary] in the [code]OMI_physics_body[/code] glTF extension format. </description> </method> <method name="from_node" qualifiers="static"> @@ -28,7 +28,7 @@ <method name="to_dictionary" qualifiers="const"> <return type="Dictionary" /> <description> - Serializes this GLTFPhysicsBody instance into a [Dictionary]. It will be in the format expected by the [code]OMI_physics_body[/code] GLTF extension. + Serializes this GLTFPhysicsBody instance into a [Dictionary]. It will be in the format expected by the [code]OMI_physics_body[/code] glTF extension. </description> </method> <method name="to_node" qualifiers="const"> diff --git a/modules/gltf/doc_classes/GLTFPhysicsShape.xml b/modules/gltf/doc_classes/GLTFPhysicsShape.xml index a4aaf9415c..53872a942f 100644 --- a/modules/gltf/doc_classes/GLTFPhysicsShape.xml +++ b/modules/gltf/doc_classes/GLTFPhysicsShape.xml @@ -1,15 +1,15 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GLTFPhysicsShape" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> - Represents a GLTF physics shape. + Represents a glTF physics shape. </brief_description> <description> - Represents a physics shape as defined by the [code]OMI_physics_shape[/code] or [code]OMI_collider[/code] GLTF extensions. This class is an intermediary between the GLTF data and Godot's nodes, and it's abstracted in a way that allows adding support for different GLTF physics extensions in the future. + Represents a physics shape as defined by the [code]OMI_physics_shape[/code] or [code]OMI_collider[/code] glTF extensions. This class is an intermediary between the glTF data and Godot's nodes, and it's abstracted in a way that allows adding support for different glTF physics extensions in the future. </description> <tutorials> <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> - <link title="OMI_physics_shape GLTF extension">https://github.com/omigroup/gltf-extensions/tree/main/extensions/2.0/OMI_physics_shape</link> - <link title="OMI_collider GLTF extension">https://github.com/omigroup/gltf-extensions/tree/main/extensions/2.0/Archived/OMI_collider</link> + <link title="OMI_physics_shape glTF extension">https://github.com/omigroup/gltf-extensions/tree/main/extensions/2.0/OMI_physics_shape</link> + <link title="OMI_collider glTF extension">https://github.com/omigroup/gltf-extensions/tree/main/extensions/2.0/Archived/OMI_collider</link> </tutorials> <methods> <method name="from_dictionary" qualifiers="static"> @@ -66,7 +66,7 @@ This is the only variable not used in the [method to_node] method, it's intended to be used alongside when deciding where to add the generated node as a child. </member> <member name="mesh_index" type="int" setter="set_mesh_index" getter="get_mesh_index" default="-1"> - The index of the shape's mesh in the GLTF file. This is only used when the shape type is "hull" (convex hull) or "trimesh" (concave trimesh). + The index of the shape's mesh in the glTF file. This is only used when the shape type is "hull" (convex hull) or "trimesh" (concave trimesh). </member> <member name="radius" type="float" setter="set_radius" getter="get_radius" default="0.5"> The radius of the shape, in meters. This is only used when the shape type is "capsule", "cylinder", or "sphere". This value should not be negative. diff --git a/modules/gltf/doc_classes/GLTFSkeleton.xml b/modules/gltf/doc_classes/GLTFSkeleton.xml index ac03a6ee9e..2dd3a37413 100644 --- a/modules/gltf/doc_classes/GLTFSkeleton.xml +++ b/modules/gltf/doc_classes/GLTFSkeleton.xml @@ -22,7 +22,7 @@ <method name="get_godot_bone_node"> <return type="Dictionary" /> <description> - Returns a [Dictionary] that maps skeleton bone indices to the indices of GLTF nodes. This property is unused during import, and only set during export. In a GLTF file, a bone is a node, so Godot converts skeleton bones to GLTF nodes. + Returns a [Dictionary] that maps skeleton bone indices to the indices of glTF nodes. This property is unused during import, and only set during export. In a glTF file, a bone is a node, so Godot converts skeleton bones to glTF nodes. </description> </method> <method name="get_godot_skeleton"> @@ -39,7 +39,7 @@ <return type="void" /> <param index="0" name="godot_bone_node" type="Dictionary" /> <description> - Sets a [Dictionary] that maps skeleton bone indices to the indices of GLTF nodes. This property is unused during import, and only set during export. In a GLTF file, a bone is a node, so Godot converts skeleton bones to GLTF nodes. + Sets a [Dictionary] that maps skeleton bone indices to the indices of glTF nodes. This property is unused during import, and only set during export. In a glTF file, a bone is a node, so Godot converts skeleton bones to glTF nodes. </description> </method> <method name="set_unique_names"> diff --git a/modules/gltf/doc_classes/GLTFSpecGloss.xml b/modules/gltf/doc_classes/GLTFSpecGloss.xml index 722fa5e9ae..11151f53d0 100644 --- a/modules/gltf/doc_classes/GLTFSpecGloss.xml +++ b/modules/gltf/doc_classes/GLTFSpecGloss.xml @@ -1,14 +1,14 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GLTFSpecGloss" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> - Archived GLTF extension for specular/glossy materials. + Archived glTF extension for specular/glossy materials. </brief_description> <description> - KHR_materials_pbrSpecularGlossiness is an archived GLTF extension. This means that it is deprecated and not recommended for new files. However, it is still supported for loading old files. + KHR_materials_pbrSpecularGlossiness is an archived glTF extension. This means that it is deprecated and not recommended for new files. However, it is still supported for loading old files. </description> <tutorials> <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> - <link title="KHR_materials_pbrSpecularGlossiness GLTF extension spec">https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Archived/KHR_materials_pbrSpecularGlossiness</link> + <link title="KHR_materials_pbrSpecularGlossiness glTF extension spec">https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Archived/KHR_materials_pbrSpecularGlossiness</link> </tutorials> <members> <member name="diffuse_factor" type="Color" setter="set_diffuse_factor" getter="get_diffuse_factor" default="Color(1, 1, 1, 1)"> diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml index 21a0527813..c049acf557 100644 --- a/modules/gltf/doc_classes/GLTFState.xml +++ b/modules/gltf/doc_classes/GLTFState.xml @@ -1,15 +1,15 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GLTFState" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> - Represents all data of a GLTF file. + Represents all data of a glTF file. </brief_description> <description> - Contains all nodes and resources of a GLTF file. This is used by [GLTFDocument] as data storage, which allows [GLTFDocument] and all [GLTFDocumentExtension] classes to remain stateless. - GLTFState can be populated by [GLTFDocument] reading a file or by converting a Godot scene. Then the data can either be used to create a Godot scene or save to a GLTF file. The code that converts to/from a Godot scene can be intercepted at arbitrary points by [GLTFDocumentExtension] classes. This allows for custom data to be stored in the GLTF file or for custom data to be converted to/from Godot nodes. + Contains all nodes and resources of a glTF file. This is used by [GLTFDocument] as data storage, which allows [GLTFDocument] and all [GLTFDocumentExtension] classes to remain stateless. + GLTFState can be populated by [GLTFDocument] reading a file or by converting a Godot scene. Then the data can either be used to create a Godot scene or save to a glTF file. The code that converts to/from a Godot scene can be intercepted at arbitrary points by [GLTFDocumentExtension] classes. This allows for custom data to be stored in the glTF file or for custom data to be converted to/from Godot nodes. </description> <tutorials> <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> - <link title="GLTF asset header schema">https://github.com/KhronosGroup/glTF/blob/main/specification/2.0/schema/asset.schema.json"</link> + <link title="glTF asset header schema">https://github.com/KhronosGroup/glTF/blob/main/specification/2.0/schema/asset.schema.json"</link> </tutorials> <methods> <method name="add_used_extension"> @@ -17,7 +17,7 @@ <param index="0" name="extension_name" type="String" /> <param index="1" name="required" type="bool" /> <description> - Appends an extension to the list of extensions used by this GLTF file during serialization. If [param required] is true, the extension will also be added to the list of required extensions. Do not run this in [method GLTFDocumentExtension._export_post], as that stage is too late to add extensions. The final list is sorted alphabetically. + Appends an extension to the list of extensions used by this glTF file during serialization. If [param required] is true, the extension will also be added to the list of required extensions. Do not run this in [method GLTFDocumentExtension._export_post], as that stage is too late to add extensions. The final list is sorted alphabetically. </description> </method> <method name="append_data_to_buffers"> @@ -38,27 +38,27 @@ <param index="0" name="extension_name" type="StringName" /> <description> Gets additional arbitrary data in this [GLTFState] instance. This can be used to keep per-file state data in [GLTFDocumentExtension] classes, which is important because they are stateless. - The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the return value can be anything you set. If nothing was set, the return value is null. + The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the return value can be anything you set. If nothing was set, the return value is null. </description> </method> <method name="get_animation_player"> <return type="AnimationPlayer" /> <param index="0" name="idx" type="int" /> <description> - Returns the [AnimationPlayer] node with the given index. These nodes are only used during the export process when converting Godot [AnimationPlayer] nodes to GLTF animations. + Returns the [AnimationPlayer] node with the given index. These nodes are only used during the export process when converting Godot [AnimationPlayer] nodes to glTF animations. </description> </method> <method name="get_animation_players_count"> <return type="int" /> <param index="0" name="idx" type="int" /> <description> - Returns the number of [AnimationPlayer] nodes in this [GLTFState]. These nodes are only used during the export process when converting Godot [AnimationPlayer] nodes to GLTF animations. + Returns the number of [AnimationPlayer] nodes in this [GLTFState]. These nodes are only used during the export process when converting Godot [AnimationPlayer] nodes to glTF animations. </description> </method> <method name="get_animations"> <return type="GLTFAnimation[]" /> <description> - Returns an array of all [GLTFAnimation]s in the GLTF file. When importing, these will be generated as animations in an [AnimationPlayer] node. When exporting, these will be generated from Godot [AnimationPlayer] nodes. + Returns an array of all [GLTFAnimation]s in the glTF file. When importing, these will be generated as animations in an [AnimationPlayer] node. When exporting, these will be generated from Godot [AnimationPlayer] nodes. </description> </method> <method name="get_buffer_views"> @@ -69,7 +69,7 @@ <method name="get_cameras"> <return type="GLTFCamera[]" /> <description> - Returns an array of all [GLTFCamera]s in the GLTF file. These are the cameras that the [member GLTFNode.camera] index refers to. + Returns an array of all [GLTFCamera]s in the glTF file. These are the cameras that the [member GLTFNode.camera] index refers to. </description> </method> <method name="get_handle_binary_image"> @@ -80,13 +80,13 @@ <method name="get_images"> <return type="Texture2D[]" /> <description> - Gets the images of the GLTF file as an array of [Texture2D]s. These are the images that the [member GLTFTexture.src_image] index refers to. + Gets the images of the glTF file as an array of [Texture2D]s. These are the images that the [member GLTFTexture.src_image] index refers to. </description> </method> <method name="get_lights"> <return type="GLTFLight[]" /> <description> - Returns an array of all [GLTFLight]s in the GLTF file. These are the lights that the [member GLTFNode.light] index refers to. + Returns an array of all [GLTFLight]s in the glTF file. These are the lights that the [member GLTFNode.light] index refers to. </description> </method> <method name="get_materials"> @@ -97,7 +97,7 @@ <method name="get_meshes"> <return type="GLTFMesh[]" /> <description> - Returns an array of all [GLTFMesh]es in the GLTF file. These are the meshes that the [member GLTFNode.mesh] index refers to. + Returns an array of all [GLTFMesh]es in the glTF file. These are the meshes that the [member GLTFNode.mesh] index refers to. </description> </method> <method name="get_node_index"> @@ -111,7 +111,7 @@ <method name="get_nodes"> <return type="GLTFNode[]" /> <description> - Returns an array of all [GLTFNode]s in the GLTF file. These are the nodes that [member GLTFNode.children] and [member root_nodes] refer to. This includes nodes that may not be generated in the Godot scene, or nodes that may generate multiple Godot scene nodes. + Returns an array of all [GLTFNode]s in the glTF file. These are the nodes that [member GLTFNode.children] and [member root_nodes] refer to. This includes nodes that may not be generated in the Godot scene, or nodes that may generate multiple Godot scene nodes. </description> </method> <method name="get_scene_node"> @@ -125,19 +125,19 @@ <method name="get_skeletons"> <return type="GLTFSkeleton[]" /> <description> - Returns an array of all [GLTFSkeleton]s in the GLTF file. These are the skeletons that the [member GLTFNode.skeleton] index refers to. + Returns an array of all [GLTFSkeleton]s in the glTF file. These are the skeletons that the [member GLTFNode.skeleton] index refers to. </description> </method> <method name="get_skins"> <return type="GLTFSkin[]" /> <description> - Returns an array of all [GLTFSkin]s in the GLTF file. These are the skins that the [member GLTFNode.skin] index refers to. + Returns an array of all [GLTFSkin]s in the glTF file. These are the skins that the [member GLTFNode.skin] index refers to. </description> </method> <method name="get_texture_samplers"> <return type="GLTFTextureSampler[]" /> <description> - Retrieves the array of texture samplers that are used by the textures contained in the GLTF. + Retrieves the array of texture samplers that are used by the textures contained in the glTF. </description> </method> <method name="get_textures"> @@ -169,7 +169,7 @@ <param index="1" name="additional_data" type="Variant" /> <description> Sets additional arbitrary data in this [GLTFState] instance. This can be used to keep per-file state data in [GLTFDocumentExtension] classes, which is important because they are stateless. - The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the second argument can be anything you want. + The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the second argument can be anything you want. </description> </method> <method name="set_animations"> @@ -250,7 +250,7 @@ <return type="void" /> <param index="0" name="texture_samplers" type="GLTFTextureSampler[]" /> <description> - Sets the array of texture samplers that are used by the textures contained in the GLTF. + Sets the array of texture samplers that are used by the textures contained in the glTF. </description> </method> <method name="set_textures"> @@ -279,17 +279,17 @@ The baking fps of the animation for either import or export. </member> <member name="base_path" type="String" setter="set_base_path" getter="get_base_path" default=""""> - The folder path associated with this GLTF data. This is used to find other files the GLTF file references, like images or binary buffers. This will be set during import when appending from a file, and will be set during export when writing to a file. + The folder path associated with this glTF data. This is used to find other files the glTF file references, like images or binary buffers. This will be set during import when appending from a file, and will be set during export when writing to a file. </member> <member name="buffers" type="PackedByteArray[]" setter="set_buffers" getter="get_buffers" default="[]"> </member> <member name="copyright" type="String" setter="set_copyright" getter="get_copyright" default=""""> - The copyright string in the asset header of the GLTF file. This is set during import if present and export if non-empty. See the GLTF asset header documentation for more information. + The copyright string in the asset header of the glTF file. This is set during import if present and export if non-empty. See the glTF asset header documentation for more information. </member> <member name="create_animations" type="bool" setter="set_create_animations" getter="get_create_animations" default="true"> </member> <member name="filename" type="String" setter="set_filename" getter="get_filename" default=""""> - The file name associated with this GLTF data. If it ends with [code].gltf[/code], this is text-based GLTF, otherwise this is binary GLB. This will be set during import when appending from a file, and will be set during export when writing to a file. If writing to a buffer, this will be an empty string. + The file name associated with this glTF data. If it ends with [code].gltf[/code], this is text-based glTF, otherwise this is binary GLB. This will be set during import when appending from a file, and will be set during export when writing to a file. If writing to a buffer, this will be an empty string. </member> <member name="glb_data" type="PackedByteArray" setter="set_glb_data" getter="get_glb_data" default="PackedByteArray()"> The binary buffer attached to a .glb file. @@ -305,10 +305,10 @@ <member name="minor_version" type="int" setter="set_minor_version" getter="get_minor_version" default="0"> </member> <member name="root_nodes" type="PackedInt32Array" setter="set_root_nodes" getter="get_root_nodes" default="PackedInt32Array()"> - The root nodes of the GLTF file. Typically, a GLTF file will only have one scene, and therefore one root node. However, a GLTF file may have multiple scenes and therefore multiple root nodes, which will be generated as siblings of each other and as children of the root node of the generated Godot scene. + The root nodes of the glTF file. Typically, a glTF file will only have one scene, and therefore one root node. However, a glTF file may have multiple scenes and therefore multiple root nodes, which will be generated as siblings of each other and as children of the root node of the generated Godot scene. </member> <member name="scene_name" type="String" setter="set_scene_name" getter="get_scene_name" default=""""> - The name of the scene. When importing, if not specified, this will be the file name. When exporting, if specified, the scene name will be saved to the GLTF file. + The name of the scene. When importing, if not specified, this will be the file name. When exporting, if specified, the scene name will be saved to the glTF file. </member> <member name="use_named_skin_binds" type="bool" setter="set_use_named_skin_binds" getter="get_use_named_skin_binds" default="false"> </member> diff --git a/modules/gltf/doc_classes/GLTFTexture.xml b/modules/gltf/doc_classes/GLTFTexture.xml index 9ad7c0f4c6..2a868a8ba3 100644 --- a/modules/gltf/doc_classes/GLTFTexture.xml +++ b/modules/gltf/doc_classes/GLTFTexture.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GLTFTexture" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> - GLTFTexture represents a texture in a GLTF file. + GLTFTexture represents a texture in a glTF file. </brief_description> <description> </description> diff --git a/modules/gltf/doc_classes/GLTFTextureSampler.xml b/modules/gltf/doc_classes/GLTFTextureSampler.xml index 2b5bad6724..d00ab463c2 100644 --- a/modules/gltf/doc_classes/GLTFTextureSampler.xml +++ b/modules/gltf/doc_classes/GLTFTextureSampler.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GLTFTextureSampler" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> - Represents a GLTF texture sampler + Represents a glTF texture sampler </brief_description> <description> - Represents a texture sampler as defined by the base GLTF spec. Texture samplers in GLTF specify how to sample data from the texture's base image, when rendering the texture on an object. + Represents a texture sampler as defined by the base glTF spec. Texture samplers in glTF specify how to sample data from the texture's base image, when rendering the texture on an object. </description> <tutorials> <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> diff --git a/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp index 07faee3dfc..676f764c11 100644 --- a/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp +++ b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp @@ -34,9 +34,6 @@ #include "scene/3d/mesh_instance_3d.h" #include "scene/resources/3d/importer_mesh.h" -void GLTFDocumentExtensionConvertImporterMesh::_bind_methods() { -} - void GLTFDocumentExtensionConvertImporterMesh::_copy_meta(Object *p_src_object, Object *p_dst_object) { List<StringName> meta_list; p_src_object->get_meta_list(&meta_list); diff --git a/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.h b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.h index ca10444eb5..b216a47a7f 100644 --- a/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.h +++ b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.h @@ -37,7 +37,6 @@ class GLTFDocumentExtensionConvertImporterMesh : public GLTFDocumentExtension { GDCLASS(GLTFDocumentExtensionConvertImporterMesh, GLTFDocumentExtension); protected: - static void _bind_methods(); static void _copy_meta(Object *p_src_object, Object *p_dst_object); public: diff --git a/modules/gltf/extensions/gltf_light.cpp b/modules/gltf/extensions/gltf_light.cpp index c1d2fea98b..f6e91c1635 100644 --- a/modules/gltf/extensions/gltf_light.cpp +++ b/modules/gltf/extensions/gltf_light.cpp @@ -170,7 +170,7 @@ Light3D *GLTFLight::to_node() const { } Ref<GLTFLight> GLTFLight::from_dictionary(const Dictionary p_dictionary) { - ERR_FAIL_COND_V_MSG(!p_dictionary.has("type"), Ref<GLTFLight>(), "Failed to parse GLTF light, missing required field 'type'."); + ERR_FAIL_COND_V_MSG(!p_dictionary.has("type"), Ref<GLTFLight>(), "Failed to parse glTF light, missing required field 'type'."); Ref<GLTFLight> light; light.instantiate(); const String &type = p_dictionary["type"]; @@ -181,7 +181,7 @@ Ref<GLTFLight> GLTFLight::from_dictionary(const Dictionary p_dictionary) { if (arr.size() == 3) { light->color = Color(arr[0], arr[1], arr[2]).linear_to_srgb(); } else { - ERR_PRINT("Error parsing GLTF light: The color must have exactly 3 numbers."); + ERR_PRINT("Error parsing glTF light: The color must have exactly 3 numbers."); } } if (p_dictionary.has("intensity")) { @@ -195,10 +195,10 @@ Ref<GLTFLight> GLTFLight::from_dictionary(const Dictionary p_dictionary) { light->inner_cone_angle = spot["innerConeAngle"]; light->outer_cone_angle = spot["outerConeAngle"]; if (light->inner_cone_angle >= light->outer_cone_angle) { - ERR_PRINT("Error parsing GLTF light: The inner angle must be smaller than the outer angle."); + ERR_PRINT("Error parsing glTF light: The inner angle must be smaller than the outer angle."); } } else if (type != "point" && type != "directional") { - ERR_PRINT("Error parsing GLTF light: Light type '" + type + "' is unknown."); + ERR_PRINT("Error parsing glTF light: Light type '" + type + "' is unknown."); } return light; } diff --git a/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp b/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp index 7e52cde059..5c26a1686b 100644 --- a/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp +++ b/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp @@ -88,7 +88,7 @@ Error GLTFDocumentExtensionPhysics::parse_node_extensions(Ref<GLTFState> p_state // "collider" is the index of the collider in the state colliders array. int node_collider_index = node_collider_ext["collider"]; Array state_colliders = p_state->get_additional_data(StringName("GLTFPhysicsShapes")); - ERR_FAIL_INDEX_V_MSG(node_collider_index, state_colliders.size(), Error::ERR_FILE_CORRUPT, "GLTF Physics: On node " + p_gltf_node->get_name() + ", the collider index " + itos(node_collider_index) + " is not in the state colliders (size: " + itos(state_colliders.size()) + ")."); + ERR_FAIL_INDEX_V_MSG(node_collider_index, state_colliders.size(), Error::ERR_FILE_CORRUPT, "glTF Physics: On node " + p_gltf_node->get_name() + ", the collider index " + itos(node_collider_index) + " is not in the state colliders (size: " + itos(state_colliders.size()) + ")."); p_gltf_node->set_additional_data(StringName("GLTFPhysicsShape"), state_colliders[node_collider_index]); } else { p_gltf_node->set_additional_data(StringName("GLTFPhysicsShape"), GLTFPhysicsShape::from_dictionary(node_collider_ext)); @@ -103,7 +103,7 @@ Error GLTFDocumentExtensionPhysics::parse_node_extensions(Ref<GLTFState> p_state int node_shape_index = node_collider.get("shape", -1); if (node_shape_index != -1) { Array state_shapes = p_state->get_additional_data(StringName("GLTFPhysicsShapes")); - ERR_FAIL_INDEX_V_MSG(node_shape_index, state_shapes.size(), Error::ERR_FILE_CORRUPT, "GLTF Physics: On node " + p_gltf_node->get_name() + ", the shape index " + itos(node_shape_index) + " is not in the state shapes (size: " + itos(state_shapes.size()) + ")."); + ERR_FAIL_INDEX_V_MSG(node_shape_index, state_shapes.size(), Error::ERR_FILE_CORRUPT, "glTF Physics: On node " + p_gltf_node->get_name() + ", the shape index " + itos(node_shape_index) + " is not in the state shapes (size: " + itos(state_shapes.size()) + ")."); p_gltf_node->set_additional_data(StringName("GLTFPhysicsColliderShape"), state_shapes[node_shape_index]); } else { // If this node is a collider but does not have a collider @@ -117,7 +117,7 @@ Error GLTFDocumentExtensionPhysics::parse_node_extensions(Ref<GLTFState> p_state int node_shape_index = node_trigger.get("shape", -1); if (node_shape_index != -1) { Array state_shapes = p_state->get_additional_data(StringName("GLTFPhysicsShapes")); - ERR_FAIL_INDEX_V_MSG(node_shape_index, state_shapes.size(), Error::ERR_FILE_CORRUPT, "GLTF Physics: On node " + p_gltf_node->get_name() + ", the shape index " + itos(node_shape_index) + " is not in the state shapes (size: " + itos(state_shapes.size()) + ")."); + ERR_FAIL_INDEX_V_MSG(node_shape_index, state_shapes.size(), Error::ERR_FILE_CORRUPT, "glTF Physics: On node " + p_gltf_node->get_name() + ", the shape index " + itos(node_shape_index) + " is not in the state shapes (size: " + itos(state_shapes.size()) + ")."); p_gltf_node->set_additional_data(StringName("GLTFPhysicsTriggerShape"), state_shapes[node_shape_index]); } else { // If this node is a trigger but does not have a trigger shape, @@ -150,7 +150,7 @@ void _setup_shape_mesh_resource_from_index_if_needed(Ref<GLTFState> p_state, Ref return; // The mesh resource is already set up. } TypedArray<GLTFMesh> state_meshes = p_state->get_meshes(); - ERR_FAIL_INDEX_MSG(shape_mesh_index, state_meshes.size(), "GLTF Physics: When importing '" + p_state->get_scene_name() + "', the shape mesh index " + itos(shape_mesh_index) + " is not in the state meshes (size: " + itos(state_meshes.size()) + ")."); + ERR_FAIL_INDEX_MSG(shape_mesh_index, state_meshes.size(), "glTF Physics: When importing '" + p_state->get_scene_name() + "', the shape mesh index " + itos(shape_mesh_index) + " is not in the state meshes (size: " + itos(state_meshes.size()) + ")."); Ref<GLTFMesh> gltf_mesh = state_meshes[shape_mesh_index]; ERR_FAIL_COND(gltf_mesh.is_null()); importer_mesh = gltf_mesh->get_mesh(); @@ -164,12 +164,12 @@ CollisionObject3D *_generate_shape_with_body(Ref<GLTFState> p_state, Ref<GLTFNod bool is_trigger = p_physics_shape->get_is_trigger(); // This method is used for the case where we must generate a parent body. // This is can happen for multiple reasons. One possibility is that this - // GLTF file is using OMI_collider but not OMI_physics_body, or at least + // glTF file is using OMI_collider but not OMI_physics_body, or at least // this particular node is not using it. Another possibility is that the - // physics body information is set up on the same GLTF node, not a parent. + // physics body information is set up on the same glTF node, not a parent. CollisionObject3D *body; if (p_physics_body.is_valid()) { - // This code is run when the physics body is on the same GLTF node. + // This code is run when the physics body is on the same glTF node. body = p_physics_body->to_node(); if (is_trigger && (p_physics_body->get_body_type() != "trigger")) { // Edge case: If the body's trigger and the collider's trigger @@ -266,7 +266,7 @@ Node3D *GLTFDocumentExtensionPhysics::generate_scene_node(Ref<GLTFState> p_state Ref<GLTFPhysicsShape> gltf_physics_shape = p_gltf_node->get_additional_data(StringName("GLTFPhysicsShape")); if (gltf_physics_shape.is_valid()) { _setup_shape_mesh_resource_from_index_if_needed(p_state, gltf_physics_shape); - // If this GLTF node specifies both a shape and a body, generate both. + // If this glTF node specifies both a shape and a body, generate both. if (gltf_physics_body.is_valid()) { return _generate_shape_with_body(p_state, p_gltf_node, gltf_physics_shape, gltf_physics_body); } @@ -309,7 +309,7 @@ Node3D *GLTFDocumentExtensionPhysics::generate_scene_node(Ref<GLTFState> p_state } } else if (!Object::cast_to<PhysicsBody3D>(ancestor_col_obj)) { if (p_gltf_node->get_additional_data(StringName("GLTFPhysicsCompoundCollider"))) { - // If the GLTF file wants this node to group solid shapes together, + // If the glTF file wants this node to group solid shapes together, // and there is no parent body, we need to create a static body. ancestor_col_obj = memnew(StaticBody3D); ret = ancestor_col_obj; @@ -386,7 +386,7 @@ void GLTFDocumentExtensionPhysics::convert_scene_node(Ref<GLTFState> p_state, Re if (cast_to<CollisionShape3D>(p_scene_node)) { CollisionShape3D *godot_shape = Object::cast_to<CollisionShape3D>(p_scene_node); Ref<GLTFPhysicsShape> gltf_shape = GLTFPhysicsShape::from_node(godot_shape); - ERR_FAIL_COND_MSG(gltf_shape.is_null(), "GLTF Physics: Could not convert CollisionShape3D to GLTFPhysicsShape. Does it have a valid Shape3D?"); + ERR_FAIL_COND_MSG(gltf_shape.is_null(), "glTF Physics: Could not convert CollisionShape3D to GLTFPhysicsShape. Does it have a valid Shape3D?"); { Ref<ImporterMesh> importer_mesh = gltf_shape->get_importer_mesh(); if (importer_mesh.is_valid()) { diff --git a/modules/gltf/extensions/physics/gltf_physics_body.cpp b/modules/gltf/extensions/physics/gltf_physics_body.cpp index 7929b46542..c11aa5d2ff 100644 --- a/modules/gltf/extensions/physics/gltf_physics_body.cpp +++ b/modules/gltf/extensions/physics/gltf_physics_body.cpp @@ -108,7 +108,7 @@ void GLTFPhysicsBody::set_body_type(String p_body_type) { } else if (p_body_type == "trigger") { body_type = PhysicsBodyType::TRIGGER; } else { - ERR_PRINT("Error setting GLTF physics body type: The body type must be one of \"static\", \"animatable\", \"character\", \"rigid\", \"vehicle\", or \"trigger\"."); + ERR_PRINT("Error setting glTF physics body type: The body type must be one of \"static\", \"animatable\", \"character\", \"rigid\", \"vehicle\", or \"trigger\"."); } } @@ -194,7 +194,7 @@ Ref<GLTFPhysicsBody> GLTFPhysicsBody::from_node(const CollisionObject3D *p_body_ physics_body->center_of_mass = body->get_center_of_mass(); physics_body->inertia_diagonal = body->get_inertia(); if (body->get_center_of_mass() != Vector3()) { - WARN_PRINT("GLTFPhysicsBody: This rigid body has a center of mass offset from the origin, which will be ignored when exporting to GLTF."); + WARN_PRINT("GLTFPhysicsBody: This rigid body has a center of mass offset from the origin, which will be ignored when exporting to glTF."); } if (cast_to<VehicleBody3D>(p_body_node)) { physics_body->body_type = PhysicsBodyType::VEHICLE; @@ -289,7 +289,7 @@ Ref<GLTFPhysicsBody> GLTFPhysicsBody::from_dictionary(const Dictionary p_diction physics_body->body_type = PhysicsBodyType::TRIGGER; #endif // DISABLE_DEPRECATED } else { - ERR_PRINT("Error parsing GLTF physics body: The body type in the GLTF file \"" + body_type_string + "\" was not recognized."); + ERR_PRINT("Error parsing glTF physics body: The body type in the glTF file \"" + body_type_string + "\" was not recognized."); } } if (motion.has("mass")) { @@ -300,7 +300,7 @@ Ref<GLTFPhysicsBody> GLTFPhysicsBody::from_dictionary(const Dictionary p_diction if (arr.size() == 3) { physics_body->set_linear_velocity(Vector3(arr[0], arr[1], arr[2])); } else { - ERR_PRINT("Error parsing GLTF physics body: The linear velocity vector must have exactly 3 numbers."); + ERR_PRINT("Error parsing glTF physics body: The linear velocity vector must have exactly 3 numbers."); } } if (motion.has("angularVelocity")) { @@ -308,7 +308,7 @@ Ref<GLTFPhysicsBody> GLTFPhysicsBody::from_dictionary(const Dictionary p_diction if (arr.size() == 3) { physics_body->set_angular_velocity(Vector3(arr[0], arr[1], arr[2])); } else { - ERR_PRINT("Error parsing GLTF physics body: The angular velocity vector must have exactly 3 numbers."); + ERR_PRINT("Error parsing glTF physics body: The angular velocity vector must have exactly 3 numbers."); } } if (motion.has("centerOfMass")) { @@ -316,7 +316,7 @@ Ref<GLTFPhysicsBody> GLTFPhysicsBody::from_dictionary(const Dictionary p_diction if (arr.size() == 3) { physics_body->set_center_of_mass(Vector3(arr[0], arr[1], arr[2])); } else { - ERR_PRINT("Error parsing GLTF physics body: The center of mass vector must have exactly 3 numbers."); + ERR_PRINT("Error parsing glTF physics body: The center of mass vector must have exactly 3 numbers."); } } if (motion.has("inertiaDiagonal")) { @@ -324,7 +324,7 @@ Ref<GLTFPhysicsBody> GLTFPhysicsBody::from_dictionary(const Dictionary p_diction if (arr.size() == 3) { physics_body->set_inertia_diagonal(Vector3(arr[0], arr[1], arr[2])); } else { - ERR_PRINT("Error parsing GLTF physics body: The inertia diagonal vector must have exactly 3 numbers."); + ERR_PRINT("Error parsing glTF physics body: The inertia diagonal vector must have exactly 3 numbers."); } } if (motion.has("inertiaOrientation")) { @@ -332,7 +332,7 @@ Ref<GLTFPhysicsBody> GLTFPhysicsBody::from_dictionary(const Dictionary p_diction if (arr.size() == 4) { physics_body->set_inertia_orientation(Quaternion(arr[0], arr[1], arr[2], arr[3])); } else { - ERR_PRINT("Error parsing GLTF physics body: The inertia orientation quaternion must have exactly 4 numbers."); + ERR_PRINT("Error parsing glTF physics body: The inertia orientation quaternion must have exactly 4 numbers."); } } return physics_body; diff --git a/modules/gltf/extensions/physics/gltf_physics_shape.cpp b/modules/gltf/extensions/physics/gltf_physics_shape.cpp index 6897bdbd3a..0340eb11b5 100644 --- a/modules/gltf/extensions/physics/gltf_physics_shape.cpp +++ b/modules/gltf/extensions/physics/gltf_physics_shape.cpp @@ -134,7 +134,7 @@ void GLTFPhysicsShape::set_importer_mesh(Ref<ImporterMesh> p_importer_mesh) { Ref<ImporterMesh> _convert_hull_points_to_mesh(const Vector<Vector3> &p_hull_points) { Ref<ImporterMesh> importer_mesh; - ERR_FAIL_COND_V_MSG(p_hull_points.size() < 3, importer_mesh, "GLTFPhysicsShape: Convex hull has fewer points (" + itos(p_hull_points.size()) + ") than the minimum of 3. At least 3 points are required in order to save to GLTF, since it uses a mesh to represent convex hulls."); + ERR_FAIL_COND_V_MSG(p_hull_points.size() < 3, importer_mesh, "GLTFPhysicsShape: Convex hull has fewer points (" + itos(p_hull_points.size()) + ") than the minimum of 3. At least 3 points are required in order to save to glTF, since it uses a mesh to represent convex hulls."); if (p_hull_points.size() > 255) { WARN_PRINT("GLTFPhysicsShape: Convex hull has more points (" + itos(p_hull_points.size()) + ") than the recommended maximum of 255. This may not load correctly in other engines."); } diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index dff1e62e82..d98b250538 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -620,7 +620,7 @@ Error GLTFDocument::_parse_nodes(Ref<GLTFState> p_state) { for (Ref<GLTFDocumentExtension> ext : document_extensions) { ERR_CONTINUE(ext.is_null()); Error err = ext->parse_node_extensions(p_state, node, extensions); - ERR_CONTINUE_MSG(err != OK, "GLTF: Encountered error " + itos(err) + " when parsing node extensions for node " + node->get_name() + " in file " + p_state->filename + ". Continuing."); + ERR_CONTINUE_MSG(err != OK, "glTF: Encountered error " + itos(err) + " when parsing node extensions for node " + node->get_name() + " in file " + p_state->filename + ". Continuing."); } } @@ -3353,7 +3353,7 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state) { ERR_CONTINUE(image.is_null()); if (image->is_compressed()) { image->decompress(); - ERR_FAIL_COND_V_MSG(image->is_compressed(), ERR_INVALID_DATA, "GLTF: Image was compressed, but could not be decompressed."); + ERR_FAIL_COND_V_MSG(image->is_compressed(), ERR_INVALID_DATA, "glTF: Image was compressed, but could not be decompressed."); } if (p_state->filename.to_lower().ends_with("gltf")) { @@ -3374,7 +3374,7 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state) { if (_image_save_extension.is_valid()) { img_name = img_name + _image_save_extension->get_image_file_extension(); Error err = _image_save_extension->save_image_at_path(p_state, image, full_texture_dir.path_join(img_name), _image_format, _lossy_quality); - ERR_FAIL_COND_V_MSG(err != OK, err, "GLTF: Failed to save image in '" + _image_format + "' format as a separate file."); + ERR_FAIL_COND_V_MSG(err != OK, err, "glTF: Failed to save image in '" + _image_format + "' format as a separate file."); } else if (_image_format == "PNG") { img_name = img_name + ".png"; image->save_png(full_texture_dir.path_join(img_name)); @@ -3382,7 +3382,7 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state) { img_name = img_name + ".jpg"; image->save_jpg(full_texture_dir.path_join(img_name), _lossy_quality); } else { - ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "GLTF: Unknown image format '" + _image_format + "'."); + ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "glTF: Unknown image format '" + _image_format + "'."); } image_dict["uri"] = relative_texture_dir.path_join(img_name).uri_encode(); } else { @@ -3412,9 +3412,9 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state) { buffer = image->save_jpg_to_buffer(_lossy_quality); image_dict["mimeType"] = "image/jpeg"; } else { - ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "GLTF: Unknown image format '" + _image_format + "'."); + ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "glTF: Unknown image format '" + _image_format + "'."); } - ERR_FAIL_COND_V_MSG(buffer.is_empty(), ERR_INVALID_DATA, "GLTF: Failed to save image in '" + _image_format + "' format."); + ERR_FAIL_COND_V_MSG(buffer.is_empty(), ERR_INVALID_DATA, "glTF: Failed to save image in '" + _image_format + "' format."); bv->byte_length = buffer.size(); p_state->buffers.write[bi].resize(p_state->buffers[bi].size() + bv->byte_length); @@ -3445,7 +3445,7 @@ Ref<Image> GLTFDocument::_parse_image_bytes_into_image(Ref<GLTFState> p_state, c for (Ref<GLTFDocumentExtension> ext : document_extensions) { ERR_CONTINUE(ext.is_null()); Error err = ext->parse_image_data(p_state, p_bytes, p_mime_type, r_image); - ERR_CONTINUE_MSG(err != OK, "GLTF: Encountered error " + itos(err) + " when parsing image " + itos(p_index) + " in file " + p_state->filename + ". Continuing."); + ERR_CONTINUE_MSG(err != OK, "glTF: Encountered error " + itos(err) + " when parsing image " + itos(p_index) + " in file " + p_state->filename + ". Continuing."); if (!r_image->is_empty()) { r_file_extension = ext->get_image_file_extension(); return r_image; @@ -3736,13 +3736,13 @@ Error GLTFDocument::_parse_textures(Ref<GLTFState> p_state) { for (Ref<GLTFDocumentExtension> ext : document_extensions) { ERR_CONTINUE(ext.is_null()); Error err = ext->parse_texture_json(p_state, texture_dict, gltf_texture); - ERR_CONTINUE_MSG(err != OK, "GLTF: Encountered error " + itos(err) + " when parsing texture JSON " + String(Variant(texture_dict)) + " in file " + p_state->filename + ". Continuing."); + ERR_CONTINUE_MSG(err != OK, "glTF: Encountered error " + itos(err) + " when parsing texture JSON " + String(Variant(texture_dict)) + " in file " + p_state->filename + ". Continuing."); if (gltf_texture->get_src_image() != -1) { break; } } if (gltf_texture->get_src_image() == -1) { - // No extensions handled it, so use the base GLTF source. + // No extensions handled it, so use the base glTF source. // This may be the fallback, or the only option anyway. ERR_FAIL_COND_V(!texture_dict.has("source"), ERR_PARSE_ERROR); gltf_texture->set_src_image(texture_dict["source"]); @@ -5631,7 +5631,7 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> p_state, const GLTFNodeIn // If none of our GLTFDocumentExtension classes generated us a node, we generate one. if (!current_node) { if (gltf_node->skin >= 0 && gltf_node->mesh >= 0 && !gltf_node->children.is_empty()) { - // GLTF specifies that skinned meshes should ignore their node transforms, + // glTF specifies that skinned meshes should ignore their node transforms, // only being controlled by the skeleton, so Godot will reparent a skinned // mesh to its skeleton. However, we still need to ensure any child nodes // keep their place in the tree, so if there are any child nodes, the skinned @@ -7138,9 +7138,9 @@ Node *GLTFDocument::_generate_scene_node_tree(Ref<GLTFState> p_state) { HashMap<ObjectID, SkinSkeletonIndex> skeleton_map; Error err = SkinTool::_create_skeletons(p_state->unique_names, p_state->skins, p_state->nodes, skeleton_map, p_state->skeletons, p_state->scene_nodes); - ERR_FAIL_COND_V_MSG(err != OK, nullptr, "GLTF: Failed to create skeletons."); + ERR_FAIL_COND_V_MSG(err != OK, nullptr, "glTF: Failed to create skeletons."); err = _create_skins(p_state); - ERR_FAIL_COND_V_MSG(err != OK, nullptr, "GLTF: Failed to create skins."); + ERR_FAIL_COND_V_MSG(err != OK, nullptr, "glTF: Failed to create skins."); // Generate the node tree. Node *single_root; if (p_state->extensions_used.has("GODOT_single_root")) { @@ -7459,7 +7459,7 @@ Error GLTFDocument::_parse_gltf_extensions(Ref<GLTFState> p_state) { Error ret = OK; for (int i = 0; i < p_state->extensions_required.size(); i++) { if (!supported_extensions.has(p_state->extensions_required[i])) { - ERR_PRINT("GLTF: Can't import file '" + p_state->filename + "', required extension '" + String(p_state->extensions_required[i]) + "' is not supported. Are you missing a GLTFDocumentExtension plugin?"); + ERR_PRINT("glTF: Can't import file '" + p_state->filename + "', required extension '" + String(p_state->extensions_required[i]) + "' is not supported. Are you missing a GLTFDocumentExtension plugin?"); ret = ERR_UNAVAILABLE; } } diff --git a/modules/gltf/structures/gltf_camera.cpp b/modules/gltf/structures/gltf_camera.cpp index d56f67a092..863e1df967 100644 --- a/modules/gltf/structures/gltf_camera.cpp +++ b/modules/gltf/structures/gltf_camera.cpp @@ -62,9 +62,9 @@ Ref<GLTFCamera> GLTFCamera::from_node(const Camera3D *p_camera) { c.instantiate(); ERR_FAIL_NULL_V_MSG(p_camera, c, "Tried to create a GLTFCamera from a Camera3D node, but the given node was null."); c->set_perspective(p_camera->get_projection() == Camera3D::ProjectionType::PROJECTION_PERSPECTIVE); - // GLTF spec (yfov) is in radians, Godot's camera (fov) is in degrees. + // glTF spec (yfov) is in radians, Godot's camera (fov) is in degrees. c->set_fov(Math::deg_to_rad(p_camera->get_fov())); - // GLTF spec (xmag and ymag) is a radius in meters, Godot's camera (size) is a diameter in meters. + // glTF spec (xmag and ymag) is a radius in meters, Godot's camera (size) is a diameter in meters. c->set_size_mag(p_camera->get_size() * 0.5f); c->set_depth_far(p_camera->get_far()); c->set_depth_near(p_camera->get_near()); @@ -74,9 +74,9 @@ Ref<GLTFCamera> GLTFCamera::from_node(const Camera3D *p_camera) { Camera3D *GLTFCamera::to_node() const { Camera3D *camera = memnew(Camera3D); camera->set_projection(perspective ? Camera3D::PROJECTION_PERSPECTIVE : Camera3D::PROJECTION_ORTHOGONAL); - // GLTF spec (yfov) is in radians, Godot's camera (fov) is in degrees. + // glTF spec (yfov) is in radians, Godot's camera (fov) is in degrees. camera->set_fov(Math::rad_to_deg(fov)); - // GLTF spec (xmag and ymag) is a radius in meters, Godot's camera (size) is a diameter in meters. + // glTF spec (xmag and ymag) is a radius in meters, Godot's camera (size) is a diameter in meters. camera->set_size(size_mag * 2.0f); camera->set_near(depth_near); camera->set_far(depth_far); @@ -84,7 +84,7 @@ Camera3D *GLTFCamera::to_node() const { } Ref<GLTFCamera> GLTFCamera::from_dictionary(const Dictionary p_dictionary) { - ERR_FAIL_COND_V_MSG(!p_dictionary.has("type"), Ref<GLTFCamera>(), "Failed to parse GLTF camera, missing required field 'type'."); + ERR_FAIL_COND_V_MSG(!p_dictionary.has("type"), Ref<GLTFCamera>(), "Failed to parse glTF camera, missing required field 'type'."); Ref<GLTFCamera> camera; camera.instantiate(); const String &type = p_dictionary["type"]; @@ -107,7 +107,7 @@ Ref<GLTFCamera> GLTFCamera::from_dictionary(const Dictionary p_dictionary) { camera->set_depth_near(ortho["znear"]); } } else { - ERR_PRINT("Error parsing GLTF camera: Camera type '" + type + "' is unknown, should be perspective or orthographic."); + ERR_PRINT("Error parsing glTF camera: Camera type '" + type + "' is unknown, should be perspective or orthographic."); } return camera; } diff --git a/modules/gltf/structures/gltf_camera.h b/modules/gltf/structures/gltf_camera.h index ef55b06a76..1a583c82cc 100644 --- a/modules/gltf/structures/gltf_camera.h +++ b/modules/gltf/structures/gltf_camera.h @@ -42,8 +42,8 @@ class GLTFCamera : public Resource { GDCLASS(GLTFCamera, Resource); private: - // GLTF has no default camera values, they should always be specified in - // the GLTF file. Here we default to Godot's default camera settings. + // glTF has no default camera values, they should always be specified in + // the glTF file. Here we default to Godot's default camera settings. bool perspective = true; real_t fov = Math::deg_to_rad(75.0); real_t size_mag = 0.5; diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp index f402e2a583..ea63e07104 100644 --- a/modules/gridmap/editor/grid_map_editor_plugin.cpp +++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp @@ -643,6 +643,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D _do_paste(); input_action = INPUT_NONE; _update_paste_indicator(); + return EditorPlugin::AFTER_GUI_INPUT_STOP; } else if (mb->is_shift_pressed() && can_edit) { input_action = INPUT_SELECT; last_selection = selection; diff --git a/modules/hdr/image_loader_hdr.cpp b/modules/hdr/image_loader_hdr.cpp index c49c62a08b..ba59bb25ee 100644 --- a/modules/hdr/image_loader_hdr.cpp +++ b/modules/hdr/image_loader_hdr.cpp @@ -68,9 +68,11 @@ Error ImageLoaderHDR::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField imgdata.resize(height * width * (int)sizeof(uint32_t)); { - uint8_t *w = imgdata.ptrw(); + uint8_t *ptr = imgdata.ptrw(); - uint8_t *ptr = (uint8_t *)w; + Vector<uint8_t> temp_read_data; + temp_read_data.resize(128); + uint8_t *temp_read_ptr = temp_read_data.ptrw(); if (width < 8 || width >= 32768) { // Read flat data @@ -113,8 +115,9 @@ Error ImageLoaderHDR::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField } } else { // Dump + f->get_buffer(temp_read_ptr, count); for (int z = 0; z < count; ++z) { - ptr[(j * width + i++) * 4 + k] = f->get_8(); + ptr[(j * width + i++) * 4 + k] = temp_read_ptr[z]; } } } @@ -122,20 +125,27 @@ Error ImageLoaderHDR::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField } } + const bool force_linear = p_flags & FLAG_FORCE_LINEAR; + //convert for (int i = 0; i < width * height; i++) { - float exp = pow(2.0f, ptr[3] - 128.0f); + int e = ptr[3] - 128; + + if (force_linear || (e < -15 || e > 15)) { + float exp = pow(2.0f, e); + Color c(ptr[0] * exp / 255.0, ptr[1] * exp / 255.0, ptr[2] * exp / 255.0); - Color c( - ptr[0] * exp / 255.0, - ptr[1] * exp / 255.0, - ptr[2] * exp / 255.0); + if (force_linear) { + c = c.srgb_to_linear(); + } - if (p_flags & FLAG_FORCE_LINEAR) { - c = c.srgb_to_linear(); + *(uint32_t *)ptr = c.to_rgbe9995(); + } else { + // https://github.com/george-steel/rgbe-rs/blob/e7cc33b7f42b4eb3272c166dac75385e48687c92/src/types.rs#L123-L129 + uint32_t e5 = (uint32_t)(e + 15); + *(uint32_t *)ptr = ((e5 << 27) | ((uint32_t)ptr[2] << 19) | ((uint32_t)ptr[1] << 10) | ((uint32_t)ptr[0] << 1)); } - *(uint32_t *)ptr = c.to_rgbe9995(); ptr += 4; } } diff --git a/modules/hdr/image_loader_hdr.h b/modules/hdr/image_loader_hdr.h index 9821db059e..0a8e91fb9e 100644 --- a/modules/hdr/image_loader_hdr.h +++ b/modules/hdr/image_loader_hdr.h @@ -37,6 +37,7 @@ class ImageLoaderHDR : public ImageFormatLoader { public: virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale); virtual void get_recognized_extensions(List<String> *p_extensions) const; + ImageLoaderHDR(); }; diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportDiagnostics_GD0107_OK_ScriptPropertyDefVal.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportDiagnostics_GD0107_OK_ScriptPropertyDefVal.generated.cs index 217f467637..9a8b3ea846 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportDiagnostics_GD0107_OK_ScriptPropertyDefVal.generated.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportDiagnostics_GD0107_OK_ScriptPropertyDefVal.generated.cs @@ -11,11 +11,27 @@ partial class ExportDiagnostics_GD0107_OK [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] internal new static global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant> GetGodotPropertyDefaultValues() { - var values = new global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant>(2); + var values = new global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant>(10); global::Godot.Node __NodeProperty_default_value = default; values.Add(PropertyName.@NodeProperty, global::Godot.Variant.From<global::Godot.Node>(__NodeProperty_default_value)); + global::Godot.Node[] __SystemArrayOfNodesProperty_default_value = default; + values.Add(PropertyName.@SystemArrayOfNodesProperty, global::Godot.Variant.CreateFrom(__SystemArrayOfNodesProperty_default_value)); + global::Godot.Collections.Array<global::Godot.Node> __GodotArrayOfNodesProperty_default_value = default; + values.Add(PropertyName.@GodotArrayOfNodesProperty, global::Godot.Variant.CreateFrom(__GodotArrayOfNodesProperty_default_value)); + global::Godot.Collections.Dictionary<global::Godot.Node, string> __GodotDictionaryWithNodeAsKeyProperty_default_value = default; + values.Add(PropertyName.@GodotDictionaryWithNodeAsKeyProperty, global::Godot.Variant.CreateFrom(__GodotDictionaryWithNodeAsKeyProperty_default_value)); + global::Godot.Collections.Dictionary<string, global::Godot.Node> __GodotDictionaryWithNodeAsValueProperty_default_value = default; + values.Add(PropertyName.@GodotDictionaryWithNodeAsValueProperty, global::Godot.Variant.CreateFrom(__GodotDictionaryWithNodeAsValueProperty_default_value)); global::Godot.Node __NodeField_default_value = default; values.Add(PropertyName.@NodeField, global::Godot.Variant.From<global::Godot.Node>(__NodeField_default_value)); + global::Godot.Node[] __SystemArrayOfNodesField_default_value = default; + values.Add(PropertyName.@SystemArrayOfNodesField, global::Godot.Variant.CreateFrom(__SystemArrayOfNodesField_default_value)); + global::Godot.Collections.Array<global::Godot.Node> __GodotArrayOfNodesField_default_value = default; + values.Add(PropertyName.@GodotArrayOfNodesField, global::Godot.Variant.CreateFrom(__GodotArrayOfNodesField_default_value)); + global::Godot.Collections.Dictionary<global::Godot.Node, string> __GodotDictionaryWithNodeAsKeyField_default_value = default; + values.Add(PropertyName.@GodotDictionaryWithNodeAsKeyField, global::Godot.Variant.CreateFrom(__GodotDictionaryWithNodeAsKeyField_default_value)); + global::Godot.Collections.Dictionary<string, global::Godot.Node> __GodotDictionaryWithNodeAsValueField_default_value = default; + values.Add(PropertyName.@GodotDictionaryWithNodeAsValueField, global::Godot.Variant.CreateFrom(__GodotDictionaryWithNodeAsValueField_default_value)); return values; } #endif // TOOLS diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportDiagnostics_GD0107.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportDiagnostics_GD0107.cs index 067783ea66..4613d883c2 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportDiagnostics_GD0107.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportDiagnostics_GD0107.cs @@ -1,4 +1,5 @@ using Godot; +using Godot.Collections; public partial class ExportDiagnostics_GD0107_OK : Node { @@ -6,7 +7,31 @@ public partial class ExportDiagnostics_GD0107_OK : Node public Node NodeField; [Export] + public Node[] SystemArrayOfNodesField; + + [Export] + public Array<Node> GodotArrayOfNodesField; + + [Export] + public Dictionary<Node, string> GodotDictionaryWithNodeAsKeyField; + + [Export] + public Dictionary<string, Node> GodotDictionaryWithNodeAsValueField; + + [Export] public Node NodeProperty { get; set; } + + [Export] + public Node[] SystemArrayOfNodesProperty { get; set; } + + [Export] + public Array<Node> GodotArrayOfNodesProperty { get; set; } + + [Export] + public Dictionary<Node, string> GodotDictionaryWithNodeAsKeyProperty { get; set; } + + [Export] + public Dictionary<string, Node> GodotDictionaryWithNodeAsValueProperty { get; set; } } public partial class ExportDiagnostics_GD0107_KO : Resource @@ -15,5 +40,29 @@ public partial class ExportDiagnostics_GD0107_KO : Resource public Node {|GD0107:NodeField|}; [Export] + public Node[] {|GD0107:SystemArrayOfNodesField|}; + + [Export] + public Array<Node> {|GD0107:GodotArrayOfNodesField|}; + + [Export] + public Dictionary<Node, string> {|GD0107:GodotDictionaryWithNodeAsKeyField|}; + + [Export] + public Dictionary<string, Node> {|GD0107:GodotDictionaryWithNodeAsValueField|}; + + [Export] public Node {|GD0107:NodeProperty|} { get; set; } + + [Export] + public Node[] {|GD0107:SystemArrayOfNodesProperty|} { get; set; } + + [Export] + public Array<Node> {|GD0107:GodotArrayOfNodesProperty|} { get; set; } + + [Export] + public Dictionary<Node, string> {|GD0107:GodotDictionaryWithNodeAsKeyProperty|} { get; set; } + + [Export] + public Dictionary<string, Node> {|GD0107:GodotDictionaryWithNodeAsValueProperty|} { get; set; } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs index efe88d8468..626f51ecae 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs @@ -196,16 +196,13 @@ namespace Godot.SourceGenerators continue; } - if (marshalType == MarshalType.GodotObjectOrDerived) + if (!isNode && MemberHasNodeType(propertyType, marshalType.Value)) { - if (!isNode && propertyType.InheritsFrom("GodotSharp", GodotClasses.Node)) - { - context.ReportDiagnostic(Diagnostic.Create( - Common.OnlyNodesShouldExportNodesRule, - property.Locations.FirstLocationWithSourceTreeOrDefault() - )); - continue; - } + context.ReportDiagnostic(Diagnostic.Create( + Common.OnlyNodesShouldExportNodesRule, + property.Locations.FirstLocationWithSourceTreeOrDefault() + )); + continue; } var propertyDeclarationSyntax = property.DeclaringSyntaxReferences @@ -315,16 +312,13 @@ namespace Godot.SourceGenerators continue; } - if (marshalType == MarshalType.GodotObjectOrDerived) + if (!isNode && MemberHasNodeType(fieldType, marshalType.Value)) { - if (!isNode && fieldType.InheritsFrom("GodotSharp", GodotClasses.Node)) - { - context.ReportDiagnostic(Diagnostic.Create( - Common.OnlyNodesShouldExportNodesRule, - field.Locations.FirstLocationWithSourceTreeOrDefault() - )); - continue; - } + context.ReportDiagnostic(Diagnostic.Create( + Common.OnlyNodesShouldExportNodesRule, + field.Locations.FirstLocationWithSourceTreeOrDefault() + )); + continue; } EqualsValueClauseSyntax? initializer = field.DeclaringSyntaxReferences @@ -424,6 +418,27 @@ namespace Godot.SourceGenerators context.AddSource(uniqueHint, SourceText.From(source.ToString(), Encoding.UTF8)); } + private static bool MemberHasNodeType(ITypeSymbol memberType, MarshalType marshalType) + { + if (marshalType == MarshalType.GodotObjectOrDerived) + { + return memberType.InheritsFrom("GodotSharp", GodotClasses.Node); + } + if (marshalType == MarshalType.GodotObjectOrDerivedArray) + { + var elementType = ((IArrayTypeSymbol)memberType).ElementType; + return elementType.InheritsFrom("GodotSharp", GodotClasses.Node); + } + if (memberType is INamedTypeSymbol { IsGenericType: true } genericType) + { + return genericType.TypeArguments + .Any(static typeArgument + => typeArgument.InheritsFrom("GodotSharp", GodotClasses.Node)); + } + + return false; + } + private struct ExportedPropertyMetadata { public ExportedPropertyMetadata(string name, MarshalType type, ITypeSymbol typeSymbol, string? value) diff --git a/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs b/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs index cd335934db..ece1ab44a2 100644 --- a/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs +++ b/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs @@ -3,6 +3,8 @@ using _BINDINGS_NAMESPACE_; using System; +[Tool] +[GlobalClass] public partial class VisualShaderNode_CLASS_ : _BASE_ { public override string _GetName() @@ -20,37 +22,37 @@ public partial class VisualShaderNode_CLASS_ : _BASE_ return ""; } - public override long _GetReturnIconType() + public override VisualShaderNode.PortType _GetReturnIconType() { return 0; } - public override long _GetInputPortCount() + public override int _GetInputPortCount() { return 0; } - public override string _GetInputPortName(long port) + public override string _GetInputPortName(int port) { return ""; } - public override long _GetInputPortType(long port) + public override VisualShaderNode.PortType _GetInputPortType(int port) { return 0; } - public override long _GetOutputPortCount() + public override int _GetOutputPortCount() { return 1; } - public override string _GetOutputPortName(long port) + public override string _GetOutputPortName(int port) { return "result"; } - public override long _GetOutputPortType(long port) + public override VisualShaderNode.PortType _GetOutputPortType(int port) { return 0; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/GodotObjectExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/GodotObjectExtensions.cs index 563a6abe9b..1fc6e54e09 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/GodotObjectExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/GodotObjectExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using Godot.NativeInterop; #nullable enable @@ -51,7 +52,7 @@ namespace Godot /// </summary> /// <param name="instance">The instance to check.</param> /// <returns>If the instance is a valid object.</returns> - public static bool IsInstanceValid(GodotObject? instance) + public static bool IsInstanceValid([NotNullWhen(true)] GodotObject? instance) { return instance != null && instance.NativeInstance != IntPtr.Zero; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs index 27f2713efa..ef66d5bbe0 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs @@ -692,10 +692,18 @@ namespace Godot // Zero length vectors have no angle, so the best we can do is either lerp or throw an error. return Lerp(to, weight); } + Vector3 axis = Cross(to); + real_t axisLengthSquared = axis.LengthSquared(); + if (axisLengthSquared == 0.0) + { + // Colinear vectors have no rotation axis or angle between them, so the best we can do is lerp. + return Lerp(to, weight); + } + axis /= Mathf.Sqrt(axisLengthSquared); real_t startLength = Mathf.Sqrt(startLengthSquared); real_t resultLength = Mathf.Lerp(startLength, Mathf.Sqrt(endLengthSquared), weight); real_t angle = AngleTo(to); - return Rotated(Cross(to).Normalized(), angle * weight) * (resultLength / startLength); + return Rotated(axis, angle * weight) * (resultLength / startLength); } /// <summary> diff --git a/modules/multiplayer/doc_classes/MultiplayerSpawner.xml b/modules/multiplayer/doc_classes/MultiplayerSpawner.xml index 3da245f806..b620292519 100644 --- a/modules/multiplayer/doc_classes/MultiplayerSpawner.xml +++ b/modules/multiplayer/doc_classes/MultiplayerSpawner.xml @@ -48,11 +48,11 @@ </methods> <members> <member name="spawn_function" type="Callable" setter="set_spawn_function" getter="get_spawn_function"> - Method called on all peers when for every custom [method spawn] requested by the authority. Will receive the [code]data[/code] parameter, and should return a [Node] that is not in the scene tree. + Method called on all peers when a custom [method spawn] is requested by the authority. Will receive the [code]data[/code] parameter, and should return a [Node] that is not in the scene tree. [b]Note:[/b] The returned node should [b]not[/b] be added to the scene with [method Node.add_child]. This is done automatically. </member> <member name="spawn_limit" type="int" setter="set_spawn_limit" getter="get_spawn_limit" default="0"> - Maximum nodes that is allowed to be spawned by this spawner. Includes both spawnable scenes and custom spawns. + Maximum number of nodes allowed to be spawned by this spawner. Includes both spawnable scenes and custom spawns. When set to [code]0[/code] (the default), there is no limit. </member> <member name="spawn_path" type="NodePath" setter="set_spawn_path" getter="get_spawn_path" default="NodePath("")"> diff --git a/modules/navigation/3d/godot_navigation_server_3d.cpp b/modules/navigation/3d/godot_navigation_server_3d.cpp index 430d527844..fc96506795 100644 --- a/modules/navigation/3d/godot_navigation_server_3d.cpp +++ b/modules/navigation/3d/godot_navigation_server_3d.cpp @@ -1298,6 +1298,7 @@ void GodotNavigationServer3D::process(real_t p_delta_time) { int _new_pm_edge_merge_count = 0; int _new_pm_edge_connection_count = 0; int _new_pm_edge_free_count = 0; + int _new_pm_obstacle_count = 0; // In c++ we can't be sure that this is performed in the main thread // even with mutable functions. @@ -1315,6 +1316,7 @@ void GodotNavigationServer3D::process(real_t p_delta_time) { _new_pm_edge_merge_count += active_maps[i]->get_pm_edge_merge_count(); _new_pm_edge_connection_count += active_maps[i]->get_pm_edge_connection_count(); _new_pm_edge_free_count += active_maps[i]->get_pm_edge_free_count(); + _new_pm_obstacle_count += active_maps[i]->get_pm_obstacle_count(); // Emit a signal if a map changed. const uint32_t new_map_iteration_id = active_maps[i]->get_iteration_id(); @@ -1332,6 +1334,7 @@ void GodotNavigationServer3D::process(real_t p_delta_time) { pm_edge_merge_count = _new_pm_edge_merge_count; pm_edge_connection_count = _new_pm_edge_connection_count; pm_edge_free_count = _new_pm_edge_free_count; + pm_obstacle_count = _new_pm_obstacle_count; } void GodotNavigationServer3D::init() { @@ -1566,6 +1569,9 @@ int GodotNavigationServer3D::get_process_info(ProcessInfo p_info) const { case INFO_EDGE_FREE_COUNT: { return pm_edge_free_count; } break; + case INFO_OBSTACLE_COUNT: { + return pm_obstacle_count; + } break; } return 0; diff --git a/modules/navigation/3d/godot_navigation_server_3d.h b/modules/navigation/3d/godot_navigation_server_3d.h index 5ba7ed1088..12a1132f07 100644 --- a/modules/navigation/3d/godot_navigation_server_3d.h +++ b/modules/navigation/3d/godot_navigation_server_3d.h @@ -95,6 +95,7 @@ class GodotNavigationServer3D : public NavigationServer3D { int pm_edge_merge_count = 0; int pm_edge_connection_count = 0; int pm_edge_free_count = 0; + int pm_obstacle_count = 0; public: GodotNavigationServer3D(); diff --git a/modules/navigation/editor/navigation_mesh_editor_plugin.cpp b/modules/navigation/editor/navigation_mesh_editor_plugin.cpp index d07d3cdff5..f37ed9b168 100644 --- a/modules/navigation/editor/navigation_mesh_editor_plugin.cpp +++ b/modules/navigation/editor/navigation_mesh_editor_plugin.cpp @@ -126,9 +126,6 @@ void NavigationMeshEditor::edit(NavigationRegion3D *p_nav_region) { node = p_nav_region; } -void NavigationMeshEditor::_bind_methods() { -} - NavigationMeshEditor::NavigationMeshEditor() { bake_hbox = memnew(HBoxContainer); diff --git a/modules/navigation/editor/navigation_mesh_editor_plugin.h b/modules/navigation/editor/navigation_mesh_editor_plugin.h index 6114c62ebf..f5a471d531 100644 --- a/modules/navigation/editor/navigation_mesh_editor_plugin.h +++ b/modules/navigation/editor/navigation_mesh_editor_plugin.h @@ -60,7 +60,6 @@ class NavigationMeshEditor : public Control { protected: void _node_removed(Node *p_node); - static void _bind_methods(); void _notification(int p_what); public: diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp index f917c988ea..9796fa350d 100644 --- a/modules/navigation/nav_map.cpp +++ b/modules/navigation/nav_map.cpp @@ -907,6 +907,7 @@ void NavMap::sync() { int _new_pm_edge_merge_count = pm_edge_merge_count; int _new_pm_edge_connection_count = pm_edge_connection_count; int _new_pm_edge_free_count = pm_edge_free_count; + int _new_pm_obstacle_count = obstacles.size(); // Check if we need to update the links. if (regenerate_polygons) { @@ -1219,6 +1220,7 @@ void NavMap::sync() { pm_edge_merge_count = _new_pm_edge_merge_count; pm_edge_connection_count = _new_pm_edge_connection_count; pm_edge_free_count = _new_pm_edge_free_count; + pm_obstacle_count = _new_pm_obstacle_count; } void NavMap::_update_rvo_obstacles_tree_2d() { diff --git a/modules/navigation/nav_map.h b/modules/navigation/nav_map.h index d6215ea57f..3dd6db21dd 100644 --- a/modules/navigation/nav_map.h +++ b/modules/navigation/nav_map.h @@ -123,6 +123,7 @@ class NavMap : public NavRid { int pm_edge_merge_count = 0; int pm_edge_connection_count = 0; int pm_edge_free_count = 0; + int pm_obstacle_count = 0; public: NavMap(); @@ -216,6 +217,7 @@ public: int get_pm_edge_merge_count() const { return pm_edge_merge_count; } int get_pm_edge_connection_count() const { return pm_edge_connection_count; } int get_pm_edge_free_count() const { return pm_edge_free_count; } + int get_pm_obstacle_count() const { return pm_obstacle_count; } private: void compute_single_step(uint32_t index, NavAgent **agent); diff --git a/modules/openxr/editor/openxr_select_runtime.cpp b/modules/openxr/editor/openxr_select_runtime.cpp index 026797c6e0..4d95b079e2 100644 --- a/modules/openxr/editor/openxr_select_runtime.cpp +++ b/modules/openxr/editor/openxr_select_runtime.cpp @@ -35,9 +35,6 @@ #include "editor/editor_settings.h" #include "editor/editor_string_names.h" -void OpenXRSelectRuntime::_bind_methods() { -} - void OpenXRSelectRuntime::_update_items() { Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); OS *os = OS::get_singleton(); diff --git a/modules/openxr/editor/openxr_select_runtime.h b/modules/openxr/editor/openxr_select_runtime.h index 60b5137f67..9a3487439c 100644 --- a/modules/openxr/editor/openxr_select_runtime.h +++ b/modules/openxr/editor/openxr_select_runtime.h @@ -40,7 +40,6 @@ public: OpenXRSelectRuntime(); protected: - static void _bind_methods(); void _notification(int p_notification); private: diff --git a/modules/openxr/extensions/platform/openxr_opengl_extension.cpp b/modules/openxr/extensions/platform/openxr_opengl_extension.cpp index d92084a220..de4a9e4b8e 100644 --- a/modules/openxr/extensions/platform/openxr_opengl_extension.cpp +++ b/modules/openxr/extensions/platform/openxr_opengl_extension.cpp @@ -56,11 +56,6 @@ // feature off. // See: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_sRGB_write_control.txt -// On OpenGLES this is not defined in our standard headers.. -#ifndef GL_FRAMEBUFFER_SRGB -#define GL_FRAMEBUFFER_SRGB 0x8DB9 -#endif - HashMap<String, bool *> OpenXROpenGLExtension::get_requested_extensions() { HashMap<String, bool *> request_extensions; @@ -196,23 +191,6 @@ void OpenXROpenGLExtension::get_usable_depth_formats(Vector<int64_t> &p_usable_d p_usable_depth_formats.push_back(GL_DEPTH_COMPONENT24); } -void OpenXROpenGLExtension::on_pre_draw_viewport(RID p_render_target) { - if (srgb_ext_is_available) { - hw_linear_to_srgb_is_enabled = glIsEnabled(GL_FRAMEBUFFER_SRGB); - if (hw_linear_to_srgb_is_enabled) { - // Disable this. - glDisable(GL_FRAMEBUFFER_SRGB); - } - } -} - -void OpenXROpenGLExtension::on_post_draw_viewport(RID p_render_target) { - if (srgb_ext_is_available && hw_linear_to_srgb_is_enabled) { - // Re-enable this. - glEnable(GL_FRAMEBUFFER_SRGB); - } -} - 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) { GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); ERR_FAIL_NULL_V(texture_storage, false); diff --git a/modules/openxr/extensions/platform/openxr_opengl_extension.h b/modules/openxr/extensions/platform/openxr_opengl_extension.h index a3052d3f53..8da3ca48f4 100644 --- a/modules/openxr/extensions/platform/openxr_opengl_extension.h +++ b/modules/openxr/extensions/platform/openxr_opengl_extension.h @@ -49,9 +49,6 @@ public: virtual void on_instance_created(const XrInstance p_instance) override; virtual void *set_session_create_and_get_next_pointer(void *p_next_pointer) override; - virtual void on_pre_draw_viewport(RID p_render_target) override; - virtual void on_post_draw_viewport(RID p_render_target) override; - virtual void get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) override; virtual void get_usable_depth_formats(Vector<int64_t> &p_usable_swap_chains) override; virtual String get_swapchain_format_name(int64_t p_swapchain_format) const override; @@ -76,9 +73,6 @@ private: Vector<RID> texture_rids; }; - bool srgb_ext_is_available = true; - bool hw_linear_to_srgb_is_enabled = false; - bool check_graphics_api_support(XrVersion p_desired_version); #ifdef ANDROID_ENABLED diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index 98e5484157..c6fd20dac7 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -525,7 +525,7 @@ bool OpenXRAPI::create_instance() { 1, // applicationVersion, we don't currently have this "Godot Game Engine", // engineName VERSION_MAJOR * 10000 + VERSION_MINOR * 100 + VERSION_PATCH, // engineVersion 4.0 -> 40000, 4.0.1 -> 40001, 4.1 -> 40100, etc. - XR_CURRENT_API_VERSION // apiVersion + XR_API_VERSION_1_0 // apiVersion }; void *next_pointer = nullptr; diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub index 68a5d499d4..4112b81622 100644 --- a/modules/text_server_adv/SCsub +++ b/modules/text_server_adv/SCsub @@ -142,7 +142,14 @@ if env["builtin_harfbuzz"]: env_harfbuzz.Append(CCFLAGS=["-DHAVE_ICU"]) if env["builtin_icu4c"]: env_harfbuzz.Prepend(CPPPATH=["#thirdparty/icu4c/common/", "#thirdparty/icu4c/i18n/"]) - env_harfbuzz.Append(CCFLAGS=["-DU_HAVE_LIB_SUFFIX=1", "-DU_LIB_SUFFIX_C_NAME=_godot", "-DHAVE_ICU_BUILTIN"]) + env_harfbuzz.Append( + CCFLAGS=[ + "-DU_STATIC_IMPLEMENTATION", + "-DU_HAVE_LIB_SUFFIX=1", + "-DU_LIB_SUFFIX_C_NAME=_godot", + "-DHAVE_ICU_BUILTIN", + ] + ) if freetype_enabled: env_harfbuzz.Append( @@ -499,6 +506,7 @@ if env["builtin_icu4c"]: ) env_text_server_adv.Append( CXXFLAGS=[ + "-DU_STATIC_IMPLEMENTATION", "-DU_HAVE_LIB_SUFFIX=1", "-DU_LIB_SUFFIX_C_NAME=_godot", "-DICU_DATA_NAME=" + icu_data_name, diff --git a/modules/text_server_adv/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct index d0d13fec3f..effed1e772 100644 --- a/modules/text_server_adv/gdextension_build/SConstruct +++ b/modules/text_server_adv/gdextension_build/SConstruct @@ -415,6 +415,7 @@ if env["platform"] == "android" or env["platform"] == "linuxbsd": env_harfbuzz.Append( CCFLAGS=[ + "-DU_STATIC_IMPLEMENTATION", "-DU_HAVE_LIB_SUFFIX=1", "-DU_LIB_SUFFIX_C_NAME=_godot", "-DHAVE_ICU_BUILTIN", @@ -746,6 +747,7 @@ env_icu.Append( ) env.Append( CXXFLAGS=[ + "-DU_STATIC_IMPLEMENTATION", "-DU_HAVE_LIB_SUFFIX=1", "-DU_LIB_SUFFIX_C_NAME=_godot", "-DICU_DATA_NAME=" + icu_data_name, diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index 499ddb703b..d0c22e9e4d 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -51,7 +51,7 @@ using namespace godot; #include "core/error/error_macros.h" #include "core/object/worker_thread_pool.h" #include "core/string/print_string.h" -#include "core/string/translation.h" +#include "core/string/translation_server.h" #include "scene/resources/image_texture.h" #include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg. @@ -3528,6 +3528,37 @@ String TextServerAdvanced::_font_get_supported_chars(const RID &p_font_rid) cons return chars; } +PackedInt32Array TextServerAdvanced::_font_get_supported_glyphs(const RID &p_font_rid) const { + FontAdvanced *fd = _get_font_data(p_font_rid); + ERR_FAIL_NULL_V(fd, PackedInt32Array()); + + MutexLock lock(fd->mutex); + if (fd->cache.is_empty()) { + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), PackedInt32Array()); + } + FontForSizeAdvanced *at_size = fd->cache.begin()->value; + + PackedInt32Array glyphs; +#ifdef MODULE_FREETYPE_ENABLED + if (at_size && at_size->face) { + FT_UInt gindex; + FT_ULong charcode = FT_Get_First_Char(at_size->face, &gindex); + while (gindex != 0) { + glyphs.push_back(gindex); + charcode = FT_Get_Next_Char(at_size->face, charcode, &gindex); + } + return glyphs; + } +#endif + if (at_size) { + const HashMap<int32_t, FontGlyph> &gl = at_size->glyph_map; + for (const KeyValue<int32_t, FontGlyph> &E : gl) { + glyphs.push_back(E.key); + } + } + return glyphs; +} + void TextServerAdvanced::_font_render_range(const RID &p_font_rid, const Vector2i &p_size, int64_t p_start, int64_t p_end) { FontAdvanced *fd = _get_font_data(p_font_rid); ERR_FAIL_NULL(fd); diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index 92bdb93bcf..fdebb8e4cd 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -871,6 +871,7 @@ public: MODBIND2RC(bool, font_has_char, const RID &, int64_t); MODBIND1RC(String, font_get_supported_chars, const RID &); + MODBIND1RC(PackedInt32Array, font_get_supported_glyphs, const RID &); MODBIND4(font_render_range, const RID &, const Vector2i &, int64_t, int64_t); MODBIND3(font_render_glyph, const RID &, const Vector2i &, int64_t); diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index b45c004011..a7ddfc719e 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -52,7 +52,7 @@ using namespace godot; #include "core/config/project_settings.h" #include "core/error/error_macros.h" #include "core/string/print_string.h" -#include "core/string/translation.h" +#include "core/string/translation_server.h" #include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg. @@ -2477,6 +2477,37 @@ String TextServerFallback::_font_get_supported_chars(const RID &p_font_rid) cons return chars; } +PackedInt32Array TextServerFallback::_font_get_supported_glyphs(const RID &p_font_rid) const { + FontFallback *fd = _get_font_data(p_font_rid); + ERR_FAIL_NULL_V(fd, PackedInt32Array()); + + MutexLock lock(fd->mutex); + if (fd->cache.is_empty()) { + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), PackedInt32Array()); + } + FontForSizeFallback *at_size = fd->cache.begin()->value; + + PackedInt32Array glyphs; +#ifdef MODULE_FREETYPE_ENABLED + if (at_size && at_size->face) { + FT_UInt gindex; + FT_ULong charcode = FT_Get_First_Char(at_size->face, &gindex); + while (gindex != 0) { + glyphs.push_back(gindex); + charcode = FT_Get_Next_Char(at_size->face, charcode, &gindex); + } + return glyphs; + } +#endif + if (at_size) { + const HashMap<int32_t, FontGlyph> &gl = at_size->glyph_map; + for (const KeyValue<int32_t, FontGlyph> &E : gl) { + glyphs.push_back(E.key); + } + } + return glyphs; +} + void TextServerFallback::_font_render_range(const RID &p_font_rid, const Vector2i &p_size, int64_t p_start, int64_t p_end) { FontFallback *fd = _get_font_data(p_font_rid); ERR_FAIL_NULL(fd); diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h index 2235247b31..1b76c6fa0f 100644 --- a/modules/text_server_fb/text_server_fb.h +++ b/modules/text_server_fb/text_server_fb.h @@ -739,6 +739,7 @@ public: MODBIND2RC(bool, font_has_char, const RID &, int64_t); MODBIND1RC(String, font_get_supported_chars, const RID &); + MODBIND1RC(PackedInt32Array, font_get_supported_glyphs, const RID &); MODBIND4(font_render_range, const RID &, const Vector2i &, int64_t, int64_t); MODBIND3(font_render_glyph, const RID &, const Vector2i &, int64_t); diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index 5169b9417f..689360aef6 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -3270,18 +3270,17 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP } List<String> copy_args; - String copy_command; - if (export_format == EXPORT_FORMAT_AAB) { - copy_command = vformat("copyAndRename%sAab", build_type); - } else if (export_format == EXPORT_FORMAT_APK) { - copy_command = vformat("copyAndRename%sApk", build_type); - } - + String copy_command = "copyAndRenameBinary"; copy_args.push_back(copy_command); copy_args.push_back("-p"); // argument to specify the start directory. copy_args.push_back(build_path); // start directory. + copy_args.push_back("-Pexport_build_type=" + build_type.to_lower()); + + String export_format_arg = export_format == EXPORT_FORMAT_AAB ? "aab" : "apk"; + copy_args.push_back("-Pexport_format=" + export_format_arg); + String export_filename = p_path.get_file(); String export_path = p_path.get_base_dir(); if (export_path.is_relative_path()) { diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle index 01d5d9ef92..05b4f379b3 100644 --- a/platform/android/java/app/build.gradle +++ b/platform/android/java/app/build.gradle @@ -211,70 +211,24 @@ android { } } -task copyAndRenameDebugApk(type: Copy) { +task copyAndRenameBinary(type: Copy) { // The 'doNotTrackState' is added to disable gradle's up-to-date checks for output files // and directories. Otherwise this check may cause permissions access failures on Windows // machines. doNotTrackState("No need for up-to-date checks for the copy-and-rename operation") - from "$buildDir/outputs/apk/debug/android_debug.apk" - into getExportPath() - rename "android_debug.apk", getExportFilename() -} + String exportPath = getExportPath() + String exportFilename = getExportFilename() + String exportBuildType = getExportBuildType() + String exportFormat = getExportFormat() -task copyAndRenameDevApk(type: Copy) { - // The 'doNotTrackState' is added to disable gradle's up-to-date checks for output files - // and directories. Otherwise this check may cause permissions access failures on Windows - // machines. - doNotTrackState("No need for up-to-date checks for the copy-and-rename operation") - - from "$buildDir/outputs/apk/dev/android_dev.apk" - into getExportPath() - rename "android_dev.apk", getExportFilename() -} - -task copyAndRenameReleaseApk(type: Copy) { - // The 'doNotTrackState' is added to disable gradle's up-to-date checks for output files - // and directories. Otherwise this check may cause permissions access failures on Windows - // machines. - doNotTrackState("No need for up-to-date checks for the copy-and-rename operation") - - from "$buildDir/outputs/apk/release/android_release.apk" - into getExportPath() - rename "android_release.apk", getExportFilename() -} - -task copyAndRenameDebugAab(type: Copy) { - // The 'doNotTrackState' is added to disable gradle's up-to-date checks for output files - // and directories. Otherwise this check may cause permissions access failures on Windows - // machines. - doNotTrackState("No need for up-to-date checks for the copy-and-rename operation") - - from "$buildDir/outputs/bundle/debug/build-debug.aab" - into getExportPath() - rename "build-debug.aab", getExportFilename() -} - -task copyAndRenameDevAab(type: Copy) { - // The 'doNotTrackState' is added to disable gradle's up-to-date checks for output files - // and directories. Otherwise this check may cause permissions access failures on Windows - // machines. - doNotTrackState("No need for up-to-date checks for the copy-and-rename operation") - - from "$buildDir/outputs/bundle/dev/build-dev.aab" - into getExportPath() - rename "build-dev.aab", getExportFilename() -} - -task copyAndRenameReleaseAab(type: Copy) { - // The 'doNotTrackState' is added to disable gradle's up-to-date checks for output files - // and directories. Otherwise this check may cause permissions access failures on Windows - // machines. - doNotTrackState("No need for up-to-date checks for the copy-and-rename operation") + boolean isAab = exportFormat == "aab" + String sourceFilepath = isAab ? "$buildDir/outputs/bundle/$exportBuildType/build-${exportBuildType}.aab" : "$buildDir/outputs/apk/$exportBuildType/android_${exportBuildType}.apk" + String sourceFilename = isAab ? "build-${exportBuildType}.aab" : "android_${exportBuildType}.apk" - from "$buildDir/outputs/bundle/release/build-release.aab" - into getExportPath() - rename "build-release.aab", getExportFilename() + from sourceFilepath + into exportPath + rename sourceFilename, exportFilename } /** diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle index eb9ad9de05..611a9c4a40 100644 --- a/platform/android/java/app/config.gradle +++ b/platform/android/java/app/config.gradle @@ -224,6 +224,22 @@ ext.getExportFilename = { return exportFilename } +ext.getExportBuildType = { + String exportBuildType = project.hasProperty("export_build_type") ? project.property("export_build_type") : "" + if (exportBuildType == null || exportBuildType.isEmpty()) { + exportBuildType = "debug" + } + return exportBuildType +} + +ext.getExportFormat = { + String exportFormat = project.hasProperty("export_format") ? project.property("export_format") : "" + if (exportFormat == null || exportFormat.isEmpty()) { + exportFormat = "apk" + } + return exportFormat +} + /** * Parse the project properties for the 'plugins_maven_repos' property and return the list * of maven repos. diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle index f5555289fd..771bda6948 100644 --- a/platform/android/java/build.gradle +++ b/platform/android/java/build.gradle @@ -35,116 +35,17 @@ ext { // `./gradlew generateGodotTemplates` build command instead after running the `scons` command(s). // The {selectedAbis} values must be from the {supportedAbis} values. selectedAbis = ["arm64"] -} -def rootDir = "../../.." -def binDir = "$rootDir/bin/" -def androidEditorBuildsDir = "$binDir/android_editor_builds/" + rootDir = "../../.." + binDir = "$rootDir/bin/" + androidEditorBuildsDir = "$binDir/android_editor_builds/" +} def getSconsTaskName(String flavor, String buildType, String abi) { return "compileGodotNativeLibs" + flavor.capitalize() + buildType.capitalize() + abi.capitalize() } /** - * Copy the generated 'android_debug.apk' binary template into the Godot bin directory. - * Depends on the app build task to ensure the binary is generated prior to copying. - */ -task copyDebugBinaryToBin(type: Copy) { - dependsOn ':app:assembleDebug' - from('app/build/outputs/apk/debug') - into(binDir) - include('android_debug.apk') -} - -/** - * Copy the generated 'android_dev.apk' binary template into the Godot bin directory. - * Depends on the app build task to ensure the binary is generated prior to copying. - */ -task copyDevBinaryToBin(type: Copy) { - dependsOn ':app:assembleDev' - from('app/build/outputs/apk/dev') - into(binDir) - include('android_dev.apk') -} - -/** - * Copy the generated 'android_release.apk' binary template into the Godot bin directory. - * Depends on the app build task to ensure the binary is generated prior to copying. - */ -task copyReleaseBinaryToBin(type: Copy) { - dependsOn ':app:assembleRelease' - from('app/build/outputs/apk/release') - into(binDir) - include('android_release.apk') -} - -/** - * Copy the Godot android library archive debug file into the app module debug libs directory. - * Depends on the library build task to ensure the AAR file is generated prior to copying. - */ -task copyDebugAARToAppModule(type: Copy) { - dependsOn ':lib:assembleTemplateDebug' - from('lib/build/outputs/aar') - into('app/libs/debug') - include('godot-lib.template_debug.aar') -} - -/** - * Copy the Godot android library archive debug file into the root bin directory. - * Depends on the library build task to ensure the AAR file is generated prior to copying. - */ -task copyDebugAARToBin(type: Copy) { - dependsOn ':lib:assembleTemplateDebug' - from('lib/build/outputs/aar') - into(binDir) - include('godot-lib.template_debug.aar') -} - -/** - * Copy the Godot android library archive dev file into the app module dev libs directory. - * Depends on the library build task to ensure the AAR file is generated prior to copying. - */ -task copyDevAARToAppModule(type: Copy) { - dependsOn ':lib:assembleTemplateDev' - from('lib/build/outputs/aar') - into('app/libs/dev') - include('godot-lib.template_debug.dev.aar') -} - -/** - * Copy the Godot android library archive dev file into the root bin directory. - * Depends on the library build task to ensure the AAR file is generated prior to copying. - */ -task copyDevAARToBin(type: Copy) { - dependsOn ':lib:assembleTemplateDev' - from('lib/build/outputs/aar') - into(binDir) - include('godot-lib.template_debug.dev.aar') -} - -/** - * Copy the Godot android library archive release file into the app module release libs directory. - * Depends on the library build task to ensure the AAR file is generated prior to copying. - */ -task copyReleaseAARToAppModule(type: Copy) { - dependsOn ':lib:assembleTemplateRelease' - from('lib/build/outputs/aar') - into('app/libs/release') - include('godot-lib.template_release.aar') -} - -/** - * Copy the Godot android library archive release file into the root bin directory. - * Depends on the library build task to ensure the AAR file is generated prior to copying. - */ -task copyReleaseAARToBin(type: Copy) { - dependsOn ':lib:assembleTemplateRelease' - from('lib/build/outputs/aar') - into(binDir) - include('godot-lib.template_release.aar') -} - -/** * Generate Godot gradle build template by zipping the source files from the app directory, as well * as the AAR files generated by 'copyDebugAAR', 'copyDevAAR' and 'copyReleaseAAR'. * The zip file also includes some gradle tools to enable gradle builds from the Godot Editor. @@ -197,7 +98,7 @@ def generateBuildTasks(String flavor = "template") { throw new GradleException("Invalid build flavor: $flavor") } - def tasks = [] + def buildTasks = [] // Only build the apks and aar files for which we have native shared libraries unless we intend // to run the scons build tasks. @@ -206,72 +107,93 @@ def generateBuildTasks(String flavor = "template") { String libsDir = isTemplate ? "lib/libs/" : "lib/libs/tools/" for (String target : supportedFlavorsBuildTypes[flavor]) { File targetLibs = new File(libsDir + target) + + String targetSuffix = target + if (target == "dev") { + targetSuffix = "debug.dev" + } + if (!excludeSconsBuildTasks || (targetLibs != null && targetLibs.isDirectory() && targetLibs.listFiles() != null && targetLibs.listFiles().length > 0)) { + String capitalizedTarget = target.capitalize() if (isTemplate) { - // Copy the generated aar library files to the build directory. - tasks += "copy${capitalizedTarget}AARToAppModule" - // Copy the generated aar library files to the bin directory. - tasks += "copy${capitalizedTarget}AARToBin" - // Copy the prebuilt binary templates to the bin directory. - tasks += "copy${capitalizedTarget}BinaryToBin" + // Copy the Godot android library archive file into the app module libs directory. + // Depends on the library build task to ensure the AAR file is generated prior to copying. + String copyAARTaskName = "copy${capitalizedTarget}AARToAppModule" + if (tasks.findByName(copyAARTaskName) != null) { + buildTasks += tasks.getByName(copyAARTaskName) + } else { + buildTasks += tasks.create(name: copyAARTaskName, type: Copy) { + dependsOn ":lib:assembleTemplate${capitalizedTarget}" + from('lib/build/outputs/aar') + include("godot-lib.template_${targetSuffix}.aar") + into("app/libs/${target}") + } + } + + // Copy the Godot android library archive file into the root bin directory. + // Depends on the library build task to ensure the AAR file is generated prior to copying. + String copyAARToBinTaskName = "copy${capitalizedTarget}AARToBin" + if (tasks.findByName(copyAARToBinTaskName) != null) { + buildTasks += tasks.getByName(copyAARToBinTaskName) + } else { + buildTasks += tasks.create(name: copyAARToBinTaskName, type: Copy) { + dependsOn ":lib:assembleTemplate${capitalizedTarget}" + from('lib/build/outputs/aar') + include("godot-lib.template_${targetSuffix}.aar") + into(binDir) + } + } + + // Copy the generated binary template into the Godot bin directory. + // Depends on the app build task to ensure the binary is generated prior to copying. + String copyBinaryTaskName = "copy${capitalizedTarget}BinaryToBin" + if (tasks.findByName(copyBinaryTaskName) != null) { + buildTasks += tasks.getByName(copyBinaryTaskName) + } else { + buildTasks += tasks.create(name: copyBinaryTaskName, type: Copy) { + dependsOn ":app:assemble${capitalizedTarget}" + from("app/build/outputs/apk/${target}") + into(binDir) + include("android_${target}.apk") + } + } } else { // Copy the generated editor apk to the bin directory. - tasks += "copyEditor${capitalizedTarget}ApkToBin" + String copyEditorApkTaskName = "copyEditor${capitalizedTarget}ApkToBin" + if (tasks.findByName(copyEditorApkTaskName) != null) { + buildTasks += tasks.getByName(copyEditorApkTaskName) + } else { + buildTasks += tasks.create(name: copyEditorApkTaskName, type: Copy) { + dependsOn ":editor:assemble${capitalizedTarget}" + from("editor/build/outputs/apk/${target}") + into(androidEditorBuildsDir) + include("android_editor-${target}*.apk") + } + } + // Copy the generated editor aab to the bin directory. - tasks += "copyEditor${capitalizedTarget}AabToBin" + String copyEditorAabTaskName = "copyEditor${capitalizedTarget}AabToBin" + if (tasks.findByName(copyEditorAabTaskName) != null) { + buildTasks += tasks.getByName(copyEditorAabTaskName) + } else { + buildTasks += tasks.create(name: copyEditorAabTaskName, type: Copy) { + dependsOn ":editor:bundle${capitalizedTarget}" + from("editor/build/outputs/bundle/${target}") + into(androidEditorBuildsDir) + include("android_editor-${target}*.aab") + } + } } } else { logger.lifecycle("No native shared libs for target $target. Skipping build.") } } - return tasks -} - -task copyEditorReleaseApkToBin(type: Copy) { - dependsOn ':editor:assembleRelease' - from('editor/build/outputs/apk/release') - into(androidEditorBuildsDir) - include('android_editor-release*.apk') -} - -task copyEditorReleaseAabToBin(type: Copy) { - dependsOn ':editor:bundleRelease' - from('editor/build/outputs/bundle/release') - into(androidEditorBuildsDir) - include('android_editor-release*.aab') -} - -task copyEditorDebugApkToBin(type: Copy) { - dependsOn ':editor:assembleDebug' - from('editor/build/outputs/apk/debug') - into(androidEditorBuildsDir) - include('android_editor-debug.apk') -} - -task copyEditorDebugAabToBin(type: Copy) { - dependsOn ':editor:bundleDebug' - from('editor/build/outputs/bundle/debug') - into(androidEditorBuildsDir) - include('android_editor-debug.aab') -} - -task copyEditorDevApkToBin(type: Copy) { - dependsOn ':editor:assembleDev' - from('editor/build/outputs/apk/dev') - into(androidEditorBuildsDir) - include('android_editor-dev.apk') -} - -task copyEditorDevAabToBin(type: Copy) { - dependsOn ':editor:bundleDev' - from('editor/build/outputs/bundle/dev') - into(androidEditorBuildsDir) - include('android_editor-dev.aab') + return buildTasks } /** diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt index fa39ccb546..111cd48405 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt @@ -39,8 +39,6 @@ import android.content.res.Configuration import android.content.res.Resources import android.graphics.Color import android.hardware.Sensor -import android.hardware.SensorEvent -import android.hardware.SensorEventListener import android.hardware.SensorManager import android.os.* import android.util.Log @@ -53,6 +51,7 @@ import androidx.core.view.WindowInsetsAnimationCompat import androidx.core.view.WindowInsetsCompat import com.google.android.vending.expansion.downloader.* import org.godotengine.godot.input.GodotEditText +import org.godotengine.godot.input.GodotInputHandler import org.godotengine.godot.io.directory.DirectoryAccessHandler import org.godotengine.godot.io.file.FileAccessHandler import org.godotengine.godot.plugin.GodotPluginRegistry @@ -73,6 +72,7 @@ import java.io.InputStream import java.lang.Exception import java.security.MessageDigest import java.util.* +import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicReference /** @@ -81,7 +81,7 @@ import java.util.concurrent.atomic.AtomicReference * Can be hosted by [Activity], [Fragment] or [Service] android components, so long as its * lifecycle methods are properly invoked. */ -class Godot(private val context: Context) : SensorEventListener { +class Godot(private val context: Context) { private companion object { private val TAG = Godot::class.java.simpleName @@ -99,15 +99,23 @@ class Godot(private val context: Context) : SensorEventListener { private val pluginRegistry: GodotPluginRegistry by lazy { GodotPluginRegistry.getPluginRegistry() } + + private val accelerometer_enabled = AtomicBoolean(false) private val mAccelerometer: Sensor? by lazy { mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) } + + private val gravity_enabled = AtomicBoolean(false) private val mGravity: Sensor? by lazy { mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY) } + + private val magnetometer_enabled = AtomicBoolean(false) private val mMagnetometer: Sensor? by lazy { mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) } + + private val gyroscope_enabled = AtomicBoolean(false) private val mGyroscope: Sensor? by lazy { mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) } @@ -127,6 +135,7 @@ class Godot(private val context: Context) : SensorEventListener { val fileAccessHandler = FileAccessHandler(context) val netUtils = GodotNetUtils(context) private val commandLineFileParser = CommandLineFileParser() + private val godotInputHandler = GodotInputHandler(context, this) /** * Task to run when the engine terminates. @@ -154,6 +163,17 @@ class Godot(private val context: Context) : SensorEventListener { private var renderViewInitialized = false private var primaryHost: GodotHost? = null + /** + * Tracks whether we're in the RESUMED lifecycle state. + * See [onResume] and [onPause] + */ + private var resumed = false + + /** + * Tracks whether [onGodotSetupCompleted] fired. + */ + private val godotMainLoopStarted = AtomicBoolean(false) + var io: GodotIO? = null private var commandLine : MutableList<String> = ArrayList<String>() @@ -416,10 +436,10 @@ class Godot(private val context: Context) : SensorEventListener { if (!meetsVulkanRequirements(activity.packageManager)) { throw IllegalStateException(activity.getString(R.string.error_missing_vulkan_requirements_message)) } - GodotVulkanRenderView(host, this) + GodotVulkanRenderView(host, this, godotInputHandler) } else { // Fallback to openGl - GodotGLRenderView(host, this, xrMode, useDebugOpengl) + GodotGLRenderView(host, this, godotInputHandler, xrMode, useDebugOpengl) } if (host == primaryHost) { @@ -520,23 +540,13 @@ class Godot(private val context: Context) : SensorEventListener { fun onResume(host: GodotHost) { Log.v(TAG, "OnResume: $host") + resumed = true if (host != primaryHost) { return } renderView?.onActivityResumed() - if (mAccelerometer != null) { - mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME) - } - if (mGravity != null) { - mSensorManager.registerListener(this, mGravity, SensorManager.SENSOR_DELAY_GAME) - } - if (mMagnetometer != null) { - mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME) - } - if (mGyroscope != null) { - mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME) - } + registerSensorsIfNeeded() if (useImmersive) { val window = requireActivity().window window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or @@ -551,14 +561,34 @@ class Godot(private val context: Context) : SensorEventListener { } } + private fun registerSensorsIfNeeded() { + if (!resumed || !godotMainLoopStarted.get()) { + return + } + + if (accelerometer_enabled.get() && mAccelerometer != null) { + mSensorManager.registerListener(godotInputHandler, mAccelerometer, SensorManager.SENSOR_DELAY_GAME) + } + if (gravity_enabled.get() && mGravity != null) { + mSensorManager.registerListener(godotInputHandler, mGravity, SensorManager.SENSOR_DELAY_GAME) + } + if (magnetometer_enabled.get() && mMagnetometer != null) { + mSensorManager.registerListener(godotInputHandler, mMagnetometer, SensorManager.SENSOR_DELAY_GAME) + } + if (gyroscope_enabled.get() && mGyroscope != null) { + mSensorManager.registerListener(godotInputHandler, mGyroscope, SensorManager.SENSOR_DELAY_GAME) + } + } + fun onPause(host: GodotHost) { Log.v(TAG, "OnPause: $host") + resumed = false if (host != primaryHost) { return } renderView?.onActivityPaused() - mSensorManager.unregisterListener(this) + mSensorManager.unregisterListener(godotInputHandler) for (plugin in pluginRegistry.allPlugins) { plugin.onMainPause() } @@ -659,6 +689,16 @@ class Godot(private val context: Context) : SensorEventListener { */ private fun onGodotMainLoopStarted() { Log.v(TAG, "OnGodotMainLoopStarted") + godotMainLoopStarted.set(true) + + accelerometer_enabled.set(java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/sensors/enable_accelerometer"))) + gravity_enabled.set(java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/sensors/enable_gravity"))) + gyroscope_enabled.set(java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/sensors/enable_gyroscope"))) + magnetometer_enabled.set(java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/sensors/enable_magnetometer"))) + + runOnUiThread { + registerSensorsIfNeeded() + } for (plugin in pluginRegistry.allPlugins) { plugin.onGodotMainLoopStarted() @@ -858,77 +898,6 @@ class Godot(private val context: Context) : SensorEventListener { } } - private fun getRotatedValues(values: FloatArray?): FloatArray? { - if (values == null || values.size != 3) { - return null - } - val rotatedValues = FloatArray(3) - when (windowManager.defaultDisplay.rotation) { - Surface.ROTATION_0 -> { - rotatedValues[0] = values[0] - rotatedValues[1] = values[1] - rotatedValues[2] = values[2] - } - Surface.ROTATION_90 -> { - rotatedValues[0] = -values[1] - rotatedValues[1] = values[0] - rotatedValues[2] = values[2] - } - Surface.ROTATION_180 -> { - rotatedValues[0] = -values[0] - rotatedValues[1] = -values[1] - rotatedValues[2] = values[2] - } - Surface.ROTATION_270 -> { - rotatedValues[0] = values[1] - rotatedValues[1] = -values[0] - rotatedValues[2] = values[2] - } - } - return rotatedValues - } - - override fun onSensorChanged(event: SensorEvent) { - if (renderView == null) { - return - } - - val rotatedValues = getRotatedValues(event.values) - - when (event.sensor.type) { - Sensor.TYPE_ACCELEROMETER -> { - rotatedValues?.let { - renderView?.queueOnRenderThread { - GodotLib.accelerometer(-it[0], -it[1], -it[2]) - } - } - } - Sensor.TYPE_GRAVITY -> { - rotatedValues?.let { - renderView?.queueOnRenderThread { - GodotLib.gravity(-it[0], -it[1], -it[2]) - } - } - } - Sensor.TYPE_MAGNETIC_FIELD -> { - rotatedValues?.let { - renderView?.queueOnRenderThread { - GodotLib.magnetometer(-it[0], -it[1], -it[2]) - } - } - } - Sensor.TYPE_GYROSCOPE -> { - rotatedValues?.let { - renderView?.queueOnRenderThread { - GodotLib.gyroscope(it[0], it[1], it[2]) - } - } - } - } - } - - override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {} - /** * Used by the native code (java_godot_wrapper.h) to vibrate the device. * @param durationMs @@ -1063,7 +1032,7 @@ class Godot(private val context: Context) : SensorEventListener { @Keep private fun initInputDevices() { - renderView?.initInputDevices() + godotInputHandler.initInputDevices() } @Keep diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java index 7fbdb34047..15a811ce83 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java @@ -83,12 +83,12 @@ class GodotGLRenderView extends GLSurfaceView implements GodotRenderView { private final GodotRenderer godotRenderer; private final SparseArray<PointerIcon> customPointerIcons = new SparseArray<>(); - public GodotGLRenderView(GodotHost host, Godot godot, XRMode xrMode, boolean useDebugOpengl) { + public GodotGLRenderView(GodotHost host, Godot godot, GodotInputHandler inputHandler, XRMode xrMode, boolean useDebugOpengl) { super(host.getActivity()); this.host = host; this.godot = godot; - this.inputHandler = new GodotInputHandler(this); + this.inputHandler = inputHandler; this.godotRenderer = new GodotRenderer(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT)); @@ -102,11 +102,6 @@ class GodotGLRenderView extends GLSurfaceView implements GodotRenderView { } @Override - public void initInputDevices() { - this.inputHandler.initInputDevices(); - } - - @Override public void queueOnRenderThread(Runnable event) { queueEvent(event); } @@ -145,11 +140,6 @@ class GodotGLRenderView extends GLSurfaceView implements GodotRenderView { } @Override - public void onBackPressed() { - godot.onBackPressed(); - } - - @Override public GodotInputHandler getInputHandler() { return inputHandler; } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java index 19ec0fd1a4..30821eaa8e 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java @@ -37,8 +37,6 @@ import android.view.SurfaceView; public interface GodotRenderView { SurfaceView getView(); - void initInputDevices(); - /** * Starts the thread that will drive Godot's rendering. */ @@ -59,8 +57,6 @@ public interface GodotRenderView { void onActivityDestroyed(); - void onBackPressed(); - GodotInputHandler getInputHandler(); void configurePointerIcon(int pointerType, String imagePath, float hotSpotX, float hotSpotY); diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java index f4411ddf2c..d5b05913d8 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java @@ -57,12 +57,12 @@ class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView { private final VkRenderer mRenderer; private final SparseArray<PointerIcon> customPointerIcons = new SparseArray<>(); - public GodotVulkanRenderView(GodotHost host, Godot godot) { + public GodotVulkanRenderView(GodotHost host, Godot godot, GodotInputHandler inputHandler) { super(host.getActivity()); this.host = host; this.godot = godot; - mInputHandler = new GodotInputHandler(this); + mInputHandler = inputHandler; mRenderer = new VkRenderer(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT)); @@ -81,11 +81,6 @@ class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView { } @Override - public void initInputDevices() { - mInputHandler.initInputDevices(); - } - - @Override public void queueOnRenderThread(Runnable event) { queueOnVkThread(event); } @@ -124,11 +119,6 @@ class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView { } @Override - public void onBackPressed() { - godot.onBackPressed(); - } - - @Override public GodotInputHandler getInputHandler() { return mInputHandler; } diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java index 273774a33d..fb41cd00c0 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java @@ -32,10 +32,14 @@ package org.godotengine.godot.input; import static org.godotengine.godot.utils.GLUtils.DEBUG; +import org.godotengine.godot.Godot; import org.godotengine.godot.GodotLib; import org.godotengine.godot.GodotRenderView; import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; import android.hardware.input.InputManager; import android.os.Build; import android.util.Log; @@ -46,6 +50,10 @@ import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.ScaleGestureDetector; +import android.view.Surface; +import android.view.WindowManager; + +import androidx.annotation.NonNull; import java.util.Collections; import java.util.HashSet; @@ -54,7 +62,7 @@ import java.util.Set; /** * Handles input related events for the {@link GodotRenderView} view. */ -public class GodotInputHandler implements InputManager.InputDeviceListener { +public class GodotInputHandler implements InputManager.InputDeviceListener, SensorEventListener { private static final String TAG = GodotInputHandler.class.getSimpleName(); private static final int ROTARY_INPUT_VERTICAL_AXIS = 1; @@ -64,8 +72,9 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { private final SparseArray<Joystick> mJoysticksDevices = new SparseArray<>(4); private final HashSet<Integer> mHardwareKeyboardIds = new HashSet<>(); - private final GodotRenderView mRenderView; + private final Godot godot; private final InputManager mInputManager; + private final WindowManager windowManager; private final GestureDetector gestureDetector; private final ScaleGestureDetector scaleGestureDetector; private final GodotGestureHandler godotGestureHandler; @@ -77,12 +86,13 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { private int rotaryInputAxis = ROTARY_INPUT_VERTICAL_AXIS; - public GodotInputHandler(GodotRenderView godotView) { - final Context context = godotView.getView().getContext(); - mRenderView = godotView; + public GodotInputHandler(Context context, Godot godot) { + this.godot = godot; mInputManager = (InputManager)context.getSystemService(Context.INPUT_SERVICE); mInputManager.registerInputDeviceListener(this, null); + windowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); + this.godotGestureHandler = new GodotGestureHandler(this); this.gestureDetector = new GestureDetector(context, godotGestureHandler); this.gestureDetector.setIsLongpressEnabled(false); @@ -174,7 +184,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { public boolean onKeyDown(final int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { - mRenderView.onBackPressed(); + godot.onBackPressed(); // press 'back' button should not terminate program //normal handle 'back' event in game logic return true; @@ -507,7 +517,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { return handleTouchEvent(event, eventActionOverride, doubleTap); } - private static float getEventTiltX(MotionEvent event) { + static float getEventTiltX(MotionEvent event) { // Orientation is returned as a radian value between 0 to pi clockwise or 0 to -pi counterclockwise. final float orientation = event.getOrientation(); @@ -520,7 +530,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { return (float)-Math.sin(orientation) * tiltMult; } - private static float getEventTiltY(MotionEvent event) { + static float getEventTiltY(MotionEvent event) { // Orientation is returned as a radian value between 0 to pi clockwise or 0 to -pi counterclockwise. final float orientation = event.getOrientation(); @@ -579,6 +589,11 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { } boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative, float pressure, float tiltX, float tiltY) { + InputEventRunnable runnable = InputEventRunnable.obtain(); + if (runnable == null) { + return false; + } + // Fix the buttonsMask switch (eventAction) { case MotionEvent.ACTION_CANCEL: @@ -594,7 +609,6 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { break; } - final int updatedButtonsMask = buttonsMask; // We don't handle ACTION_BUTTON_PRESS and ACTION_BUTTON_RELEASE events as they typically // follow ACTION_DOWN and ACTION_UP events. As such, handling them would result in duplicate // stream of events to the engine. @@ -607,11 +621,8 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { case MotionEvent.ACTION_HOVER_MOVE: case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_SCROLL: { - if (shouldDispatchInputToRenderThread()) { - mRenderView.queueOnRenderThread(() -> GodotLib.dispatchMouseEvent(eventAction, updatedButtonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative, pressure, tiltX, tiltY)); - } else { - GodotLib.dispatchMouseEvent(eventAction, updatedButtonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative, pressure, tiltX, tiltY); - } + runnable.setMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative, pressure, tiltX, tiltY); + dispatchInputEventRunnable(runnable); return true; } } @@ -627,22 +638,14 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { } boolean handleTouchEvent(final MotionEvent event, int eventActionOverride, boolean doubleTap) { - final int pointerCount = event.getPointerCount(); - if (pointerCount == 0) { + if (event.getPointerCount() == 0) { return true; } - final float[] positions = new float[pointerCount * 6]; // pointerId1, x1, y1, pressure1, tiltX1, tiltY1, pointerId2, etc... - - for (int i = 0; i < pointerCount; i++) { - positions[i * 6 + 0] = event.getPointerId(i); - positions[i * 6 + 1] = event.getX(i); - positions[i * 6 + 2] = event.getY(i); - positions[i * 6 + 3] = event.getPressure(i); - positions[i * 6 + 4] = getEventTiltX(event); - positions[i * 6 + 5] = getEventTiltY(event); + InputEventRunnable runnable = InputEventRunnable.obtain(); + if (runnable == null) { + return false; } - final int actionPointerId = event.getPointerId(event.getActionIndex()); switch (eventActionOverride) { case MotionEvent.ACTION_DOWN: @@ -651,11 +654,8 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_POINTER_DOWN: { - if (shouldDispatchInputToRenderThread()) { - mRenderView.queueOnRenderThread(() -> GodotLib.dispatchTouchEvent(eventActionOverride, actionPointerId, pointerCount, positions, doubleTap)); - } else { - GodotLib.dispatchTouchEvent(eventActionOverride, actionPointerId, pointerCount, positions, doubleTap); - } + runnable.setTouchEvent(event, eventActionOverride, doubleTap); + dispatchInputEventRunnable(runnable); return true; } } @@ -663,58 +663,128 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { } void handleMagnifyEvent(float x, float y, float factor) { - if (shouldDispatchInputToRenderThread()) { - mRenderView.queueOnRenderThread(() -> GodotLib.magnify(x, y, factor)); - } else { - GodotLib.magnify(x, y, factor); + InputEventRunnable runnable = InputEventRunnable.obtain(); + if (runnable == null) { + return; } + + runnable.setMagnifyEvent(x, y, factor); + dispatchInputEventRunnable(runnable); } void handlePanEvent(float x, float y, float deltaX, float deltaY) { - if (shouldDispatchInputToRenderThread()) { - mRenderView.queueOnRenderThread(() -> GodotLib.pan(x, y, deltaX, deltaY)); - } else { - GodotLib.pan(x, y, deltaX, deltaY); + InputEventRunnable runnable = InputEventRunnable.obtain(); + if (runnable == null) { + return; } + + runnable.setPanEvent(x, y, deltaX, deltaY); + dispatchInputEventRunnable(runnable); } private void handleJoystickButtonEvent(int device, int button, boolean pressed) { - if (shouldDispatchInputToRenderThread()) { - mRenderView.queueOnRenderThread(() -> GodotLib.joybutton(device, button, pressed)); - } else { - GodotLib.joybutton(device, button, pressed); + InputEventRunnable runnable = InputEventRunnable.obtain(); + if (runnable == null) { + return; } + + runnable.setJoystickButtonEvent(device, button, pressed); + dispatchInputEventRunnable(runnable); } private void handleJoystickAxisEvent(int device, int axis, float value) { - if (shouldDispatchInputToRenderThread()) { - mRenderView.queueOnRenderThread(() -> GodotLib.joyaxis(device, axis, value)); - } else { - GodotLib.joyaxis(device, axis, value); + InputEventRunnable runnable = InputEventRunnable.obtain(); + if (runnable == null) { + return; } + + runnable.setJoystickAxisEvent(device, axis, value); + dispatchInputEventRunnable(runnable); } private void handleJoystickHatEvent(int device, int hatX, int hatY) { - if (shouldDispatchInputToRenderThread()) { - mRenderView.queueOnRenderThread(() -> GodotLib.joyhat(device, hatX, hatY)); - } else { - GodotLib.joyhat(device, hatX, hatY); + InputEventRunnable runnable = InputEventRunnable.obtain(); + if (runnable == null) { + return; } + + runnable.setJoystickHatEvent(device, hatX, hatY); + dispatchInputEventRunnable(runnable); } private void handleJoystickConnectionChangedEvent(int device, boolean connected, String name) { - if (shouldDispatchInputToRenderThread()) { - mRenderView.queueOnRenderThread(() -> GodotLib.joyconnectionchanged(device, connected, name)); - } else { - GodotLib.joyconnectionchanged(device, connected, name); + InputEventRunnable runnable = InputEventRunnable.obtain(); + if (runnable == null) { + return; } + + runnable.setJoystickConnectionChangedEvent(device, connected, name); + dispatchInputEventRunnable(runnable); } void handleKeyEvent(int physicalKeycode, int unicode, int keyLabel, boolean pressed, boolean echo) { + InputEventRunnable runnable = InputEventRunnable.obtain(); + if (runnable == null) { + return; + } + + runnable.setKeyEvent(physicalKeycode, unicode, keyLabel, pressed, echo); + dispatchInputEventRunnable(runnable); + } + + private void dispatchInputEventRunnable(@NonNull InputEventRunnable runnable) { if (shouldDispatchInputToRenderThread()) { - mRenderView.queueOnRenderThread(() -> GodotLib.key(physicalKeycode, unicode, keyLabel, pressed, echo)); + godot.runOnRenderThread(runnable); } else { - GodotLib.key(physicalKeycode, unicode, keyLabel, pressed, echo); + runnable.run(); + } + } + + @Override + public void onSensorChanged(SensorEvent event) { + final float[] values = event.values; + if (values == null || values.length != 3) { + return; } + + InputEventRunnable runnable = InputEventRunnable.obtain(); + if (runnable == null) { + return; + } + + float rotatedValue0 = 0f; + float rotatedValue1 = 0f; + float rotatedValue2 = 0f; + switch (windowManager.getDefaultDisplay().getRotation()) { + case Surface.ROTATION_0: + rotatedValue0 = values[0]; + rotatedValue1 = values[1]; + rotatedValue2 = values[2]; + break; + + case Surface.ROTATION_90: + rotatedValue0 = -values[1]; + rotatedValue1 = values[0]; + rotatedValue2 = values[2]; + break; + + case Surface.ROTATION_180: + rotatedValue0 = -values[0]; + rotatedValue1 = -values[1]; + rotatedValue2 = values[2]; + break; + + case Surface.ROTATION_270: + rotatedValue0 = values[1]; + rotatedValue1 = -values[0]; + rotatedValue2 = values[2]; + break; + } + + runnable.setSensorEvent(event.sensor.getType(), rotatedValue0, rotatedValue1, rotatedValue2); + godot.runOnRenderThread(runnable); } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) {} } diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/InputEventRunnable.java b/platform/android/java/lib/src/org/godotengine/godot/input/InputEventRunnable.java new file mode 100644 index 0000000000..a282791b2e --- /dev/null +++ b/platform/android/java/lib/src/org/godotengine/godot/input/InputEventRunnable.java @@ -0,0 +1,353 @@ +/**************************************************************************/ +/* InputEventRunnable.java */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +package org.godotengine.godot.input; + +import org.godotengine.godot.GodotLib; + +import android.hardware.Sensor; +import android.util.Log; +import android.view.MotionEvent; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.util.Pools; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Used to dispatch input events. + * + * This is a specialized version of @{@link Runnable} which allows to allocate a finite pool of + * objects for input events dispatching, thus avoid the creation (and garbage collection) of + * spurious @{@link Runnable} objects. + */ +final class InputEventRunnable implements Runnable { + private static final String TAG = InputEventRunnable.class.getSimpleName(); + + private static final int MAX_TOUCH_POINTER_COUNT = 10; // assuming 10 fingers as max supported concurrent touch pointers + + private static final Pools.Pool<InputEventRunnable> POOL = new Pools.Pool<>() { + private static final int MAX_POOL_SIZE = 120 * 10; // up to 120Hz input events rate for up to 5 secs (ANR limit) * 2 + + private final ArrayBlockingQueue<InputEventRunnable> queue = new ArrayBlockingQueue<>(MAX_POOL_SIZE); + private final AtomicInteger createdCount = new AtomicInteger(); + + @Nullable + @Override + public InputEventRunnable acquire() { + InputEventRunnable instance = queue.poll(); + if (instance == null) { + int creationCount = createdCount.incrementAndGet(); + if (creationCount <= MAX_POOL_SIZE) { + instance = new InputEventRunnable(creationCount - 1); + } + } + + return instance; + } + + @Override + public boolean release(@NonNull InputEventRunnable instance) { + return queue.offer(instance); + } + }; + + @Nullable + static InputEventRunnable obtain() { + InputEventRunnable runnable = POOL.acquire(); + if (runnable == null) { + Log.w(TAG, "Input event pool is at capacity"); + } + return runnable; + } + + /** + * Used to track when this instance was created and added to the pool. Primarily used for + * debug purposes. + */ + private final int creationRank; + + private InputEventRunnable(int creationRank) { + this.creationRank = creationRank; + } + + /** + * Set of supported input events. + */ + private enum EventType { + MOUSE, + TOUCH, + MAGNIFY, + PAN, + JOYSTICK_BUTTON, + JOYSTICK_AXIS, + JOYSTICK_HAT, + JOYSTICK_CONNECTION_CHANGED, + KEY, + SENSOR + } + + private EventType currentEventType = null; + + // common event fields + private float eventX; + private float eventY; + private float eventDeltaX; + private float eventDeltaY; + private boolean eventPressed; + + // common touch / mouse fields + private int eventAction; + private boolean doubleTap; + + // Mouse event fields and setter + private int buttonsMask; + private boolean sourceMouseRelative; + private float pressure; + private float tiltX; + private float tiltY; + void setMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative, float pressure, float tiltX, float tiltY) { + this.currentEventType = EventType.MOUSE; + this.eventAction = eventAction; + this.buttonsMask = buttonsMask; + this.eventX = x; + this.eventY = y; + this.eventDeltaX = deltaX; + this.eventDeltaY = deltaY; + this.doubleTap = doubleClick; + this.sourceMouseRelative = sourceMouseRelative; + this.pressure = pressure; + this.tiltX = tiltX; + this.tiltY = tiltY; + } + + // Touch event fields and setter + private int actionPointerId; + private int pointerCount; + private final float[] positions = new float[MAX_TOUCH_POINTER_COUNT * 6]; // pointerId1, x1, y1, pressure1, tiltX1, tiltY1, pointerId2, etc... + void setTouchEvent(MotionEvent event, int eventAction, boolean doubleTap) { + this.currentEventType = EventType.TOUCH; + this.eventAction = eventAction; + this.doubleTap = doubleTap; + this.actionPointerId = event.getPointerId(event.getActionIndex()); + this.pointerCount = Math.min(event.getPointerCount(), MAX_TOUCH_POINTER_COUNT); + for (int i = 0; i < pointerCount; i++) { + positions[i * 6 + 0] = event.getPointerId(i); + positions[i * 6 + 1] = event.getX(i); + positions[i * 6 + 2] = event.getY(i); + positions[i * 6 + 3] = event.getPressure(i); + positions[i * 6 + 4] = GodotInputHandler.getEventTiltX(event); + positions[i * 6 + 5] = GodotInputHandler.getEventTiltY(event); + } + } + + // Magnify event fields and setter + private float magnifyFactor; + void setMagnifyEvent(float x, float y, float factor) { + this.currentEventType = EventType.MAGNIFY; + this.eventX = x; + this.eventY = y; + this.magnifyFactor = factor; + } + + // Pan event setter + void setPanEvent(float x, float y, float deltaX, float deltaY) { + this.currentEventType = EventType.PAN; + this.eventX = x; + this.eventY = y; + this.eventDeltaX = deltaX; + this.eventDeltaY = deltaY; + } + + // common joystick field + private int joystickDevice; + + // Joystick button event fields and setter + private int button; + void setJoystickButtonEvent(int device, int button, boolean pressed) { + this.currentEventType = EventType.JOYSTICK_BUTTON; + this.joystickDevice = device; + this.button = button; + this.eventPressed = pressed; + } + + // Joystick axis event fields and setter + private int axis; + private float value; + void setJoystickAxisEvent(int device, int axis, float value) { + this.currentEventType = EventType.JOYSTICK_AXIS; + this.joystickDevice = device; + this.axis = axis; + this.value = value; + } + + // Joystick hat event fields and setter + private int hatX; + private int hatY; + void setJoystickHatEvent(int device, int hatX, int hatY) { + this.currentEventType = EventType.JOYSTICK_HAT; + this.joystickDevice = device; + this.hatX = hatX; + this.hatY = hatY; + } + + // Joystick connection changed event fields and setter + private boolean connected; + private String joystickName; + void setJoystickConnectionChangedEvent(int device, boolean connected, String name) { + this.currentEventType = EventType.JOYSTICK_CONNECTION_CHANGED; + this.joystickDevice = device; + this.connected = connected; + this.joystickName = name; + } + + // Key event fields and setter + private int physicalKeycode; + private int unicode; + private int keyLabel; + private boolean echo; + void setKeyEvent(int physicalKeycode, int unicode, int keyLabel, boolean pressed, boolean echo) { + this.currentEventType = EventType.KEY; + this.physicalKeycode = physicalKeycode; + this.unicode = unicode; + this.keyLabel = keyLabel; + this.eventPressed = pressed; + this.echo = echo; + } + + // Sensor event fields and setter + private int sensorType; + private float rotatedValue0; + private float rotatedValue1; + private float rotatedValue2; + void setSensorEvent(int sensorType, float rotatedValue0, float rotatedValue1, float rotatedValue2) { + this.currentEventType = EventType.SENSOR; + this.sensorType = sensorType; + this.rotatedValue0 = rotatedValue0; + this.rotatedValue1 = rotatedValue1; + this.rotatedValue2 = rotatedValue2; + } + + @Override + public void run() { + try { + if (currentEventType == null) { + Log.w(TAG, "Invalid event type"); + return; + } + + switch (currentEventType) { + case MOUSE: + GodotLib.dispatchMouseEvent( + eventAction, + buttonsMask, + eventX, + eventY, + eventDeltaX, + eventDeltaY, + doubleTap, + sourceMouseRelative, + pressure, + tiltX, + tiltY); + break; + + case TOUCH: + GodotLib.dispatchTouchEvent( + eventAction, + actionPointerId, + pointerCount, + positions, + doubleTap); + break; + + case MAGNIFY: + GodotLib.magnify(eventX, eventY, magnifyFactor); + break; + + case PAN: + GodotLib.pan(eventX, eventY, eventDeltaX, eventDeltaY); + break; + + case JOYSTICK_BUTTON: + GodotLib.joybutton(joystickDevice, button, eventPressed); + break; + + case JOYSTICK_AXIS: + GodotLib.joyaxis(joystickDevice, axis, value); + break; + + case JOYSTICK_HAT: + GodotLib.joyhat(joystickDevice, hatX, hatY); + break; + + case JOYSTICK_CONNECTION_CHANGED: + GodotLib.joyconnectionchanged(joystickDevice, connected, joystickName); + break; + + case KEY: + GodotLib.key(physicalKeycode, unicode, keyLabel, eventPressed, echo); + break; + + case SENSOR: + switch (sensorType) { + case Sensor.TYPE_ACCELEROMETER: + GodotLib.accelerometer(-rotatedValue0, -rotatedValue1, -rotatedValue2); + break; + + case Sensor.TYPE_GRAVITY: + GodotLib.gravity(-rotatedValue0, -rotatedValue1, -rotatedValue2); + break; + + case Sensor.TYPE_MAGNETIC_FIELD: + GodotLib.magnetometer(-rotatedValue0, -rotatedValue1, -rotatedValue2); + break; + + case Sensor.TYPE_GYROSCOPE: + GodotLib.gyroscope(rotatedValue0, rotatedValue1, rotatedValue2); + break; + } + break; + } + } finally { + recycle(); + } + } + + /** + * Release the current instance back to the pool + */ + private void recycle() { + currentEventType = null; + POOL.release(this); + } +} diff --git a/platform/android/java/lib/src/org/godotengine/godot/io/file/FileData.kt b/platform/android/java/lib/src/org/godotengine/godot/io/file/FileData.kt index f2c0577c21..d0b8a8dffa 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/io/file/FileData.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/io/file/FileData.kt @@ -53,7 +53,7 @@ internal class FileData(filePath: String, accessFlag: FileAccessFlags) : DataAcc fun fileLastModified(filepath: String): Long { return try { - File(filepath).lastModified() + File(filepath).lastModified() / 1000L } catch (e: SecurityException) { 0L } diff --git a/platform/android/java/lib/src/org/godotengine/godot/io/file/MediaStoreData.kt b/platform/android/java/lib/src/org/godotengine/godot/io/file/MediaStoreData.kt index 5410eed727..146fc04da4 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/io/file/MediaStoreData.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/io/file/MediaStoreData.kt @@ -203,7 +203,7 @@ internal class MediaStoreData(context: Context, filePath: String, accessFlag: Fi } val dataItem = result[0] - return dataItem.dateModified.toLong() + return dataItem.dateModified.toLong() / 1000L } fun rename(context: Context, from: String, to: String): Boolean { diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java index 711bca02e7..8976dd65db 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java +++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java @@ -43,6 +43,7 @@ import androidx.annotation.Nullable; import java.lang.reflect.Constructor; import java.util.Collection; +import java.util.Collections; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -82,6 +83,9 @@ public final class GodotPluginRegistry { * Retrieve the full set of loaded plugins. */ public Collection<GodotPlugin> getAllPlugins() { + if (registry.isEmpty()) { + return Collections.emptyList(); + } return registry.values(); } diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index ec42e5bbd8..a4a425f685 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -51,6 +51,7 @@ #include "core/config/project_settings.h" #include "core/input/input.h" #include "main/main.h" +#include "servers/xr_server.h" #ifdef TOOLS_ENABLED #include "editor/editor_settings.h" @@ -270,7 +271,18 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, } if (step.get() == STEP_SHOW_LOGO) { - Main::setup_boot_logo(); + bool xr_enabled; + if (XRServer::get_xr_mode() == XRServer::XRMODE_DEFAULT) { + xr_enabled = GLOBAL_GET("xr/shaders/enabled"); + } else { + xr_enabled = XRServer::get_xr_mode() == XRServer::XRMODE_ON; + } + // Unlike PCVR, there's no additional 2D screen onto which to render the boot logo, + // so we skip this step if xr is enabled. + if (!xr_enabled) { + Main::setup_boot_logo(); + } + step.increment(); return true; } diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp index 70ea4b09c1..91bf7b48a6 100644 --- a/platform/android/java_godot_wrapper.cpp +++ b/platform/android/java_godot_wrapper.cpp @@ -213,25 +213,27 @@ bool GodotJavaWrapper::has_get_clipboard() { } String GodotJavaWrapper::get_clipboard() { + String clipboard; if (_get_clipboard) { JNIEnv *env = get_jni_env(); ERR_FAIL_NULL_V(env, String()); jstring s = (jstring)env->CallObjectMethod(godot_instance, _get_clipboard); - return jstring_to_string(s, env); - } else { - return String(); + clipboard = jstring_to_string(s, env); + env->DeleteLocalRef(s); } + return clipboard; } String GodotJavaWrapper::get_input_fallback_mapping() { + String input_fallback_mapping; if (_get_input_fallback_mapping) { JNIEnv *env = get_jni_env(); ERR_FAIL_NULL_V(env, String()); jstring fallback_mapping = (jstring)env->CallObjectMethod(godot_instance, _get_input_fallback_mapping); - return jstring_to_string(fallback_mapping, env); - } else { - return String(); + input_fallback_mapping = jstring_to_string(fallback_mapping, env); + env->DeleteLocalRef(fallback_mapping); } + return input_fallback_mapping; } bool GodotJavaWrapper::has_set_clipboard() { @@ -324,14 +326,15 @@ Vector<String> GodotJavaWrapper::get_gdextension_list_config_file() const { } String GodotJavaWrapper::get_ca_certificates() const { + String ca_certificates; if (_get_ca_certificates) { JNIEnv *env = get_jni_env(); ERR_FAIL_NULL_V(env, String()); jstring s = (jstring)env->CallObjectMethod(godot_instance, _get_ca_certificates); - return jstring_to_string(s, env); - } else { - return String(); + ca_certificates = jstring_to_string(s, env); + env->DeleteLocalRef(s); } + return ca_certificates; } void GodotJavaWrapper::init_input_devices() { diff --git a/platform/ios/godot_app_delegate.h b/platform/ios/godot_app_delegate.h index a9bfcbb0b2..85dc6bb390 100644 --- a/platform/ios/godot_app_delegate.h +++ b/platform/ios/godot_app_delegate.h @@ -32,7 +32,7 @@ typedef NSObject<UIApplicationDelegate> ApplicationDelegateService; -@interface GodotApplicalitionDelegate : NSObject <UIApplicationDelegate> +@interface GodotApplicationDelegate : NSObject <UIApplicationDelegate> @property(class, readonly, strong) NSArray<ApplicationDelegateService *> *services; diff --git a/platform/ios/godot_app_delegate.m b/platform/ios/godot_app_delegate.m index 74e8705bc3..53e53cd0c6 100644 --- a/platform/ios/godot_app_delegate.m +++ b/platform/ios/godot_app_delegate.m @@ -32,11 +32,11 @@ #import "app_delegate.h" -@interface GodotApplicalitionDelegate () +@interface GodotApplicationDelegate () @end -@implementation GodotApplicalitionDelegate +@implementation GodotApplicationDelegate static NSMutableArray<ApplicationDelegateService *> *services = nil; diff --git a/platform/ios/main.m b/platform/ios/main.m index 33b1034d98..89a00c9ae9 100644 --- a/platform/ios/main.m +++ b/platform/ios/main.m @@ -46,7 +46,7 @@ int main(int argc, char *argv[]) { gargv = argv; @autoreleasepool { - NSString *className = NSStringFromClass([GodotApplicalitionDelegate class]); + NSString *className = NSStringFromClass([GodotApplicationDelegate class]); UIApplicationMain(argc, argv, nil, className); } return 0; diff --git a/platform/linuxbsd/export/export_plugin.cpp b/platform/linuxbsd/export/export_plugin.cpp index 936adddda3..0032b898d2 100644 --- a/platform/linuxbsd/export/export_plugin.cpp +++ b/platform/linuxbsd/export/export_plugin.cpp @@ -61,6 +61,20 @@ Error EditorExportPlatformLinuxBSD::_export_debug_script(const Ref<EditorExportP } Error EditorExportPlatformLinuxBSD::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { + String custom_debug = p_preset->get("custom_template/debug"); + String custom_release = p_preset->get("custom_template/release"); + String arch = p_preset->get("binary_format/architecture"); + + String template_path = p_debug ? custom_debug : custom_release; + template_path = template_path.strip_edges(); + if (!template_path.is_empty()) { + String exe_arch = _get_exe_arch(template_path); + if (arch != exe_arch) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), vformat(TTR("Mismatching custom export template executable architecture, found \"%s\", expected \"%s\"."), exe_arch, arch)); + return ERR_CANT_CREATE; + } + } + bool export_as_zip = p_path.ends_with("zip"); String pkg_name; @@ -205,8 +219,76 @@ bool EditorExportPlatformLinuxBSD::is_executable(const String &p_path) const { return is_elf(p_path) || is_shebang(p_path); } +bool EditorExportPlatformLinuxBSD::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const { + String err; + bool valid = EditorExportPlatformPC::has_valid_export_configuration(p_preset, err, r_missing_templates, p_debug); + + String custom_debug = p_preset->get("custom_template/debug").operator String().strip_edges(); + String custom_release = p_preset->get("custom_template/release").operator String().strip_edges(); + String arch = p_preset->get("binary_format/architecture"); + + if (!custom_debug.is_empty() && FileAccess::exists(custom_debug)) { + String exe_arch = _get_exe_arch(custom_debug); + if (arch != exe_arch) { + err += vformat(TTR("Mismatching custom debug export template executable architecture: found \"%s\", expected \"%s\"."), exe_arch, arch) + "\n"; + } + } + if (!custom_release.is_empty() && FileAccess::exists(custom_release)) { + String exe_arch = _get_exe_arch(custom_release); + if (arch != exe_arch) { + err += vformat(TTR("Mismatching custom release export template executable architecture: found \"%s\", expected \"%s\"."), exe_arch, arch) + "\n"; + } + } + + if (!err.is_empty()) { + r_error = err; + } + + return valid; +} + +String EditorExportPlatformLinuxBSD::_get_exe_arch(const String &p_path) const { + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + if (f.is_null()) { + return "invalid"; + } + + // Read and check ELF magic number. + { + uint32_t magic = f->get_32(); + if (magic != 0x464c457f) { // 0x7F + "ELF" + return "invalid"; + } + } + + // Process header. + int64_t header_pos = f->get_position(); + f->seek(header_pos + 14); + uint16_t machine = f->get_16(); + f->close(); + + switch (machine) { + case 0x0003: + return "x86_32"; + case 0x003e: + return "x86_64"; + case 0x0014: + return "ppc32"; + case 0x0015: + return "ppc64"; + case 0x0028: + return "arm32"; + case 0x00b7: + return "arm64"; + case 0x00f3: + return "rv64"; + default: + return "unknown"; + } +} + Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) { - // Patch the header of the "pck" section in the ELF file so that it corresponds to the embedded data + // Patch the header of the "pck" section in the ELF file so that it corresponds to the embedded data. Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ_WRITE); if (f.is_null()) { @@ -214,7 +296,7 @@ Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int return ERR_CANT_OPEN; } - // Read and check ELF magic number + // Read and check ELF magic number. { uint32_t magic = f->get_32(); if (magic != 0x464c457f) { // 0x7F + "ELF" @@ -223,7 +305,7 @@ Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int } } - // Read program architecture bits from class field + // Read program architecture bits from class field. int bits = f->get_8() * 32; @@ -231,7 +313,7 @@ Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int add_message(EXPORT_MESSAGE_ERROR, TTR("PCK Embedding"), TTR("32-bit executables cannot have embedded data >= 4 GiB.")); } - // Get info about the section header table + // Get info about the section header table. int64_t section_table_pos; int64_t section_header_size; @@ -249,13 +331,13 @@ Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int int num_sections = f->get_16(); int string_section_idx = f->get_16(); - // Load the strings table + // Load the strings table. uint8_t *strings; { - // Jump to the strings section header + // Jump to the strings section header. f->seek(section_table_pos + string_section_idx * section_header_size); - // Read strings data size and offset + // Read strings data size and offset. int64_t string_data_pos; int64_t string_data_size; if (bits == 32) { @@ -268,7 +350,7 @@ Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int string_data_size = f->get_64(); } - // Read strings data + // Read strings data. f->seek(string_data_pos); strings = (uint8_t *)memalloc(string_data_size); if (!strings) { @@ -277,7 +359,7 @@ Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int f->get_buffer(strings, string_data_size); } - // Search for the "pck" section + // Search for the "pck" section. bool found = false; for (int i = 0; i < num_sections; ++i) { diff --git a/platform/linuxbsd/export/export_plugin.h b/platform/linuxbsd/export/export_plugin.h index 21bd81ed2f..bbc55b82ce 100644 --- a/platform/linuxbsd/export/export_plugin.h +++ b/platform/linuxbsd/export/export_plugin.h @@ -69,11 +69,13 @@ class EditorExportPlatformLinuxBSD : public EditorExportPlatformPC { bool is_shebang(const String &p_path) const; Error _export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path); + String _get_exe_arch(const String &p_path) const; public: virtual void get_export_options(List<ExportOption> *r_options) const override; virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override; virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const override; + virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override; virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; virtual String get_template_file_name(const String &p_target, const String &p_arch) const override; virtual Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) override; diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp index 2ff02d2e74..057fb4ec16 100644 --- a/platform/macos/export/export_plugin.cpp +++ b/platform/macos/export/export_plugin.cpp @@ -1092,7 +1092,7 @@ void EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_pres List<String> args; args.push_back("sign"); - if (p_path.get_extension() != "dmg") { + if (!p_ent_path.is_empty()) { args.push_back("--entitlements-xml-path"); args.push_back(p_ent_path); } @@ -1153,7 +1153,7 @@ void EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_pres args.push_back("runtime"); } - if (p_path.get_extension() != "dmg") { + if (!p_ent_path.is_empty()) { args.push_back("--entitlements"); args.push_back(p_ent_path); } @@ -1237,7 +1237,7 @@ void EditorExportPlatformMacOS::_code_sign_directory(const Ref<EditorExportPrese } if (extensions_to_sign.has(current_file.get_extension())) { - String ent_path = p_ent_path; + String ent_path; bool set_bundle_id = false; if (sandbox && FileAccess::exists(current_file_path)) { int ftype = MachO::get_filetype(current_file_path); @@ -1357,7 +1357,7 @@ Error EditorExportPlatformMacOS::_copy_and_sign_files(Ref<DirAccess> &dir_access _code_sign_directory(p_preset, p_in_app_path, p_ent_path, p_helper_ent_path, p_should_error_on_non_code_sign); } else { if (extensions_to_sign.has(p_in_app_path.get_extension())) { - String ent_path = p_ent_path; + String ent_path; bool set_bundle_id = false; if (p_sandbox && FileAccess::exists(p_in_app_path)) { int ftype = MachO::get_filetype(p_in_app_path); diff --git a/platform/web/audio_driver_web.cpp b/platform/web/audio_driver_web.cpp index 22487d2756..5e046d7050 100644 --- a/platform/web/audio_driver_web.cpp +++ b/platform/web/audio_driver_web.cpp @@ -312,6 +312,11 @@ bool AudioDriverWeb::is_sample_playback_active(const Ref<AudioSamplePlayback> &p return godot_audio_sample_is_active(itos(p_playback->get_instance_id()).utf8().get_data()) != 0; } +double AudioDriverWeb::get_sample_playback_position(const Ref<AudioSamplePlayback> &p_playback) { + ERR_FAIL_COND_V_MSG(p_playback.is_null(), false, "Parameter p_playback is null."); + return godot_audio_get_sample_playback_position(itos(p_playback->get_instance_id()).utf8().get_data()); +} + void AudioDriverWeb::update_sample_playback_pitch_scale(const Ref<AudioSamplePlayback> &p_playback, float p_pitch_scale) { ERR_FAIL_COND_MSG(p_playback.is_null(), "Parameter p_playback is null."); godot_audio_sample_update_pitch_scale( diff --git a/platform/web/audio_driver_web.h b/platform/web/audio_driver_web.h index 46c5ce4de1..d352fa4692 100644 --- a/platform/web/audio_driver_web.h +++ b/platform/web/audio_driver_web.h @@ -96,6 +96,7 @@ public: virtual void stop_sample_playback(const Ref<AudioSamplePlayback> &p_playback) override; virtual void set_sample_playback_pause(const Ref<AudioSamplePlayback> &p_playback, bool p_paused) override; virtual bool is_sample_playback_active(const Ref<AudioSamplePlayback> &p_playback) override; + virtual double get_sample_playback_position(const Ref<AudioSamplePlayback> &p_playback) override; virtual void update_sample_playback_pitch_scale(const Ref<AudioSamplePlayback> &p_playback, float p_pitch_scale = 0.0f) override; virtual void set_sample_playback_bus_volumes_linear(const Ref<AudioSamplePlayback> &p_playback, const HashMap<StringName, Vector<AudioFrame>> &p_bus_volumes) override; diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp index 40de4e523b..4e55cc137a 100644 --- a/platform/web/display_server_web.cpp +++ b/platform/web/display_server_web.cpp @@ -902,8 +902,10 @@ void DisplayServerWeb::process_joypads() { for (int b = 0; b < s_btns_num; b++) { // Buttons 6 and 7 in the standard mapping need to be // axis to be handled as JoyAxis::TRIGGER by Godot. - if (s_standard && (b == 6 || b == 7)) { - input->joy_axis(idx, (JoyAxis)b, s_btns[b]); + if (s_standard && (b == 6)) { + input->joy_axis(idx, JoyAxis::TRIGGER_LEFT, s_btns[b]); + } else if (s_standard && (b == 7)) { + input->joy_axis(idx, JoyAxis::TRIGGER_RIGHT, s_btns[b]); } else { input->joy_button(idx, (JoyButton)b, s_btns[b]); } diff --git a/platform/web/emscripten_helpers.py b/platform/web/emscripten_helpers.py index 2cee3e8110..8fcabb21c7 100644 --- a/platform/web/emscripten_helpers.py +++ b/platform/web/emscripten_helpers.py @@ -51,11 +51,13 @@ def create_template_zip(env, js, wasm, worker, side): js, wasm, "#platform/web/js/libs/audio.worklet.js", + "#platform/web/js/libs/audio.position.worklet.js", ] out_files = [ zip_dir.File(binary_name + ".js"), zip_dir.File(binary_name + ".wasm"), zip_dir.File(binary_name + ".audio.worklet.js"), + zip_dir.File(binary_name + ".audio.position.worklet.js"), ] if env["threads"]: in_files.append(worker) @@ -74,6 +76,7 @@ def create_template_zip(env, js, wasm, worker, side): "offline.html", "godot.editor.js", "godot.editor.audio.worklet.js", + "godot.editor.audio.position.worklet.js", "logo.svg", "favicon.png", ] diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp index d83e465e8e..d8c1b6033d 100644 --- a/platform/web/export/export_plugin.cpp +++ b/platform/web/export/export_plugin.cpp @@ -242,6 +242,7 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese } cache_files.push_back(name + ".worker.js"); cache_files.push_back(name + ".audio.worklet.js"); + cache_files.push_back(name + ".audio.position.worklet.js"); replaces["___GODOT_CACHE___"] = Variant(cache_files).to_json_string(); // Heavy files that are cached on demand. @@ -835,6 +836,7 @@ Error EditorExportPlatformWeb::_export_project(const Ref<EditorExportPreset> &p_ DirAccess::remove_file_or_error(basepath + ".js"); DirAccess::remove_file_or_error(basepath + ".worker.js"); DirAccess::remove_file_or_error(basepath + ".audio.worklet.js"); + DirAccess::remove_file_or_error(basepath + ".audio.position.worklet.js"); DirAccess::remove_file_or_error(basepath + ".service.worker.js"); DirAccess::remove_file_or_error(basepath + ".pck"); DirAccess::remove_file_or_error(basepath + ".png"); diff --git a/platform/web/godot_audio.h b/platform/web/godot_audio.h index dd5bec00cf..4961ebd2bb 100644 --- a/platform/web/godot_audio.h +++ b/platform/web/godot_audio.h @@ -55,6 +55,7 @@ extern void godot_audio_sample_start(const char *p_playback_object_id, const cha extern void godot_audio_sample_stop(const char *p_playback_object_id); extern void godot_audio_sample_set_pause(const char *p_playback_object_id, bool p_pause); extern int godot_audio_sample_is_active(const char *p_playback_object_id); +extern double godot_audio_get_sample_playback_position(const char *p_playback_object_id); extern void godot_audio_sample_update_pitch_scale(const char *p_playback_object_id, float p_pitch_scale); extern void godot_audio_sample_set_volumes_linear(const char *p_playback_object_id, int *p_buses_buf, int p_buses_size, float *p_volumes_buf, int p_volumes_size); extern void godot_audio_sample_set_finished_callback(void (*p_callback)(const char *)); diff --git a/platform/web/js/engine/config.js b/platform/web/js/engine/config.js index 8c4e1b1b24..61b488cf81 100644 --- a/platform/web/js/engine/config.js +++ b/platform/web/js/engine/config.js @@ -299,6 +299,8 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused- return `${loadPath}.worker.js`; } else if (path.endsWith('.audio.worklet.js')) { return `${loadPath}.audio.worklet.js`; + } else if (path.endsWith('.audio.position.worklet.js')) { + return `${loadPath}.audio.position.worklet.js`; } else if (path.endsWith('.js')) { return `${loadPath}.js`; } else if (path in gdext) { diff --git a/platform/web/js/libs/audio.position.worklet.js b/platform/web/js/libs/audio.position.worklet.js new file mode 100644 index 0000000000..bf3ac4ae2d --- /dev/null +++ b/platform/web/js/libs/audio.position.worklet.js @@ -0,0 +1,50 @@ +/**************************************************************************/ +/* godot.audio.position.worklet.js */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +class GodotPositionReportingProcessor extends AudioWorkletProcessor { + constructor() { + super(); + this.position = 0; + } + + process(inputs, _outputs, _parameters) { + if (inputs.length > 0) { + const input = inputs[0]; + if (input.length > 0) { + this.position += input[0].length; + this.port.postMessage({ 'type': 'position', 'data': this.position }); + return true; + } + } + return true; + } +} + +registerProcessor('godot-position-reporting-processor', GodotPositionReportingProcessor); diff --git a/platform/web/js/libs/library_godot_audio.js b/platform/web/js/libs/library_godot_audio.js index 8b7c572196..0ba6eed464 100644 --- a/platform/web/js/libs/library_godot_audio.js +++ b/platform/web/js/libs/library_godot_audio.js @@ -330,6 +330,7 @@ class SampleNodeBus { * startTime?: number * loopMode?: LoopMode * volume?: Float32Array + * start?: boolean * }} SampleNodeOptions */ @@ -421,9 +422,15 @@ class SampleNode { /** @type {number} */ this.offset = options.offset ?? 0; /** @type {number} */ + this._playbackPosition = options.offset; + /** @type {number} */ this.startTime = options.startTime ?? 0; /** @type {boolean} */ this.isPaused = false; + /** @type {boolean} */ + this.isStarted = false; + /** @type {boolean} */ + this.isCanceled = false; /** @type {number} */ this.pauseTime = 0; /** @type {number} */ @@ -440,6 +447,8 @@ class SampleNode { this._source = GodotAudio.ctx.createBufferSource(); this._onended = null; + /** @type {AudioWorkletNode | null} */ + this._positionWorklet = null; this.setPlaybackRate(options.playbackRate ?? 44100); this._source.buffer = this.getSample().getAudioBuffer(); @@ -449,6 +458,8 @@ class SampleNode { const bus = GodotAudio.Bus.getBus(params.busIndex); const sampleNodeBus = this.getSampleNodeBus(bus); sampleNodeBus.setVolume(options.volume); + + this.connectPositionWorklet(options.start); } /** @@ -460,6 +471,14 @@ class SampleNode { } /** + * Gets the playback position. + * @returns {number} + */ + getPlaybackPosition() { + return this._playbackPosition; + } + + /** * Sets the playback rate. * @param {number} val Value to set. * @returns {void} @@ -508,8 +527,12 @@ class SampleNode { * @returns {void} */ start() { + if (this.isStarted) { + return; + } this._resetSourceStartTime(); this._source.start(this.startTime, this.offset); + this.isStarted = true; } /** @@ -585,17 +608,73 @@ class SampleNode { } /** + * Sets up and connects the source to the GodotPositionReportingProcessor + * If the worklet module is not loaded in, it will be added + */ + connectPositionWorklet(start) { + try { + this._positionWorklet = this.createPositionWorklet(); + this._source.connect(this._positionWorklet); + if (start) { + this.start(); + } + } catch (error) { + if (error?.name !== 'InvalidStateError') { + throw error; + } + const path = GodotConfig.locate_file('godot.audio.position.worklet.js'); + GodotAudio.ctx.audioWorklet + .addModule(path) + .then(() => { + if (!this.isCanceled) { + this._positionWorklet = this.createPositionWorklet(); + this._source.connect(this._positionWorklet); + if (start) { + this.start(); + } + } + }).catch((addModuleError) => { + GodotRuntime.error('Failed to create PositionWorklet.', addModuleError); + }); + } + } + + /** + * Creates the AudioWorkletProcessor used to track playback position. + * @returns {AudioWorkletNode} + */ + createPositionWorklet() { + const worklet = new AudioWorkletNode( + GodotAudio.ctx, + 'godot-position-reporting-processor' + ); + worklet.port.onmessage = (event) => { + switch (event.data['type']) { + case 'position': + this._playbackPosition = (parseInt(event.data.data, 10) / this.getSample().sampleRate) + this.offset; + break; + default: + // Do nothing. + } + }; + return worklet; + } + + /** * Clears the `SampleNode`. * @returns {void} */ clear() { + this.isCanceled = true; this.isPaused = false; this.pauseTime = 0; if (this._source != null) { this._source.removeEventListener('ended', this._onended); this._onended = null; - this._source.stop(); + if (this.isStarted) { + this._source.stop(); + } this._source.disconnect(); this._source = null; } @@ -605,6 +684,12 @@ class SampleNode { } this._sampleNodeBuses.clear(); + if (this._positionWorklet) { + this._positionWorklet.disconnect(); + this._positionWorklet.port.onmessage = null; + this._positionWorklet = null; + } + GodotAudio.SampleNode.delete(this.id); } @@ -645,7 +730,9 @@ class SampleNode { const pauseTime = this.isPaused ? this.pauseTime : 0; + this.connectPositionWorklet(); this._source.start(this.startTime, this.offset + pauseTime); + this.isStarted = true; } /** @@ -1262,7 +1349,7 @@ const _GodotAudio = { startOptions ) { GodotAudio.SampleNode.stopSampleNode(playbackObjectId); - const sampleNode = GodotAudio.SampleNode.create( + GodotAudio.SampleNode.create( { busIndex, id: playbackObjectId, @@ -1270,7 +1357,6 @@ const _GodotAudio = { }, startOptions ); - sampleNode.start(); }, /** @@ -1590,6 +1676,7 @@ const _GodotAudio = { offset, volume, playbackRate: 1, + start: true, }; GodotAudio.start_sample( playbackObjectId, @@ -1635,6 +1722,22 @@ const _GodotAudio = { return Number(GodotAudio.sampleNodes.has(playbackObjectId)); }, + godot_audio_get_sample_playback_position__proxy: 'sync', + godot_audio_get_sample_playback_position__sig: 'di', + /** + * Returns the position of the playback position. + * @param {number} playbackObjectIdStrPtr Playback object id pointer + * @returns {number} + */ + godot_audio_get_sample_playback_position: function (playbackObjectIdStrPtr) { + const playbackObjectId = GodotRuntime.parseString(playbackObjectIdStrPtr); + const sampleNode = GodotAudio.SampleNode.getSampleNodeOrNull(playbackObjectId); + if (sampleNode == null) { + return 0; + } + return sampleNode.getPlaybackPosition(); + }, + godot_audio_sample_update_pitch_scale__proxy: 'sync', godot_audio_sample_update_pitch_scale__sig: 'vii', /** diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 88ab9a4af0..f0fe56a9c8 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -5612,6 +5612,8 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, return id; } +BitField<DisplayServerWindows::DriverID> DisplayServerWindows::tested_drivers = 0; + // WinTab API. bool DisplayServerWindows::wintab_available = false; WTOpenPtr DisplayServerWindows::wintab_WTOpen = nullptr; @@ -5774,6 +5776,8 @@ void DisplayServerWindows::tablet_set_current_driver(const String &p_driver) { DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) { KeyMappingWindows::initialize(); + tested_drivers.clear(); + drop_events = false; key_event_pos = 0; @@ -5942,7 +5946,6 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win wc.lpszClassName = L"Engine"; if (!RegisterClassExW(&wc)) { - MessageBoxW(nullptr, L"Failed To Register The Window Class.", L"ERROR", MB_OK | MB_ICONEXCLAMATION); r_error = ERR_UNAVAILABLE; return; } @@ -5953,11 +5956,13 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win #if defined(VULKAN_ENABLED) if (rendering_driver == "vulkan") { rendering_context = memnew(RenderingContextDriverVulkanWindows); + tested_drivers.set_flag(DRIVER_ID_RD_VULKAN); } #endif #if defined(D3D12_ENABLED) if (rendering_driver == "d3d12") { rendering_context = memnew(RenderingContextDriverD3D12); + tested_drivers.set_flag(DRIVER_ID_RD_D3D12); } #endif @@ -5969,6 +5974,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win if (failed && fallback_to_vulkan && rendering_driver != "vulkan") { memdelete(rendering_context); rendering_context = memnew(RenderingContextDriverVulkanWindows); + tested_drivers.set_flag(DRIVER_ID_RD_VULKAN); if (rendering_context->initialize() == OK) { WARN_PRINT("Your video card drivers seem not to support Direct3D 12, switching to Vulkan."); rendering_driver = "vulkan"; @@ -5981,6 +5987,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win if (failed && fallback_to_d3d12 && rendering_driver != "d3d12") { memdelete(rendering_context); rendering_context = memnew(RenderingContextDriverD3D12); + tested_drivers.set_flag(DRIVER_ID_RD_D3D12); if (rendering_context->initialize() == OK) { WARN_PRINT("Your video card drivers seem not to support Vulkan, switching to Direct3D 12."); rendering_driver = "d3d12"; @@ -6051,6 +6058,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win } if (force_angle || (gl_info["version"].operator int() < 30003)) { + tested_drivers.set_flag(DRIVER_ID_COMPAT_OPENGL3); if (show_warning) { WARN_PRINT("Your video card drivers seem not to support the required OpenGL 3.3 version, switching to ANGLE."); } @@ -6060,6 +6068,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win if (rendering_driver == "opengl3") { gl_manager_native = memnew(GLManagerNative_Windows); + tested_drivers.set_flag(DRIVER_ID_COMPAT_OPENGL3); if (gl_manager_native->initialize() != OK) { memdelete(gl_manager_native); @@ -6072,6 +6081,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win } if (rendering_driver == "opengl3_angle") { gl_manager_angle = memnew(GLManagerANGLE_Windows); + tested_drivers.set_flag(DRIVER_ID_COMPAT_ANGLE_D3D11); if (gl_manager_angle->initialize() != OK) { memdelete(gl_manager_angle); @@ -6205,32 +6215,41 @@ Vector<String> DisplayServerWindows::get_rendering_drivers_func() { DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) { DisplayServer *ds = memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, r_error)); if (r_error != OK) { - if (p_rendering_driver == "vulkan") { - String executable_name = OS::get_singleton()->get_executable_path().get_file(); - OS::get_singleton()->alert( - vformat("Your video card drivers seem not to support the required Vulkan version.\n\n" - "If possible, consider updating your video card drivers or using the OpenGL 3 driver.\n\n" - "You can enable the OpenGL 3 driver by starting the engine from the\n" - "command line with the command:\n\n \"%s\" --rendering-driver opengl3\n\n" - "If you have recently updated your video card drivers, try rebooting.", - executable_name), - "Unable to initialize Vulkan video driver"); - } else if (p_rendering_driver == "d3d12") { + if (tested_drivers == 0) { + OS::get_singleton()->alert("Failed to register the window class.", "Unable to initialize DisplayServer"); + } else if (tested_drivers.has_flag(DRIVER_ID_RD_VULKAN) || tested_drivers.has_flag(DRIVER_ID_RD_D3D12)) { + Vector<String> drivers; + if (tested_drivers.has_flag(DRIVER_ID_RD_VULKAN)) { + drivers.push_back("Vulkan"); + } + if (tested_drivers.has_flag(DRIVER_ID_RD_D3D12)) { + drivers.push_back("Direct3D 12"); + } String executable_name = OS::get_singleton()->get_executable_path().get_file(); OS::get_singleton()->alert( - vformat("Your video card drivers seem not to support the required DirectX 12 version.\n\n" + vformat("Your video card drivers seem not to support the required %s version.\n\n" "If possible, consider updating your video card drivers or using the OpenGL 3 driver.\n\n" "You can enable the OpenGL 3 driver by starting the engine from the\n" "command line with the command:\n\n \"%s\" --rendering-driver opengl3\n\n" "If you have recently updated your video card drivers, try rebooting.", + String(" or ").join(drivers), executable_name), - "Unable to initialize DirectX 12 video driver"); + "Unable to initialize video driver"); } else { + Vector<String> drivers; + if (tested_drivers.has_flag(DRIVER_ID_COMPAT_OPENGL3)) { + drivers.push_back("OpenGL 3.3"); + } + if (tested_drivers.has_flag(DRIVER_ID_COMPAT_ANGLE_D3D11)) { + drivers.push_back("Direct3D 11"); + } OS::get_singleton()->alert( - "Your video card drivers seem not to support the required OpenGL 3.3 version.\n\n" - "If possible, consider updating your video card drivers.\n\n" - "If you have recently updated your video card drivers, try rebooting.", - "Unable to initialize OpenGL video driver"); + vformat( + "Your video card drivers seem not to support the required %s version.\n\n" + "If possible, consider updating your video card drivers.\n\n" + "If you have recently updated your video card drivers, try rebooting.", + String(" or ").join(drivers)), + "Unable to initialize video driver"); } } return ds; diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index de5b813953..26328ba876 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -388,6 +388,14 @@ class DisplayServerWindows : public DisplayServer { String tablet_driver; Vector<String> tablet_drivers; + enum DriverID { + DRIVER_ID_COMPAT_OPENGL3 = 1 << 0, + DRIVER_ID_COMPAT_ANGLE_D3D11 = 1 << 1, + DRIVER_ID_RD_VULKAN = 1 << 2, + DRIVER_ID_RD_D3D12 = 1 << 3, + }; + static BitField<DriverID> tested_drivers; + enum TimerID { TIMER_ID_MOVE_REDRAW = 1, TIMER_ID_WINDOW_ACTIVATION = 2, diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp index 12694b0155..b465bd4ecd 100644 --- a/platform/windows/export/export_plugin.cpp +++ b/platform/windows/export/export_plugin.cpp @@ -187,6 +187,12 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> template_path = template_path.strip_edges(); if (template_path.is_empty()) { template_path = find_export_template(get_template_file_name(p_debug ? "debug" : "release", arch)); + } else { + String exe_arch = _get_exe_arch(template_path); + if (arch != exe_arch) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), vformat(TTR("Mismatching custom export template executable architecture: found \"%s\", expected \"%s\"."), exe_arch, arch)); + return ERR_CANT_CREATE; + } } int export_angle = p_preset->get("application/export_angle"); @@ -753,9 +759,26 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p } bool EditorExportPlatformWindows::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const { - String err = ""; + String err; bool valid = EditorExportPlatformPC::has_valid_export_configuration(p_preset, err, r_missing_templates, p_debug); + String custom_debug = p_preset->get("custom_template/debug").operator String().strip_edges(); + String custom_release = p_preset->get("custom_template/release").operator String().strip_edges(); + String arch = p_preset->get("binary_format/architecture"); + + if (!custom_debug.is_empty() && FileAccess::exists(custom_debug)) { + String exe_arch = _get_exe_arch(custom_debug); + if (arch != exe_arch) { + err += vformat(TTR("Mismatching custom debug export template executable architecture: found \"%s\", expected \"%s\"."), exe_arch, arch) + "\n"; + } + } + if (!custom_release.is_empty() && FileAccess::exists(custom_release)) { + String exe_arch = _get_exe_arch(custom_release); + if (arch != exe_arch) { + err += vformat(TTR("Mismatching custom release export template executable architecture: found \"%s\", expected \"%s\"."), exe_arch, arch) + "\n"; + } + } + String rcedit_path = EDITOR_GET("export/windows/rcedit"); if (p_preset->get("application/modify_resources") && rcedit_path.is_empty()) { err += TTR("The rcedit tool must be configured in the Editor Settings (Export > Windows > rcedit) to change the icon or app information data.") + "\n"; @@ -769,7 +792,7 @@ bool EditorExportPlatformWindows::has_valid_export_configuration(const Ref<Edito } bool EditorExportPlatformWindows::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const { - String err = ""; + String err; bool valid = true; List<ExportOption> options; @@ -793,6 +816,43 @@ bool EditorExportPlatformWindows::has_valid_project_configuration(const Ref<Edit return valid; } +String EditorExportPlatformWindows::_get_exe_arch(const String &p_path) const { + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + if (f.is_null()) { + return "invalid"; + } + + // Jump to the PE header and check the magic number. + { + f->seek(0x3c); + uint32_t pe_pos = f->get_32(); + + f->seek(pe_pos); + uint32_t magic = f->get_32(); + if (magic != 0x00004550) { + return "invalid"; + } + } + + // Process header. + uint16_t machine = f->get_16(); + f->close(); + + switch (machine) { + case 0x014c: + return "x86_32"; + case 0x8664: + return "x86_64"; + case 0x01c0: + case 0x01c4: + return "arm32"; + case 0xaa64: + return "arm64"; + default: + return "unknown"; + } +} + Error EditorExportPlatformWindows::fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) { // Patch the header of the "pck" section in the PE file so that it corresponds to the embedded data diff --git a/platform/windows/export/export_plugin.h b/platform/windows/export/export_plugin.h index c644b1f9e1..6ccb4a15a7 100644 --- a/platform/windows/export/export_plugin.h +++ b/platform/windows/export/export_plugin.h @@ -73,6 +73,8 @@ class EditorExportPlatformWindows : public EditorExportPlatformPC { Error _rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path, bool p_console_icon); Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path); + String _get_exe_arch(const String &p_path) const; + public: virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; virtual Error modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) override; diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 9025f53f42..40b265785f 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -379,6 +379,8 @@ Error OS_Windows::open_dynamic_library(const String &p_path, void *&p_library_ha //this code exists so gdextension can load .dll files from within the executable path path = get_executable_path().get_base_dir().path_join(p_path.get_file()); } + // Path to load from may be different from original if we make copies. + String load_path = path; ERR_FAIL_COND_V(!FileAccess::exists(path), ERR_FILE_NOT_FOUND); @@ -387,25 +389,22 @@ Error OS_Windows::open_dynamic_library(const String &p_path, void *&p_library_ha if (p_data != nullptr && p_data->generate_temp_files) { // Copy the file to the same directory as the original with a prefix in the name. // This is so relative path to dependencies are satisfied. - String copy_path = path.get_base_dir().path_join("~" + path.get_file()); + load_path = path.get_base_dir().path_join("~" + path.get_file()); // If there's a left-over copy (possibly from a crash) then delete it first. - if (FileAccess::exists(copy_path)) { - DirAccess::remove_absolute(copy_path); + if (FileAccess::exists(load_path)) { + DirAccess::remove_absolute(load_path); } - Error copy_err = DirAccess::copy_absolute(path, copy_path); + Error copy_err = DirAccess::copy_absolute(path, load_path); if (copy_err) { ERR_PRINT("Error copying library: " + path); return ERR_CANT_CREATE; } - FileAccess::set_hidden_attribute(copy_path, true); + FileAccess::set_hidden_attribute(load_path, true); - // Save the copied path so it can be deleted later. - path = copy_path; - - Error pdb_err = WindowsUtils::copy_and_rename_pdb(path); + Error pdb_err = WindowsUtils::copy_and_rename_pdb(load_path); if (pdb_err != OK && pdb_err != ERR_SKIP) { WARN_PRINT(vformat("Failed to rename the PDB file. The original PDB file for '%s' will be loaded.", path)); } @@ -421,21 +420,21 @@ Error OS_Windows::open_dynamic_library(const String &p_path, void *&p_library_ha DLL_DIRECTORY_COOKIE cookie = nullptr; if (p_data != nullptr && p_data->also_set_library_path && has_dll_directory_api) { - cookie = add_dll_directory((LPCWSTR)(path.get_base_dir().utf16().get_data())); + cookie = add_dll_directory((LPCWSTR)(load_path.get_base_dir().utf16().get_data())); } - p_library_handle = (void *)LoadLibraryExW((LPCWSTR)(path.utf16().get_data()), nullptr, (p_data != nullptr && p_data->also_set_library_path && has_dll_directory_api) ? LOAD_LIBRARY_SEARCH_DEFAULT_DIRS : 0); + p_library_handle = (void *)LoadLibraryExW((LPCWSTR)(load_path.utf16().get_data()), nullptr, (p_data != nullptr && p_data->also_set_library_path && has_dll_directory_api) ? LOAD_LIBRARY_SEARCH_DEFAULT_DIRS : 0); if (!p_library_handle) { if (p_data != nullptr && p_data->generate_temp_files) { - DirAccess::remove_absolute(path); + DirAccess::remove_absolute(load_path); } #ifdef DEBUG_ENABLED DWORD err_code = GetLastError(); - HashSet<String> checekd_libs; + HashSet<String> checked_libs; HashSet<String> missing_libs; - debug_dynamic_library_check_dependencies(path, path, checekd_libs, missing_libs); + debug_dynamic_library_check_dependencies(load_path, load_path, checked_libs, missing_libs); if (!missing_libs.is_empty()) { String missing; for (const String &E : missing_libs) { @@ -464,7 +463,8 @@ Error OS_Windows::open_dynamic_library(const String &p_path, void *&p_library_ha } if (p_data != nullptr && p_data->generate_temp_files) { - temp_libraries[p_library_handle] = path; + // Save the copied path so it can be deleted later. + temp_libraries[p_library_handle] = load_path; } return OK; diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index 8e91dce425..89a0479de3 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -242,7 +242,7 @@ void AudioStreamPlayer2D::seek(float p_seconds) { void AudioStreamPlayer2D::stop() { setplay.set(-1); - internal->stop(); + internal->stop_basic(); } bool AudioStreamPlayer2D::is_playing() const { @@ -430,7 +430,7 @@ void AudioStreamPlayer2D::_bind_methods() { } AudioStreamPlayer2D::AudioStreamPlayer2D() { - internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer2D::play), true)); + internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer2D::play), callable_mp(this, &AudioStreamPlayer2D::stop), true)); cached_global_panning_strength = GLOBAL_GET("audio/general/2d_panning_strength"); set_hide_clip_children(true); } diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index 1d3f1ceada..bfbdb49f22 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -688,6 +688,7 @@ void GPUParticles2D::_notification(int p_what) { RS::get_singleton()->particles_set_speed_scale(particles, 0); } set_process_internal(true); + set_physics_process_internal(true); previous_position = get_global_position(); } break; @@ -711,15 +712,6 @@ void GPUParticles2D::_notification(int p_what) { } break; case NOTIFICATION_INTERNAL_PROCESS: { - const Vector3 velocity = Vector3((get_global_position() - previous_position).x, (get_global_position() - previous_position).y, 0.0) / - get_process_delta_time(); - - if (velocity != previous_velocity) { - RS::get_singleton()->particles_set_emitter_velocity(particles, velocity); - previous_velocity = velocity; - } - previous_position = get_global_position(); - if (one_shot) { time += get_process_delta_time(); if (time > emission_time) { @@ -739,6 +731,19 @@ void GPUParticles2D::_notification(int p_what) { } } } break; + + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + // Update velocity in physics process, so that velocity calculations remain correct + // if the physics tick rate is lower than the rendered framerate (especially without physics interpolation). + const Vector3 velocity = Vector3((get_global_position() - previous_position).x, (get_global_position() - previous_position).y, 0.0) / + get_physics_process_delta_time(); + + if (velocity != previous_velocity) { + RS::get_singleton()->particles_set_emitter_velocity(particles, velocity); + previous_velocity = velocity; + } + previous_position = get_global_position(); + } break; } } diff --git a/scene/2d/navigation_link_2d.cpp b/scene/2d/navigation_link_2d.cpp index 04ba550888..111f5a7b78 100644 --- a/scene/2d/navigation_link_2d.cpp +++ b/scene/2d/navigation_link_2d.cpp @@ -41,6 +41,9 @@ void NavigationLink2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationLink2D::set_enabled); ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationLink2D::is_enabled); + ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationLink2D::set_navigation_map); + ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationLink2D::get_navigation_map); + ClassDB::bind_method(D_METHOD("set_bidirectional", "bidirectional"), &NavigationLink2D::set_bidirectional); ClassDB::bind_method(D_METHOD("is_bidirectional"), &NavigationLink2D::is_bidirectional); @@ -106,12 +109,7 @@ bool NavigationLink2D::_get(const StringName &p_name, Variant &r_ret) const { void NavigationLink2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - if (enabled) { - NavigationServer2D::get_singleton()->link_set_map(link, get_world_2d()->get_navigation_map()); - } - current_global_transform = get_global_transform(); - NavigationServer2D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position)); - NavigationServer2D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position)); + _link_enter_navigation_map(); } break; case NOTIFICATION_TRANSFORM_CHANGED: { @@ -120,36 +118,15 @@ void NavigationLink2D::_notification(int p_what) { case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { set_physics_process_internal(false); - if (is_inside_tree()) { - Transform2D new_global_transform = get_global_transform(); - if (current_global_transform != new_global_transform) { - current_global_transform = new_global_transform; - NavigationServer2D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position)); - NavigationServer2D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position)); - queue_redraw(); - } - } + _link_update_transform(); } break; case NOTIFICATION_EXIT_TREE: { - NavigationServer2D::get_singleton()->link_set_map(link, RID()); + _link_exit_navigation_map(); } break; case NOTIFICATION_DRAW: { #ifdef DEBUG_ENABLED - if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled())) { - Color color; - if (enabled) { - color = NavigationServer2D::get_singleton()->get_debug_navigation_link_connection_color(); - } else { - color = NavigationServer2D::get_singleton()->get_debug_navigation_link_connection_disabled_color(); - } - - real_t radius = NavigationServer2D::get_singleton()->map_get_link_connection_radius(get_world_2d()->get_navigation_map()); - - draw_line(get_start_position(), get_end_position(), color); - draw_arc(get_start_position(), radius, 0, Math_TAU, 10, color); - draw_arc(get_end_position(), radius, 0, Math_TAU, 10, color); - } + _update_debug_mesh(); #endif // DEBUG_ENABLED } break; } @@ -188,15 +165,32 @@ void NavigationLink2D::set_enabled(bool p_enabled) { enabled = p_enabled; - NavigationServer3D::get_singleton()->link_set_enabled(link, enabled); + NavigationServer2D::get_singleton()->link_set_enabled(link, enabled); #ifdef DEBUG_ENABLED - if (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled()) { - queue_redraw(); - } + queue_redraw(); #endif // DEBUG_ENABLED } +void NavigationLink2D::set_navigation_map(RID p_navigation_map) { + if (map_override == p_navigation_map) { + return; + } + + map_override = p_navigation_map; + + NavigationServer2D::get_singleton()->link_set_map(link, map_override); +} + +RID NavigationLink2D::get_navigation_map() const { + if (map_override.is_valid()) { + return map_override; + } else if (is_inside_tree()) { + return get_world_2d()->get_navigation_map(); + } + return RID(); +} + void NavigationLink2D::set_bidirectional(bool p_bidirectional) { if (bidirectional == p_bidirectional) { return; @@ -255,9 +249,7 @@ void NavigationLink2D::set_start_position(Vector2 p_position) { update_configuration_warnings(); #ifdef DEBUG_ENABLED - if (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled()) { - queue_redraw(); - } + queue_redraw(); #endif // DEBUG_ENABLED } @@ -277,9 +269,7 @@ void NavigationLink2D::set_end_position(Vector2 p_position) { update_configuration_warnings(); #ifdef DEBUG_ENABLED - if (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled()) { - queue_redraw(); - } + queue_redraw(); #endif // DEBUG_ENABLED } @@ -347,6 +337,69 @@ PackedStringArray NavigationLink2D::get_configuration_warnings() const { return warnings; } +void NavigationLink2D::_link_enter_navigation_map() { + if (!is_inside_tree()) { + return; + } + + if (map_override.is_valid()) { + NavigationServer2D::get_singleton()->link_set_map(link, map_override); + } else { + NavigationServer2D::get_singleton()->link_set_map(link, get_world_2d()->get_navigation_map()); + } + + current_global_transform = get_global_transform(); + + NavigationServer2D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position)); + NavigationServer2D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position)); + NavigationServer2D::get_singleton()->link_set_enabled(link, enabled); + + queue_redraw(); +} + +void NavigationLink2D::_link_exit_navigation_map() { + NavigationServer2D::get_singleton()->link_set_map(link, RID()); +} + +void NavigationLink2D::_link_update_transform() { + if (!is_inside_tree()) { + return; + } + + Transform2D new_global_transform = get_global_transform(); + if (current_global_transform != new_global_transform) { + current_global_transform = new_global_transform; + NavigationServer2D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position)); + NavigationServer2D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position)); + queue_redraw(); + } +} + +#ifdef DEBUG_ENABLED +void NavigationLink2D::_update_debug_mesh() { + if (!is_inside_tree()) { + return; + } + + if (!Engine::get_singleton()->is_editor_hint() && !NavigationServer2D::get_singleton()->get_debug_enabled()) { + return; + } + + Color color; + if (enabled) { + color = NavigationServer2D::get_singleton()->get_debug_navigation_link_connection_color(); + } else { + color = NavigationServer2D::get_singleton()->get_debug_navigation_link_connection_disabled_color(); + } + + real_t radius = NavigationServer2D::get_singleton()->map_get_link_connection_radius(get_world_2d()->get_navigation_map()); + + draw_line(get_start_position(), get_end_position(), color); + draw_arc(get_start_position(), radius, 0, Math_TAU, 10, color); + draw_arc(get_end_position(), radius, 0, Math_TAU, 10, color); +} +#endif // DEBUG_ENABLED + NavigationLink2D::NavigationLink2D() { link = NavigationServer2D::get_singleton()->link_create(); diff --git a/scene/2d/navigation_link_2d.h b/scene/2d/navigation_link_2d.h index 2929691c04..c724096607 100644 --- a/scene/2d/navigation_link_2d.h +++ b/scene/2d/navigation_link_2d.h @@ -38,6 +38,7 @@ class NavigationLink2D : public Node2D { bool enabled = true; RID link; + RID map_override; bool bidirectional = true; uint32_t navigation_layers = 1; Vector2 end_position; @@ -47,6 +48,10 @@ class NavigationLink2D : public Node2D { Transform2D current_global_transform; +#ifdef DEBUG_ENABLED + void _update_debug_mesh(); +#endif // DEBUG_ENABLED + protected: static void _bind_methods(); void _notification(int p_what); @@ -66,6 +71,9 @@ public: void set_enabled(bool p_enabled); bool is_enabled() const { return enabled; } + void set_navigation_map(RID p_navigation_map); + RID get_navigation_map() const; + void set_bidirectional(bool p_bidirectional); bool is_bidirectional() const { return bidirectional; } @@ -97,6 +105,11 @@ public: NavigationLink2D(); ~NavigationLink2D(); + +private: + void _link_enter_navigation_map(); + void _link_exit_navigation_map(); + void _link_update_transform(); }; #endif // NAVIGATION_LINK_2D_H diff --git a/scene/2d/physics/shape_cast_2d.cpp b/scene/2d/physics/shape_cast_2d.cpp index 00be84b622..b92978bcad 100644 --- a/scene/2d/physics/shape_cast_2d.cpp +++ b/scene/2d/physics/shape_cast_2d.cpp @@ -382,7 +382,7 @@ bool ShapeCast2D::is_collide_with_bodies_enabled() const { return collide_with_bodies; } -Array ShapeCast2D::_get_collision_result() const { +Array ShapeCast2D::get_collision_result() const { Array ret; for (int i = 0; i < result.size(); ++i) { @@ -464,7 +464,7 @@ void ShapeCast2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collide_with_bodies", "enable"), &ShapeCast2D::set_collide_with_bodies); ClassDB::bind_method(D_METHOD("is_collide_with_bodies_enabled"), &ShapeCast2D::is_collide_with_bodies_enabled); - ClassDB::bind_method(D_METHOD("_get_collision_result"), &ShapeCast2D::_get_collision_result); + ClassDB::bind_method(D_METHOD("get_collision_result"), &ShapeCast2D::get_collision_result); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D"), "set_shape", "get_shape"); @@ -473,7 +473,7 @@ void ShapeCast2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0,100,0.01,suffix:px"), "set_margin", "get_margin"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_results"), "set_max_results", "get_max_results"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "collision_result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "", "_get_collision_result"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "collision_result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "", "get_collision_result"); ADD_GROUP("Collide With", "collide_with"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_areas", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_areas", "is_collide_with_areas_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_bodies", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_bodies", "is_collide_with_bodies_enabled"); diff --git a/scene/2d/physics/shape_cast_2d.h b/scene/2d/physics/shape_cast_2d.h index 6b8fd5b798..d866dd4edb 100644 --- a/scene/2d/physics/shape_cast_2d.h +++ b/scene/2d/physics/shape_cast_2d.h @@ -60,7 +60,6 @@ class ShapeCast2D : public Node2D { real_t collision_safe_fraction = 1.0; real_t collision_unsafe_fraction = 1.0; - Array _get_collision_result() const; void _shape_changed(); protected: @@ -102,6 +101,7 @@ public: void force_shapecast_update(); bool is_colliding() const; + Array get_collision_result() const; int get_collision_count() const; Object *get_collider(int p_idx) const; RID get_collider_rid(int p_idx) const; diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp index 4266060466..42f7a75c0a 100644 --- a/scene/2d/polygon_2d.cpp +++ b/scene/2d/polygon_2d.cpp @@ -682,7 +682,7 @@ void Polygon2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "uv"), "set_uv", "get_uv"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "vertex_colors"), "set_vertex_colors", "get_vertex_colors"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons"), "set_polygons", "get_polygons"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "_set_bones", "_get_bones"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_bones", "_get_bones"); ADD_PROPERTY(PropertyInfo(Variant::INT, "internal_vertex_count", PROPERTY_HINT_RANGE, "0,1000"), "set_internal_vertex_count", "get_internal_vertex_count"); } diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index c12b95314e..b10f2097da 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -532,6 +532,18 @@ TileData *TileMap::get_cell_tile_data(int p_layer, const Vector2i &p_coords, boo } } +bool TileMap::is_cell_flipped_h(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { + return get_cell_alternative_tile(p_layer, p_coords, p_use_proxies) & TileSetAtlasSource::TRANSFORM_FLIP_H; +} + +bool TileMap::is_cell_flipped_v(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { + return get_cell_alternative_tile(p_layer, p_coords, p_use_proxies) & TileSetAtlasSource::TRANSFORM_FLIP_V; +} + +bool TileMap::is_cell_transposed(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { + return get_cell_alternative_tile(p_layer, p_coords, p_use_proxies) & TileSetAtlasSource::TRANSFORM_TRANSPOSE; +} + Ref<TileMapPattern> TileMap::get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array) { TILEMAP_CALL_FOR_LAYER_V(p_layer, Ref<TileMapPattern>(), get_pattern, p_coords_array); } @@ -926,6 +938,10 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "layer", "coords", "use_proxies"), &TileMap::get_cell_alternative_tile, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_cell_tile_data", "layer", "coords", "use_proxies"), &TileMap::get_cell_tile_data, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("is_cell_flipped_h", "layer", "coords", "use_proxies"), &TileMap::is_cell_flipped_h, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("is_cell_flipped_v", "layer", "coords", "use_proxies"), &TileMap::is_cell_flipped_v, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("is_cell_transposed", "layer", "coords", "use_proxies"), &TileMap::is_cell_transposed, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_coords_for_body_rid", "body"), &TileMap::get_coords_for_body_rid); ClassDB::bind_method(D_METHOD("get_layer_for_body_rid", "body"), &TileMap::get_layer_for_body_rid); diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 690102f730..142dc1193f 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -167,6 +167,10 @@ public: // Helper method to make accessing the data easier. TileData *get_cell_tile_data(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; + bool is_cell_flipped_h(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; + bool is_cell_flipped_v(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; + bool is_cell_transposed(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; + // Patterns. Ref<TileMapPattern> get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array); Vector2i map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref<TileMapPattern> p_pattern); diff --git a/scene/2d/tile_map_layer.cpp b/scene/2d/tile_map_layer.cpp index 437790bb99..7b125a6895 100644 --- a/scene/2d/tile_map_layer.cpp +++ b/scene/2d/tile_map_layer.cpp @@ -1773,6 +1773,10 @@ void TileMapLayer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "coords"), &TileMapLayer::get_cell_alternative_tile); ClassDB::bind_method(D_METHOD("get_cell_tile_data", "coords"), &TileMapLayer::get_cell_tile_data); + ClassDB::bind_method(D_METHOD("is_cell_flipped_h", "coords"), &TileMapLayer::is_cell_flipped_h); + ClassDB::bind_method(D_METHOD("is_cell_flipped_v", "coords"), &TileMapLayer::is_cell_flipped_v); + ClassDB::bind_method(D_METHOD("is_cell_transposed", "coords"), &TileMapLayer::is_cell_transposed); + ClassDB::bind_method(D_METHOD("get_used_cells"), &TileMapLayer::get_used_cells); ClassDB::bind_method(D_METHOD("get_used_cells_by_id", "source_id", "atlas_coords", "alternative_tile"), &TileMapLayer::get_used_cells_by_id, DEFVAL(TileSet::INVALID_SOURCE), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetSource::INVALID_TILE_ALTERNATIVE)); ClassDB::bind_method(D_METHOD("get_used_rect"), &TileMapLayer::get_used_rect); @@ -2490,6 +2494,18 @@ Rect2i TileMapLayer::get_used_rect() const { return used_rect_cache; } +bool TileMapLayer::is_cell_flipped_h(const Vector2i &p_coords) const { + return get_cell_alternative_tile(p_coords) & TileSetAtlasSource::TRANSFORM_FLIP_H; +} + +bool TileMapLayer::is_cell_flipped_v(const Vector2i &p_coords) const { + return get_cell_alternative_tile(p_coords) & TileSetAtlasSource::TRANSFORM_FLIP_V; +} + +bool TileMapLayer::is_cell_transposed(const Vector2i &p_coords) const { + return get_cell_alternative_tile(p_coords) & TileSetAtlasSource::TRANSFORM_TRANSPOSE; +} + Ref<TileMapPattern> TileMapLayer::get_pattern(TypedArray<Vector2i> p_coords_array) { ERR_FAIL_COND_V(tile_set.is_null(), nullptr); diff --git a/scene/2d/tile_map_layer.h b/scene/2d/tile_map_layer.h index c71f13d7be..1a6d182094 100644 --- a/scene/2d/tile_map_layer.h +++ b/scene/2d/tile_map_layer.h @@ -438,6 +438,10 @@ public: TypedArray<Vector2i> get_used_cells_by_id(int p_source_id = TileSet::INVALID_SOURCE, const Vector2i &p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE) const; Rect2i get_used_rect() const; + bool is_cell_flipped_h(const Vector2i &p_coords) const; + bool is_cell_flipped_v(const Vector2i &p_coords) const; + bool is_cell_transposed(const Vector2i &p_coords) const; + // Patterns. Ref<TileMapPattern> get_pattern(TypedArray<Vector2i> p_coords_array); void set_pattern(const Vector2i &p_position, const Ref<TileMapPattern> p_pattern); diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index 6888462876..4d3f494ccf 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -562,7 +562,7 @@ void AudioStreamPlayer3D::seek(float p_seconds) { void AudioStreamPlayer3D::stop() { setplay.set(-1); - internal->stop(); + internal->stop_basic(); } bool AudioStreamPlayer3D::is_playing() const { @@ -862,7 +862,7 @@ void AudioStreamPlayer3D::_bind_methods() { } AudioStreamPlayer3D::AudioStreamPlayer3D() { - internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer3D::play), true)); + internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer3D::play), callable_mp(this, &AudioStreamPlayer3D::stop), true)); velocity_tracker.instantiate(); set_disable_scale(true); cached_global_panning_strength = GLOBAL_GET("audio/general/3d_panning_strength"); diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index 8515aacba7..c70fa3ca2e 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -31,7 +31,9 @@ #include "camera_3d.h" #include "core/math/projection.h" +#include "core/math/transform_interpolator.h" #include "scene/main/viewport.h" +#include "servers/rendering/rendering_server_constants.h" void Camera3D::_update_audio_listener_state() { } @@ -88,7 +90,16 @@ void Camera3D::_update_camera() { return; } - RenderingServer::get_singleton()->camera_set_transform(camera, get_camera_transform()); + if (!is_physics_interpolated_and_enabled()) { + RenderingServer::get_singleton()->camera_set_transform(camera, get_camera_transform()); + } else { + // Ideally we shouldn't be moving a physics interpolated camera within a frame, + // because it will break smooth interpolation, but it may occur on e.g. level load. + if (!Engine::get_singleton()->is_in_physics_frame() && camera.is_valid()) { + _physics_interpolation_ensure_transform_calculated(true); + RenderingServer::get_singleton()->camera_set_transform(camera, _interpolation_data.camera_xform_interpolated); + } + } if (is_part_of_edited_scene() || !is_current()) { return; @@ -97,6 +108,64 @@ void Camera3D::_update_camera() { get_viewport()->_camera_3d_transform_changed_notify(); } +void Camera3D::_physics_interpolated_changed() { + _update_process_mode(); +} + +void Camera3D::_physics_interpolation_ensure_data_flipped() { + // The curr -> previous update can either occur + // on the INTERNAL_PHYSICS_PROCESS OR + // on NOTIFICATION_TRANSFORM_CHANGED, + // if NOTIFICATION_TRANSFORM_CHANGED takes place + // earlier than INTERNAL_PHYSICS_PROCESS on a tick. + // This is to ensure that the data keeps flowing, but the new data + // doesn't overwrite before prev has been set. + + // Keep the data flowing. + uint64_t tick = Engine::get_singleton()->get_physics_frames(); + if (_interpolation_data.last_update_physics_tick != tick) { + _interpolation_data.xform_prev = _interpolation_data.xform_curr; + _interpolation_data.last_update_physics_tick = tick; + physics_interpolation_flip_data(); + } +} + +void Camera3D::_physics_interpolation_ensure_transform_calculated(bool p_force) const { + DEV_CHECK_ONCE(!Engine::get_singleton()->is_in_physics_frame()); + + InterpolationData &id = _interpolation_data; + uint64_t frame = Engine::get_singleton()->get_frames_drawn(); + + if (id.last_update_frame != frame || p_force) { + id.last_update_frame = frame; + + TransformInterpolator::interpolate_transform_3d(id.xform_prev, id.xform_curr, id.xform_interpolated, Engine::get_singleton()->get_physics_interpolation_fraction()); + + Transform3D &tr = id.camera_xform_interpolated; + tr = _get_adjusted_camera_transform(id.xform_interpolated); + } +} + +void Camera3D::set_desired_process_modes(bool p_process_internal, bool p_physics_process_internal) { + _desired_process_internal = p_process_internal; + _desired_physics_process_internal = p_physics_process_internal; + _update_process_mode(); +} + +void Camera3D::_update_process_mode() { + bool process = _desired_process_internal; + bool physics_process = _desired_physics_process_internal; + + if (is_physics_interpolated_and_enabled()) { + if (is_current()) { + process = true; + physics_process = true; + } + } + set_process_internal(process); + set_physics_process_internal(physics_process); +} + void Camera3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_WORLD: { @@ -118,11 +187,58 @@ void Camera3D::_notification(int p_what) { #endif } break; + case NOTIFICATION_INTERNAL_PROCESS: { + if (is_physics_interpolated_and_enabled() && camera.is_valid()) { + _physics_interpolation_ensure_transform_calculated(); + +#ifdef RENDERING_SERVER_DEBUG_PHYSICS_INTERPOLATION + print_line("\t\tinterpolated Camera3D: " + rtos(_interpolation_data.xform_interpolated.origin.x) + "\t( prev " + rtos(_interpolation_data.xform_prev.origin.x) + ", curr " + rtos(_interpolation_data.xform_curr.origin.x) + " ) on tick " + itos(Engine::get_singleton()->get_physics_frames())); +#endif + + RenderingServer::get_singleton()->camera_set_transform(camera, _interpolation_data.camera_xform_interpolated); + } + } break; + + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + if (is_physics_interpolated_and_enabled()) { + _physics_interpolation_ensure_data_flipped(); + _interpolation_data.xform_curr = get_global_transform(); + } + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + if (is_physics_interpolated_and_enabled()) { + _physics_interpolation_ensure_data_flipped(); + _interpolation_data.xform_curr = get_global_transform(); +#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) + if (!Engine::get_singleton()->is_in_physics_frame()) { + PHYSICS_INTERPOLATION_NODE_WARNING(get_instance_id(), "Interpolated Camera3D triggered from outside physics process"); + } +#endif + } _request_camera_update(); if (doppler_tracking != DOPPLER_TRACKING_DISABLED) { velocity_tracker->update_position(get_global_transform().origin); } + // Allow auto-reset when first adding to the tree, as a convenience. + if (_is_physics_interpolation_reset_requested() && is_inside_tree()) { + _notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION); + _set_physics_interpolation_reset_requested(false); + } + } break; + + case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: { + if (is_inside_tree()) { + _interpolation_data.xform_curr = get_global_transform(); + _interpolation_data.xform_prev = _interpolation_data.xform_curr; + } + } break; + + case NOTIFICATION_PAUSED: { + if (is_physics_interpolated_and_enabled() && is_inside_tree() && is_visible_in_tree()) { + _physics_interpolation_ensure_transform_calculated(true); + RenderingServer::get_singleton()->camera_set_transform(camera, _interpolation_data.camera_xform_interpolated); + } } break; case NOTIFICATION_EXIT_WORLD: { @@ -151,23 +267,34 @@ void Camera3D::_notification(int p_what) { if (viewport) { viewport->find_world_3d()->_register_camera(this); } + _update_process_mode(); } break; case NOTIFICATION_LOST_CURRENT: { if (viewport) { viewport->find_world_3d()->_remove_camera(this); } + _update_process_mode(); } break; } } -Transform3D Camera3D::get_camera_transform() const { - Transform3D tr = get_global_transform().orthonormalized(); +Transform3D Camera3D::_get_adjusted_camera_transform(const Transform3D &p_xform) const { + Transform3D tr = p_xform.orthonormalized(); tr.origin += tr.basis.get_column(1) * v_offset; tr.origin += tr.basis.get_column(0) * h_offset; return tr; } +Transform3D Camera3D::get_camera_transform() const { + if (is_physics_interpolated_and_enabled() && !Engine::get_singleton()->is_in_physics_frame()) { + _physics_interpolation_ensure_transform_calculated(); + return _interpolation_data.camera_xform_interpolated; + } + + return _get_adjusted_camera_transform(get_global_transform()); +} + Projection Camera3D::_get_camera_projection(real_t p_near) const { Size2 viewport_size = get_viewport()->get_visible_rect().size; Projection cm; @@ -379,6 +506,11 @@ Point2 Camera3D::unproject_position(const Vector3 &p_pos) const { Plane p(get_camera_transform().xform_inv(p_pos), 1.0); p = cm.xform4(p); + + // Prevent divide by zero. + // TODO: Investigate, this was causing NaNs. + ERR_FAIL_COND_V(p.d == 0, Point2()); + p.normal /= p.d; Point2 res; diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h index dbf2ffc1dd..3e9f940ad6 100644 --- a/scene/3d/camera_3d.h +++ b/scene/3d/camera_3d.h @@ -98,7 +98,39 @@ private: RID pyramid_shape; Vector<Vector3> pyramid_shape_points; + /////////////////////////////////////////////////////// + // INTERPOLATION FUNCTIONS + void _physics_interpolation_ensure_transform_calculated(bool p_force = false) const; + void _physics_interpolation_ensure_data_flipped(); + + // These can be set by derived Camera3Ds, if they wish to do processing + // (while still allowing physics interpolation to function). + bool _desired_process_internal = false; + bool _desired_physics_process_internal = false; + + mutable struct InterpolationData { + Transform3D xform_curr; + Transform3D xform_prev; + Transform3D xform_interpolated; + Transform3D camera_xform_interpolated; // After modification according to camera type. + uint32_t last_update_physics_tick = 0; + uint32_t last_update_frame = UINT32_MAX; + } _interpolation_data; + + void _update_process_mode(); + protected: + // Use from derived classes to set process modes instead of setting directly. + // This is because physics interpolation may need to request process modes additionally. + void set_desired_process_modes(bool p_process_internal, bool p_physics_process_internal); + + // Opportunity for derived classes to interpolate extra attributes. + virtual void physics_interpolation_flip_data() {} + + virtual void _physics_interpolated_changed() override; + virtual Transform3D _get_adjusted_camera_transform(const Transform3D &p_xform) const; + /////////////////////////////////////////////////////// + void _update_camera(); virtual void _request_camera_update(); void _update_camera_mode(); diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index 3771b385e5..2cef607d29 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -459,14 +459,6 @@ void GPUParticles3D::_notification(int p_what) { // Use internal process when emitting and one_shot is on so that when // the shot ends the editor can properly update. case NOTIFICATION_INTERNAL_PROCESS: { - const Vector3 velocity = (get_global_position() - previous_position) / get_process_delta_time(); - - if (velocity != previous_velocity) { - RS::get_singleton()->particles_set_emitter_velocity(particles, velocity); - previous_velocity = velocity; - } - previous_position = get_global_position(); - if (one_shot) { time += get_process_delta_time(); if (time > emission_time) { @@ -487,8 +479,21 @@ void GPUParticles3D::_notification(int p_what) { } } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + // Update velocity in physics process, so that velocity calculations remain correct + // if the physics tick rate is lower than the rendered framerate (especially without physics interpolation). + const Vector3 velocity = (get_global_position() - previous_position) / get_physics_process_delta_time(); + + if (velocity != previous_velocity) { + RS::get_singleton()->particles_set_emitter_velocity(particles, velocity); + previous_velocity = velocity; + } + previous_position = get_global_position(); + } break; + case NOTIFICATION_ENTER_TREE: { set_process_internal(false); + set_physics_process_internal(false); if (sub_emitter != NodePath()) { _attach_sub_emitter(); } @@ -499,6 +504,7 @@ void GPUParticles3D::_notification(int p_what) { } previous_position = get_global_transform().origin; set_process_internal(true); + set_physics_process_internal(true); } break; case NOTIFICATION_EXIT_TREE: { diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index 038a78609f..3f8b0dfb8e 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -709,7 +709,7 @@ void LightmapGI::_gen_new_positions_from_octree(const GenProbesOctree *p_cell, f const Vector3 *pp = probe_positions.ptr(); bool exists = false; for (int j = 0; j < ppcount; j++) { - if (pp[j].is_equal_approx(real_pos)) { + if (pp[j].distance_to(real_pos) < (p_cell_size * 0.5f)) { exists = true; break; } @@ -1072,6 +1072,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa if (env.is_valid()) { environment_image = RS::get_singleton()->environment_bake_panorama(env->get_rid(), true, Size2i(128, 64)); + environment_transform = Basis::from_euler(env->get_sky_rotation()).inverse(); } } } break; diff --git a/scene/3d/navigation_link_3d.cpp b/scene/3d/navigation_link_3d.cpp index dc776ebea2..bebba9a6c0 100644 --- a/scene/3d/navigation_link_3d.cpp +++ b/scene/3d/navigation_link_3d.cpp @@ -152,6 +152,9 @@ void NavigationLink3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationLink3D::set_enabled); ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationLink3D::is_enabled); + ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationLink3D::set_navigation_map); + ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationLink3D::get_navigation_map); + ClassDB::bind_method(D_METHOD("set_bidirectional", "bidirectional"), &NavigationLink3D::set_bidirectional); ClassDB::bind_method(D_METHOD("is_bidirectional"), &NavigationLink3D::is_bidirectional); @@ -217,16 +220,7 @@ bool NavigationLink3D::_get(const StringName &p_name, Variant &r_ret) const { void NavigationLink3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - if (enabled) { - NavigationServer3D::get_singleton()->link_set_map(link, get_world_3d()->get_navigation_map()); - } - current_global_transform = get_global_transform(); - NavigationServer3D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position)); - NavigationServer3D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position)); - -#ifdef DEBUG_ENABLED - _update_debug_mesh(); -#endif // DEBUG_ENABLED + _link_enter_navigation_map(); } break; case NOTIFICATION_TRANSFORM_CHANGED: { @@ -235,30 +229,11 @@ void NavigationLink3D::_notification(int p_what) { case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { set_physics_process_internal(false); - if (is_inside_tree()) { - Transform3D new_global_transform = get_global_transform(); - if (current_global_transform != new_global_transform) { - current_global_transform = new_global_transform; - NavigationServer3D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position)); - NavigationServer3D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position)); -#ifdef DEBUG_ENABLED - if (debug_instance.is_valid()) { - RS::get_singleton()->instance_set_transform(debug_instance, current_global_transform); - } -#endif // DEBUG_ENABLED - } - } + _link_update_transform(); } break; case NOTIFICATION_EXIT_TREE: { - NavigationServer3D::get_singleton()->link_set_map(link, RID()); - -#ifdef DEBUG_ENABLED - if (debug_instance.is_valid()) { - RS::get_singleton()->instance_set_scenario(debug_instance, RID()); - RS::get_singleton()->instance_set_visible(debug_instance, false); - } -#endif // DEBUG_ENABLED + _link_exit_navigation_map(); } break; } } @@ -320,6 +295,25 @@ void NavigationLink3D::set_enabled(bool p_enabled) { update_gizmos(); } +void NavigationLink3D::set_navigation_map(RID p_navigation_map) { + if (map_override == p_navigation_map) { + return; + } + + map_override = p_navigation_map; + + NavigationServer3D::get_singleton()->link_set_map(link, map_override); +} + +RID NavigationLink3D::get_navigation_map() const { + if (map_override.is_valid()) { + return map_override; + } else if (is_inside_tree()) { + return get_world_3d()->get_navigation_map(); + } + return RID(); +} + void NavigationLink3D::set_bidirectional(bool p_bidirectional) { if (bidirectional == p_bidirectional) { return; @@ -467,3 +461,53 @@ PackedStringArray NavigationLink3D::get_configuration_warnings() const { return warnings; } + +void NavigationLink3D::_link_enter_navigation_map() { + if (!is_inside_tree()) { + return; + } + + if (map_override.is_valid()) { + NavigationServer3D::get_singleton()->link_set_map(link, map_override); + } else { + NavigationServer3D::get_singleton()->link_set_map(link, get_world_3d()->get_navigation_map()); + } + + current_global_transform = get_global_transform(); + NavigationServer3D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position)); + NavigationServer3D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position)); + NavigationServer3D::get_singleton()->link_set_enabled(link, enabled); + +#ifdef DEBUG_ENABLED + if (NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) { + _update_debug_mesh(); + } +#endif // DEBUG_ENABLED +} + +void NavigationLink3D::_link_exit_navigation_map() { + NavigationServer3D::get_singleton()->link_set_map(link, RID()); +#ifdef DEBUG_ENABLED + if (debug_instance.is_valid()) { + RS::get_singleton()->instance_set_visible(debug_instance, false); + } +#endif // DEBUG_ENABLED +} + +void NavigationLink3D::_link_update_transform() { + if (!is_inside_tree()) { + return; + } + + Transform3D new_global_transform = get_global_transform(); + if (current_global_transform != new_global_transform) { + current_global_transform = new_global_transform; + NavigationServer3D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position)); + NavigationServer3D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position)); +#ifdef DEBUG_ENABLED + if (NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) { + _update_debug_mesh(); + } +#endif // DEBUG_ENABLED + } +} diff --git a/scene/3d/navigation_link_3d.h b/scene/3d/navigation_link_3d.h index 1867082811..e894761f40 100644 --- a/scene/3d/navigation_link_3d.h +++ b/scene/3d/navigation_link_3d.h @@ -38,6 +38,7 @@ class NavigationLink3D : public Node3D { bool enabled = true; RID link; + RID map_override; bool bidirectional = true; uint32_t navigation_layers = 1; Vector3 end_position; @@ -72,6 +73,9 @@ public: void set_enabled(bool p_enabled); bool is_enabled() const { return enabled; } + void set_navigation_map(RID p_navigation_map); + RID get_navigation_map() const; + void set_bidirectional(bool p_bidirectional); bool is_bidirectional() const { return bidirectional; } @@ -100,6 +104,11 @@ public: real_t get_travel_cost() const { return travel_cost; } PackedStringArray get_configuration_warnings() const override; + +private: + void _link_enter_navigation_map(); + void _link_exit_navigation_map(); + void _link_update_transform(); }; #endif // NAVIGATION_LINK_3D_H diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp index 40e04f0fb4..d7397a932d 100644 --- a/scene/3d/navigation_region_3d.cpp +++ b/scene/3d/navigation_region_3d.cpp @@ -686,6 +686,8 @@ void NavigationRegion3D::_update_debug_edge_connections_mesh() { Vector<Vector3> vertex_array; vertex_array.resize(connections_count * 6); + Vector3 *vertex_array_ptrw = vertex_array.ptrw(); + int vertex_array_index = 0; for (int i = 0; i < connections_count; i++) { Vector3 connection_pathway_start = NavigationServer3D::get_singleton()->region_get_connection_pathway_start(region, i); @@ -705,13 +707,12 @@ void NavigationRegion3D::_update_debug_edge_connections_mesh() { Vector3 left_end_pos = connection_pathway_end + (end_right_dir * half_edge_connection_margin); Vector3 right_end_pos = connection_pathway_end + (end_left_dir * half_edge_connection_margin); - vertex_array.push_back(right_end_pos); - vertex_array.push_back(left_start_pos); - vertex_array.push_back(right_start_pos); - - vertex_array.push_back(left_end_pos); - vertex_array.push_back(right_end_pos); - vertex_array.push_back(right_start_pos); + vertex_array_ptrw[vertex_array_index++] = connection_pathway_start; + vertex_array_ptrw[vertex_array_index++] = connection_pathway_end; + vertex_array_ptrw[vertex_array_index++] = left_start_pos; + vertex_array_ptrw[vertex_array_index++] = right_start_pos; + vertex_array_ptrw[vertex_array_index++] = left_end_pos; + vertex_array_ptrw[vertex_array_index++] = right_end_pos; } if (vertex_array.size() == 0) { @@ -724,7 +725,7 @@ void NavigationRegion3D::_update_debug_edge_connections_mesh() { mesh_array.resize(Mesh::ARRAY_MAX); mesh_array[Mesh::ARRAY_VERTEX] = vertex_array; - debug_edge_connections_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, mesh_array); + debug_edge_connections_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, mesh_array); debug_edge_connections_mesh->surface_set_material(0, edge_connections_material); RS::get_singleton()->instance_set_base(debug_edge_connections_instance, debug_edge_connections_mesh->get_rid()); diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index 2e08afb30d..86ce8a881a 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -30,6 +30,7 @@ #include "node_3d.h" +#include "core/math/transform_interpolator.h" #include "scene/3d/visual_instance_3d.h" #include "scene/main/viewport.h" #include "scene/property_utils.h" @@ -176,6 +177,7 @@ void Node3D::_notification(int p_what) { data.parent = nullptr; data.C = nullptr; _update_visibility_parent(true); + _disable_client_physics_interpolation(); } break; case NOTIFICATION_ENTER_WORLD: { @@ -226,6 +228,12 @@ void Node3D::_notification(int p_what) { } #endif } break; + + case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: { + if (data.client_physics_interpolation_data) { + data.client_physics_interpolation_data->global_xform_prev = data.client_physics_interpolation_data->global_xform_curr; + } + } break; } } @@ -341,6 +349,119 @@ Transform3D Node3D::get_transform() const { return data.local_transform; } +// Return false to timeout and remove from the client interpolation list. +bool Node3D::update_client_physics_interpolation_data() { + if (!is_inside_tree() || !_is_physics_interpolated_client_side()) { + return false; + } + + ERR_FAIL_NULL_V(data.client_physics_interpolation_data, false); + ClientPhysicsInterpolationData &pid = *data.client_physics_interpolation_data; + + uint64_t tick = Engine::get_singleton()->get_physics_frames(); + + // Has this update been done already this tick? + // (For instance, get_global_transform_interpolated() could be called multiple times.) + if (pid.current_physics_tick != tick) { + // Timeout? + if (tick >= pid.timeout_physics_tick) { + return false; + } + + if (pid.current_physics_tick == (tick - 1)) { + // Normal interpolation situation, there is a continuous flow of data + // from one tick to the next... + pid.global_xform_prev = pid.global_xform_curr; + } else { + // There has been a gap, we cannot sensibly offer interpolation over + // a multitick gap, so we will teleport. + pid.global_xform_prev = get_global_transform(); + } + pid.current_physics_tick = tick; + } + + pid.global_xform_curr = get_global_transform(); + return true; +} + +void Node3D::_disable_client_physics_interpolation() { + // Disable any current client side interpolation. + // (This can always restart as normal if you later re-attach the node to the SceneTree.) + if (data.client_physics_interpolation_data) { + memdelete(data.client_physics_interpolation_data); + data.client_physics_interpolation_data = nullptr; + + SceneTree *tree = get_tree(); + if (tree && _client_physics_interpolation_node_3d_list.in_list()) { + tree->client_physics_interpolation_remove_node_3d(&_client_physics_interpolation_node_3d_list); + } + } + _set_physics_interpolated_client_side(false); +} + +Transform3D Node3D::_get_global_transform_interpolated(real_t p_interpolation_fraction) { + ERR_FAIL_COND_V(!is_inside_tree(), Transform3D()); + + // Set in motion the mechanisms for client side interpolation if not already active. + if (!_is_physics_interpolated_client_side()) { + _set_physics_interpolated_client_side(true); + + ERR_FAIL_COND_V(data.client_physics_interpolation_data != nullptr, Transform3D()); + data.client_physics_interpolation_data = memnew(ClientPhysicsInterpolationData); + data.client_physics_interpolation_data->global_xform_curr = get_global_transform(); + data.client_physics_interpolation_data->global_xform_prev = data.client_physics_interpolation_data->global_xform_curr; + data.client_physics_interpolation_data->current_physics_tick = Engine::get_singleton()->get_physics_frames(); + } + + // Storing the last tick we requested client interpolation allows us to timeout + // and remove client interpolated nodes from the list to save processing. + // We use some arbitrary timeout here, but this could potentially be user defined. + + // Note: This timeout has to be larger than the number of ticks in a frame, otherwise the interpolated + // data will stop flowing before the next frame is drawn. This should only be relevant at high tick rates. + // We could alternatively do this by frames rather than ticks and avoid this problem, but then the behavior + // would be machine dependent. + data.client_physics_interpolation_data->timeout_physics_tick = Engine::get_singleton()->get_physics_frames() + 256; + + // Make sure data is up to date. + update_client_physics_interpolation_data(); + + // Interpolate the current data. + const Transform3D &xform_curr = data.client_physics_interpolation_data->global_xform_curr; + const Transform3D &xform_prev = data.client_physics_interpolation_data->global_xform_prev; + + Transform3D res; + TransformInterpolator::interpolate_transform_3d(xform_prev, xform_curr, res, p_interpolation_fraction); + + SceneTree *tree = get_tree(); + + // This should not happen, as is_inside_tree() is checked earlier. + ERR_FAIL_NULL_V(tree, res); + if (!_client_physics_interpolation_node_3d_list.in_list()) { + tree->client_physics_interpolation_add_node_3d(&_client_physics_interpolation_node_3d_list); + } + + return res; +} + +Transform3D Node3D::get_global_transform_interpolated() { + // Pass through if physics interpolation is switched off. + // This is a convenience, as it allows you to easy turn off interpolation + // without changing any code. + if (!is_physics_interpolated_and_enabled()) { + return get_global_transform(); + } + + // If we are in the physics frame, the interpolated global transform is meaningless. + // However, there is an exception, we may want to use this as a means of starting off the client + // interpolation pump if not already started (when _is_physics_interpolated_client_side() is false). + if (Engine::get_singleton()->is_in_physics_frame() && _is_physics_interpolated_client_side()) { + return get_global_transform(); + } + + return _get_global_transform_interpolated(Engine::get_singleton()->get_physics_interpolation_fraction()); +} + Transform3D Node3D::get_global_transform() const { ERR_FAIL_COND_V(!is_inside_tree(), Transform3D()); @@ -1140,6 +1261,7 @@ void Node3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_global_transform", "global"), &Node3D::set_global_transform); ClassDB::bind_method(D_METHOD("get_global_transform"), &Node3D::get_global_transform); + ClassDB::bind_method(D_METHOD("get_global_transform_interpolated"), &Node3D::get_global_transform_interpolated); ClassDB::bind_method(D_METHOD("set_global_position", "position"), &Node3D::set_global_position); ClassDB::bind_method(D_METHOD("get_global_position"), &Node3D::get_global_position); ClassDB::bind_method(D_METHOD("set_global_basis", "basis"), &Node3D::set_global_basis); @@ -1236,4 +1358,27 @@ void Node3D::_bind_methods() { } Node3D::Node3D() : - xform_change(this) {} + xform_change(this), _client_physics_interpolation_node_3d_list(this) { + // Default member initializer for bitfield is a C++20 extension, so: + + data.top_level = false; + data.inside_world = false; + + data.ignore_notification = false; + data.notify_local_transform = false; + data.notify_transform = false; + + data.visible = true; + data.disable_scale = false; + data.vi_visible = true; + +#ifdef TOOLS_ENABLED + data.gizmos_disabled = false; + data.gizmos_dirty = false; + data.transform_gizmo_visible = true; +#endif +} + +Node3D::~Node3D() { + _disable_client_physics_interpolation(); +} diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index c1667221df..217ee28cf1 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -85,7 +85,15 @@ private: DIRTY_GLOBAL_TRANSFORM = 4 }; + struct ClientPhysicsInterpolationData { + Transform3D global_xform_curr; + Transform3D global_xform_prev; + uint64_t current_physics_tick = 0; + uint64_t timeout_physics_tick = 0; + }; + mutable SelfList<Node> xform_change; + SelfList<Node3D> _client_physics_interpolation_node_3d_list; // This Data struct is to avoid namespace pollution in derived classes. @@ -101,8 +109,19 @@ private: Viewport *viewport = nullptr; - bool top_level = false; - bool inside_world = false; + bool top_level : 1; + bool inside_world : 1; + + // This is cached, and only currently kept up to date in visual instances. + // This is set if a visual instance is (a) in the tree AND (b) visible via is_visible_in_tree() call. + bool vi_visible : 1; + + bool ignore_notification : 1; + bool notify_local_transform : 1; + bool notify_transform : 1; + + bool visible : 1; + bool disable_scale : 1; RID visibility_parent; @@ -110,18 +129,13 @@ private: List<Node3D *> children; List<Node3D *>::Element *C = nullptr; - bool ignore_notification = false; - bool notify_local_transform = false; - bool notify_transform = false; - - bool visible = true; - bool disable_scale = false; + ClientPhysicsInterpolationData *client_physics_interpolation_data = nullptr; #ifdef TOOLS_ENABLED Vector<Ref<Node3DGizmo>> gizmos; - bool gizmos_disabled = false; - bool gizmos_dirty = false; - bool transform_gizmo_visible = true; + bool gizmos_disabled : 1; + bool gizmos_dirty : 1; + bool transform_gizmo_visible : 1; #endif } data; @@ -150,6 +164,11 @@ protected: _FORCE_INLINE_ void _update_local_transform() const; _FORCE_INLINE_ void _update_rotation_and_scale() const; + void _set_vi_visible(bool p_visible) { data.vi_visible = p_visible; } + bool _is_vi_visible() const { return data.vi_visible; } + Transform3D _get_global_transform_interpolated(real_t p_interpolation_fraction); + void _disable_client_physics_interpolation(); + void _notification(int p_what); static void _bind_methods(); @@ -208,6 +227,9 @@ public: Quaternion get_quaternion() const; Transform3D get_global_transform() const; + Transform3D get_global_transform_interpolated(); + bool update_client_physics_interpolation_data(); + #ifdef TOOLS_ENABLED virtual Transform3D get_global_gizmo_transform() const; virtual Transform3D get_local_gizmo_transform() const; @@ -279,6 +301,7 @@ public: NodePath get_visibility_parent() const; Node3D(); + ~Node3D(); }; VARIANT_ENUM_CAST(Node3D::RotationEditMode) diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp index 150771545b..6982df12f6 100644 --- a/scene/3d/occluder_instance_3d.cpp +++ b/scene/3d/occluder_instance_3d.cpp @@ -129,9 +129,6 @@ void Occluder3D::_notification(int p_what) { } } -void Occluder3D::_bind_methods() { -} - Occluder3D::Occluder3D() { occluder = RS::get_singleton()->occluder_create(); } diff --git a/scene/3d/occluder_instance_3d.h b/scene/3d/occluder_instance_3d.h index 91445710b3..62e9478527 100644 --- a/scene/3d/occluder_instance_3d.h +++ b/scene/3d/occluder_instance_3d.h @@ -49,7 +49,6 @@ protected: void _update(); virtual void _update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) = 0; - static void _bind_methods(); void _notification(int p_what); public: diff --git a/scene/3d/physics/shape_cast_3d.cpp b/scene/3d/physics/shape_cast_3d.cpp index ada238c7f2..8ad651fdf5 100644 --- a/scene/3d/physics/shape_cast_3d.cpp +++ b/scene/3d/physics/shape_cast_3d.cpp @@ -157,7 +157,7 @@ void ShapeCast3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collide_with_bodies", "enable"), &ShapeCast3D::set_collide_with_bodies); ClassDB::bind_method(D_METHOD("is_collide_with_bodies_enabled"), &ShapeCast3D::is_collide_with_bodies_enabled); - ClassDB::bind_method(D_METHOD("_get_collision_result"), &ShapeCast3D::_get_collision_result); + ClassDB::bind_method(D_METHOD("get_collision_result"), &ShapeCast3D::get_collision_result); ClassDB::bind_method(D_METHOD("set_debug_shape_custom_color", "debug_shape_custom_color"), &ShapeCast3D::set_debug_shape_custom_color); ClassDB::bind_method(D_METHOD("get_debug_shape_custom_color"), &ShapeCast3D::get_debug_shape_custom_color); @@ -169,7 +169,7 @@ void ShapeCast3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0,100,0.01,suffix:m"), "set_margin", "get_margin"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_results"), "set_max_results", "get_max_results"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "collision_result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "", "_get_collision_result"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "collision_result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "", "get_collision_result"); ADD_GROUP("Collide With", "collide_with"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_areas", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_areas", "is_collide_with_areas_enabled"); @@ -475,7 +475,7 @@ bool ShapeCast3D::is_collide_with_bodies_enabled() const { return collide_with_bodies; } -Array ShapeCast3D::_get_collision_result() const { +Array ShapeCast3D::get_collision_result() const { Array ret; for (int i = 0; i < result.size(); ++i) { diff --git a/scene/3d/physics/shape_cast_3d.h b/scene/3d/physics/shape_cast_3d.h index 19b73e3f72..9fc5e71670 100644 --- a/scene/3d/physics/shape_cast_3d.h +++ b/scene/3d/physics/shape_cast_3d.h @@ -73,8 +73,6 @@ class ShapeCast3D : public Node3D { real_t collision_safe_fraction = 1.0; real_t collision_unsafe_fraction = 1.0; - Array _get_collision_result() const; - RID debug_instance; Ref<ArrayMesh> debug_mesh; @@ -123,6 +121,7 @@ public: Ref<StandardMaterial3D> get_debug_material(); + Array get_collision_result() const; int get_collision_count() const; Object *get_collider(int p_idx) const; RID get_collider_rid(int p_idx) const; diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp index 0d6316ee35..2476e7d5cd 100644 --- a/scene/3d/skeleton_ik_3d.cpp +++ b/scene/3d/skeleton_ik_3d.cpp @@ -503,7 +503,11 @@ Transform3D SkeletonIK3D::_get_target_transform() { Node3D *target_node_override = cast_to<Node3D>(target_node_override_ref.get_validated_object()); if (target_node_override && target_node_override->is_inside_tree()) { - return target_node_override->get_global_transform(); + // Make sure to use the interpolated transform as target. + // When physics interpolation is off this will pass through to get_global_transform(). + // When using interpolation, ensure that the target matches the interpolated visual position + // of the target when updating the IK each frame. + return target_node_override->get_global_transform_interpolated(); } else { return target; } diff --git a/scene/3d/skeleton_modifier_3d.cpp b/scene/3d/skeleton_modifier_3d.cpp index 9851214194..d5c603112e 100644 --- a/scene/3d/skeleton_modifier_3d.cpp +++ b/scene/3d/skeleton_modifier_3d.cpp @@ -37,7 +37,7 @@ void SkeletonModifier3D::_validate_property(PropertyInfo &p_property) const { PackedStringArray SkeletonModifier3D::get_configuration_warnings() const { PackedStringArray warnings = Node3D::get_configuration_warnings(); if (skeleton_id.is_null()) { - warnings.push_back(RTR("Skeleton3D node not set! SkeletonModifier3D must be child of Skeleton3D or set a path to an external skeleton.")); + warnings.push_back(RTR("Skeleton3D node not set! SkeletonModifier3D must be child of Skeleton3D.")); } return warnings; } diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp index f02cd9b700..4fe5dd2385 100644 --- a/scene/3d/soft_body_3d.cpp +++ b/scene/3d/soft_body_3d.cpp @@ -218,7 +218,13 @@ bool SoftBody3D::_set_property_pinned_points_attachment(int p_item, const String if ("spatial_attachment_path" == p_what) { PinnedPoint *w = pinned_points.ptrw(); - callable_mp(this, &SoftBody3D::_pin_point_deferred).call_deferred(Variant(w[p_item].point_index), true, p_value); + + if (is_inside_tree()) { + callable_mp(this, &SoftBody3D::_pin_point_deferred).call_deferred(Variant(w[p_item].point_index), true, p_value); + } else { + pin_point(w[p_item].point_index, true, p_value); + _make_cache_dirty(); + } } else if ("offset" == p_what) { PinnedPoint *w = pinned_points.ptrw(); w[p_item].offset = p_value; diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index f14ae3a285..79a01450dd 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -30,6 +30,8 @@ #include "visual_instance_3d.h" +#include "core/config/project_settings.h" + AABB VisualInstance3D::get_aabb() const { AABB ret; GDVIRTUAL_CALL(_get_aabb, ret); @@ -41,7 +43,38 @@ void VisualInstance3D::_update_visibility() { return; } - RS::get_singleton()->instance_set_visible(get_instance(), is_visible_in_tree()); + bool already_visible = _is_vi_visible(); + bool visible = is_visible_in_tree(); + _set_vi_visible(visible); + + // If making visible, make sure the rendering server is up to date with the transform. + if (visible && !already_visible) { + if (!_is_using_identity_transform()) { + Transform3D gt = get_global_transform(); + RS::get_singleton()->instance_set_transform(instance, gt); + } + } + + RS::get_singleton()->instance_set_visible(instance, visible); +} + +void VisualInstance3D::_physics_interpolated_changed() { + RenderingServer::get_singleton()->instance_set_interpolated(instance, is_physics_interpolated()); +} + +void VisualInstance3D::set_instance_use_identity_transform(bool p_enable) { + // Prevent sending instance transforms when using global coordinates. + _set_use_identity_transform(p_enable); + + if (is_inside_tree()) { + if (p_enable) { + // Want to make sure instance is using identity transform. + RS::get_singleton()->instance_set_transform(instance, Transform3D()); + } else { + // Want to make sure instance is up to date. + RS::get_singleton()->instance_set_transform(instance, get_global_transform()); + } + } } void VisualInstance3D::_notification(int p_what) { @@ -53,13 +86,52 @@ void VisualInstance3D::_notification(int p_what) { } break; case NOTIFICATION_TRANSFORM_CHANGED: { - Transform3D gt = get_global_transform(); - RenderingServer::get_singleton()->instance_set_transform(instance, gt); + if (_is_vi_visible() || is_physics_interpolated_and_enabled()) { + if (!_is_using_identity_transform()) { + RenderingServer::get_singleton()->instance_set_transform(instance, get_global_transform()); + + // For instance when first adding to the tree, when the previous transform is + // unset, to prevent streaking from the origin. + if (_is_physics_interpolation_reset_requested() && is_physics_interpolated_and_enabled() && is_inside_tree()) { + if (_is_vi_visible()) { + _notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION); + } + _set_physics_interpolation_reset_requested(false); + } + } + } + } break; + + case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: { + if (_is_vi_visible() && is_physics_interpolated() && is_inside_tree()) { + // We must ensure the RenderingServer transform is up to date before resetting. + // This is because NOTIFICATION_TRANSFORM_CHANGED is deferred, + // and cannot be relied to be called in order before NOTIFICATION_RESET_PHYSICS_INTERPOLATION. + if (!_is_using_identity_transform()) { + RenderingServer::get_singleton()->instance_set_transform(instance, get_global_transform()); + } + + RenderingServer::get_singleton()->instance_reset_physics_interpolation(instance); + } +#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) + else if (GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) { + + String node_name = is_inside_tree() ? String(get_path()) : String(get_name()); + if (!_is_vi_visible()) { + WARN_PRINT("[Physics interpolation] NOTIFICATION_RESET_PHYSICS_INTERPOLATION only works with unhidden nodes: \"" + node_name + "\"."); + } + if (!is_physics_interpolated()) { + WARN_PRINT("[Physics interpolation] NOTIFICATION_RESET_PHYSICS_INTERPOLATION only works with interpolated nodes: \"" + node_name + "\"."); + } + } +#endif + } break; case NOTIFICATION_EXIT_WORLD: { RenderingServer::get_singleton()->instance_set_scenario(instance, RID()); RenderingServer::get_singleton()->instance_attach_skeleton(instance, RID()); + _set_vi_visible(false); } break; case NOTIFICATION_VISIBILITY_CHANGED: { diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h index 59ede26ac1..9b02c928b7 100644 --- a/scene/3d/visual_instance_3d.h +++ b/scene/3d/visual_instance_3d.h @@ -45,6 +45,9 @@ class VisualInstance3D : public Node3D { protected: void _update_visibility(); + virtual void _physics_interpolated_changed() override; + void set_instance_use_identity_transform(bool p_enable); + void _notification(int p_what); static void _bind_methods(); void _validate_property(PropertyInfo &p_property) const; diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index e4baae1afb..cdc85d2b2d 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -754,9 +754,6 @@ AnimationNode::NodeTimeInfo AnimationNodeAdd2::_process(const AnimationMixer::Pl return nti; } -void AnimationNodeAdd2::_bind_methods() { -} - AnimationNodeAdd2::AnimationNodeAdd2() { add_input("in"); add_input("add"); @@ -800,9 +797,6 @@ AnimationNode::NodeTimeInfo AnimationNodeAdd3::_process(const AnimationMixer::Pl return nti; } -void AnimationNodeAdd3::_bind_methods() { -} - AnimationNodeAdd3::AnimationNodeAdd3() { add_input("-add"); add_input("in"); @@ -845,9 +839,6 @@ bool AnimationNodeBlend2::has_filter() const { return true; } -void AnimationNodeBlend2::_bind_methods() { -} - AnimationNodeBlend2::AnimationNodeBlend2() { add_input("in"); add_input("blend"); @@ -887,9 +878,6 @@ AnimationNode::NodeTimeInfo AnimationNodeBlend3::_process(const AnimationMixer:: return amount > 0.5 ? nti2 : (amount < -0.5 ? nti0 : nti1); // Hacky but good enough. } -void AnimationNodeBlend3::_bind_methods() { -} - AnimationNodeBlend3::AnimationNodeBlend3() { add_input("-blend"); add_input("in"); @@ -932,9 +920,6 @@ AnimationNode::NodeTimeInfo AnimationNodeSub2::_process(const AnimationMixer::Pl return blend_input(0, pi, FILTER_IGNORE, sync, p_test_only); } -void AnimationNodeSub2::_bind_methods() { -} - AnimationNodeSub2::AnimationNodeSub2() { add_input("in"); add_input("sub"); @@ -972,9 +957,6 @@ AnimationNode::NodeTimeInfo AnimationNodeTimeScale::_process(const AnimationMixe return blend_input(0, pi, FILTER_IGNORE, true, p_test_only); } -void AnimationNodeTimeScale::_bind_methods() { -} - AnimationNodeTimeScale::AnimationNodeTimeScale() { add_input("in"); } @@ -1014,9 +996,6 @@ AnimationNode::NodeTimeInfo AnimationNodeTimeSeek::_process(const AnimationMixer return blend_input(0, pi, FILTER_IGNORE, true, p_test_only); } -void AnimationNodeTimeSeek::_bind_methods() { -} - AnimationNodeTimeSeek::AnimationNodeTimeSeek() { add_input("in"); } diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h index c7ef7ed624..2add35d009 100644 --- a/scene/animation/animation_blend_tree.h +++ b/scene/animation/animation_blend_tree.h @@ -200,9 +200,6 @@ class AnimationNodeAdd2 : public AnimationNodeSync { StringName add_amount = PNAME("add_amount"); -protected: - static void _bind_methods(); - public: void get_parameter_list(List<PropertyInfo> *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; @@ -220,9 +217,6 @@ class AnimationNodeAdd3 : public AnimationNodeSync { StringName add_amount = PNAME("add_amount"); -protected: - static void _bind_methods(); - public: void get_parameter_list(List<PropertyInfo> *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; @@ -240,9 +234,6 @@ class AnimationNodeBlend2 : public AnimationNodeSync { StringName blend_amount = PNAME("blend_amount"); -protected: - static void _bind_methods(); - public: virtual void get_parameter_list(List<PropertyInfo> *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; @@ -259,9 +250,6 @@ class AnimationNodeBlend3 : public AnimationNodeSync { StringName blend_amount = PNAME("blend_amount"); -protected: - static void _bind_methods(); - public: virtual void get_parameter_list(List<PropertyInfo> *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; @@ -277,9 +265,6 @@ class AnimationNodeSub2 : public AnimationNodeSync { StringName sub_amount = PNAME("sub_amount"); -protected: - static void _bind_methods(); - public: void get_parameter_list(List<PropertyInfo> *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; @@ -297,9 +282,6 @@ class AnimationNodeTimeScale : public AnimationNode { StringName scale = PNAME("scale"); -protected: - static void _bind_methods(); - public: virtual void get_parameter_list(List<PropertyInfo> *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; @@ -316,9 +298,6 @@ class AnimationNodeTimeSeek : public AnimationNode { StringName seek_pos_request = PNAME("seek_request"); -protected: - static void _bind_methods(); - public: virtual void get_parameter_list(List<PropertyInfo> *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 6a61e8693d..f8bbd704f4 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -61,6 +61,11 @@ Ref<Tween> Tweener::_get_tween() { return Ref<Tween>(ObjectDB::get_instance(tween_id)); } +void Tweener::_finish() { + finished = true; + emit_signal(SceneStringName(finished)); +} + void Tweener::_bind_methods() { ADD_SIGNAL(MethodInfo("finished")); } @@ -612,9 +617,8 @@ bool PropertyTweener::step(double &r_delta) { return true; } else { target_instance->set_indexed(property, final_val); - finished = true; r_delta = elapsed_time - delay - duration; - emit_signal(SceneStringName(finished)); + _finish(); return false; } } @@ -672,9 +676,8 @@ bool IntervalTweener::step(double &r_delta) { r_delta = 0; return true; } else { - finished = true; r_delta = elapsed_time - duration; - emit_signal(SceneStringName(finished)); + _finish(); return false; } } @@ -715,9 +718,8 @@ bool CallbackTweener::step(double &r_delta) { ERR_FAIL_V_MSG(false, "Error calling method from CallbackTweener: " + Variant::get_callable_error_text(callback, nullptr, 0, ce) + "."); } - finished = true; r_delta = elapsed_time - delay; - emit_signal(SceneStringName(finished)); + _finish(); return false; } @@ -801,9 +803,8 @@ bool MethodTweener::step(double &r_delta) { r_delta = 0; return true; } else { - finished = true; r_delta = elapsed_time - delay - duration; - emit_signal(SceneStringName(finished)); + _finish(); return false; } } diff --git a/scene/animation/tween.h b/scene/animation/tween.h index f5ae5e9776..40e1da0ad3 100644 --- a/scene/animation/tween.h +++ b/scene/animation/tween.h @@ -50,6 +50,7 @@ protected: static void _bind_methods(); Ref<Tween> _get_tween(); + void _finish(); double elapsed_time = 0; bool finished = false; diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp index e90c1aa245..183c4af950 100644 --- a/scene/audio/audio_stream_player.cpp +++ b/scene/audio/audio_stream_player.cpp @@ -112,7 +112,7 @@ void AudioStreamPlayer::seek(float p_seconds) { } void AudioStreamPlayer::stop() { - internal->stop(); + internal->stop_basic(); } bool AudioStreamPlayer::is_playing() const { @@ -283,7 +283,7 @@ void AudioStreamPlayer::_bind_methods() { } AudioStreamPlayer::AudioStreamPlayer() { - internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer::play), false)); + internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer::play), callable_mp(this, &AudioStreamPlayer::stop), false)); } AudioStreamPlayer::~AudioStreamPlayer() { diff --git a/scene/audio/audio_stream_player_internal.cpp b/scene/audio/audio_stream_player_internal.cpp index 36c14e03d5..206408e3a7 100644 --- a/scene/audio/audio_stream_player_internal.cpp +++ b/scene/audio/audio_stream_player_internal.cpp @@ -132,7 +132,7 @@ Ref<AudioStreamPlayback> AudioStreamPlayerInternal::play_basic() { } ERR_FAIL_COND_V_MSG(!node->is_inside_tree(), stream_playback, "Playback can only happen when a node is inside the scene tree"); if (stream->is_monophonic() && is_playing()) { - stop(); + stop_callable.call(); } stream_playback = stream->instantiate_playback(); ERR_FAIL_COND_V_MSG(stream_playback.is_null(), stream_playback, "Failed to instantiate playback."); @@ -242,7 +242,7 @@ void AudioStreamPlayerInternal::set_stream(Ref<AudioStream> p_stream) { if (stream.is_valid()) { stream->disconnect(SNAME("parameter_list_changed"), callable_mp(this, &AudioStreamPlayerInternal::_update_stream_parameters)); } - stop(); + stop_callable.call(); stream = p_stream; _update_stream_parameters(); if (stream.is_valid()) { @@ -253,12 +253,12 @@ void AudioStreamPlayerInternal::set_stream(Ref<AudioStream> p_stream) { void AudioStreamPlayerInternal::seek(float p_seconds) { if (is_playing()) { - stop(); + stop_callable.call(); play_callable.call(p_seconds); } } -void AudioStreamPlayerInternal::stop() { +void AudioStreamPlayerInternal::stop_basic() { for (Ref<AudioStreamPlayback> &playback : stream_playbacks) { AudioServer::get_singleton()->stop_playback_stream(playback); } @@ -289,7 +289,7 @@ void AudioStreamPlayerInternal::set_playing(bool p_enable) { if (p_enable) { play_callable.call(0.0); } else { - stop(); + stop_callable.call(); } } @@ -339,9 +339,10 @@ StringName AudioStreamPlayerInternal::get_bus() const { return SceneStringName(Master); } -AudioStreamPlayerInternal::AudioStreamPlayerInternal(Node *p_node, const Callable &p_play_callable, bool p_physical) { +AudioStreamPlayerInternal::AudioStreamPlayerInternal(Node *p_node, const Callable &p_play_callable, const Callable &p_stop_callable, bool p_physical) { node = p_node; play_callable = p_play_callable; + stop_callable = p_stop_callable; physical = p_physical; bus = SceneStringName(Master); diff --git a/scene/audio/audio_stream_player_internal.h b/scene/audio/audio_stream_player_internal.h index ec4489067e..7d8faeba06 100644 --- a/scene/audio/audio_stream_player_internal.h +++ b/scene/audio/audio_stream_player_internal.h @@ -53,6 +53,7 @@ private: Node *node = nullptr; Callable play_callable; + Callable stop_callable; bool physical = false; AudioServer::PlaybackType playback_type = AudioServer::PlaybackType::PLAYBACK_TYPE_DEFAULT; @@ -94,7 +95,7 @@ public: Ref<AudioStreamPlayback> play_basic(); void seek(float p_seconds); - void stop(); + void stop_basic(); bool is_playing() const; float get_playback_position(); @@ -110,7 +111,7 @@ public: void set_playback_type(AudioServer::PlaybackType p_playback_type); AudioServer::PlaybackType get_playback_type() const; - AudioStreamPlayerInternal(Node *p_node, const Callable &p_play_callable, bool p_physical); + AudioStreamPlayerInternal(Node *p_node, const Callable &p_play_callable, const Callable &p_stop_callable, bool p_physical); }; #endif // AUDIO_STREAM_PLAYER_INTERNAL_H diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index d169e82e5d..2d425490d1 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -37,7 +37,7 @@ #include "core/os/keyboard.h" #include "core/os/os.h" #include "core/string/print_string.h" -#include "core/string/translation.h" +#include "core/string/translation_server.h" #include "scene/gui/label.h" #include "scene/gui/panel.h" #include "scene/main/canvas_layer.h" diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 1da3668ebe..e9fe78e162 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -1448,6 +1448,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V bool line_clicked = false; float text_rect_begin = 0.0; int char_pos = -1; + bool char_clicked = false; Line &l = p_frame->lines[p_line]; MutexLock lock(l.text_buf->get_mutex()); @@ -1578,6 +1579,9 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V } if (p_click.y >= rect.position.y && p_click.y <= rect.position.y + rect.size.y) { + if (!p_meta) { + char_pos = rtl ? TS->shaped_text_get_range(rid).y : TS->shaped_text_get_range(rid).x; + } if ((!rtl && p_click.x >= rect.position.x) || (rtl && p_click.x <= rect.position.x + rect.size.x)) { if (p_meta) { int64_t glyph_idx = TS->shaped_text_hit_test_grapheme(rid, p_click.x - rect.position.x); @@ -1592,6 +1596,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V obj_rect.position.y += baseline_y; if (p_click.y >= obj_rect.position.y && p_click.y <= obj_rect.position.y + obj_rect.size.y) { char_pos = glyphs[glyph_idx].start; + char_clicked = true; } break; } @@ -1602,18 +1607,21 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V float fd = TS->font_get_descent(glyphs[glyph_idx].font_rid, glyphs[glyph_idx].font_size); if (p_click.y >= baseline_y - fa && p_click.y <= baseline_y + fd) { char_pos = glyphs[glyph_idx].start; + char_clicked = true; } } else if (!(glyphs[glyph_idx].flags & TextServer::GRAPHEME_IS_VIRTUAL)) { // Hex code box. Vector2 gl_size = TS->get_hex_code_box_size(glyphs[glyph_idx].font_size, glyphs[glyph_idx].index); if (p_click.y >= baseline_y - gl_size.y * 0.9 && p_click.y <= baseline_y + gl_size.y * 0.2) { char_pos = glyphs[glyph_idx].start; + char_clicked = true; } } } } else { char_pos = TS->shaped_text_hit_test_position(rid, p_click.x - rect.position.x); char_pos = TS->shaped_text_closest_character_pos(rid, char_pos); + char_clicked = true; } } line_clicked = true; @@ -1621,7 +1629,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V } // If table hit was detected, and line hit is in the table bounds use table hit. - if (table_hit && (((char_pos + p_frame->lines[p_line].char_offset) >= table_range.x && (char_pos + p_frame->lines[p_line].char_offset) <= table_range.y) || char_pos == -1)) { + if (table_hit && (((char_pos + p_frame->lines[p_line].char_offset) >= table_range.x && (char_pos + p_frame->lines[p_line].char_offset) <= table_range.y) || !char_clicked)) { if (r_click_frame != nullptr) { *r_click_frame = table_click_frame; } diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index ac6ebd5cc1..422783b01b 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -38,7 +38,7 @@ #include "core/os/keyboard.h" #include "core/os/os.h" #include "core/string/string_builder.h" -#include "core/string/translation.h" +#include "core/string/translation_server.h" #include "scene/gui/label.h" #include "scene/main/window.h" #include "scene/theme/theme_db.h" @@ -4328,25 +4328,12 @@ Point2i TextEdit::get_line_column_at_pos(const Point2i &p_pos, bool p_allow_out_ return Point2i(text[row].length(), row); } - int col = 0; int colx = p_pos.x - (theme_cache.style_normal->get_margin(SIDE_LEFT) + gutters_width + gutter_padding); colx += first_visible_col; if (!editable) { colx -= theme_cache.style_readonly->get_offset().x / 2; colx += theme_cache.style_normal->get_offset().x / 2; } - col = _get_char_pos_for_line(colx, row, wrap_index); - if (get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE && wrap_index < get_line_wrap_count(row)) { - // Move back one if we are at the end of the row. - Vector<String> rows2 = get_line_wrapped_text(row); - int row_end_col = 0; - for (int i = 0; i < wrap_index + 1; i++) { - row_end_col += rows2[i].length(); - } - if (col >= row_end_col) { - col -= 1; - } - } RID text_rid = text.get_line_data(row)->get_line_rid(wrap_index); float wrap_indent = (text.is_indent_wrapped_lines() && wrap_index > 0) ? get_indent_level(row) * theme_cache.font->get_char_size(' ', theme_cache.font_size).width : 0.0; @@ -4355,7 +4342,7 @@ Point2i TextEdit::get_line_column_at_pos(const Point2i &p_pos, bool p_allow_out_ } else { colx -= wrap_indent; } - col = TS->shaped_text_hit_test_position(text_rid, colx); + int col = TS->shaped_text_hit_test_position(text_rid, colx); if (!caret_mid_grapheme_enabled) { col = TS->shaped_text_closest_character_pos(text_rid, col); } @@ -7531,7 +7518,7 @@ int TextEdit::_get_column_x_offset_for_line(int p_char, int p_line, int p_column int row = 0; Vector<Vector2i> rows2 = text.get_line_wrap_ranges(p_line); for (int i = 0; i < rows2.size(); i++) { - if ((p_char >= rows2[i].x) && (p_char <= rows2[i].y)) { + if ((p_char >= rows2[i].x) && (p_char < rows2[i].y || (i == rows2.size() - 1 && p_char == rows2[i].y))) { row = i; break; } diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 0396f3ab4a..5c46abc732 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -138,6 +138,12 @@ void Node::_notification(int p_notification) { get_tree()->nodes_in_tree_count++; orphan_node_count--; + + // Allow physics interpolated nodes to automatically reset when added to the tree + // (this is to save the user from doing this manually each time). + if (get_tree()->is_physics_interpolation_enabled()) { + _set_physics_interpolation_reset_requested(true); + } } break; case NOTIFICATION_EXIT_TREE: { @@ -437,6 +443,18 @@ void Node::_propagate_physics_interpolated(bool p_interpolated) { data.blocked--; } +void Node::_propagate_physics_interpolation_reset_requested(bool p_requested) { + if (is_physics_interpolated()) { + data.physics_interpolation_reset_requested = p_requested; + } + + data.blocked++; + for (KeyValue<StringName, Node *> &K : data.children) { + K.value->_propagate_physics_interpolation_reset_requested(p_requested); + } + data.blocked--; +} + void Node::move_child(Node *p_child, int p_index) { ERR_FAIL_COND_MSG(data.inside_tree && !Thread::is_main_thread(), "Moving child node positions inside the SceneTree is only allowed from the main thread. Use call_deferred(\"move_child\",child,index)."); ERR_FAIL_NULL(p_child); @@ -890,15 +908,23 @@ void Node::set_physics_interpolation_mode(PhysicsInterpolationMode p_mode) { } // If swapping from interpolated to non-interpolated, use this as an extra means to cause a reset. - if (is_physics_interpolated() && !interpolate) { - reset_physics_interpolation(); + if (is_physics_interpolated() && !interpolate && is_inside_tree()) { + propagate_notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION); } _propagate_physics_interpolated(interpolate); } void Node::reset_physics_interpolation() { - propagate_notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION); + if (is_inside_tree()) { + propagate_notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION); + + // If `reset_physics_interpolation()` is called explicitly by the user + // (e.g. from scripts) then we prevent deferred auto-resets taking place. + // The user is trusted to call reset in the right order, and auto-reset + // will interfere with their control of prev / curr, so should be turned off. + _propagate_physics_interpolation_reset_requested(false); + } } bool Node::_is_enabled() const { @@ -3825,6 +3851,9 @@ Node::Node() { data.unhandled_key_input = false; data.physics_interpolated = true; + data.physics_interpolation_reset_requested = false; + data.physics_interpolated_client_side = false; + data.use_identity_transform = false; data.parent_owned = false; data.in_constructor = true; diff --git a/scene/main/node.h b/scene/main/node.h index ee195ddef9..2f6372dad5 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -225,6 +225,21 @@ private: // is switched on. bool physics_interpolated : 1; + // We can auto-reset physics interpolation when e.g. adding a node for the first time. + bool physics_interpolation_reset_requested : 1; + + // Most nodes need not be interpolated in the scene tree, physics interpolation + // is normally only needed in the RenderingServer. However if we need to read the + // interpolated transform of a node in the SceneTree, it is necessary to duplicate + // the interpolation logic client side, in order to prevent stalling the RenderingServer + // by reading back. + bool physics_interpolated_client_side : 1; + + // For certain nodes (e.g. CPU particles in global mode) + // it can be useful to not send the instance transform to the + // RenderingServer, and specify the mesh in world space. + bool use_identity_transform : 1; + bool parent_owned : 1; bool in_constructor : 1; bool use_placeholder : 1; @@ -263,6 +278,7 @@ private: void _propagate_exit_tree(); void _propagate_after_exit_tree(); void _propagate_physics_interpolated(bool p_interpolated); + void _propagate_physics_interpolation_reset_requested(bool p_requested); void _propagate_process_owner(Node *p_owner, int p_pause_notification, int p_enabled_notification); void _propagate_groups_dirty(); Array _get_node_and_resource(const NodePath &p_path); @@ -334,6 +350,15 @@ protected: void _set_owner_nocheck(Node *p_owner); void _set_name_nocheck(const StringName &p_name); + void _set_physics_interpolated_client_side(bool p_enable) { data.physics_interpolated_client_side = p_enable; } + bool _is_physics_interpolated_client_side() const { return data.physics_interpolated_client_side; } + + void _set_physics_interpolation_reset_requested(bool p_enable) { data.physics_interpolation_reset_requested = p_enable; } + bool _is_physics_interpolation_reset_requested() const { return data.physics_interpolation_reset_requested; } + + void _set_use_identity_transform(bool p_enable) { data.use_identity_transform = p_enable; } + bool _is_using_identity_transform() const { return data.use_identity_transform; } + //call from SceneTree void _call_input(const Ref<InputEvent> &p_event); void _call_shortcut_input(const Ref<InputEvent> &p_event); diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index ced6d9aaa6..f0c9e8a866 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -59,6 +59,7 @@ #include "servers/navigation_server_3d.h" #include "servers/physics_server_2d.h" #ifndef _3D_DISABLED +#include "scene/3d/node_3d.h" #include "scene/resources/3d/world_3d.h" #include "servers/physics_server_3d.h" #endif // _3D_DISABLED @@ -118,6 +119,29 @@ void SceneTreeTimer::release_connections() { SceneTreeTimer::SceneTreeTimer() {} +#ifndef _3D_DISABLED +// This should be called once per physics tick, to make sure the transform previous and current +// is kept up to date on the few Node3Ds that are using client side physics interpolation. +void SceneTree::ClientPhysicsInterpolation::physics_process() { + for (SelfList<Node3D> *E = _node_3d_list.first(); E;) { + Node3D *node_3d = E->self(); + + SelfList<Node3D> *current = E; + + // Get the next element here BEFORE we potentially delete one. + E = E->next(); + + // This will return false if the Node3D has timed out .. + // i.e. if get_global_transform_interpolated() has not been called + // for a few seconds, we can delete from the list to keep processing + // to a minimum. + if (!node_3d->update_client_physics_interpolation_data()) { + _node_3d_list.remove(current); + } + } +} +#endif + void SceneTree::tree_changed() { emit_signal(tree_changed_name); } @@ -466,9 +490,31 @@ bool SceneTree::is_physics_interpolation_enabled() const { return _physics_interpolation_enabled; } +#ifndef _3D_DISABLED +void SceneTree::client_physics_interpolation_add_node_3d(SelfList<Node3D> *p_elem) { + // This ensures that _update_physics_interpolation_data() will be called at least once every + // physics tick, to ensure the previous and current transforms are kept up to date. + _client_physics_interpolation._node_3d_list.add(p_elem); +} + +void SceneTree::client_physics_interpolation_remove_node_3d(SelfList<Node3D> *p_elem) { + _client_physics_interpolation._node_3d_list.remove(p_elem); +} +#endif + void SceneTree::iteration_prepare() { if (_physics_interpolation_enabled) { + // Make sure any pending transforms from the last tick / frame + // are flushed before pumping the interpolation prev and currents. + flush_transform_notifications(); RenderingServer::get_singleton()->tick(); + +#ifndef _3D_DISABLED + // Any objects performing client physics interpolation + // should be given an opportunity to keep their previous transforms + // up to date before each new physics tick. + _client_physics_interpolation.physics_process(); +#endif } } @@ -503,6 +549,14 @@ bool SceneTree::physics_process(double p_time) { return _quit; } +void SceneTree::iteration_end() { + // When physics interpolation is active, we want all pending transforms + // to be flushed to the RenderingServer before finishing a physics tick. + if (_physics_interpolation_enabled) { + flush_transform_notifications(); + } +} + bool SceneTree::process(double p_time) { if (MainLoop::process(p_time)) { _quit = true; @@ -570,6 +624,10 @@ bool SceneTree::process(double p_time) { #endif // _3D_DISABLED #endif // TOOLS_ENABLED + if (_physics_interpolation_enabled) { + RenderingServer::get_singleton()->pre_draw(true); + } + return _quit; } @@ -1761,6 +1819,13 @@ SceneTree::SceneTree() { set_physics_interpolation_enabled(GLOBAL_DEF("physics/common/physics_interpolation", false)); + // Always disable jitter fix if physics interpolation is enabled - + // Jitter fix will interfere with interpolation, and is not necessary + // when interpolation is active. + if (is_physics_interpolation_enabled()) { + Engine::get_singleton()->set_physics_jitter_fix(0); + } + // Initialize network state. set_multiplayer(MultiplayerAPI::create_default_interface()); diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index 6f0a61ec51..7e44541105 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -41,6 +41,9 @@ class PackedScene; class Node; +#ifndef _3D_DISABLED +class Node3D; +#endif class Window; class Material; class Mesh; @@ -120,6 +123,13 @@ private: bool changed = false; }; +#ifndef _3D_DISABLED + struct ClientPhysicsInterpolation { + SelfList<Node3D>::List _node_3d_list; + void physics_process(); + } _client_physics_interpolation; +#endif + Window *root = nullptr; double physics_process_time = 0.0; @@ -315,6 +325,7 @@ public: virtual void iteration_prepare() override; virtual bool physics_process(double p_time) override; + virtual void iteration_end() override; virtual bool process(double p_time) override; virtual void finalize() override; @@ -423,6 +434,11 @@ public: void set_physics_interpolation_enabled(bool p_enabled); bool is_physics_interpolation_enabled() const; +#ifndef _3D_DISABLED + void client_physics_interpolation_add_node_3d(SelfList<Node3D> *p_elem); + void client_physics_interpolation_remove_node_3d(SelfList<Node3D> *p_elem); +#endif + SceneTree(); ~SceneTree(); }; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 1302e3c53e..c85fda2371 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1511,6 +1511,7 @@ void Viewport::_gui_show_tooltip() { r.size *= win_scale; vr = window->get_usable_parent_rect(); } + r.size = r.size.ceil(); r.size = r.size.min(panel->get_max_size()); if (r.size.x + r.position.x > vr.size.x + vr.position.x) { @@ -5024,6 +5025,13 @@ Viewport::Viewport() { #endif // _3D_DISABLED set_sdf_oversize(sdf_oversize); // Set to server. + + // Physics interpolation mode for viewports is a special case. + // Typically viewports will be housed within Controls, + // and Controls default to PHYSICS_INTERPOLATION_MODE_OFF. + // Viewports can thus inherit physics interpolation OFF, which is unexpected. + // Setting to ON allows each viewport to have a fresh interpolation state. + set_physics_interpolation_mode(Node::PHYSICS_INTERPOLATION_MODE_ON); } Viewport::~Viewport() { diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 23843938a4..c2704a3848 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -34,7 +34,7 @@ #include "core/config/project_settings.h" #include "core/debugger/engine_debugger.h" #include "core/input/shortcut.h" -#include "core/string/translation.h" +#include "core/string/translation_server.h" #include "core/variant/variant_parser.h" #include "scene/gui/control.h" #include "scene/theme/theme_db.h" diff --git a/scene/resources/2d/navigation_polygon.cpp b/scene/resources/2d/navigation_polygon.cpp index 4a290db86b..8218197fd9 100644 --- a/scene/resources/2d/navigation_polygon.cpp +++ b/scene/resources/2d/navigation_polygon.cpp @@ -104,7 +104,7 @@ void NavigationPolygon::_set_polygons(const TypedArray<Vector<int32_t>> &p_array } polygons.resize(p_array.size()); for (int i = 0; i < p_array.size(); i++) { - polygons.write[i].indices = p_array[i]; + polygons.write[i] = p_array[i]; } } @@ -113,7 +113,7 @@ TypedArray<Vector<int32_t>> NavigationPolygon::_get_polygons() const { TypedArray<Vector<int32_t>> ret; ret.resize(polygons.size()); for (int i = 0; i < ret.size(); i++) { - ret[i] = polygons[i].indices; + ret[i] = polygons[i]; } return ret; @@ -141,9 +141,7 @@ TypedArray<Vector<Vector2>> NavigationPolygon::_get_outlines() const { void NavigationPolygon::add_polygon(const Vector<int> &p_polygon) { RWLockWrite write_lock(rwlock); - Polygon polygon; - polygon.indices = p_polygon; - polygons.push_back(polygon); + polygons.push_back(p_polygon); { MutexLock lock(navigation_mesh_generation); navigation_mesh.unref(); @@ -164,7 +162,7 @@ int NavigationPolygon::get_polygon_count() const { Vector<int> NavigationPolygon::get_polygon(int p_idx) { RWLockRead read_lock(rwlock); ERR_FAIL_INDEX_V(p_idx, polygons.size(), Vector<int>()); - return polygons[p_idx].indices; + return polygons[p_idx]; } void NavigationPolygon::clear_polygons() { @@ -189,10 +187,19 @@ void NavigationPolygon::clear() { void NavigationPolygon::set_data(const Vector<Vector2> &p_vertices, const Vector<Vector<int>> &p_polygons) { RWLockWrite write_lock(rwlock); vertices = p_vertices; - polygons.resize(p_polygons.size()); - for (int i = 0; i < p_polygons.size(); i++) { - polygons.write[i].indices = p_polygons[i]; + polygons = p_polygons; + { + MutexLock lock(navigation_mesh_generation); + navigation_mesh.unref(); } +} + +void NavigationPolygon::set_data(const Vector<Vector2> &p_vertices, const Vector<Vector<int>> &p_polygons, const Vector<Vector<Vector2>> &p_outlines) { + RWLockWrite write_lock(rwlock); + vertices = p_vertices; + polygons = p_polygons; + outlines = p_outlines; + rect_cache_dirty = true; { MutexLock lock(navigation_mesh_generation); navigation_mesh.unref(); @@ -202,10 +209,14 @@ void NavigationPolygon::set_data(const Vector<Vector2> &p_vertices, const Vector void NavigationPolygon::get_data(Vector<Vector2> &r_vertices, Vector<Vector<int>> &r_polygons) { RWLockRead read_lock(rwlock); r_vertices = vertices; - r_polygons.resize(polygons.size()); - for (int i = 0; i < polygons.size(); i++) { - r_polygons.write[i] = polygons[i].indices; - } + r_polygons = polygons; +} + +void NavigationPolygon::get_data(Vector<Vector2> &r_vertices, Vector<Vector<int>> &r_polygons, Vector<Vector<Vector2>> &r_outlines) { + RWLockRead read_lock(rwlock); + r_vertices = vertices; + r_polygons = polygons; + r_outlines = outlines; } Ref<NavigationMesh> NavigationPolygon::get_navigation_mesh() { @@ -237,6 +248,31 @@ Ref<NavigationMesh> NavigationPolygon::get_navigation_mesh() { return navigation_mesh; } +void NavigationPolygon::set_outlines(const Vector<Vector<Vector2>> &p_outlines) { + RWLockWrite write_lock(rwlock); + outlines = p_outlines; + rect_cache_dirty = true; +} + +Vector<Vector<Vector2>> NavigationPolygon::get_outlines() const { + RWLockRead read_lock(rwlock); + return outlines; +} + +void NavigationPolygon::set_polygons(const Vector<Vector<int>> &p_polygons) { + RWLockWrite write_lock(rwlock); + polygons = p_polygons; + { + MutexLock lock(navigation_mesh_generation); + navigation_mesh.unref(); + } +} + +Vector<Vector<int>> NavigationPolygon::get_polygons() const { + RWLockRead read_lock(rwlock); + return polygons; +} + void NavigationPolygon::add_outline(const Vector<Vector2> &p_outline) { RWLockWrite write_lock(rwlock); outlines.push_back(p_outline); @@ -364,7 +400,7 @@ void NavigationPolygon::make_polygons_from_outlines() { for (List<TPPLPoly>::Element *I = out_poly.front(); I; I = I->next()) { TPPLPoly &tp = I->get(); - struct Polygon p; + Vector<int> p; for (int64_t i = 0; i < tp.GetNumPoints(); i++) { HashMap<Vector2, int>::Iterator E = points.find(tp[i]); @@ -372,7 +408,7 @@ void NavigationPolygon::make_polygons_from_outlines() { E = points.insert(tp[i], vertices.size()); vertices.push_back(tp[i]); } - p.indices.push_back(E->value); + p.push_back(E->value); } polygons.push_back(p); diff --git a/scene/resources/2d/navigation_polygon.h b/scene/resources/2d/navigation_polygon.h index 4e99660b0e..f43335b397 100644 --- a/scene/resources/2d/navigation_polygon.h +++ b/scene/resources/2d/navigation_polygon.h @@ -39,10 +39,7 @@ class NavigationPolygon : public Resource { RWLock rwlock; Vector<Vector2> vertices; - struct Polygon { - Vector<int> indices; - }; - Vector<Polygon> polygons; + Vector<Vector<int>> polygons; Vector<Vector<Vector2>> outlines; Vector<Vector<Vector2>> baked_outlines; @@ -109,6 +106,8 @@ public: Vector<Vector2> get_outline(int p_idx) const; void remove_outline(int p_idx); int get_outline_count() const; + void set_outlines(const Vector<Vector<Vector2>> &p_outlines); + Vector<Vector<Vector2>> get_outlines() const; void clear_outlines(); #ifndef DISABLE_DEPRECATED @@ -116,7 +115,7 @@ public: #endif // DISABLE_DEPRECATED void set_polygons(const Vector<Vector<int>> &p_polygons); - const Vector<Vector<int>> &get_polygons() const; + Vector<Vector<int>> get_polygons() const; Vector<int> get_polygon(int p_idx); void clear_polygons(); @@ -155,7 +154,9 @@ public: void clear(); void set_data(const Vector<Vector2> &p_vertices, const Vector<Vector<int>> &p_polygons); + void set_data(const Vector<Vector2> &p_vertices, const Vector<Vector<int>> &p_polygons, const Vector<Vector<Vector2>> &p_outlines); void get_data(Vector<Vector2> &r_vertices, Vector<Vector<int>> &r_polygons); + void get_data(Vector<Vector2> &r_vertices, Vector<Vector<int>> &r_polygons, Vector<Vector<Vector2>> &r_outlines); NavigationPolygon() {} ~NavigationPolygon() {} diff --git a/scene/resources/2d/tile_set.cpp b/scene/resources/2d/tile_set.cpp index d124577d25..dd6ae5096a 100644 --- a/scene/resources/2d/tile_set.cpp +++ b/scene/resources/2d/tile_set.cpp @@ -5275,11 +5275,26 @@ Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords, int bool TileSetAtlasSource::is_position_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Vector2 p_position) const { Size2 size = get_tile_texture_region(p_atlas_coords).size; - Rect2 rect = Rect2(-size / 2 - get_tile_data(p_atlas_coords, p_alternative_tile)->get_texture_origin(), size); + TileData *tile_data = get_tile_data(p_atlas_coords, p_alternative_tile); + if (tile_data->get_transpose()) { + size = Size2(size.y, size.x); + } + Rect2 rect = Rect2(-size / 2 - tile_data->get_texture_origin(), size); return rect.has_point(p_position); } +bool TileSetAtlasSource::is_rect_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Rect2 p_rect) const { + Size2 size = get_tile_texture_region(p_atlas_coords).size; + TileData *tile_data = get_tile_data(p_atlas_coords, p_alternative_tile); + if (tile_data->get_transpose()) { + size = Size2(size.y, size.x); + } + Rect2 rect = Rect2(-size / 2 - tile_data->get_texture_origin(), size); + + return p_rect.intersection(rect) == p_rect; +} + int TileSetAtlasSource::alternative_no_transform(int p_alternative_id) { return p_alternative_id & ~(TRANSFORM_FLIP_H | TRANSFORM_FLIP_V | TRANSFORM_TRANSPOSE); } @@ -6487,18 +6502,19 @@ Ref<NavigationPolygon> TileData::get_navigation_polygon(int p_layer_id, bool p_f transformed_polygon.instantiate(); PackedVector2Array new_points = get_transformed_vertices(layer_tile_data.navigation_polygon->get_vertices(), p_flip_h, p_flip_v, p_transpose); - transformed_polygon->set_vertices(new_points); - int num_polygons = layer_tile_data.navigation_polygon->get_polygon_count(); - for (int i = 0; i < num_polygons; ++i) { - transformed_polygon->add_polygon(layer_tile_data.navigation_polygon->get_polygon(i)); - } + const Vector<Vector<Vector2>> outlines = layer_tile_data.navigation_polygon->get_outlines(); + int outline_count = outlines.size(); - for (int i = 0; i < layer_tile_data.navigation_polygon->get_outline_count(); i++) { - PackedVector2Array new_outline = get_transformed_vertices(layer_tile_data.navigation_polygon->get_outline(i), p_flip_h, p_flip_v, p_transpose); - transformed_polygon->add_outline(new_outline); + Vector<Vector<Vector2>> new_outlines; + new_outlines.resize(outline_count); + + for (int i = 0; i < outline_count; i++) { + new_outlines.write[i] = get_transformed_vertices(outlines[i], p_flip_h, p_flip_v, p_transpose); } + transformed_polygon->set_data(new_points, layer_tile_data.navigation_polygon->get_polygons(), new_outlines); + layer_tile_data.transformed_navigation_polygon[key] = transformed_polygon; return transformed_polygon; } else { diff --git a/scene/resources/2d/tile_set.h b/scene/resources/2d/tile_set.h index e083fa45b9..51df972c8d 100644 --- a/scene/resources/2d/tile_set.h +++ b/scene/resources/2d/tile_set.h @@ -763,6 +763,7 @@ public: Vector2i get_atlas_grid_size() const; Rect2i get_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const; bool is_position_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Vector2 p_position) const; + bool is_rect_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Rect2 p_rect) const; static int alternative_no_transform(int p_alternative_id); diff --git a/scene/resources/3d/importer_mesh.cpp b/scene/resources/3d/importer_mesh.cpp index f912d2650d..b0633c06b9 100644 --- a/scene/resources/3d/importer_mesh.cpp +++ b/scene/resources/3d/importer_mesh.cpp @@ -256,6 +256,33 @@ void ImporterMesh::set_surface_material(int p_surface, const Ref<Material> &p_ma mesh.unref(); } +void ImporterMesh::optimize_indices_for_cache() { + if (!SurfaceTool::optimize_vertex_cache_func) { + return; + } + + for (int i = 0; i < surfaces.size(); i++) { + if (surfaces[i].primitive != Mesh::PRIMITIVE_TRIANGLES) { + continue; + } + + Vector<Vector3> vertices = surfaces[i].arrays[RS::ARRAY_VERTEX]; + PackedInt32Array indices = surfaces[i].arrays[RS::ARRAY_INDEX]; + + unsigned int index_count = indices.size(); + unsigned int vertex_count = vertices.size(); + + if (index_count == 0) { + continue; + } + + int *indices_ptr = indices.ptrw(); + SurfaceTool::optimize_vertex_cache_func((unsigned int *)indices_ptr, (const unsigned int *)indices_ptr, index_count, vertex_count); + + surfaces.write[i].arrays[RS::ARRAY_INDEX] = indices; + } +} + #define VERTEX_SKIN_FUNC(bone_count, vert_idx, read_array, write_array, transform_array, bone_array, weight_array) \ Vector3 transformed_vert; \ for (unsigned int weight_idx = 0; weight_idx < bone_count; weight_idx++) { \ @@ -269,7 +296,7 @@ void ImporterMesh::set_surface_material(int p_surface, const Ref<Material> &p_ma } \ write_array[vert_idx] = transformed_vert; -void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_split_angle, Array p_bone_transform_array) { +void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_split_angle, Array p_bone_transform_array, bool p_raycast_normals) { if (!SurfaceTool::simplify_scale_func) { return; } @@ -432,6 +459,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli unsigned int index_target = 12; // Start with the smallest target, 4 triangles unsigned int last_index_count = 0; + // Only used for normal raycasting int split_vertex_count = vertex_count; LocalVector<Vector3> split_vertex_normals; LocalVector<int> split_vertex_indices; @@ -441,7 +469,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli RandomPCG pcg; pcg.seed(123456789); // Keep seed constant across imports - Ref<StaticRaycaster> raycaster = StaticRaycaster::create(); + Ref<StaticRaycaster> raycaster = p_raycast_normals ? StaticRaycaster::create() : Ref<StaticRaycaster>(); if (raycaster.is_valid()) { raycaster->add_mesh(vertices, indices, 0); raycaster->commit(); @@ -488,19 +516,22 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli } new_indices.resize(new_index_count); - - LocalVector<LocalVector<int>> vertex_corners; - vertex_corners.resize(vertex_count); { int *ptrw = new_indices.ptrw(); for (unsigned int j = 0; j < new_index_count; j++) { - const int &remapped = vertex_inverse_remap[ptrw[j]]; - vertex_corners[remapped].push_back(j); - ptrw[j] = remapped; + ptrw[j] = vertex_inverse_remap[ptrw[j]]; } } if (raycaster.is_valid()) { + LocalVector<LocalVector<int>> vertex_corners; + vertex_corners.resize(vertex_count); + + int *ptrw = new_indices.ptrw(); + for (unsigned int j = 0; j < new_index_count; j++) { + vertex_corners[ptrw[j]].push_back(j); + } + float error_factor = 1.0f / (scale * MAX(mesh_error, 0.15)); const float ray_bias = 0.05; float ray_length = ray_bias + mesh_error * scale * 3.0f; @@ -671,7 +702,10 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli } } - surfaces.write[i].split_normals(split_vertex_indices, split_vertex_normals); + if (raycaster.is_valid()) { + surfaces.write[i].split_normals(split_vertex_indices, split_vertex_normals); + } + surfaces.write[i].lods.sort_custom<Surface::LODComparator>(); for (int j = 0; j < surfaces.write[i].lods.size(); j++) { @@ -682,6 +716,10 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli } } +void ImporterMesh::_generate_lods_bind(float p_normal_merge_angle, float p_normal_split_angle, Array p_skin_pose_transform_array) { + generate_lods(p_normal_merge_angle, p_normal_split_angle, p_skin_pose_transform_array); +} + bool ImporterMesh::has_mesh() const { return mesh.is_valid(); } @@ -811,6 +849,10 @@ void ImporterMesh::create_shadow_mesh() { index_wptr[j] = vertex_remap[index]; } + if (SurfaceTool::optimize_vertex_cache_func) { + SurfaceTool::optimize_vertex_cache_func((unsigned int *)index_wptr, (const unsigned int *)index_wptr, index_count, new_vertices.size()); + } + new_surface[RS::ARRAY_INDEX] = new_indices; // Make sure the same LODs as the full version are used. @@ -829,6 +871,10 @@ void ImporterMesh::create_shadow_mesh() { index_wptr[k] = vertex_remap[index]; } + if (SurfaceTool::optimize_vertex_cache_func) { + SurfaceTool::optimize_vertex_cache_func((unsigned int *)index_wptr, (const unsigned int *)index_wptr, index_count, new_vertices.size()); + } + lods[surfaces[i].lods[j].distance] = new_indices; } } @@ -1062,9 +1108,12 @@ Ref<NavigationMesh> ImporterMesh::create_navigation_mesh() { } HashMap<Vector3, int> unique_vertices; - LocalVector<int> face_indices; + Vector<Vector<int>> face_polygons; + face_polygons.resize(faces.size()); for (int i = 0; i < faces.size(); i++) { + Vector<int> face_indices; + face_indices.resize(3); for (int j = 0; j < 3; j++) { Vector3 v = faces[i].vertex[j]; int idx; @@ -1074,8 +1123,9 @@ Ref<NavigationMesh> ImporterMesh::create_navigation_mesh() { idx = unique_vertices.size(); unique_vertices[v] = idx; } - face_indices.push_back(idx); + face_indices.write[j] = idx; } + face_polygons.write[i] = face_indices; } Vector<Vector3> vertices; @@ -1086,16 +1136,7 @@ Ref<NavigationMesh> ImporterMesh::create_navigation_mesh() { Ref<NavigationMesh> nm; nm.instantiate(); - nm->set_vertices(vertices); - - Vector<int> v3; - v3.resize(3); - for (uint32_t i = 0; i < face_indices.size(); i += 3) { - v3.write[0] = face_indices[i + 0]; - v3.write[1] = face_indices[i + 1]; - v3.write[2] = face_indices[i + 2]; - nm->add_polygon(v3); - } + nm->set_data(vertices, face_polygons); return nm; } @@ -1367,7 +1408,7 @@ void ImporterMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_surface_name", "surface_idx", "name"), &ImporterMesh::set_surface_name); ClassDB::bind_method(D_METHOD("set_surface_material", "surface_idx", "material"), &ImporterMesh::set_surface_material); - ClassDB::bind_method(D_METHOD("generate_lods", "normal_merge_angle", "normal_split_angle", "bone_transform_array"), &ImporterMesh::generate_lods); + ClassDB::bind_method(D_METHOD("generate_lods", "normal_merge_angle", "normal_split_angle", "bone_transform_array"), &ImporterMesh::_generate_lods_bind); ClassDB::bind_method(D_METHOD("get_mesh", "base_mesh"), &ImporterMesh::get_mesh, DEFVAL(Ref<ArrayMesh>())); ClassDB::bind_method(D_METHOD("clear"), &ImporterMesh::clear); diff --git a/scene/resources/3d/importer_mesh.h b/scene/resources/3d/importer_mesh.h index ff8683449b..5eb4ee884e 100644 --- a/scene/resources/3d/importer_mesh.h +++ b/scene/resources/3d/importer_mesh.h @@ -86,6 +86,8 @@ protected: void _set_data(const Dictionary &p_data); Dictionary _get_data() const; + void _generate_lods_bind(float p_normal_merge_angle, float p_normal_split_angle, Array p_skin_pose_transform_array); + static void _bind_methods(); public: @@ -112,7 +114,9 @@ public: void set_surface_material(int p_surface, const Ref<Material> &p_material); - void generate_lods(float p_normal_merge_angle, float p_normal_split_angle, Array p_skin_pose_transform_array); + void optimize_indices_for_cache(); + + void generate_lods(float p_normal_merge_angle, float p_normal_split_angle, Array p_skin_pose_transform_array, bool p_raycast_normals = false); void create_shadow_mesh(); Ref<ImporterMesh> get_shadow_mesh() const; diff --git a/scene/resources/3d/primitive_meshes.cpp b/scene/resources/3d/primitive_meshes.cpp index ee772f960a..128822d2cb 100644 --- a/scene/resources/3d/primitive_meshes.cpp +++ b/scene/resources/3d/primitive_meshes.cpp @@ -324,22 +324,43 @@ Vector2 PrimitiveMesh::get_uv2_scale(Vector2 p_margin_scale) const { } float PrimitiveMesh::get_lightmap_texel_size() const { - float texel_size = GLOBAL_GET("rendering/lightmapping/primitive_meshes/texel_size"); + return texel_size; +} - if (texel_size <= 0.0) { - texel_size = 0.2; +void PrimitiveMesh::_on_settings_changed() { + float new_texel_size = float(GLOBAL_GET("rendering/lightmapping/primitive_meshes/texel_size")); + if (new_texel_size <= 0.0) { + new_texel_size = 0.2; + } + if (texel_size == new_texel_size) { + return; } - return texel_size; + texel_size = new_texel_size; + _update_lightmap_size(); + request_update(); } PrimitiveMesh::PrimitiveMesh() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); mesh = RenderingServer::get_singleton()->mesh_create(); + + ERR_FAIL_NULL(ProjectSettings::get_singleton()); + texel_size = float(GLOBAL_GET("rendering/lightmapping/primitive_meshes/texel_size")); + if (texel_size <= 0.0) { + texel_size = 0.2; + } + ProjectSettings *project_settings = ProjectSettings::get_singleton(); + project_settings->connect("settings_changed", callable_mp(this, &PrimitiveMesh::_on_settings_changed)); } PrimitiveMesh::~PrimitiveMesh() { ERR_FAIL_NULL(RenderingServer::get_singleton()); RenderingServer::get_singleton()->free(mesh); + + ERR_FAIL_NULL(ProjectSettings::get_singleton()); + ProjectSettings *project_settings = ProjectSettings::get_singleton(); + project_settings->disconnect("settings_changed", callable_mp(this, &PrimitiveMesh::_on_settings_changed)); } /** @@ -350,7 +371,6 @@ void CapsuleMesh::_update_lightmap_size() { if (get_add_uv2()) { // size must have changed, update lightmap size hint Size2i _lightmap_size_hint; - float texel_size = get_lightmap_texel_size(); float padding = get_uv2_padding(); float radial_length = radius * Math_PI * 0.5; // circumference of 90 degree bend @@ -365,7 +385,6 @@ void CapsuleMesh::_update_lightmap_size() { void CapsuleMesh::_create_mesh_array(Array &p_arr) const { bool _add_uv2 = get_add_uv2(); - float texel_size = get_lightmap_texel_size(); float _uv2_padding = get_uv2_padding() * texel_size; create_mesh_array(p_arr, radius, height, radial_segments, rings, _add_uv2, _uv2_padding); @@ -613,7 +632,6 @@ void BoxMesh::_update_lightmap_size() { if (get_add_uv2()) { // size must have changed, update lightmap size hint Size2i _lightmap_size_hint; - float texel_size = get_lightmap_texel_size(); float padding = get_uv2_padding(); float width = (size.x + size.z) / texel_size; @@ -632,7 +650,6 @@ void BoxMesh::_create_mesh_array(Array &p_arr) const { // With 3 faces along the width and 2 along the height of the texture we need to adjust our scale // accordingly. bool _add_uv2 = get_add_uv2(); - float texel_size = get_lightmap_texel_size(); float _uv2_padding = get_uv2_padding() * texel_size; BoxMesh::create_mesh_array(p_arr, size, subdivide_w, subdivide_h, subdivide_d, _add_uv2, _uv2_padding); @@ -937,7 +954,6 @@ void CylinderMesh::_update_lightmap_size() { if (get_add_uv2()) { // size must have changed, update lightmap size hint Size2i _lightmap_size_hint; - float texel_size = get_lightmap_texel_size(); float padding = get_uv2_padding(); float top_circumference = top_radius * Math_PI * 2.0; @@ -957,7 +973,6 @@ void CylinderMesh::_update_lightmap_size() { void CylinderMesh::_create_mesh_array(Array &p_arr) const { bool _add_uv2 = get_add_uv2(); - float texel_size = get_lightmap_texel_size(); float _uv2_padding = get_uv2_padding() * texel_size; create_mesh_array(p_arr, top_radius, bottom_radius, height, radial_segments, rings, cap_top, cap_bottom, _add_uv2, _uv2_padding); @@ -1244,7 +1259,6 @@ void PlaneMesh::_update_lightmap_size() { if (get_add_uv2()) { // size must have changed, update lightmap size hint Size2i _lightmap_size_hint; - float texel_size = get_lightmap_texel_size(); float padding = get_uv2_padding(); _lightmap_size_hint.x = MAX(1.0, (size.x / texel_size) + padding); @@ -1416,7 +1430,6 @@ void PrismMesh::_update_lightmap_size() { if (get_add_uv2()) { // size must have changed, update lightmap size hint Size2i _lightmap_size_hint; - float texel_size = get_lightmap_texel_size(); float padding = get_uv2_padding(); // left_to_right does not effect the surface area of the prism so we ignore that. @@ -1440,7 +1453,6 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { // Only used if we calculate UV2 bool _add_uv2 = get_add_uv2(); - float texel_size = get_lightmap_texel_size(); float _uv2_padding = get_uv2_padding() * texel_size; float horizontal_total = size.x + size.z + 2.0 * _uv2_padding; @@ -1762,7 +1774,6 @@ void SphereMesh::_update_lightmap_size() { if (get_add_uv2()) { // size must have changed, update lightmap size hint Size2i _lightmap_size_hint; - float texel_size = get_lightmap_texel_size(); float padding = get_uv2_padding(); float _width = radius * Math_TAU; @@ -1776,7 +1787,6 @@ void SphereMesh::_update_lightmap_size() { void SphereMesh::_create_mesh_array(Array &p_arr) const { bool _add_uv2 = get_add_uv2(); - float texel_size = get_lightmap_texel_size(); float _uv2_padding = get_uv2_padding() * texel_size; create_mesh_array(p_arr, radius, height, radial_segments, rings, is_hemisphere, _add_uv2, _uv2_padding); @@ -1950,7 +1960,6 @@ void TorusMesh::_update_lightmap_size() { if (get_add_uv2()) { // size must have changed, update lightmap size hint Size2i _lightmap_size_hint; - float texel_size = get_lightmap_texel_size(); float padding = get_uv2_padding(); float min_radius = inner_radius; @@ -2000,7 +2009,6 @@ void TorusMesh::_create_mesh_array(Array &p_arr) const { // Only used if we calculate UV2 bool _add_uv2 = get_add_uv2(); - float texel_size = get_lightmap_texel_size(); float _uv2_padding = get_uv2_padding() * texel_size; float horizontal_total = max_radius * Math_TAU + _uv2_padding; diff --git a/scene/resources/3d/primitive_meshes.h b/scene/resources/3d/primitive_meshes.h index 4d2d0760b3..fc2489923a 100644 --- a/scene/resources/3d/primitive_meshes.h +++ b/scene/resources/3d/primitive_meshes.h @@ -67,6 +67,9 @@ protected: // assume primitive triangles as the type, correct for all but one and it will change this :) Mesh::PrimitiveType primitive_type = Mesh::PRIMITIVE_TRIANGLES; + // Copy of our texel_size project setting. + float texel_size = 0.2; + static void _bind_methods(); virtual void _create_mesh_array(Array &p_arr) const {} @@ -76,6 +79,8 @@ protected: float get_lightmap_texel_size() const; virtual void _update_lightmap_size(){}; + void _on_settings_changed(); + public: virtual int get_surface_count() const override; virtual int surface_get_array_len(int p_idx) const override; diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 104187775d..6b65ea4cfb 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -1482,8 +1482,8 @@ Error FontFile::_load_bitmap_font(const String &p_path, List<String> *r_image_fi switch (block_type) { case 1: /* info */ { ERR_FAIL_COND_V_MSG(block_size < 15, ERR_CANT_CREATE, "Invalid BMFont info block size."); - base_size = f->get_16(); - if (base_size <= 0) { + base_size = ABS(static_cast<int16_t>(f->get_16())); + if (base_size == 0) { base_size = 16; } uint8_t flags = f->get_8(); @@ -1776,7 +1776,10 @@ Error FontFile::_load_bitmap_font(const String &p_path, List<String> *r_image_fi if (type == "info") { if (keys.has("size")) { - base_size = keys["size"].to_int(); + base_size = ABS(keys["size"].to_int()); + if (base_size == 0) { + base_size = 16; + } } if (keys.has("outline")) { outline = keys["outline"].to_int(); diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp index 2866ae7219..67ed65df0d 100644 --- a/scene/resources/navigation_mesh.cpp +++ b/scene/resources/navigation_mesh.cpp @@ -61,12 +61,12 @@ void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) { int rlen = iarr.size(); const int *r = iarr.ptr(); + Vector<int> polygon; for (int j = 0; j < rlen; j += 3) { - Polygon polygon; - polygon.indices.resize(3); - polygon.indices.write[0] = r[j + 0] + from; - polygon.indices.write[1] = r[j + 1] + from; - polygon.indices.write[2] = r[j + 2] + from; + polygon.resize(3); + polygon.write[0] = r[j + 0] + from; + polygon.write[1] = r[j + 1] + from; + polygon.write[2] = r[j + 2] + from; polygons.push_back(polygon); } } @@ -318,7 +318,7 @@ void NavigationMesh::_set_polygons(const Array &p_array) { RWLockWrite write_lock(rwlock); polygons.resize(p_array.size()); for (int i = 0; i < p_array.size(); i++) { - polygons.write[i].indices = p_array[i]; + polygons.write[i] = p_array[i]; } notify_property_list_changed(); } @@ -328,17 +328,26 @@ Array NavigationMesh::_get_polygons() const { Array ret; ret.resize(polygons.size()); for (int i = 0; i < ret.size(); i++) { - ret[i] = polygons[i].indices; + ret[i] = polygons[i]; } return ret; } +void NavigationMesh::set_polygons(const Vector<Vector<int>> &p_polygons) { + RWLockWrite write_lock(rwlock); + polygons = p_polygons; + notify_property_list_changed(); +} + +Vector<Vector<int>> NavigationMesh::get_polygons() const { + RWLockRead read_lock(rwlock); + return polygons; +} + void NavigationMesh::add_polygon(const Vector<int> &p_polygon) { RWLockWrite write_lock(rwlock); - Polygon polygon; - polygon.indices = p_polygon; - polygons.push_back(polygon); + polygons.push_back(p_polygon); notify_property_list_changed(); } @@ -350,7 +359,7 @@ int NavigationMesh::get_polygon_count() const { Vector<int> NavigationMesh::get_polygon(int p_idx) { RWLockRead read_lock(rwlock); ERR_FAIL_INDEX_V(p_idx, polygons.size(), Vector<int>()); - return polygons[p_idx].indices; + return polygons[p_idx]; } void NavigationMesh::clear_polygons() { @@ -367,19 +376,13 @@ void NavigationMesh::clear() { void NavigationMesh::set_data(const Vector<Vector3> &p_vertices, const Vector<Vector<int>> &p_polygons) { RWLockWrite write_lock(rwlock); vertices = p_vertices; - polygons.resize(p_polygons.size()); - for (int i = 0; i < p_polygons.size(); i++) { - polygons.write[i].indices = p_polygons[i]; - } + polygons = p_polygons; } void NavigationMesh::get_data(Vector<Vector3> &r_vertices, Vector<Vector<int>> &r_polygons) { RWLockRead read_lock(rwlock); r_vertices = vertices; - r_polygons.resize(polygons.size()); - for (int i = 0; i < polygons.size(); i++) { - r_polygons.write[i] = polygons[i].indices; - } + r_polygons = polygons; } #ifdef DEBUG_ENABLED diff --git a/scene/resources/navigation_mesh.h b/scene/resources/navigation_mesh.h index 0ec2cc1bb1..f695ba3d80 100644 --- a/scene/resources/navigation_mesh.h +++ b/scene/resources/navigation_mesh.h @@ -39,10 +39,7 @@ class NavigationMesh : public Resource { RWLock rwlock; Vector<Vector3> vertices; - struct Polygon { - Vector<int> indices; - }; - Vector<Polygon> polygons; + Vector<Vector<int>> polygons; Ref<ArrayMesh> debug_mesh; protected: @@ -194,6 +191,8 @@ public: int get_polygon_count() const; Vector<int> get_polygon(int p_idx); void clear_polygons(); + void set_polygons(const Vector<Vector<int>> &p_polygons); + Vector<Vector<int>> get_polygons() const; void clear(); diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 4bedcb1820..1fa52b9c73 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -412,7 +412,6 @@ String VisualShaderNode::get_warning(Shader::Mode p_mode, VisualShader::Type p_t } VisualShaderNode::Category VisualShaderNode::get_category() const { - WARN_PRINT(get_caption() + " is missing a category."); return CATEGORY_NONE; } @@ -1825,7 +1824,7 @@ void VisualShader::reset_state() { void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const { //mode - p_list->push_back(PropertyInfo(Variant::INT, PNAME("mode"), PROPERTY_HINT_ENUM, "Node3D,CanvasItem,Particles,Sky,Fog")); + p_list->push_back(PropertyInfo(Variant::INT, PNAME("mode"), PROPERTY_HINT_ENUM, "Spatial,CanvasItem,Particles,Sky,Fog")); //render modes HashMap<String, String> blend_mode_enums; @@ -3004,9 +3003,9 @@ VisualShader::VisualShader() { /////////////////////////////////////////////////////////// const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { - // Node3D + // Spatial - // Node3D, Vertex + // Spatial, Vertex { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "vertex", "VERTEX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "vertex_id", "VERTEX_ID" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" }, @@ -3043,7 +3042,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom2", "CUSTOM2" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom3", "CUSTOM3" }, - // Node3D, Fragment + // Spatial, Fragment { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "vertex", "VERTEX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" }, @@ -3075,7 +3074,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR_UINT, "camera_visible_layers", "CAMERA_VISIBLE_LAYERS" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "node_position_view", "NODE_POSITION_VIEW" }, - // Node3D, Light + // Spatial, Light { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, @@ -3883,9 +3882,9 @@ VisualShaderNodeParameterRef::VisualShaderNodeParameterRef() { const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = { //////////////////////////////////////////////////////////////////////// - // Node3D. + // Spatial. //////////////////////////////////////////////////////////////////////// - // Node3D, Vertex. + // Spatial, Vertex. //////////////////////////////////////////////////////////////////////// { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Vertex", "VERTEX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Normal", "NORMAL" }, @@ -3900,7 +3899,7 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "Model View Matrix", "MODELVIEW_MATRIX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "Projection Matrix", "PROJECTION_MATRIX" }, //////////////////////////////////////////////////////////////////////// - // Node3D, Fragment. + // Spatial, Fragment. //////////////////////////////////////////////////////////////////////// { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Albedo", "ALBEDO" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha", "ALPHA" }, @@ -3932,7 +3931,7 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Depth", "DEPTH" }, //////////////////////////////////////////////////////////////////////// - // Node3D, Light. + // Spatial, Light. //////////////////////////////////////////////////////////////////////// { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Diffuse", "DIFFUSE_LIGHT" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Specular", "SPECULAR_LIGHT" }, diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp index 7ab150c141..e2c8686911 100644 --- a/servers/audio/audio_stream.cpp +++ b/servers/audio/audio_stream.cpp @@ -342,12 +342,6 @@ bool AudioStreamMicrophone::is_monophonic() const { return true; } -void AudioStreamMicrophone::_bind_methods() { -} - -AudioStreamMicrophone::AudioStreamMicrophone() { -} - int AudioStreamPlaybackMicrophone::_mix_internal(AudioFrame *p_buffer, int p_frames) { AudioDriver::get_singleton()->lock(); diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h index 0ca4777d5c..d02dc6aae7 100644 --- a/servers/audio/audio_stream.h +++ b/servers/audio/audio_stream.h @@ -219,9 +219,6 @@ class AudioStreamMicrophone : public AudioStream { HashSet<AudioStreamPlaybackMicrophone *> playbacks; -protected: - static void _bind_methods(); - public: virtual Ref<AudioStreamPlayback> instantiate_playback() override; virtual String get_stream_name() const override; @@ -229,8 +226,6 @@ public: virtual double get_length() const override; //if supported, otherwise return 0 virtual bool is_monophonic() const override; - - AudioStreamMicrophone(); }; class AudioStreamPlaybackMicrophone : public AudioStreamPlaybackResampled { diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp index 54840adcae..f0f894d03b 100644 --- a/servers/audio_server.cpp +++ b/servers/audio_server.cpp @@ -1379,6 +1379,12 @@ bool AudioServer::is_playback_active(Ref<AudioStreamPlayback> p_playback) { float AudioServer::get_playback_position(Ref<AudioStreamPlayback> p_playback) { ERR_FAIL_COND_V(p_playback.is_null(), 0); + // Samples. + if (p_playback->get_is_sample() && p_playback->get_sample_playback().is_valid()) { + Ref<AudioSamplePlayback> sample_playback = p_playback->get_sample_playback(); + return AudioServer::get_singleton()->get_sample_playback_position(sample_playback); + } + AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback); if (!playback_node) { return 0; @@ -1847,6 +1853,11 @@ bool AudioServer::is_sample_playback_active(const Ref<AudioSamplePlayback> &p_pl return AudioDriver::get_singleton()->is_sample_playback_active(p_playback); } +double AudioServer::get_sample_playback_position(const Ref<AudioSamplePlayback> &p_playback) { + ERR_FAIL_COND_V_MSG(p_playback.is_null(), false, "Parameter p_playback is null."); + return AudioDriver::get_singleton()->get_sample_playback_position(p_playback); +} + void AudioServer::update_sample_playback_pitch_scale(const Ref<AudioSamplePlayback> &p_playback, float p_pitch_scale) { ERR_FAIL_COND_MSG(p_playback.is_null(), "Parameter p_playback is null."); return AudioDriver::get_singleton()->update_sample_playback_pitch_scale(p_playback, p_pitch_scale); diff --git a/servers/audio_server.h b/servers/audio_server.h index fd6cdb451e..2d6fc60860 100644 --- a/servers/audio_server.h +++ b/servers/audio_server.h @@ -141,6 +141,7 @@ public: virtual void stop_sample_playback(const Ref<AudioSamplePlayback> &p_playback) {} virtual void set_sample_playback_pause(const Ref<AudioSamplePlayback> &p_playback, bool p_paused) {} virtual bool is_sample_playback_active(const Ref<AudioSamplePlayback> &p_playback) { return false; } + virtual double get_sample_playback_position(const Ref<AudioSamplePlayback> &p_playback) { return false; } virtual void update_sample_playback_pitch_scale(const Ref<AudioSamplePlayback> &p_playback, float p_pitch_scale = 0.0f) {} virtual void set_sample_playback_bus_volumes_linear(const Ref<AudioSamplePlayback> &p_playback, const HashMap<StringName, Vector<AudioFrame>> &p_bus_volumes) {} @@ -484,6 +485,7 @@ public: void stop_sample_playback(const Ref<AudioSamplePlayback> &p_playback); void set_sample_playback_pause(const Ref<AudioSamplePlayback> &p_playback, bool p_paused); bool is_sample_playback_active(const Ref<AudioSamplePlayback> &p_playback); + double get_sample_playback_position(const Ref<AudioSamplePlayback> &p_playback); void update_sample_playback_pitch_scale(const Ref<AudioSamplePlayback> &p_playback, float p_pitch_scale = 0.0f); AudioServer(); diff --git a/servers/movie_writer/movie_writer.cpp b/servers/movie_writer/movie_writer.cpp index 9df05ba94a..aebed4b432 100644 --- a/servers/movie_writer/movie_writer.cpp +++ b/servers/movie_writer/movie_writer.cpp @@ -185,6 +185,10 @@ void MovieWriter::add_frame() { RID main_vp_rid = RenderingServer::get_singleton()->viewport_find_from_screen_attachment(DisplayServer::MAIN_WINDOW_ID); RID main_vp_texture = RenderingServer::get_singleton()->viewport_get_texture(main_vp_rid); Ref<Image> vp_tex = RenderingServer::get_singleton()->texture_2d_get(main_vp_texture); + if (RenderingServer::get_singleton()->viewport_is_using_hdr_2d(main_vp_rid)) { + vp_tex->convert(Image::FORMAT_RGBA8); + vp_tex->linear_to_srgb(); + } RenderingServer::get_singleton()->viewport_set_measure_render_time(main_vp_rid, true); cpu_time += RenderingServer::get_singleton()->viewport_get_measured_render_time_cpu(main_vp_rid); diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp index b21c6b60f0..398ff1e1f3 100644 --- a/servers/navigation_server_3d.cpp +++ b/servers/navigation_server_3d.cpp @@ -216,6 +216,7 @@ void NavigationServer3D::_bind_methods() { BIND_ENUM_CONSTANT(INFO_EDGE_MERGE_COUNT); BIND_ENUM_CONSTANT(INFO_EDGE_CONNECTION_COUNT); BIND_ENUM_CONSTANT(INFO_EDGE_FREE_COUNT); + BIND_ENUM_CONSTANT(INFO_OBSTACLE_COUNT); } NavigationServer3D *NavigationServer3D::get_singleton() { diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h index 17c0771732..cdacf8e7e4 100644 --- a/servers/navigation_server_3d.h +++ b/servers/navigation_server_3d.h @@ -369,6 +369,7 @@ public: INFO_EDGE_MERGE_COUNT, INFO_EDGE_CONNECTION_COUNT, INFO_EDGE_FREE_COUNT, + INFO_OBSTACLE_COUNT, }; virtual int get_process_info(ProcessInfo p_info) const = 0; diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index ad19f36257..cb655f9b04 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -1749,7 +1749,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co int *render_info = p_render_data->render_info ? p_render_data->render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_VISIBLE] : (int *)nullptr; _fill_instance_data(RENDER_LIST_OPAQUE, render_info); _fill_instance_data(RENDER_LIST_MOTION, render_info); - _fill_instance_data(RENDER_LIST_ALPHA); + _fill_instance_data(RENDER_LIST_ALPHA, render_info); RD::get_singleton()->draw_command_end_label(); @@ -1827,7 +1827,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co clear_color.r *= bg_energy_multiplier; clear_color.g *= bg_energy_multiplier; clear_color.b *= bg_energy_multiplier; - if (!p_render_data->transparent_bg && rb->has_custom_data(RB_SCOPE_FOG) && environment_get_fog_enabled(p_render_data->environment)) { + if (!p_render_data->transparent_bg && (rb->has_custom_data(RB_SCOPE_FOG) || environment_get_fog_enabled(p_render_data->environment))) { draw_sky_fog_only = true; RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.srgb_to_linear())); } @@ -1837,7 +1837,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co clear_color.r *= bg_energy_multiplier; clear_color.g *= bg_energy_multiplier; clear_color.b *= bg_energy_multiplier; - if (!p_render_data->transparent_bg && rb->has_custom_data(RB_SCOPE_FOG) && environment_get_fog_enabled(p_render_data->environment)) { + if (!p_render_data->transparent_bg && (rb->has_custom_data(RB_SCOPE_FOG) || environment_get_fog_enabled(p_render_data->environment))) { draw_sky_fog_only = true; RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.srgb_to_linear())); } diff --git a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl index d629f2738d..b730b2c819 100644 --- a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl @@ -237,7 +237,7 @@ void main() { // This is an ad-hoc term to fade out the SSR as roughness increases. Values used // are meant to match the visual appearance of a ReflectionProbe. - float roughness_fade = smoothstep(0.4, 0.7, 1.0 - normal_roughness.w); + float roughness_fade = smoothstep(0.4, 0.7, 1.0 - roughness); // Schlick term. float metallic = texelFetch(source_metallic, ssC << 1, 0).w; diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl index 4a630b0b0a..cf15497c8c 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl @@ -1310,6 +1310,8 @@ void fragment_shader(in SceneData scene_data) { #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) +#ifndef AMBIENT_LIGHT_DISABLED + if (scene_data.use_reflection_cubemap) { #ifdef LIGHT_ANISOTROPY_USED // https://google.github.io/filament/Filament.html#lighting/imagebasedlights/anisotropy @@ -1397,14 +1399,15 @@ void fragment_shader(in SceneData scene_data) { #endif //USE_RADIANCE_CUBEMAP_ARRAY specular_light += clearcoat_light * horizon * horizon * Fc * scene_data.ambient_light_color_energy.a; } -#endif +#endif // LIGHT_CLEARCOAT_USED +#endif // !AMBIENT_LIGHT_DISABLED #endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) //radiance /// GI /// #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) - +#ifndef AMBIENT_LIGHT_DISABLED #ifdef USE_LIGHTMAP //lightmap @@ -1698,9 +1701,6 @@ void fragment_shader(in SceneData scene_data) { //finalize ambient light here { -#if defined(AMBIENT_LIGHT_DISABLED) - ambient_light = vec3(0.0, 0.0, 0.0); -#else ambient_light *= albedo.rgb; ambient_light *= ao; @@ -1713,15 +1713,14 @@ void fragment_shader(in SceneData scene_data) { ambient_light *= 1.0 - ssil.a; ambient_light += ssil.rgb * albedo.rgb; } -#endif // AMBIENT_LIGHT_DISABLED } - +#endif // AMBIENT_LIGHT_DISABLED // convert ao to direct light ao ao = mix(1.0, ao, ao_light_affect); //this saves some VGPRs vec3 f0 = F0(metallic, specular, albedo); - +#ifndef AMBIENT_LIGHT_DISABLED { #if defined(DIFFUSE_TOON) //simplify for toon, as @@ -1743,6 +1742,7 @@ void fragment_shader(in SceneData scene_data) { #endif } +#endif // !AMBIENT_LIGHT_DISABLED #endif //GI !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) #if !defined(MODE_RENDER_DEPTH) @@ -2242,7 +2242,7 @@ void fragment_shader(in SceneData scene_data) { alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0)); #if defined(ALPHA_SCISSOR_USED) - if (alpha < alpha_scissor) { + if (alpha < alpha_scissor_threshold) { discard; } #endif // ALPHA_SCISSOR_USED diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl index 530a7a37db..e7623e0062 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl @@ -1071,6 +1071,8 @@ void main() { #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) +#ifndef AMBIENT_LIGHT_DISABLED + if (scene_data.use_reflection_cubemap) { #ifdef LIGHT_ANISOTROPY_USED // https://google.github.io/filament/Filament.html#lighting/imagebasedlights/anisotropy @@ -1158,13 +1160,14 @@ void main() { #endif //USE_RADIANCE_CUBEMAP_ARRAY specular_light += clearcoat_light * horizon * horizon * Fc * scene_data.ambient_light_color_energy.a; } -#endif +#endif // LIGHT_CLEARCOAT_USED +#endif // !AMBIENT_LIGHT_DISABLED #endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) //radiance #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) - +#ifndef AMBIENT_LIGHT_DISABLED #ifdef USE_LIGHTMAP //lightmap @@ -1274,14 +1277,11 @@ void main() { } //Reflection probes // finalize ambient light here - { -#if defined(AMBIENT_LIGHT_DISABLED) - ambient_light = vec3(0.0, 0.0, 0.0); -#else - ambient_light *= albedo.rgb; - ambient_light *= ao; -#endif // AMBIENT_LIGHT_DISABLED - } + + ambient_light *= albedo.rgb; + ambient_light *= ao; + +#endif // !AMBIENT_LIGHT_DISABLED // convert ao to direct light ao ao = mix(1.0, ao, ao_light_affect); @@ -1289,6 +1289,7 @@ void main() { //this saves some VGPRs vec3 f0 = F0(metallic, specular, albedo); +#ifndef AMBIENT_LIGHT_DISABLED { #if defined(DIFFUSE_TOON) //simplify for toon, as @@ -1310,6 +1311,7 @@ void main() { #endif } +#endif // !AMBIENT_LIGHT_DISABLED #endif // !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) #if !defined(MODE_RENDER_DEPTH) @@ -1765,7 +1767,7 @@ void main() { alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0)); #if defined(ALPHA_SCISSOR_USED) - if (alpha < alpha_scissor) { + if (alpha < alpha_scissor_threshold) { discard; } #endif // !ALPHA_SCISSOR_USED diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp index 99622996d4..c5454e748a 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp @@ -1439,12 +1439,10 @@ void MeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS:: multimesh->motion_vectors_current_offset = 0; multimesh->motion_vectors_previous_offset = 0; multimesh->motion_vectors_last_change = -1; + multimesh->motion_vectors_enabled = false; if (multimesh->instances) { uint32_t buffer_size = multimesh->instances * multimesh->stride_cache * sizeof(float); - if (multimesh->motion_vectors_enabled) { - buffer_size *= 2; - } multimesh->buffer = RD::get_singleton()->storage_buffer_create(buffer_size); } @@ -1934,6 +1932,7 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b ERR_FAIL_NULL(multimesh); ERR_FAIL_COND(p_buffer.size() != (multimesh->instances * (int)multimesh->stride_cache)); + bool used_motion_vectors = multimesh->motion_vectors_enabled; bool uses_motion_vectors = (RSG::viewport->get_num_viewports_with_motion_vectors() > 0) || (RendererCompositorStorage::get_singleton()->get_num_compositor_effects_with_motion_vectors() > 0); if (uses_motion_vectors) { _multimesh_enable_motion_vectors(multimesh); @@ -1952,6 +1951,11 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b { const float *r = p_buffer.ptr(); RD::get_singleton()->buffer_update(multimesh->buffer, multimesh->motion_vectors_current_offset * multimesh->stride_cache * sizeof(float), p_buffer.size() * sizeof(float), r); + if (multimesh->motion_vectors_enabled && !used_motion_vectors) { + // Motion vectors were just enabled, and the other half of the buffer will be empty. + // Need to ensure that both halves are filled for correct operation. + RD::get_singleton()->buffer_update(multimesh->buffer, multimesh->motion_vectors_previous_offset * multimesh->stride_cache * sizeof(float), p_buffer.size() * sizeof(float), r); + } multimesh->buffer_set = true; } diff --git a/servers/rendering/renderer_rd/storage_rd/render_data_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_data_rd.cpp index ac4fbba75b..ddc4d09279 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_data_rd.cpp +++ b/servers/rendering/renderer_rd/storage_rd/render_data_rd.cpp @@ -30,9 +30,6 @@ #include "render_data_rd.h" -void RenderDataRD::_bind_methods() { -} - Ref<RenderSceneBuffers> RenderDataRD::get_render_scene_buffers() const { return render_buffers; } diff --git a/servers/rendering/renderer_rd/storage_rd/render_data_rd.h b/servers/rendering/renderer_rd/storage_rd/render_data_rd.h index 3cd397b8ed..6ebb85442b 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_data_rd.h +++ b/servers/rendering/renderer_rd/storage_rd/render_data_rd.h @@ -38,9 +38,6 @@ class RenderDataRD : public RenderData { GDCLASS(RenderDataRD, RenderData); -protected: - static void _bind_methods(); - public: // Access methods to expose data externally virtual Ref<RenderSceneBuffers> get_render_scene_buffers() const override; diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp index ba8aafda6d..148a556b46 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp @@ -34,9 +34,6 @@ #include "servers/rendering/renderer_rd/storage_rd/texture_storage.h" #include "servers/rendering/rendering_server_default.h" -void RenderSceneDataRD::_bind_methods() { -} - Transform3D RenderSceneDataRD::get_cam_transform() const { return cam_transform; } diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h index 5579a97792..b2c93acd44 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h @@ -95,8 +95,6 @@ public: virtual RID get_uniform_buffer() const override; private: - static void _bind_methods(); - RID uniform_buffer; // loaded into this uniform buffer (supplied externally) // This struct is loaded into Set 1 - Binding 0, populated at start of rendering a frame, must match with shader code diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp index b02d3def88..06753c3fb7 100644 --- a/servers/rendering/renderer_scene_cull.cpp +++ b/servers/rendering/renderer_scene_cull.cpp @@ -34,10 +34,16 @@ #include "core/object/worker_thread_pool.h" #include "core/os/os.h" #include "rendering_light_culler.h" +#include "rendering_server_constants.h" #include "rendering_server_default.h" #include <new> +#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) +// This is used only to obtain node paths for user-friendly physics interpolation warnings. +#include "scene/main/node.h" +#endif + /* HALTON SEQUENCE */ #ifndef _3D_DISABLED @@ -53,6 +59,20 @@ static float get_halton_value(int p_index, int p_base) { } #endif // _3D_DISABLED +/* EVENT QUEUING */ + +void RendererSceneCull::tick() { + if (_interpolation_data.interpolation_enabled) { + update_interpolation_tick(true); + } +} + +void RendererSceneCull::pre_draw(bool p_will_draw) { + if (_interpolation_data.interpolation_enabled) { + update_interpolation_frame(p_will_draw); + } +} + /* CAMERA API */ RID RendererSceneCull::camera_allocate() { @@ -93,6 +113,7 @@ void RendererSceneCull::camera_set_frustum(RID p_camera, float p_size, Vector2 p void RendererSceneCull::camera_set_transform(RID p_camera, const Transform3D &p_transform) { Camera *camera = camera_owner.get_or_null(p_camera); ERR_FAIL_NULL(camera); + camera->transform = p_transform.orthonormalized(); } @@ -924,8 +945,45 @@ void RendererSceneCull::instance_set_transform(RID p_instance, const Transform3D Instance *instance = instance_owner.get_or_null(p_instance); ERR_FAIL_NULL(instance); - if (instance->transform == p_transform) { - return; //must be checked to avoid worst evil +#ifdef RENDERING_SERVER_DEBUG_PHYSICS_INTERPOLATION + print_line("instance_set_transform " + rtos(p_transform.origin.x) + " .. tick " + itos(Engine::get_singleton()->get_physics_frames())); +#endif + + if (!_interpolation_data.interpolation_enabled || !instance->interpolated || !instance->scenario) { + if (instance->transform == p_transform) { + return; // Must be checked to avoid worst evil. + } + +#ifdef DEBUG_ENABLED + + for (int i = 0; i < 4; i++) { + const Vector3 &v = i < 3 ? p_transform.basis.rows[i] : p_transform.origin; + ERR_FAIL_COND(!v.is_finite()); + } + +#endif + instance->transform = p_transform; + _instance_queue_update(instance, true); + +#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) + if (_interpolation_data.interpolation_enabled && !instance->interpolated && Engine::get_singleton()->is_in_physics_frame()) { + PHYSICS_INTERPOLATION_NODE_WARNING(instance->object_id, "Non-interpolated instance triggered from physics process"); + } +#endif + + return; + } + + float new_checksum = TransformInterpolator::checksum_transform_3d(p_transform); + bool checksums_match = (instance->transform_checksum_curr == new_checksum) && (instance->transform_checksum_prev == new_checksum); + + // We can't entirely reject no changes because we need the interpolation + // system to keep on stewing. + + // Optimized check. First checks the checksums. If they pass it does the slow check at the end. + // Alternatively we can do this non-optimized and ignore the checksum... if no change. + if (checksums_match && (instance->transform_curr == p_transform) && (instance->transform_prev == p_transform)) { + return; } #ifdef DEBUG_ENABLED @@ -936,8 +994,69 @@ void RendererSceneCull::instance_set_transform(RID p_instance, const Transform3D } #endif - instance->transform = p_transform; + + instance->transform_curr = p_transform; + +#ifdef RENDERING_SERVER_DEBUG_PHYSICS_INTERPOLATION + print_line("\tprev " + rtos(instance->transform_prev.origin.x) + ", curr " + rtos(instance->transform_curr.origin.x)); +#endif + + // Keep checksums up to date. + instance->transform_checksum_curr = new_checksum; + + if (!instance->on_interpolate_transform_list) { + _interpolation_data.instance_transform_update_list_curr->push_back(p_instance); + instance->on_interpolate_transform_list = true; + } else { + DEV_ASSERT(_interpolation_data.instance_transform_update_list_curr->size()); + } + + // If the instance is invisible, then we are simply updating the data flow, there is no need to calculate the interpolated + // transform or anything else. + // Ideally we would not even call the VisualServer::set_transform() when invisible but that would entail having logic + // to keep track of the previous transform on the SceneTree side. The "early out" below is less efficient but a lot cleaner codewise. + if (!instance->visible) { + return; + } + + // Decide on the interpolation method... slerp if possible. + instance->interpolation_method = TransformInterpolator::find_method(instance->transform_prev.basis, instance->transform_curr.basis); + + if (!instance->on_interpolate_list) { + _interpolation_data.instance_interpolate_update_list.push_back(p_instance); + instance->on_interpolate_list = true; + } else { + DEV_ASSERT(_interpolation_data.instance_interpolate_update_list.size()); + } + _instance_queue_update(instance, true); + +#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) + if (!Engine::get_singleton()->is_in_physics_frame()) { + PHYSICS_INTERPOLATION_NODE_WARNING(instance->object_id, "Interpolated instance triggered from outside physics process"); + } +#endif +} + +void RendererSceneCull::instance_set_interpolated(RID p_instance, bool p_interpolated) { + Instance *instance = instance_owner.get_or_null(p_instance); + ERR_FAIL_NULL(instance); + instance->interpolated = p_interpolated; +} + +void RendererSceneCull::instance_reset_physics_interpolation(RID p_instance) { + Instance *instance = instance_owner.get_or_null(p_instance); + ERR_FAIL_NULL(instance); + + if (_interpolation_data.interpolation_enabled && instance->interpolated) { + instance->transform_prev = instance->transform_curr; + instance->transform_checksum_prev = instance->transform_checksum_curr; + +#ifdef RENDERING_SERVER_DEBUG_PHYSICS_INTERPOLATION + print_line("instance_reset_physics_interpolation .. tick " + itos(Engine::get_singleton()->get_physics_frames())); + print_line("\tprev " + rtos(instance->transform_prev.origin.x) + ", curr " + rtos(instance->transform_curr.origin.x)); +#endif + } } void RendererSceneCull::instance_attach_object_instance_id(RID p_instance, ObjectID p_id) { @@ -990,6 +1109,23 @@ void RendererSceneCull::instance_set_visible(RID p_instance, bool p_visible) { if (p_visible) { if (instance->scenario != nullptr) { + // Special case for physics interpolation, we want to ensure the interpolated data is up to date + if (_interpolation_data.interpolation_enabled && instance->interpolated && !instance->on_interpolate_list) { + // Do all the extra work we normally do on instance_set_transform(), because this is optimized out for hidden instances. + // This prevents a glitch of stale interpolation transform data when unhiding before the next physics tick. + instance->interpolation_method = TransformInterpolator::find_method(instance->transform_prev.basis, instance->transform_curr.basis); + _interpolation_data.instance_interpolate_update_list.push_back(p_instance); + instance->on_interpolate_list = true; + + // We must also place on the transform update list for a tick, so the system + // can auto-detect if the instance is no longer moving, and remove from the interpolate lists again. + // If this step is ignored, an unmoving instance could remain on the interpolate lists indefinitely + // (or rather until the object is deleted) and cause unnecessary updates and drawcalls. + if (!instance->on_interpolate_transform_list) { + _interpolation_data.instance_transform_update_list_curr->push_back(p_instance); + instance->on_interpolate_transform_list = true; + } + } _instance_queue_update(instance, true, false); } } else if (instance->indexer_id.is_valid()) { @@ -1574,11 +1710,22 @@ void RendererSceneCull::instance_geometry_get_shader_parameter_list(RID p_instan void RendererSceneCull::_update_instance(Instance *p_instance) { p_instance->version++; + // When not using interpolation the transform is used straight. + const Transform3D *instance_xform = &p_instance->transform; + + // Can possibly use the most up to date current transform here when using physics interpolation ... + // uncomment the next line for this.. + //if (_interpolation_data.interpolation_enabled && p_instance->interpolated) { + // instance_xform = &p_instance->transform_curr; + //} + // However it does seem that using the interpolated transform (transform) works for keeping AABBs + // up to date to avoid culling errors. + if (p_instance->base_type == RS::INSTANCE_LIGHT) { InstanceLightData *light = static_cast<InstanceLightData *>(p_instance->base_data); - RSG::light_storage->light_instance_set_transform(light->instance, p_instance->transform); - RSG::light_storage->light_instance_set_aabb(light->instance, p_instance->transform.xform(p_instance->aabb)); + RSG::light_storage->light_instance_set_transform(light->instance, *instance_xform); + RSG::light_storage->light_instance_set_aabb(light->instance, instance_xform->xform(p_instance->aabb)); light->make_shadow_dirty(); RS::LightBakeMode bake_mode = RSG::light_storage->light_get_bake_mode(p_instance->base); @@ -1601,7 +1748,7 @@ void RendererSceneCull::_update_instance(Instance *p_instance) { } else if (p_instance->base_type == RS::INSTANCE_REFLECTION_PROBE) { InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData *>(p_instance->base_data); - RSG::light_storage->reflection_probe_instance_set_transform(reflection_probe->instance, p_instance->transform); + RSG::light_storage->reflection_probe_instance_set_transform(reflection_probe->instance, *instance_xform); if (p_instance->scenario && p_instance->array_index >= 0) { InstanceData &idata = p_instance->scenario->instance_data[p_instance->array_index]; @@ -1610,17 +1757,17 @@ void RendererSceneCull::_update_instance(Instance *p_instance) { } else if (p_instance->base_type == RS::INSTANCE_DECAL) { InstanceDecalData *decal = static_cast<InstanceDecalData *>(p_instance->base_data); - RSG::texture_storage->decal_instance_set_transform(decal->instance, p_instance->transform); + RSG::texture_storage->decal_instance_set_transform(decal->instance, *instance_xform); } else if (p_instance->base_type == RS::INSTANCE_LIGHTMAP) { InstanceLightmapData *lightmap = static_cast<InstanceLightmapData *>(p_instance->base_data); - RSG::light_storage->lightmap_instance_set_transform(lightmap->instance, p_instance->transform); + RSG::light_storage->lightmap_instance_set_transform(lightmap->instance, *instance_xform); } else if (p_instance->base_type == RS::INSTANCE_VOXEL_GI) { InstanceVoxelGIData *voxel_gi = static_cast<InstanceVoxelGIData *>(p_instance->base_data); - scene_render->voxel_gi_instance_set_transform_to_data(voxel_gi->probe_instance, p_instance->transform); + scene_render->voxel_gi_instance_set_transform_to_data(voxel_gi->probe_instance, *instance_xform); } else if (p_instance->base_type == RS::INSTANCE_PARTICLES) { - RSG::particles_storage->particles_set_emission_transform(p_instance->base, p_instance->transform); + RSG::particles_storage->particles_set_emission_transform(p_instance->base, *instance_xform); } else if (p_instance->base_type == RS::INSTANCE_PARTICLES_COLLISION) { InstanceParticlesCollisionData *collision = static_cast<InstanceParticlesCollisionData *>(p_instance->base_data); @@ -1628,13 +1775,13 @@ void RendererSceneCull::_update_instance(Instance *p_instance) { if (RSG::particles_storage->particles_collision_is_heightfield(p_instance->base)) { heightfield_particle_colliders_update_list.insert(p_instance); } - RSG::particles_storage->particles_collision_instance_set_transform(collision->instance, p_instance->transform); + RSG::particles_storage->particles_collision_instance_set_transform(collision->instance, *instance_xform); } else if (p_instance->base_type == RS::INSTANCE_FOG_VOLUME) { InstanceFogVolumeData *volume = static_cast<InstanceFogVolumeData *>(p_instance->base_data); - scene_render->fog_volume_instance_set_transform(volume->instance, p_instance->transform); + scene_render->fog_volume_instance_set_transform(volume->instance, *instance_xform); } else if (p_instance->base_type == RS::INSTANCE_OCCLUDER) { if (p_instance->scenario) { - RendererSceneOcclusionCull::get_singleton()->scenario_set_instance(p_instance->scenario->self, p_instance->self, p_instance->base, p_instance->transform, p_instance->visible); + RendererSceneOcclusionCull::get_singleton()->scenario_set_instance(p_instance->scenario->self, p_instance->self, p_instance->base, *instance_xform, p_instance->visible); } } @@ -1654,7 +1801,7 @@ void RendererSceneCull::_update_instance(Instance *p_instance) { } AABB new_aabb; - new_aabb = p_instance->transform.xform(p_instance->aabb); + new_aabb = instance_xform->xform(p_instance->aabb); p_instance->transformed_aabb = new_aabb; if ((1 << p_instance->base_type) & RS::INSTANCE_GEOMETRY_MASK) { @@ -1681,11 +1828,11 @@ void RendererSceneCull::_update_instance(Instance *p_instance) { } ERR_FAIL_NULL(geom->geometry_instance); - geom->geometry_instance->set_transform(p_instance->transform, p_instance->aabb, p_instance->transformed_aabb); + geom->geometry_instance->set_transform(*instance_xform, p_instance->aabb, p_instance->transformed_aabb); } // note: we had to remove is equal approx check here, it meant that det == 0.000004 won't work, which is the case for some of our scenes. - if (p_instance->scenario == nullptr || !p_instance->visible || p_instance->transform.basis.determinant() == 0) { + if (p_instance->scenario == nullptr || !p_instance->visible || instance_xform->basis.determinant() == 0) { p_instance->prev_transformed_aabb = p_instance->transformed_aabb; return; } @@ -3089,7 +3236,7 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c Vector<Instance *> lights_with_shadow; for (Instance *E : scenario->directional_lights) { - if (!E->visible) { + if (!E->visible || !(E->layer_mask & p_visible_layers)) { continue; } @@ -4180,6 +4327,8 @@ bool RendererSceneCull::free(RID p_rid) { Instance *instance = instance_owner.get_or_null(p_rid); + _interpolation_data.notify_free_instance(p_rid, *instance); + instance_geometry_set_lightmap(p_rid, RID(), Rect2(), 0); instance_set_scenario(p_rid, RID()); instance_set_base(p_rid, RID()); @@ -4240,6 +4389,106 @@ void RendererSceneCull::set_scene_render(RendererSceneRender *p_scene_render) { geometry_instance_pair_mask = scene_render->geometry_instance_get_pair_mask(); } +/* INTERPOLATION API */ + +void RendererSceneCull::update_interpolation_tick(bool p_process) { + // TODO (MultiMesh): Update interpolation in storage. + + // INSTANCES + + // Detect any that were on the previous transform list that are no longer active; + // we should remove them from the interpolate list. + + for (const RID &rid : *_interpolation_data.instance_transform_update_list_prev) { + Instance *instance = instance_owner.get_or_null(rid); + + bool active = true; + + // No longer active? (Either the instance deleted or no longer being transformed.) + if (instance && !instance->on_interpolate_transform_list) { + active = false; + instance->on_interpolate_list = false; + + // Make sure the most recent transform is set... + instance->transform = instance->transform_curr; + + // ... and that both prev and current are the same, just in case of any interpolations. + instance->transform_prev = instance->transform_curr; + + // Make sure instances are updated one more time to ensure the AABBs are correct. + _instance_queue_update(instance, true); + } + + if (!instance) { + active = false; + } + + if (!active) { + _interpolation_data.instance_interpolate_update_list.erase(rid); + } + } + + // Now for any in the transform list (being actively interpolated), keep the previous transform + // value up to date, ready for the next tick. + if (p_process) { + for (const RID &rid : *_interpolation_data.instance_transform_update_list_curr) { + Instance *instance = instance_owner.get_or_null(rid); + if (instance) { + instance->transform_prev = instance->transform_curr; + instance->transform_checksum_prev = instance->transform_checksum_curr; + instance->on_interpolate_transform_list = false; + } + } + } + + // We maintain a mirror list for the transform updates, so we can detect when an instance + // is no longer being transformed, and remove it from the interpolate list. + SWAP(_interpolation_data.instance_transform_update_list_curr, _interpolation_data.instance_transform_update_list_prev); + + // Prepare for the next iteration. + _interpolation_data.instance_transform_update_list_curr->clear(); +} + +void RendererSceneCull::update_interpolation_frame(bool p_process) { + // TODO (MultiMesh): Update interpolation in storage. + + if (p_process) { + real_t f = Engine::get_singleton()->get_physics_interpolation_fraction(); + + for (const RID &rid : _interpolation_data.instance_interpolate_update_list) { + Instance *instance = instance_owner.get_or_null(rid); + if (instance) { + TransformInterpolator::interpolate_transform_3d_via_method(instance->transform_prev, instance->transform_curr, instance->transform, f, instance->interpolation_method); + +#ifdef RENDERING_SERVER_DEBUG_PHYSICS_INTERPOLATION + print_line("\t\tinterpolated: " + rtos(instance->transform.origin.x) + "\t( prev " + rtos(instance->transform_prev.origin.x) + ", curr " + rtos(instance->transform_curr.origin.x) + " ) on tick " + itos(Engine::get_singleton()->get_physics_frames())); +#endif + + // Make sure AABBs are constantly up to date through the interpolation. + _instance_queue_update(instance, true); + } + } + } +} + +void RendererSceneCull::set_physics_interpolation_enabled(bool p_enabled) { + _interpolation_data.interpolation_enabled = p_enabled; +} + +void RendererSceneCull::InterpolationData::notify_free_instance(RID p_rid, Instance &r_instance) { + r_instance.on_interpolate_list = false; + r_instance.on_interpolate_transform_list = false; + + if (!interpolation_enabled) { + return; + } + + // If the instance was on any of the lists, remove. + instance_interpolate_update_list.erase_multiple_unordered(p_rid); + instance_transform_update_list_curr->erase_multiple_unordered(p_rid); + instance_transform_update_list_prev->erase_multiple_unordered(p_rid); +} + RendererSceneCull::RendererSceneCull() { render_pass = 1; singleton = this; diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h index 0039d14475..53f1f2d246 100644 --- a/servers/rendering/renderer_scene_cull.h +++ b/servers/rendering/renderer_scene_cull.h @@ -32,6 +32,7 @@ #define RENDERER_SCENE_CULL_H #include "core/math/dynamic_bvh.h" +#include "core/math/transform_interpolator.h" #include "core/templates/bin_sorted_array.h" #include "core/templates/local_vector.h" #include "core/templates/paged_allocator.h" @@ -66,6 +67,11 @@ public: static RendererSceneCull *singleton; + /* EVENT QUEUING */ + + void tick(); + void pre_draw(bool p_will_draw); + /* CAMERA API */ struct Camera { @@ -406,8 +412,16 @@ public: RID mesh_instance; //only used for meshes and when skeleton/blendshapes exist + // This is the main transform to be drawn with ... + // This will either be the interpolated transform (when using fixed timestep interpolation) + // or the ONLY transform (when not using FTI). Transform3D transform; + // For interpolation we store the current transform (this physics tick) + // and the transform in the previous tick. + Transform3D transform_curr; + Transform3D transform_prev; + float lod_bias; bool ignore_occlusion_culling; @@ -418,13 +432,23 @@ public: RS::ShadowCastingSetting cast_shadows; uint32_t layer_mask; - //fit in 32 bits - bool mirror : 8; - bool receive_shadows : 8; - bool visible : 8; - bool baked_light : 2; //this flag is only to know if it actually did use baked light - bool dynamic_gi : 2; //same above for dynamic objects - bool redraw_if_visible : 4; + // Fit in 32 bits. + bool mirror : 1; + bool receive_shadows : 1; + bool visible : 1; + bool baked_light : 1; // This flag is only to know if it actually did use baked light. + bool dynamic_gi : 1; // Same as above for dynamic objects. + bool redraw_if_visible : 1; + + bool on_interpolate_list : 1; + bool on_interpolate_transform_list : 1; + bool interpolated : 1; + TransformInterpolator::Method interpolation_method : 3; + + // For fixed timestep interpolation. + // Note 32 bits is plenty for checksum, no need for real_t + float transform_checksum_curr; + float transform_checksum_prev; Instance *lightmap = nullptr; Rect2 lightmap_uv_scale; @@ -574,6 +598,14 @@ public: baked_light = true; dynamic_gi = false; redraw_if_visible = false; + + on_interpolate_list = false; + on_interpolate_transform_list = false; + interpolated = true; + interpolation_method = TransformInterpolator::INTERP_LERP; + transform_checksum_curr = 0.0; + transform_checksum_prev = 0.0; + lightmap_slice_index = 0; lightmap = nullptr; lightmap_cull_index = 0; @@ -1027,6 +1059,8 @@ public: virtual void instance_set_layer_mask(RID p_instance, uint32_t p_mask); virtual void instance_set_pivot_data(RID p_instance, float p_sorting_offset, bool p_use_aabb_center); virtual void instance_set_transform(RID p_instance, const Transform3D &p_transform); + virtual void instance_set_interpolated(RID p_instance, bool p_interpolated); + virtual void instance_reset_physics_interpolation(RID p_instance); virtual void instance_attach_object_instance_id(RID p_instance, ObjectID p_id); virtual void instance_set_blend_shape_weight(RID p_instance, int p_shape, float p_weight); virtual void instance_set_surface_override_material(RID p_instance, int p_surface, RID p_material); @@ -1393,6 +1427,22 @@ public: virtual void update_visibility_notifiers(); + /* INTERPOLATION */ + + void update_interpolation_tick(bool p_process = true); + void update_interpolation_frame(bool p_process = true); + virtual void set_physics_interpolation_enabled(bool p_enabled); + + struct InterpolationData { + void notify_free_instance(RID p_rid, Instance &r_instance); + LocalVector<RID> instance_interpolate_update_list; + LocalVector<RID> instance_transform_update_lists[2]; + LocalVector<RID> *instance_transform_update_list_curr = &instance_transform_update_lists[0]; + LocalVector<RID> *instance_transform_update_list_prev = &instance_transform_update_lists[1]; + + bool interpolation_enabled = false; + } _interpolation_data; + RendererSceneCull(); virtual ~RendererSceneCull(); }; diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index 1049cb63cd..7e45eba1de 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -1271,6 +1271,13 @@ void RendererViewport::viewport_set_use_hdr_2d(RID p_viewport, bool p_use_hdr_2d RSG::texture_storage->render_target_set_use_hdr(viewport->render_target, p_use_hdr_2d); } +bool RendererViewport::viewport_is_using_hdr_2d(RID p_viewport) const { + Viewport *viewport = viewport_owner.get_or_null(p_viewport); + ERR_FAIL_NULL_V(viewport, false); + + return viewport->use_hdr_2d; +} + void RendererViewport::viewport_set_screen_space_aa(RID p_viewport, RS::ViewportScreenSpaceAA p_mode) { Viewport *viewport = viewport_owner.get_or_null(p_viewport); ERR_FAIL_NULL(viewport); diff --git a/servers/rendering/renderer_viewport.h b/servers/rendering/renderer_viewport.h index b36fc7f57f..bf97905f86 100644 --- a/servers/rendering/renderer_viewport.h +++ b/servers/rendering/renderer_viewport.h @@ -262,6 +262,8 @@ public: void viewport_set_transparent_background(RID p_viewport, bool p_enabled); void viewport_set_use_hdr_2d(RID p_viewport, bool p_use_hdr_2d); + bool viewport_is_using_hdr_2d(RID p_viewport) const; + void viewport_set_global_canvas_transform(RID p_viewport, const Transform2D &p_transform); void viewport_set_canvas_stacking(RID p_viewport, RID p_canvas, int p_layer, int p_sublayer); diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index 22044a8c2d..da48b24a1a 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -5065,13 +5065,19 @@ void RenderingDevice::swap_buffers() { void RenderingDevice::submit() { _THREAD_SAFE_METHOD_ + ERR_FAIL_COND_MSG(is_main_instance, "Only local devices can submit and sync."); + ERR_FAIL_COND_MSG(local_device_processing, "device already submitted, call sync to wait until done."); _end_frame(); _execute_frame(false); + local_device_processing = true; } void RenderingDevice::sync() { _THREAD_SAFE_METHOD_ + ERR_FAIL_COND_MSG(is_main_instance, "Only local devices can submit and sync."); + ERR_FAIL_COND_MSG(!local_device_processing, "sync can only be called after a submit"); _begin_frame(); + local_device_processing = false; } void RenderingDevice::_free_pending_resources(int p_frame) { @@ -5323,7 +5329,7 @@ Error RenderingDevice::initialize(RenderingContextDriver *p_context, DisplayServ Error err; RenderingContextDriver::SurfaceID main_surface = 0; - const bool main_instance = (singleton == this) && (p_main_window != DisplayServer::INVALID_WINDOW_ID); + is_main_instance = (singleton == this) && (p_main_window != DisplayServer::INVALID_WINDOW_ID); if (p_main_window != DisplayServer::INVALID_WINDOW_ID) { // Retrieve the surface from the main window if it was specified. main_surface = p_context->surface_get_from_window(p_main_window); @@ -5371,7 +5377,7 @@ Error RenderingDevice::initialize(RenderingContextDriver *p_context, DisplayServ err = driver->initialize(device_index, frame_count); ERR_FAIL_COND_V_MSG(err != OK, FAILED, "Failed to initialize driver for device."); - if (main_instance) { + if (is_main_instance) { // Only the singleton instance with a display should print this information. String rendering_method; if (OS::get_singleton()->get_current_rendering_method() == "mobile") { @@ -5499,7 +5505,7 @@ Error RenderingDevice::initialize(RenderingContextDriver *p_context, DisplayServ compute_list = nullptr; bool project_pipeline_cache_enable = GLOBAL_GET("rendering/rendering_device/pipeline_cache/enable"); - if (main_instance && project_pipeline_cache_enable) { + if (is_main_instance && project_pipeline_cache_enable) { // Only the instance that is not a local device and is also the singleton is allowed to manage a pipeline cache. pipeline_cache_file_path = vformat("user://vulkan/pipelines.%s.%s", OS::get_singleton()->get_current_rendering_method(), diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 38ffd5d23d..52f086e54f 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -88,6 +88,9 @@ private: RenderingDeviceDriver *driver = nullptr; RenderingContextDriver::Device device; + bool local_device_processing = false; + bool is_main_instance = false; + protected: static void _bind_methods(); diff --git a/servers/rendering/rendering_method.h b/servers/rendering/rendering_method.h index aa5e7d83cc..57fbf97d8c 100644 --- a/servers/rendering/rendering_method.h +++ b/servers/rendering/rendering_method.h @@ -83,6 +83,8 @@ public: virtual void instance_set_layer_mask(RID p_instance, uint32_t p_mask) = 0; virtual void instance_set_pivot_data(RID p_instance, float p_sorting_offset, bool p_use_aabb_center) = 0; virtual void instance_set_transform(RID p_instance, const Transform3D &p_transform) = 0; + virtual void instance_set_interpolated(RID p_instance, bool p_interpolated) = 0; + virtual void instance_reset_physics_interpolation(RID p_instance) = 0; virtual void instance_attach_object_instance_id(RID p_instance, ObjectID p_id) = 0; virtual void instance_set_blend_shape_weight(RID p_instance, int p_shape, float p_weight) = 0; virtual void instance_set_surface_override_material(RID p_instance, int p_surface, RID p_material) = 0; @@ -350,6 +352,16 @@ public: virtual bool free(RID p_rid) = 0; + /* Physics interpolation */ + + virtual void update_interpolation_tick(bool p_process = true) = 0; + virtual void set_physics_interpolation_enabled(bool p_enabled) = 0; + + /* Event queueing */ + + virtual void tick() = 0; + virtual void pre_draw(bool p_will_draw) = 0; + RenderingMethod(); virtual ~RenderingMethod(); }; diff --git a/servers/rendering/rendering_server_constants.h b/servers/rendering/rendering_server_constants.h new file mode 100644 index 0000000000..6d27a3a022 --- /dev/null +++ b/servers/rendering/rendering_server_constants.h @@ -0,0 +1,48 @@ +/**************************************************************************/ +/* rendering_server_constants.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 RENDERING_SERVER_CONSTANTS_H +#define RENDERING_SERVER_CONSTANTS_H + +// Use for constants etc. that need not be included as often as rendering_server.h +// to reduce dependencies and prevent slow compilation. + +// This is a "cheap" include, and can be used from scene side code as well as servers. + +// N.B. ONLY allow these defined in DEV_ENABLED builds, they will slow +// performance, and are only necessary to use for debugging. +#ifdef DEV_ENABLED + +// Uncomment this define to produce debugging output for physics interpolation. +//#define RENDERING_SERVER_DEBUG_PHYSICS_INTERPOLATION + +#endif // DEV_ENABLED + +#endif // RENDERING_SERVER_CONSTANTS_H diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp index 51ff009eaf..86efccef9a 100644 --- a/servers/rendering/rendering_server_default.cpp +++ b/servers/rendering/rendering_server_default.cpp @@ -381,12 +381,9 @@ void RenderingServerDefault::_thread_loop() { /* INTERPOLATION */ -void RenderingServerDefault::tick() { - RSG::canvas->tick(); -} - void RenderingServerDefault::set_physics_interpolation_enabled(bool p_enabled) { RSG::canvas->set_physics_interpolation_enabled(p_enabled); + RSG::scene->set_physics_interpolation_enabled(p_enabled); } /* EVENT QUEUING */ @@ -411,6 +408,15 @@ void RenderingServerDefault::draw(bool p_swap_buffers, double frame_step) { } } +void RenderingServerDefault::tick() { + RSG::canvas->tick(); + RSG::scene->tick(); +} + +void RenderingServerDefault::pre_draw(bool p_will_draw) { + RSG::scene->pre_draw(p_will_draw); +} + void RenderingServerDefault::_call_on_render_thread(const Callable &p_callable) { p_callable.call(); } diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 164ec3cc09..039f038b8f 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -639,6 +639,7 @@ public: FUNC3(viewport_set_canvas_transform, RID, RID, const Transform2D &) FUNC2(viewport_set_transparent_background, RID, bool) FUNC2(viewport_set_use_hdr_2d, RID, bool) + FUNC1RC(bool, viewport_is_using_hdr_2d, RID) FUNC2(viewport_set_snap_2d_transforms_to_pixel, RID, bool) FUNC2(viewport_set_snap_2d_vertices_to_pixel, RID, bool) @@ -802,6 +803,8 @@ public: FUNC2(instance_set_layer_mask, RID, uint32_t) FUNC3(instance_set_pivot_data, RID, float, bool) FUNC2(instance_set_transform, RID, const Transform3D &) + FUNC2(instance_set_interpolated, RID, bool) + FUNC1(instance_reset_physics_interpolation, RID) FUNC2(instance_attach_object_instance_id, RID, ObjectID) FUNC3(instance_set_blend_shape_weight, RID, int, float) FUNC3(instance_set_surface_override_material, RID, int, RID) @@ -1048,7 +1051,6 @@ public: /* INTERPOLATION */ - virtual void tick() override; virtual void set_physics_interpolation_enabled(bool p_enabled) override; /* EVENT QUEUING */ @@ -1060,6 +1062,8 @@ public: virtual bool has_changed() const override; virtual void init() override; virtual void finish() override; + virtual void tick() override; + virtual void pre_draw(bool p_will_draw) override; virtual bool is_on_render_thread() override { return Thread::get_caller_id() == server_thread; diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 0235d72cfa..66fcefe228 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -898,6 +898,13 @@ bool ShaderLanguage::_lookup_next(Token &r_tk) { return false; } +ShaderLanguage::Token ShaderLanguage::_peek() { + TkPos pre_pos = _get_tkpos(); + Token tk = _get_token(); + _set_tkpos(pre_pos); + return tk; +} + String ShaderLanguage::token_debug(const String &p_code) { clear(); @@ -8061,7 +8068,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun if (!expr) { return ERR_PARSE_ERROR; } - is_condition = expr->type == Node::NODE_TYPE_OPERATOR && expr->get_datatype() == TYPE_BOOL; + is_condition = expr->get_datatype() == TYPE_BOOL; if (expr->type == Node::NODE_TYPE_OPERATOR) { OperatorNode *op = static_cast<OperatorNode *>(expr); @@ -8077,7 +8084,12 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR_CONDITION) { if (tk.type == TK_COMMA) { if (!is_condition) { - _set_error(RTR("The middle expression is expected to be a boolean operator.")); + _set_error(RTR("The middle expression is expected to have a boolean data type.")); + return ERR_PARSE_ERROR; + } + tk = _peek(); + if (tk.type == TK_SEMICOLON) { + _set_error(vformat(RTR("Expected expression, found: '%s'."), get_token_text(tk))); return ERR_PARSE_ERROR; } continue; @@ -8088,6 +8100,11 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun } } else if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR_EXPRESSION) { if (tk.type == TK_COMMA) { + tk = _peek(); + if (tk.type == TK_PARENTHESIS_CLOSE) { + _set_error(vformat(RTR("Expected expression, found: '%s'."), get_token_text(tk))); + return ERR_PARSE_ERROR; + } continue; } if (tk.type != TK_PARENTHESIS_CLOSE) { @@ -8106,7 +8123,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun return ERR_PARSE_ERROR; } if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR_CONDITION && !is_condition) { - _set_error(RTR("The middle expression is expected to be a boolean operator.")); + _set_error(RTR("The middle expression is expected to have a boolean data type.")); return ERR_PARSE_ERROR; } } @@ -9217,6 +9234,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f tk = _get_token(); if (tk.type == TK_IDENTIFIER) { current_uniform_group_name = tk.text; + current_uniform_subgroup_name = ""; tk = _get_token(); if (tk.type == TK_PERIOD) { tk = _get_token(); diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index 4a67c8f2d2..ad3b78546d 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -1037,6 +1037,7 @@ private: Token _make_token(TokenType p_type, const StringName &p_text = StringName()); Token _get_token(); bool _lookup_next(Token &r_tk); + Token _peek(); ShaderNode *shader = nullptr; diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 70b585d683..9fc67b04b1 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -3116,6 +3116,8 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("instance_set_layer_mask", "instance", "mask"), &RenderingServer::instance_set_layer_mask); ClassDB::bind_method(D_METHOD("instance_set_pivot_data", "instance", "sorting_offset", "use_aabb_center"), &RenderingServer::instance_set_pivot_data); ClassDB::bind_method(D_METHOD("instance_set_transform", "instance", "transform"), &RenderingServer::instance_set_transform); + ClassDB::bind_method(D_METHOD("instance_set_interpolated", "instance", "interpolated"), &RenderingServer::instance_set_interpolated); + ClassDB::bind_method(D_METHOD("instance_reset_physics_interpolation", "instance"), &RenderingServer::instance_reset_physics_interpolation); ClassDB::bind_method(D_METHOD("instance_attach_object_instance_id", "instance", "id"), &RenderingServer::instance_attach_object_instance_id); ClassDB::bind_method(D_METHOD("instance_set_blend_shape_weight", "instance", "shape", "weight"), &RenderingServer::instance_set_blend_shape_weight); ClassDB::bind_method(D_METHOD("instance_set_surface_override_material", "instance", "surface", "material"), &RenderingServer::instance_set_surface_override_material); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 693c822488..86d9d5dbd3 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -924,6 +924,7 @@ public: virtual void viewport_set_canvas_transform(RID p_viewport, RID p_canvas, const Transform2D &p_offset) = 0; virtual void viewport_set_transparent_background(RID p_viewport, bool p_enabled) = 0; virtual void viewport_set_use_hdr_2d(RID p_viewport, bool p_use_hdr) = 0; + virtual bool viewport_is_using_hdr_2d(RID p_viewport) const = 0; virtual void viewport_set_snap_2d_transforms_to_pixel(RID p_viewport, bool p_enabled) = 0; virtual void viewport_set_snap_2d_vertices_to_pixel(RID p_viewport, bool p_enabled) = 0; @@ -1334,6 +1335,8 @@ public: virtual void instance_set_layer_mask(RID p_instance, uint32_t p_mask) = 0; virtual void instance_set_pivot_data(RID p_instance, float p_sorting_offset, bool p_use_aabb_center) = 0; virtual void instance_set_transform(RID p_instance, const Transform3D &p_transform) = 0; + virtual void instance_set_interpolated(RID p_instance, bool p_interpolated) = 0; + virtual void instance_reset_physics_interpolation(RID p_instance) = 0; virtual void instance_attach_object_instance_id(RID p_instance, ObjectID p_id) = 0; virtual void instance_set_blend_shape_weight(RID p_instance, int p_shape, float p_weight) = 0; virtual void instance_set_surface_override_material(RID p_instance, int p_surface, RID p_material) = 0; @@ -1647,7 +1650,6 @@ public: /* INTERPOLATION */ - virtual void tick() = 0; virtual void set_physics_interpolation_enabled(bool p_enabled) = 0; /* EVENT QUEUING */ @@ -1659,6 +1661,8 @@ public: virtual bool has_changed() const = 0; virtual void init(); virtual void finish() = 0; + virtual void tick() = 0; + virtual void pre_draw(bool p_will_draw) = 0; /* STATUS INFORMATION */ diff --git a/servers/text/text_server_dummy.h b/servers/text/text_server_dummy.h index a5ab444f55..1a945ac221 100644 --- a/servers/text/text_server_dummy.h +++ b/servers/text/text_server_dummy.h @@ -88,6 +88,7 @@ public: virtual int64_t font_get_char_from_glyph_index(const RID &p_font_rid, int64_t p_size, int64_t p_glyph_index) const override { return 0; } virtual bool font_has_char(const RID &p_font_rid, int64_t p_char) const override { return false; } virtual String font_get_supported_chars(const RID &p_font_rid) const override { return String(); } + virtual PackedInt32Array font_get_supported_glyphs(const RID &p_font_rid) const override { return PackedInt32Array(); }; virtual void font_draw_glyph(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const override {} virtual void font_draw_glyph_outline(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const override {} diff --git a/servers/text/text_server_extension.cpp b/servers/text/text_server_extension.cpp index 509d49a1e4..d387c8ff7e 100644 --- a/servers/text/text_server_extension.cpp +++ b/servers/text/text_server_extension.cpp @@ -196,6 +196,7 @@ void TextServerExtension::_bind_methods() { GDVIRTUAL_BIND(_font_has_char, "font_rid", "char"); GDVIRTUAL_BIND(_font_get_supported_chars, "font_rid"); + GDVIRTUAL_BIND(_font_get_supported_glyphs, "font_rid"); GDVIRTUAL_BIND(_font_render_range, "font_rid", "size", "start", "end"); GDVIRTUAL_BIND(_font_render_glyph, "font_rid", "size", "index"); @@ -927,6 +928,12 @@ String TextServerExtension::font_get_supported_chars(const RID &p_font_rid) cons return ret; } +PackedInt32Array TextServerExtension::font_get_supported_glyphs(const RID &p_font_rid) const { + PackedInt32Array ret; + GDVIRTUAL_REQUIRED_CALL(_font_get_supported_glyphs, p_font_rid, ret); + return ret; +} + void TextServerExtension::font_render_range(const RID &p_font_rid, const Vector2i &p_size, int64_t p_start, int64_t p_end) { GDVIRTUAL_CALL(_font_render_range, p_font_rid, p_size, p_start, p_end); } diff --git a/servers/text/text_server_extension.h b/servers/text/text_server_extension.h index 16a03b6592..52654c010c 100644 --- a/servers/text/text_server_extension.h +++ b/servers/text/text_server_extension.h @@ -323,8 +323,10 @@ public: virtual bool font_has_char(const RID &p_font_rid, int64_t p_char) const override; virtual String font_get_supported_chars(const RID &p_font_rid) const override; + virtual PackedInt32Array font_get_supported_glyphs(const RID &p_font_rid) const override; GDVIRTUAL2RC(bool, _font_has_char, RID, int64_t); GDVIRTUAL1RC(String, _font_get_supported_chars, RID); + GDVIRTUAL1RC(PackedInt32Array, _font_get_supported_glyphs, RID); virtual void font_render_range(const RID &p_font, const Vector2i &p_size, int64_t p_start, int64_t p_end) override; virtual void font_render_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_index) override; diff --git a/servers/text_server.cpp b/servers/text_server.cpp index e7a1511064..f391c79514 100644 --- a/servers/text_server.cpp +++ b/servers/text_server.cpp @@ -352,6 +352,7 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("font_has_char", "font_rid", "char"), &TextServer::font_has_char); ClassDB::bind_method(D_METHOD("font_get_supported_chars", "font_rid"), &TextServer::font_get_supported_chars); + ClassDB::bind_method(D_METHOD("font_get_supported_glyphs", "font_rid"), &TextServer::font_get_supported_glyphs); ClassDB::bind_method(D_METHOD("font_render_range", "font_rid", "size", "start", "end"), &TextServer::font_render_range); ClassDB::bind_method(D_METHOD("font_render_glyph", "font_rid", "size", "index"), &TextServer::font_render_glyph); @@ -1563,7 +1564,7 @@ int64_t TextServer::shaped_text_prev_grapheme_pos(const RID &p_shaped, int64_t p int64_t TextServer::shaped_text_prev_character_pos(const RID &p_shaped, int64_t p_pos) const { const PackedInt32Array &chars = shaped_text_get_character_breaks(p_shaped); - int64_t prev = 0; + int64_t prev = shaped_text_get_range(p_shaped).x; for (const int32_t &E : chars) { if (E >= p_pos) { return prev; @@ -1575,7 +1576,7 @@ int64_t TextServer::shaped_text_prev_character_pos(const RID &p_shaped, int64_t int64_t TextServer::shaped_text_next_character_pos(const RID &p_shaped, int64_t p_pos) const { const PackedInt32Array &chars = shaped_text_get_character_breaks(p_shaped); - int64_t prev = 0; + int64_t prev = shaped_text_get_range(p_shaped).x; for (const int32_t &E : chars) { if (E > p_pos) { return E; @@ -1587,7 +1588,7 @@ int64_t TextServer::shaped_text_next_character_pos(const RID &p_shaped, int64_t int64_t TextServer::shaped_text_closest_character_pos(const RID &p_shaped, int64_t p_pos) const { const PackedInt32Array &chars = shaped_text_get_character_breaks(p_shaped); - int64_t prev = 0; + int64_t prev = shaped_text_get_range(p_shaped).x; for (const int32_t &E : chars) { if (E == p_pos) { return E; diff --git a/servers/text_server.h b/servers/text_server.h index a77953e6f2..ba3fdaa35e 100644 --- a/servers/text_server.h +++ b/servers/text_server.h @@ -396,6 +396,7 @@ public: virtual bool font_has_char(const RID &p_font_rid, int64_t p_char) const = 0; virtual String font_get_supported_chars(const RID &p_font_rid) const = 0; + virtual PackedInt32Array font_get_supported_glyphs(const RID &p_font_rid) const = 0; virtual void font_render_range(const RID &p_font, const Vector2i &p_size, int64_t p_start, int64_t p_end) = 0; virtual void font_render_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_index) = 0; diff --git a/tests/core/config/test_project_settings.h b/tests/core/config/test_project_settings.h index 8fc2489f8b..0e1058a626 100644 --- a/tests/core/config/test_project_settings.h +++ b/tests/core/config/test_project_settings.h @@ -126,10 +126,9 @@ TEST_CASE("[ProjectSettings] localize_path") { CHECK_EQ(ProjectSettings::get_singleton()->localize_path("path\\.\\filename"), "res://path/filename"); #endif - // FIXME?: These checks pass, but that doesn't seems correct - CHECK_EQ(ProjectSettings::get_singleton()->localize_path("../filename"), "res://filename"); - CHECK_EQ(ProjectSettings::get_singleton()->localize_path("../path/filename"), "res://path/filename"); - CHECK_EQ(ProjectSettings::get_singleton()->localize_path("..\\path\\filename"), "res://path/filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("../filename"), "../filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("../path/filename"), "../path/filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("..\\path\\filename"), "../path/filename"); CHECK_EQ(ProjectSettings::get_singleton()->localize_path("/testroot/filename"), "/testroot/filename"); CHECK_EQ(ProjectSettings::get_singleton()->localize_path("/testroot/path/filename"), "/testroot/path/filename"); diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h index cf57183a02..933eeff524 100644 --- a/tests/core/string/test_string.h +++ b/tests/core/string/test_string.h @@ -638,64 +638,90 @@ TEST_CASE("[String] Ends with") { } TEST_CASE("[String] Splitting") { - String s = "Mars,Jupiter,Saturn,Uranus"; - const char *slices_l[3] = { "Mars", "Jupiter", "Saturn,Uranus" }; - MULTICHECK_SPLIT(s, split, ",", true, 2, slices_l, 3); - - const char *slices_r[3] = { "Mars,Jupiter", "Saturn", "Uranus" }; - MULTICHECK_SPLIT(s, rsplit, ",", true, 2, slices_r, 3); - - s = "test"; - const char *slices_3[4] = { "t", "e", "s", "t" }; - MULTICHECK_SPLIT(s, split, "", true, 0, slices_3, 4); - - s = ""; - const char *slices_4[1] = { "" }; - MULTICHECK_SPLIT(s, split, "", true, 0, slices_4, 1); - MULTICHECK_SPLIT(s, split, "", false, 0, slices_4, 0); - - s = "Mars Jupiter Saturn Uranus"; - const char *slices_s[4] = { "Mars", "Jupiter", "Saturn", "Uranus" }; - Vector<String> l = s.split_spaces(); - for (int i = 0; i < l.size(); i++) { - CHECK(l[i] == slices_s[i]); + { + const String s = "Mars,Jupiter,Saturn,Uranus"; + + const char *slices_l[3] = { "Mars", "Jupiter", "Saturn,Uranus" }; + MULTICHECK_SPLIT(s, split, ",", true, 2, slices_l, 3); + + const char *slices_r[3] = { "Mars,Jupiter", "Saturn", "Uranus" }; + MULTICHECK_SPLIT(s, rsplit, ",", true, 2, slices_r, 3); } - s = "1.2;2.3 4.5"; - const double slices_d[3] = { 1.2, 2.3, 4.5 }; + { + const String s = "test"; + const char *slices[4] = { "t", "e", "s", "t" }; + MULTICHECK_SPLIT(s, split, "", true, 0, slices, 4); + } - Vector<double> d_arr; - d_arr = s.split_floats(";"); - CHECK(d_arr.size() == 2); - for (int i = 0; i < d_arr.size(); i++) { - CHECK(ABS(d_arr[i] - slices_d[i]) <= 0.00001); + { + const String s = ""; + const char *slices[1] = { "" }; + MULTICHECK_SPLIT(s, split, "", true, 0, slices, 1); + MULTICHECK_SPLIT(s, split, "", false, 0, slices, 0); } - Vector<String> keys; - keys.push_back(";"); - keys.push_back(" "); - - Vector<float> f_arr; - f_arr = s.split_floats_mk(keys); - CHECK(f_arr.size() == 3); - for (int i = 0; i < f_arr.size(); i++) { - CHECK(ABS(f_arr[i] - slices_d[i]) <= 0.00001); + { + const String s = "Mars Jupiter Saturn Uranus"; + const char *slices[4] = { "Mars", "Jupiter", "Saturn", "Uranus" }; + Vector<String> l = s.split_spaces(); + for (int i = 0; i < l.size(); i++) { + CHECK(l[i] == slices[i]); + } } - s = "1;2 4"; - const int slices_i[3] = { 1, 2, 4 }; + { + const String s = "1.2;2.3 4.5"; + const double slices[3] = { 1.2, 2.3, 4.5 }; + + const Vector<double> d_arr = s.split_floats(";"); + CHECK(d_arr.size() == 2); + for (int i = 0; i < d_arr.size(); i++) { + CHECK(ABS(d_arr[i] - slices[i]) <= 0.00001); + } - Vector<int> ii; - ii = s.split_ints(";"); - CHECK(ii.size() == 2); - for (int i = 0; i < ii.size(); i++) { - CHECK(ii[i] == slices_i[i]); + const Vector<String> keys = { ";", " " }; + const Vector<float> f_arr = s.split_floats_mk(keys); + CHECK(f_arr.size() == 3); + for (int i = 0; i < f_arr.size(); i++) { + CHECK(ABS(f_arr[i] - slices[i]) <= 0.00001); + } } - ii = s.split_ints_mk(keys); - CHECK(ii.size() == 3); - for (int i = 0; i < ii.size(); i++) { - CHECK(ii[i] == slices_i[i]); + { + const String s = " -2.0 5"; + const double slices[10] = { 0, -2, 0, 0, 0, 0, 0, 0, 0, 5 }; + + const Vector<double> arr = s.split_floats(" "); + CHECK(arr.size() == 10); + for (int i = 0; i < arr.size(); i++) { + CHECK(ABS(arr[i] - slices[i]) <= 0.00001); + } + + const Vector<String> keys = { ";", " " }; + const Vector<float> mk = s.split_floats_mk(keys); + CHECK(mk.size() == 10); + for (int i = 0; i < mk.size(); i++) { + CHECK(mk[i] == slices[i]); + } + } + + { + const String s = "1;2 4"; + const int slices[3] = { 1, 2, 4 }; + + const Vector<int> arr = s.split_ints(";"); + CHECK(arr.size() == 2); + for (int i = 0; i < arr.size(); i++) { + CHECK(arr[i] == slices[i]); + } + + const Vector<String> keys = { ";", " " }; + const Vector<int> mk = s.split_ints_mk(keys); + CHECK(mk.size() == 3); + for (int i = 0; i < mk.size(); i++) { + CHECK(mk[i] == slices[i]); + } } } @@ -1594,7 +1620,7 @@ TEST_CASE("[String] Path functions") { static const char *base_name[8] = { "C:\\Godot\\project\\test", "/Godot/project/test", "../Godot/project/test", "Godot\\test", "C:\\test", "res://test", "user://test", "/" }; static const char *ext[8] = { "tscn", "xscn", "scn", "doc", "", "", "", "test" }; static const char *file[8] = { "test.tscn", "test.xscn", "test.scn", "test.doc", "test.", "test", "test", ".test" }; - static const char *simplified[8] = { "C:/Godot/project/test.tscn", "/Godot/project/test.xscn", "Godot/project/test.scn", "Godot/test.doc", "C:/test.", "res://test", "user://test", "/.test" }; + static const char *simplified[8] = { "C:/Godot/project/test.tscn", "/Godot/project/test.xscn", "../Godot/project/test.scn", "Godot/test.doc", "C:/test.", "res://test", "user://test", "/.test" }; static const bool abs[8] = { true, true, false, false, true, true, true, true }; for (int i = 0; i < 8; i++) { diff --git a/tests/core/string/test_translation.h b/tests/core/string/test_translation.h index acdd851b29..7c389191e3 100644 --- a/tests/core/string/test_translation.h +++ b/tests/core/string/test_translation.h @@ -34,6 +34,7 @@ #include "core/string/optimized_translation.h" #include "core/string/translation.h" #include "core/string/translation_po.h" +#include "core/string/translation_server.h" #ifdef TOOLS_ENABLED #include "editor/import/resource_importer_csv_translation.h" diff --git a/tests/core/string/test_translation_server.h b/tests/core/string/test_translation_server.h index 2c20574309..ac1599f2e8 100644 --- a/tests/core/string/test_translation_server.h +++ b/tests/core/string/test_translation_server.h @@ -31,7 +31,7 @@ #ifndef TEST_TRANSLATION_SERVER_H #define TEST_TRANSLATION_SERVER_H -#include "core/string/translation.h" +#include "core/string/translation_server.h" #include "tests/test_macros.h" diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h index b2d9f5100e..69e27fe7a0 100644 --- a/tests/scene/test_text_edit.h +++ b/tests/scene/test_text_edit.h @@ -1763,6 +1763,28 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { CHECK_FALSE(text_edit->has_selection()); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 4); + + // Wrapped lines. + text_edit->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY); + text_edit->set_text("this is some text\nfor selection"); + text_edit->set_size(Size2(110, 100)); + MessageQueue::get_singleton()->flush(); + + // Line 0 wraps: 'this is ', 'some text'. + // Line 1 wraps: 'for ', 'selection'. + CHECK(text_edit->is_line_wrapped(0)); + + // Select to the first character of a wrapped line. + SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(0, 11).get_center(), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); + SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(0, 8).get_center(), MouseButtonMask::LEFT, Key::NONE); + CHECK(text_edit->has_selection()); + CHECK(text_edit->get_selected_text() == "so"); + CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_POINTER); + CHECK(text_edit->get_selection_origin_line() == 0); + CHECK(text_edit->get_selection_origin_column() == 10); + CHECK(text_edit->get_caret_line() == 0); + CHECK(text_edit->get_caret_column() == 8); + CHECK(text_edit->is_dragging_cursor()); } SUBCASE("[TextEdit] mouse word select") { @@ -5713,6 +5735,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { CHECK(text_edit->get_caret_count() == 2); MessageQueue::get_singleton()->flush(); + // Lines 0 and 4 are wrapped into 2 parts: 'this is ' and 'some'. CHECK(text_edit->is_line_wrapped(0)); SIGNAL_DISCARD("text_set"); SIGNAL_DISCARD("text_changed"); @@ -5762,9 +5785,9 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); - text_edit->set_caret_column(12, false); // Normal up over wrapped line to line 0. + text_edit->set_caret_column(12, false); SEND_GUI_ACTION("ui_text_caret_up"); CHECK(text_edit->get_viewport()->is_input_handled()); CHECK(text_edit->get_caret_line() == 0); @@ -5777,6 +5800,23 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); + + // Normal up from column 0 to a wrapped line. + text_edit->remove_secondary_carets(); + text_edit->set_caret_line(5); + text_edit->set_caret_column(0); + SEND_GUI_ACTION("ui_text_caret_up"); + CHECK(text_edit->get_viewport()->is_input_handled()); + CHECK(text_edit->get_caret_line() == 4); + CHECK(text_edit->get_caret_column() == 8); + CHECK_FALSE(text_edit->has_selection(0)); + + // Normal up to column 0 of a wrapped line. + SEND_GUI_ACTION("ui_text_caret_up"); + CHECK(text_edit->get_viewport()->is_input_handled()); + CHECK(text_edit->get_caret_line() == 4); + CHECK(text_edit->get_caret_column() == 0); + CHECK_FALSE(text_edit->has_selection(0)); } SUBCASE("[TextEdit] ui_text_caret_down") { @@ -5792,6 +5832,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { MessageQueue::get_singleton()->flush(); + // Lines 3 and 7 are wrapped into 2 parts: 'this is ' and 'some'. CHECK(text_edit->is_line_wrapped(3)); SIGNAL_DISCARD("text_set"); SIGNAL_DISCARD("text_changed"); @@ -5841,9 +5882,9 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); - text_edit->set_caret_column(7, false); // Normal down over wrapped line to last wrapped line. + text_edit->set_caret_column(7, false); SEND_GUI_ACTION("ui_text_caret_down"); CHECK(text_edit->get_viewport()->is_input_handled()); CHECK(text_edit->get_caret_line() == 3); @@ -5856,6 +5897,23 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); + + // Normal down to column 0 of a wrapped line. + text_edit->remove_secondary_carets(); + text_edit->set_caret_line(3); + text_edit->set_caret_column(0); + SEND_GUI_ACTION("ui_text_caret_down"); + CHECK(text_edit->get_viewport()->is_input_handled()); + CHECK(text_edit->get_caret_line() == 3); + CHECK(text_edit->get_caret_column() == 8); + CHECK_FALSE(text_edit->has_selection(0)); + + // Normal down out of visual column 0 of a wrapped line moves to start of next line. + SEND_GUI_ACTION("ui_text_caret_down"); + CHECK(text_edit->get_viewport()->is_input_handled()); + CHECK(text_edit->get_caret_line() == 4); + CHECK(text_edit->get_caret_column() == 0); + CHECK_FALSE(text_edit->has_selection(0)); } SUBCASE("[TextEdit] ui_text_caret_document_start") { @@ -7162,7 +7220,7 @@ TEST_CASE("[SceneTree][TextEdit] multicaret") { CHECK(text_edit->get_caret_line(0) == 2); CHECK(text_edit->get_caret_column(0) == 5); CHECK(text_edit->get_caret_line(1) == 2); - CHECK(text_edit->get_caret_column(1) == 10); + CHECK(text_edit->get_caret_column(1) == 6); // Cannot add caret below from last line last line wrap. text_edit->add_caret_at_carets(true); @@ -7171,7 +7229,7 @@ TEST_CASE("[SceneTree][TextEdit] multicaret") { CHECK(text_edit->get_caret_line(0) == 2); CHECK(text_edit->get_caret_column(0) == 5); CHECK(text_edit->get_caret_line(1) == 2); - CHECK(text_edit->get_caret_column(1) == 10); + CHECK(text_edit->get_caret_column(1) == 6); // Add caret above from not first line wrap. text_edit->remove_secondary_carets(); diff --git a/thirdparty/README.md b/thirdparty/README.md index 9a56f6baa4..bc2964c392 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -748,7 +748,7 @@ with the provided patch. ## openxr - Upstream: https://github.com/KhronosGroup/OpenXR-SDK -- Version: 1.0.34 (288d3a7ebc1ad959f62d51da75baa3d27438c499, 2024) +- Version: 1.1.38 (f90488c4fb1537f4256d09d4a4d3ad5543ebaf24, 2024) - License: Apache 2.0 Files extracted from upstream source: @@ -845,7 +845,7 @@ Files extracted from upstream source: Some downstream changes have been made and are identified by `// -- GODOT begin --` and `// -- GODOT end --` comments. They can be reapplied using the patches included in the `patches` -folder. +folder, in order. ## squish diff --git a/thirdparty/openxr/include/openxr/openxr.h b/thirdparty/openxr/include/openxr/openxr.h index c0c826b981..9ac66d8c69 100644 --- a/thirdparty/openxr/include/openxr/openxr.h +++ b/thirdparty/openxr/include/openxr/openxr.h @@ -19,13 +19,17 @@ extern "C" { +// XR_VERSION_1_0 is a preprocessor guard. Do not pass it to API calls. #define XR_VERSION_1_0 1 #include "openxr_platform_defines.h" #define XR_MAKE_VERSION(major, minor, patch) \ ((((major) & 0xffffULL) << 48) | (((minor) & 0xffffULL) << 32) | ((patch) & 0xffffffffULL)) // OpenXR current version number. -#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 0, 34) +#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 1, 38) + +// OpenXR 1.0 version number +#define XR_API_VERSION_1_0 XR_MAKE_VERSION(1, 0, XR_VERSION_PATCH(XR_CURRENT_API_VERSION)) #define XR_VERSION_MAJOR(version) (uint16_t)(((uint64_t)(version) >> 48)& 0xffffULL) #define XR_VERSION_MINOR(version) (uint16_t)(((uint64_t)(version) >> 32) & 0xffffULL) @@ -41,7 +45,6 @@ extern "C" { #define XR_NULL_HANDLE 0 #endif #endif - #define XR_NULL_SYSTEM_ID 0 @@ -96,13 +99,20 @@ extern "C" { #define XR_DEFINE_HANDLE(object) typedef uint64_t object; #endif #endif - + + +#if !defined(XR_DEFINE_OPAQUE_64) + #if (XR_PTR_SIZE == 8) + #define XR_DEFINE_OPAQUE_64(object) typedef struct object##_T* object; + #else + #define XR_DEFINE_OPAQUE_64(object) typedef uint64_t object; + #endif +#endif #if !defined(XR_DEFINE_ATOM) #define XR_DEFINE_ATOM(object) typedef uint64_t object; #endif - typedef uint64_t XrVersion; typedef uint64_t XrFlags64; @@ -190,6 +200,8 @@ typedef enum XrResult { XR_ERROR_LOCALIZED_NAME_INVALID = -49, XR_ERROR_GRAPHICS_REQUIREMENTS_CALL_MISSING = -50, XR_ERROR_RUNTIME_UNAVAILABLE = -51, + XR_ERROR_EXTENSION_DEPENDENCY_NOT_ENABLED = -1000710001, + XR_ERROR_PERMISSION_INSUFFICIENT = -1000710000, XR_ERROR_ANDROID_THREAD_SETTINGS_ID_INVALID_KHR = -1000003000, XR_ERROR_ANDROID_THREAD_SETTINGS_FAILURE_KHR = -1000003001, XR_ERROR_CREATE_SPATIAL_ANCHOR_FAILED_MSFT = -1000039001, @@ -239,10 +251,15 @@ typedef enum XrResult { XR_ERROR_SPACE_NETWORK_REQUEST_FAILED_FB = -1000169003, XR_ERROR_SPACE_CLOUD_STORAGE_DISABLED_FB = -1000169004, XR_ERROR_PASSTHROUGH_COLOR_LUT_BUFFER_SIZE_MISMATCH_META = -1000266000, + XR_ENVIRONMENT_DEPTH_NOT_AVAILABLE_META = 1000291000, XR_ERROR_HINT_ALREADY_SET_QCOM = -1000306000, XR_ERROR_NOT_AN_ANCHOR_HTC = -1000319000, XR_ERROR_SPACE_NOT_LOCATABLE_EXT = -1000429000, XR_ERROR_PLANE_DETECTION_PERMISSION_DENIED_EXT = -1000429001, + XR_ERROR_FUTURE_PENDING_EXT = -1000469001, + XR_ERROR_FUTURE_INVALID_EXT = -1000469002, + XR_ERROR_EXTENSION_DEPENDENCY_NOT_ENABLED_KHR = XR_ERROR_EXTENSION_DEPENDENCY_NOT_ENABLED, + XR_ERROR_PERMISSION_INSUFFICIENT_KHR = XR_ERROR_PERMISSION_INSUFFICIENT, XR_RESULT_MAX_ENUM = 0x7FFFFFFF } XrResult; @@ -297,6 +314,9 @@ typedef enum XrStructureType { XR_TYPE_ACTIONS_SYNC_INFO = 61, XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO = 62, XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO = 63, + XR_TYPE_SPACES_LOCATE_INFO = 1000471000, + XR_TYPE_SPACE_LOCATIONS = 1000471001, + XR_TYPE_SPACE_VELOCITIES = 1000471002, XR_TYPE_COMPOSITION_LAYER_CUBE_KHR = 1000006000, XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR = 1000008000, XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR = 1000010000, @@ -562,6 +582,14 @@ typedef enum XrStructureType { XR_TYPE_FACE_TRACKER_CREATE_INFO2_FB = 1000287014, XR_TYPE_FACE_EXPRESSION_INFO2_FB = 1000287015, XR_TYPE_FACE_EXPRESSION_WEIGHTS2_FB = 1000287016, + XR_TYPE_ENVIRONMENT_DEPTH_PROVIDER_CREATE_INFO_META = 1000291000, + XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_CREATE_INFO_META = 1000291001, + XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_STATE_META = 1000291002, + XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_ACQUIRE_INFO_META = 1000291003, + XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_VIEW_META = 1000291004, + XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_META = 1000291005, + XR_TYPE_ENVIRONMENT_DEPTH_HAND_REMOVAL_SET_INFO_META = 1000291006, + XR_TYPE_SYSTEM_ENVIRONMENT_DEPTH_PROPERTIES_META = 1000291007, XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC = 1000317001, XR_TYPE_PASSTHROUGH_COLOR_HTC = 1000317002, XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC = 1000317003, @@ -583,12 +611,19 @@ typedef enum XrStructureType { XR_TYPE_PLANE_DETECTOR_LOCATION_EXT = 1000429005, XR_TYPE_PLANE_DETECTOR_POLYGON_BUFFER_EXT = 1000429006, XR_TYPE_SYSTEM_PLANE_DETECTION_PROPERTIES_EXT = 1000429007, + XR_TYPE_FUTURE_CANCEL_INFO_EXT = 1000469000, + XR_TYPE_FUTURE_POLL_INFO_EXT = 1000469001, + XR_TYPE_FUTURE_COMPLETION_EXT = 1000469002, + XR_TYPE_FUTURE_POLL_RESULT_EXT = 1000469003, XR_TYPE_EVENT_DATA_USER_PRESENCE_CHANGED_EXT = 1000470000, XR_TYPE_SYSTEM_USER_PRESENCE_PROPERTIES_EXT = 1000470001, XR_TYPE_GRAPHICS_BINDING_VULKAN2_KHR = XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR, XR_TYPE_SWAPCHAIN_IMAGE_VULKAN2_KHR = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR, XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN2_KHR = XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR, XR_TYPE_DEVICE_PCM_SAMPLE_RATE_GET_INFO_FB = XR_TYPE_DEVICE_PCM_SAMPLE_RATE_STATE_FB, + XR_TYPE_SPACES_LOCATE_INFO_KHR = XR_TYPE_SPACES_LOCATE_INFO, + XR_TYPE_SPACE_LOCATIONS_KHR = XR_TYPE_SPACE_LOCATIONS, + XR_TYPE_SPACE_VELOCITIES_KHR = XR_TYPE_SPACE_VELOCITIES, XR_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF } XrStructureType; @@ -601,8 +636,9 @@ typedef enum XrFormFactor { typedef enum XrViewConfigurationType { XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO = 1, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO = 2, - XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO = 1000037000, + XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO_WITH_FOVEATED_INSET = 1000037000, XR_VIEW_CONFIGURATION_TYPE_SECONDARY_MONO_FIRST_PERSON_OBSERVER_MSFT = 1000054000, + XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO_WITH_FOVEATED_INSET, XR_VIEW_CONFIGURATION_TYPE_MAX_ENUM = 0x7FFFFFFF } XrViewConfigurationType; @@ -617,10 +653,11 @@ typedef enum XrReferenceSpaceType { XR_REFERENCE_SPACE_TYPE_VIEW = 1, XR_REFERENCE_SPACE_TYPE_LOCAL = 2, XR_REFERENCE_SPACE_TYPE_STAGE = 3, + XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR = 1000426000, XR_REFERENCE_SPACE_TYPE_UNBOUNDED_MSFT = 1000038000, XR_REFERENCE_SPACE_TYPE_COMBINED_EYE_VARJO = 1000121000, XR_REFERENCE_SPACE_TYPE_LOCALIZATION_MAP_ML = 1000139000, - XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT = 1000426000, + XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT = XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR, XR_REFERENCE_SPACE_TYPE_MAX_ENUM = 0x7FFFFFFF } XrReferenceSpaceType; @@ -683,6 +720,8 @@ typedef enum XrObjectType { XR_OBJECT_TYPE_SPACE_USER_FB = 1000241000, XR_OBJECT_TYPE_PASSTHROUGH_COLOR_LUT_META = 1000266000, XR_OBJECT_TYPE_FACE_TRACKER2_FB = 1000287012, + XR_OBJECT_TYPE_ENVIRONMENT_DEPTH_PROVIDER_META = 1000291000, + XR_OBJECT_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_META = 1000291001, XR_OBJECT_TYPE_PASSTHROUGH_HTC = 1000317000, XR_OBJECT_TYPE_PLANE_DETECTOR_EXT = 1000429000, XR_OBJECT_TYPE_MAX_ENUM = 0x7FFFFFFF @@ -734,6 +773,7 @@ typedef XrFlags64 XrCompositionLayerFlags; static const XrCompositionLayerFlags XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT = 0x00000001; static const XrCompositionLayerFlags XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT = 0x00000002; static const XrCompositionLayerFlags XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT = 0x00000004; +static const XrCompositionLayerFlags XR_COMPOSITION_LAYER_INVERTED_ALPHA_BIT_EXT = 0x00000008; typedef XrFlags64 XrViewStateFlags; @@ -1595,6 +1635,91 @@ XRAPI_ATTR XrResult XRAPI_CALL xrStopHapticFeedback( #endif /* !XR_NO_PROTOTYPES */ +// XR_VERSION_1_1 is a preprocessor guard. Do not pass it to API calls. +#define XR_VERSION_1_1 1 +// OpenXR 1.1 version number +#define XR_API_VERSION_1_1 XR_MAKE_VERSION(1, 1, XR_VERSION_PATCH(XR_CURRENT_API_VERSION)) + +#define XR_UUID_SIZE 16 +typedef struct XrColor3f { + float r; + float g; + float b; +} XrColor3f; + +typedef struct XrExtent3Df { + float width; + float height; + float depth; +} XrExtent3Df; + +typedef struct XrSpheref { + XrPosef center; + float radius; +} XrSpheref; + +typedef struct XrBoxf { + XrPosef center; + XrExtent3Df extents; +} XrBoxf; + +typedef struct XrFrustumf { + XrPosef pose; + XrFovf fov; + float nearZ; + float farZ; +} XrFrustumf; + +typedef struct XrUuid { + uint8_t data[XR_UUID_SIZE]; +} XrUuid; + +typedef struct XrSpacesLocateInfo { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpace baseSpace; + XrTime time; + uint32_t spaceCount; + const XrSpace* spaces; +} XrSpacesLocateInfo; + +typedef struct XrSpaceLocationData { + XrSpaceLocationFlags locationFlags; + XrPosef pose; +} XrSpaceLocationData; + +typedef struct XrSpaceLocations { + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t locationCount; + XrSpaceLocationData* locations; +} XrSpaceLocations; + +typedef struct XrSpaceVelocityData { + XrSpaceVelocityFlags velocityFlags; + XrVector3f linearVelocity; + XrVector3f angularVelocity; +} XrSpaceVelocityData; + +// XrSpaceVelocities extends XrSpaceLocations +typedef struct XrSpaceVelocities { + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t velocityCount; + XrSpaceVelocityData* velocities; +} XrSpaceVelocities; + +typedef XrResult (XRAPI_PTR *PFN_xrLocateSpaces)(XrSession session, const XrSpacesLocateInfo* locateInfo, XrSpaceLocations* spaceLocations); + +#ifndef XR_NO_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrLocateSpaces( + XrSession session, + const XrSpacesLocateInfo* locateInfo, + XrSpaceLocations* spaceLocations); +#endif /* !XR_NO_PROTOTYPES */ + + +// XR_KHR_composition_layer_cube is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_composition_layer_cube 1 #define XR_KHR_composition_layer_cube_SPEC_VERSION 8 #define XR_KHR_COMPOSITION_LAYER_CUBE_EXTENSION_NAME "XR_KHR_composition_layer_cube" @@ -1611,6 +1736,7 @@ typedef struct XrCompositionLayerCubeKHR { +// XR_KHR_composition_layer_depth is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_composition_layer_depth 1 #define XR_KHR_composition_layer_depth_SPEC_VERSION 6 #define XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME "XR_KHR_composition_layer_depth" @@ -1627,6 +1753,7 @@ typedef struct XrCompositionLayerDepthInfoKHR { +// XR_KHR_composition_layer_cylinder is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_composition_layer_cylinder 1 #define XR_KHR_composition_layer_cylinder_SPEC_VERSION 4 #define XR_KHR_COMPOSITION_LAYER_CYLINDER_EXTENSION_NAME "XR_KHR_composition_layer_cylinder" @@ -1645,6 +1772,7 @@ typedef struct XrCompositionLayerCylinderKHR { +// XR_KHR_composition_layer_equirect is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_composition_layer_equirect 1 #define XR_KHR_composition_layer_equirect_SPEC_VERSION 3 #define XR_KHR_COMPOSITION_LAYER_EQUIRECT_EXTENSION_NAME "XR_KHR_composition_layer_equirect" @@ -1663,6 +1791,7 @@ typedef struct XrCompositionLayerEquirectKHR { +// XR_KHR_visibility_mask is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_visibility_mask 1 #define XR_KHR_visibility_mask_SPEC_VERSION 2 #define XR_KHR_VISIBILITY_MASK_EXTENSION_NAME "XR_KHR_visibility_mask" @@ -1706,6 +1835,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetVisibilityMaskKHR( #endif /* !XR_NO_PROTOTYPES */ +// XR_KHR_composition_layer_color_scale_bias is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_composition_layer_color_scale_bias 1 #define XR_KHR_composition_layer_color_scale_bias_SPEC_VERSION 5 #define XR_KHR_COMPOSITION_LAYER_COLOR_SCALE_BIAS_EXTENSION_NAME "XR_KHR_composition_layer_color_scale_bias" @@ -1719,6 +1849,7 @@ typedef struct XrCompositionLayerColorScaleBiasKHR { +// XR_KHR_loader_init is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_loader_init 1 #define XR_KHR_loader_init_SPEC_VERSION 2 #define XR_KHR_LOADER_INIT_EXTENSION_NAME "XR_KHR_loader_init" @@ -1737,6 +1868,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrInitializeLoaderKHR( #endif /* !XR_NO_PROTOTYPES */ +// XR_KHR_composition_layer_equirect2 is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_composition_layer_equirect2 1 #define XR_KHR_composition_layer_equirect2_SPEC_VERSION 1 #define XR_KHR_COMPOSITION_LAYER_EQUIRECT2_EXTENSION_NAME "XR_KHR_composition_layer_equirect2" @@ -1756,6 +1888,7 @@ typedef struct XrCompositionLayerEquirect2KHR { +// XR_KHR_binding_modification is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_binding_modification 1 #define XR_KHR_binding_modification_SPEC_VERSION 1 #define XR_KHR_BINDING_MODIFICATION_EXTENSION_NAME "XR_KHR_binding_modification" @@ -1774,11 +1907,55 @@ typedef struct XrBindingModificationsKHR { +// XR_KHR_swapchain_usage_input_attachment_bit is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_swapchain_usage_input_attachment_bit 1 #define XR_KHR_swapchain_usage_input_attachment_bit_SPEC_VERSION 3 #define XR_KHR_SWAPCHAIN_USAGE_INPUT_ATTACHMENT_BIT_EXTENSION_NAME "XR_KHR_swapchain_usage_input_attachment_bit" +// XR_KHR_locate_spaces is a preprocessor guard. Do not pass it to API calls. +#define XR_KHR_locate_spaces 1 +#define XR_KHR_locate_spaces_SPEC_VERSION 1 +#define XR_KHR_LOCATE_SPACES_EXTENSION_NAME "XR_KHR_locate_spaces" +typedef XrSpacesLocateInfo XrSpacesLocateInfoKHR; + +typedef XrSpaceLocationData XrSpaceLocationDataKHR; + +typedef XrSpaceLocations XrSpaceLocationsKHR; + +typedef XrSpaceVelocityData XrSpaceVelocityDataKHR; + +typedef XrSpaceVelocities XrSpaceVelocitiesKHR; + +typedef XrResult (XRAPI_PTR *PFN_xrLocateSpacesKHR)(XrSession session, const XrSpacesLocateInfo* locateInfo, XrSpaceLocations* spaceLocations); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrLocateSpacesKHR( + XrSession session, + const XrSpacesLocateInfo* locateInfo, + XrSpaceLocations* spaceLocations); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ + + +// XR_KHR_maintenance1 is a preprocessor guard. Do not pass it to API calls. +#define XR_KHR_maintenance1 1 +#define XR_KHR_maintenance1_SPEC_VERSION 1 +#define XR_KHR_MAINTENANCE1_EXTENSION_NAME "XR_KHR_maintenance1" +typedef XrColor3f XrColor3fKHR; + +typedef XrExtent3Df XrExtent3DfKHR; + +typedef XrSpheref XrSpherefKHR; + +typedef XrBoxf XrBoxfKHR; + +typedef XrFrustumf XrFrustumfKHR; + + + +// XR_EXT_performance_settings is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_performance_settings 1 #define XR_EXT_performance_settings_SPEC_VERSION 4 #define XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME "XR_EXT_performance_settings" @@ -1831,6 +2008,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrPerfSettingsSetPerformanceLevelEXT( #endif /* !XR_NO_PROTOTYPES */ +// XR_EXT_thermal_query is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_thermal_query 1 #define XR_EXT_thermal_query_SPEC_VERSION 2 #define XR_EXT_THERMAL_QUERY_EXTENSION_NAME "XR_EXT_thermal_query" @@ -1848,6 +2026,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrThermalGetTemperatureTrendEXT( #endif /* !XR_NO_PROTOTYPES */ +// XR_EXT_debug_utils is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_debug_utils 1 XR_DEFINE_HANDLE(XrDebugUtilsMessengerEXT) #define XR_EXT_debug_utils_SPEC_VERSION 5 @@ -1953,6 +2132,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSessionInsertDebugUtilsLabelEXT( #endif /* !XR_NO_PROTOTYPES */ +// XR_EXT_eye_gaze_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_eye_gaze_interaction 1 #define XR_EXT_eye_gaze_interaction_SPEC_VERSION 2 #define XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME "XR_EXT_eye_gaze_interaction" @@ -1972,6 +2152,7 @@ typedef struct XrEyeGazeSampleTimeEXT { +// XR_EXTX_overlay is a preprocessor guard. Do not pass it to API calls. #define XR_EXTX_overlay 1 #define XR_EXTX_overlay_SPEC_VERSION 5 #define XR_EXTX_OVERLAY_EXTENSION_NAME "XR_EXTX_overlay" @@ -2001,16 +2182,19 @@ typedef struct XrEventDataMainSessionVisibilityChangedEXTX { +// XR_VARJO_quad_views is a preprocessor guard. Do not pass it to API calls. #define XR_VARJO_quad_views 1 #define XR_VARJO_quad_views_SPEC_VERSION 1 #define XR_VARJO_QUAD_VIEWS_EXTENSION_NAME "XR_VARJO_quad_views" +// XR_MSFT_unbounded_reference_space is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_unbounded_reference_space 1 #define XR_MSFT_unbounded_reference_space_SPEC_VERSION 1 #define XR_MSFT_UNBOUNDED_REFERENCE_SPACE_EXTENSION_NAME "XR_MSFT_unbounded_reference_space" +// XR_MSFT_spatial_anchor is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_spatial_anchor 1 XR_DEFINE_HANDLE(XrSpatialAnchorMSFT) #define XR_MSFT_spatial_anchor_SPEC_VERSION 2 @@ -2052,6 +2236,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpatialAnchorMSFT( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_composition_layer_image_layout is a preprocessor guard. Do not pass it to API calls. #define XR_FB_composition_layer_image_layout 1 #define XR_FB_composition_layer_image_layout_SPEC_VERSION 1 #define XR_FB_COMPOSITION_LAYER_IMAGE_LAYOUT_EXTENSION_NAME "XR_FB_composition_layer_image_layout" @@ -2069,8 +2254,9 @@ typedef struct XrCompositionLayerImageLayoutFB { +// XR_FB_composition_layer_alpha_blend is a preprocessor guard. Do not pass it to API calls. #define XR_FB_composition_layer_alpha_blend 1 -#define XR_FB_composition_layer_alpha_blend_SPEC_VERSION 2 +#define XR_FB_composition_layer_alpha_blend_SPEC_VERSION 3 #define XR_FB_COMPOSITION_LAYER_ALPHA_BLEND_EXTENSION_NAME "XR_FB_composition_layer_alpha_blend" typedef enum XrBlendFactorFB { @@ -2094,16 +2280,19 @@ typedef struct XrCompositionLayerAlphaBlendFB { +// XR_MND_headless is a preprocessor guard. Do not pass it to API calls. #define XR_MND_headless 1 #define XR_MND_headless_SPEC_VERSION 2 #define XR_MND_HEADLESS_EXTENSION_NAME "XR_MND_headless" +// XR_OCULUS_android_session_state_enable is a preprocessor guard. Do not pass it to API calls. #define XR_OCULUS_android_session_state_enable 1 #define XR_OCULUS_android_session_state_enable_SPEC_VERSION 1 #define XR_OCULUS_ANDROID_SESSION_STATE_ENABLE_EXTENSION_NAME "XR_OCULUS_android_session_state_enable" +// XR_EXT_view_configuration_depth_range is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_view_configuration_depth_range 1 #define XR_EXT_view_configuration_depth_range_SPEC_VERSION 1 #define XR_EXT_VIEW_CONFIGURATION_DEPTH_RANGE_EXTENSION_NAME "XR_EXT_view_configuration_depth_range" @@ -2119,6 +2308,7 @@ typedef struct XrViewConfigurationDepthRangeEXT { +// XR_EXT_conformance_automation is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_conformance_automation 1 #define XR_EXT_conformance_automation_SPEC_VERSION 3 #define XR_EXT_CONFORMANCE_AUTOMATION_EXTENSION_NAME "XR_EXT_conformance_automation" @@ -2164,6 +2354,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetInputDeviceLocationEXT( #endif /* !XR_NO_PROTOTYPES */ +// XR_MSFT_spatial_graph_bridge is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_spatial_graph_bridge 1 XR_DEFINE_HANDLE(XrSpatialGraphNodeBindingMSFT) #define XR_GUID_SIZE_MSFT 16 @@ -2231,11 +2422,13 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialGraphNodeBindingPropertiesMSFT( #endif /* !XR_NO_PROTOTYPES */ +// XR_MSFT_hand_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_hand_interaction 1 #define XR_MSFT_hand_interaction_SPEC_VERSION 1 #define XR_MSFT_HAND_INTERACTION_EXTENSION_NAME "XR_MSFT_hand_interaction" +// XR_EXT_hand_tracking is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_hand_tracking 1 #define XR_HAND_JOINT_COUNT_EXT 26 @@ -2356,6 +2549,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrLocateHandJointsEXT( #endif /* !XR_NO_PROTOTYPES */ +// XR_MSFT_hand_tracking_mesh is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_hand_tracking_mesh 1 #define XR_MSFT_hand_tracking_mesh_SPEC_VERSION 4 #define XR_MSFT_HAND_TRACKING_MESH_EXTENSION_NAME "XR_MSFT_hand_tracking_mesh" @@ -2442,6 +2636,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrUpdateHandMeshMSFT( #endif /* !XR_NO_PROTOTYPES */ +// XR_MSFT_secondary_view_configuration is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_secondary_view_configuration 1 #define XR_MSFT_secondary_view_configuration_SPEC_VERSION 1 #define XR_MSFT_SECONDARY_VIEW_CONFIGURATION_EXTENSION_NAME "XR_MSFT_secondary_view_configuration" @@ -2494,11 +2689,13 @@ typedef struct XrSecondaryViewConfigurationSwapchainCreateInfoMSFT { +// XR_MSFT_first_person_observer is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_first_person_observer 1 #define XR_MSFT_first_person_observer_SPEC_VERSION 1 #define XR_MSFT_FIRST_PERSON_OBSERVER_EXTENSION_NAME "XR_MSFT_first_person_observer" +// XR_MSFT_controller_model is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_controller_model 1 #define XR_NULL_CONTROLLER_MODEL_KEY_MSFT 0 @@ -2574,11 +2771,13 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetControllerModelStateMSFT( #endif /* !XR_NO_PROTOTYPES */ +// XR_EXT_win32_appcontainer_compatible is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_win32_appcontainer_compatible 1 #define XR_EXT_win32_appcontainer_compatible_SPEC_VERSION 1 #define XR_EXT_WIN32_APPCONTAINER_COMPATIBLE_EXTENSION_NAME "XR_EXT_win32_appcontainer_compatible" +// XR_EPIC_view_configuration_fov is a preprocessor guard. Do not pass it to API calls. #define XR_EPIC_view_configuration_fov 1 #define XR_EPIC_view_configuration_fov_SPEC_VERSION 2 #define XR_EPIC_VIEW_CONFIGURATION_FOV_EXTENSION_NAME "XR_EPIC_view_configuration_fov" @@ -2592,6 +2791,7 @@ typedef struct XrViewConfigurationViewFovEPIC { +// XR_MSFT_composition_layer_reprojection is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_composition_layer_reprojection 1 #define XR_MSFT_composition_layer_reprojection_SPEC_VERSION 1 #define XR_MSFT_COMPOSITION_LAYER_REPROJECTION_EXTENSION_NAME "XR_MSFT_composition_layer_reprojection" @@ -2634,11 +2834,13 @@ XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateReprojectionModesMSFT( #endif /* !XR_NO_PROTOTYPES */ +// XR_HUAWEI_controller_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_HUAWEI_controller_interaction 1 #define XR_HUAWEI_controller_interaction_SPEC_VERSION 1 #define XR_HUAWEI_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_HUAWEI_controller_interaction" +// XR_FB_swapchain_update_state is a preprocessor guard. Do not pass it to API calls. #define XR_FB_swapchain_update_state 1 #define XR_FB_swapchain_update_state_SPEC_VERSION 3 #define XR_FB_SWAPCHAIN_UPDATE_STATE_EXTENSION_NAME "XR_FB_swapchain_update_state" @@ -2663,6 +2865,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSwapchainStateFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_composition_layer_secure_content is a preprocessor guard. Do not pass it to API calls. #define XR_FB_composition_layer_secure_content 1 #define XR_FB_composition_layer_secure_content_SPEC_VERSION 1 #define XR_FB_COMPOSITION_LAYER_SECURE_CONTENT_EXTENSION_NAME "XR_FB_composition_layer_secure_content" @@ -2681,6 +2884,7 @@ typedef struct XrCompositionLayerSecureContentFB { +// XR_FB_body_tracking is a preprocessor guard. Do not pass it to API calls. #define XR_FB_body_tracking 1 XR_DEFINE_HANDLE(XrBodyTrackerFB) #define XR_FB_body_tracking_SPEC_VERSION 1 @@ -2842,6 +3046,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetBodySkeletonFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_EXT_dpad_binding is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_dpad_binding 1 #define XR_EXT_dpad_binding_SPEC_VERSION 1 #define XR_EXT_DPAD_BINDING_EXTENSION_NAME "XR_EXT_dpad_binding" @@ -2861,6 +3066,7 @@ typedef struct XrInteractionProfileDpadBindingEXT { +// XR_VALVE_analog_threshold is a preprocessor guard. Do not pass it to API calls. #define XR_VALVE_analog_threshold 1 #define XR_VALVE_analog_threshold_SPEC_VERSION 2 #define XR_VALVE_ANALOG_THRESHOLD_EXTENSION_NAME "XR_VALVE_analog_threshold" @@ -2877,6 +3083,7 @@ typedef struct XrInteractionProfileAnalogThresholdVALVE { +// XR_EXT_hand_joints_motion_range is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_hand_joints_motion_range 1 #define XR_EXT_hand_joints_motion_range_SPEC_VERSION 1 #define XR_EXT_HAND_JOINTS_MOTION_RANGE_EXTENSION_NAME "XR_EXT_hand_joints_motion_range" @@ -2895,21 +3102,25 @@ typedef struct XrHandJointsMotionRangeInfoEXT { +// XR_EXT_samsung_odyssey_controller is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_samsung_odyssey_controller 1 #define XR_EXT_samsung_odyssey_controller_SPEC_VERSION 1 #define XR_EXT_SAMSUNG_ODYSSEY_CONTROLLER_EXTENSION_NAME "XR_EXT_samsung_odyssey_controller" +// XR_EXT_hp_mixed_reality_controller is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_hp_mixed_reality_controller 1 #define XR_EXT_hp_mixed_reality_controller_SPEC_VERSION 1 #define XR_EXT_HP_MIXED_REALITY_CONTROLLER_EXTENSION_NAME "XR_EXT_hp_mixed_reality_controller" +// XR_MND_swapchain_usage_input_attachment_bit is a preprocessor guard. Do not pass it to API calls. #define XR_MND_swapchain_usage_input_attachment_bit 1 #define XR_MND_swapchain_usage_input_attachment_bit_SPEC_VERSION 2 #define XR_MND_SWAPCHAIN_USAGE_INPUT_ATTACHMENT_BIT_EXTENSION_NAME "XR_MND_swapchain_usage_input_attachment_bit" +// XR_MSFT_scene_understanding is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_scene_understanding 1 XR_DEFINE_HANDLE(XrSceneObserverMSFT) @@ -3240,6 +3451,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSceneMeshBuffersMSFT( #endif /* !XR_NO_PROTOTYPES */ +// XR_MSFT_scene_understanding_serialization is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_scene_understanding_serialization 1 #define XR_MSFT_scene_understanding_serialization_SPEC_VERSION 2 #define XR_MSFT_SCENE_UNDERSTANDING_SERIALIZATION_EXTENSION_NAME "XR_MSFT_scene_understanding_serialization" @@ -3280,6 +3492,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSerializedSceneFragmentDataMSFT( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_display_refresh_rate is a preprocessor guard. Do not pass it to API calls. #define XR_FB_display_refresh_rate 1 #define XR_FB_display_refresh_rate_SPEC_VERSION 1 #define XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME "XR_FB_display_refresh_rate" @@ -3313,11 +3526,13 @@ XRAPI_ATTR XrResult XRAPI_CALL xrRequestDisplayRefreshRateFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_HTC_vive_cosmos_controller_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_HTC_vive_cosmos_controller_interaction 1 #define XR_HTC_vive_cosmos_controller_interaction_SPEC_VERSION 1 #define XR_HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_HTC_vive_cosmos_controller_interaction" +// XR_HTCX_vive_tracker_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_HTCX_vive_tracker_interaction 1 #define XR_HTCX_vive_tracker_interaction_SPEC_VERSION 3 #define XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME "XR_HTCX_vive_tracker_interaction" @@ -3347,6 +3562,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateViveTrackerPathsHTCX( #endif /* !XR_NO_PROTOTYPES */ +// XR_HTC_facial_tracking is a preprocessor guard. Do not pass it to API calls. #define XR_HTC_facial_tracking 1 #define XR_FACIAL_EXPRESSION_EYE_COUNT_HTC 14 @@ -3466,21 +3682,25 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetFacialExpressionsHTC( #endif /* !XR_NO_PROTOTYPES */ +// XR_HTC_vive_focus3_controller_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_HTC_vive_focus3_controller_interaction 1 #define XR_HTC_vive_focus3_controller_interaction_SPEC_VERSION 2 #define XR_HTC_VIVE_FOCUS3_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_HTC_vive_focus3_controller_interaction" +// XR_HTC_hand_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_HTC_hand_interaction 1 #define XR_HTC_hand_interaction_SPEC_VERSION 1 #define XR_HTC_HAND_INTERACTION_EXTENSION_NAME "XR_HTC_hand_interaction" +// XR_HTC_vive_wrist_tracker_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_HTC_vive_wrist_tracker_interaction 1 #define XR_HTC_vive_wrist_tracker_interaction_SPEC_VERSION 1 #define XR_HTC_VIVE_WRIST_TRACKER_INTERACTION_EXTENSION_NAME "XR_HTC_vive_wrist_tracker_interaction" +// XR_FB_color_space is a preprocessor guard. Do not pass it to API calls. #define XR_FB_color_space 1 #define XR_FB_color_space_SPEC_VERSION 3 #define XR_FB_COLOR_SPACE_EXTENSION_NAME "XR_FB_color_space" @@ -3521,6 +3741,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetColorSpaceFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_hand_tracking_mesh is a preprocessor guard. Do not pass it to API calls. #define XR_FB_hand_tracking_mesh 1 #define XR_FB_hand_tracking_mesh_SPEC_VERSION 3 #define XR_FB_HAND_TRACKING_MESH_EXTENSION_NAME "XR_FB_hand_tracking_mesh" @@ -3572,6 +3793,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetHandMeshFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_hand_tracking_aim is a preprocessor guard. Do not pass it to API calls. #define XR_FB_hand_tracking_aim 1 #define XR_FB_hand_tracking_aim_SPEC_VERSION 2 #define XR_FB_HAND_TRACKING_AIM_EXTENSION_NAME "XR_FB_hand_tracking_aim" @@ -3602,6 +3824,7 @@ typedef struct XrHandTrackingAimStateFB { +// XR_FB_hand_tracking_capsules is a preprocessor guard. Do not pass it to API calls. #define XR_FB_hand_tracking_capsules 1 #define XR_HAND_TRACKING_CAPSULE_POINT_COUNT_FB 2 #define XR_HAND_TRACKING_CAPSULE_COUNT_FB 19 @@ -3624,9 +3847,9 @@ typedef struct XrHandTrackingCapsulesStateFB { +// XR_FB_spatial_entity is a preprocessor guard. Do not pass it to API calls. #define XR_FB_spatial_entity 1 XR_DEFINE_ATOM(XrAsyncRequestIdFB) -#define XR_UUID_SIZE_EXT 16 #define XR_FB_spatial_entity_SPEC_VERSION 3 #define XR_FB_SPATIAL_ENTITY_EXTENSION_NAME "XR_FB_spatial_entity" @@ -3672,9 +3895,7 @@ typedef struct XrSpaceComponentStatusFB { XrBool32 changePending; } XrSpaceComponentStatusFB; -typedef struct XrUuidEXT { - uint8_t data[XR_UUID_SIZE_EXT]; -} XrUuidEXT; +typedef XrUuid XrUuidEXT; typedef struct XrEventDataSpatialAnchorCreateCompleteFB { XrStructureType type; @@ -3732,6 +3953,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceComponentStatusFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_foveation is a preprocessor guard. Do not pass it to API calls. #define XR_FB_foveation 1 XR_DEFINE_HANDLE(XrFoveationProfileFB) #define XR_FB_foveation_SPEC_VERSION 1 @@ -3781,6 +4003,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrDestroyFoveationProfileFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_foveation_configuration is a preprocessor guard. Do not pass it to API calls. #define XR_FB_foveation_configuration 1 #define XR_FB_foveation_configuration_SPEC_VERSION 1 #define XR_FB_FOVEATION_CONFIGURATION_EXTENSION_NAME "XR_FB_foveation_configuration" @@ -3809,6 +4032,7 @@ typedef struct XrFoveationLevelProfileCreateInfoFB { +// XR_FB_keyboard_tracking is a preprocessor guard. Do not pass it to API calls. #define XR_FB_keyboard_tracking 1 #define XR_MAX_KEYBOARD_TRACKING_NAME_SIZE_FB 128 #define XR_FB_keyboard_tracking_SPEC_VERSION 1 @@ -3871,6 +4095,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrCreateKeyboardSpaceFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_triangle_mesh is a preprocessor guard. Do not pass it to API calls. #define XR_FB_triangle_mesh 1 XR_DEFINE_HANDLE(XrTriangleMeshFB) #define XR_FB_triangle_mesh_SPEC_VERSION 2 @@ -3943,12 +4168,13 @@ XRAPI_ATTR XrResult XRAPI_CALL xrTriangleMeshEndVertexBufferUpdateFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_passthrough is a preprocessor guard. Do not pass it to API calls. #define XR_FB_passthrough 1 XR_DEFINE_HANDLE(XrPassthroughFB) XR_DEFINE_HANDLE(XrPassthroughLayerFB) XR_DEFINE_HANDLE(XrGeometryInstanceFB) #define XR_PASSTHROUGH_COLOR_MAP_MONO_SIZE_FB 256 -#define XR_FB_passthrough_SPEC_VERSION 3 +#define XR_FB_passthrough_SPEC_VERSION 4 #define XR_FB_PASSTHROUGH_EXTENSION_NAME "XR_FB_passthrough" typedef enum XrPassthroughLayerPurposeFB { @@ -4007,7 +4233,6 @@ typedef struct XrPassthroughLayerCreateInfoFB { XrPassthroughLayerPurposeFB purpose; } XrPassthroughLayerCreateInfoFB; -// XrCompositionLayerPassthroughFB extends XrCompositionLayerBaseHeader typedef struct XrCompositionLayerPassthroughFB { XrStructureType type; const void* XR_MAY_ALIAS next; @@ -4133,6 +4358,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGeometryInstanceSetTransformFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_render_model is a preprocessor guard. Do not pass it to API calls. #define XR_FB_render_model 1 #define XR_NULL_RENDER_MODEL_KEY_FB 0 @@ -4216,6 +4442,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrLoadRenderModelFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_VARJO_foveated_rendering is a preprocessor guard. Do not pass it to API calls. #define XR_VARJO_foveated_rendering 1 #define XR_VARJO_foveated_rendering_SPEC_VERSION 3 #define XR_VARJO_FOVEATED_RENDERING_EXTENSION_NAME "XR_VARJO_foveated_rendering" @@ -4242,6 +4469,7 @@ typedef struct XrSystemFoveatedRenderingPropertiesVARJO { +// XR_VARJO_composition_layer_depth_test is a preprocessor guard. Do not pass it to API calls. #define XR_VARJO_composition_layer_depth_test 1 #define XR_VARJO_composition_layer_depth_test_SPEC_VERSION 2 #define XR_VARJO_COMPOSITION_LAYER_DEPTH_TEST_EXTENSION_NAME "XR_VARJO_composition_layer_depth_test" @@ -4255,6 +4483,7 @@ typedef struct XrCompositionLayerDepthTestVARJO { +// XR_VARJO_environment_depth_estimation is a preprocessor guard. Do not pass it to API calls. #define XR_VARJO_environment_depth_estimation 1 #define XR_VARJO_environment_depth_estimation_SPEC_VERSION 1 #define XR_VARJO_ENVIRONMENT_DEPTH_ESTIMATION_EXTENSION_NAME "XR_VARJO_environment_depth_estimation" @@ -4269,6 +4498,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetEnvironmentDepthEstimationVARJO( #endif /* !XR_NO_PROTOTYPES */ +// XR_VARJO_marker_tracking is a preprocessor guard. Do not pass it to API calls. #define XR_VARJO_marker_tracking 1 #define XR_VARJO_marker_tracking_SPEC_VERSION 1 #define XR_VARJO_MARKER_TRACKING_EXTENSION_NAME "XR_VARJO_marker_tracking" @@ -4330,6 +4560,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrCreateMarkerSpaceVARJO( #endif /* !XR_NO_PROTOTYPES */ +// XR_VARJO_view_offset is a preprocessor guard. Do not pass it to API calls. #define XR_VARJO_view_offset 1 #define XR_VARJO_view_offset_SPEC_VERSION 1 #define XR_VARJO_VIEW_OFFSET_EXTENSION_NAME "XR_VARJO_view_offset" @@ -4344,16 +4575,19 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetViewOffsetVARJO( #endif /* !XR_NO_PROTOTYPES */ +// XR_VARJO_xr4_controller_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_VARJO_xr4_controller_interaction 1 #define XR_VARJO_xr4_controller_interaction_SPEC_VERSION 1 #define XR_VARJO_XR4_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_VARJO_xr4_controller_interaction" +// XR_ML_ml2_controller_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_ML_ml2_controller_interaction 1 #define XR_ML_ml2_controller_interaction_SPEC_VERSION 1 #define XR_ML_ML2_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_ML_ml2_controller_interaction" +// XR_ML_frame_end_info is a preprocessor guard. Do not pass it to API calls. #define XR_ML_frame_end_info 1 #define XR_ML_frame_end_info_SPEC_VERSION 1 #define XR_ML_FRAME_END_INFO_EXTENSION_NAME "XR_ML_frame_end_info" @@ -4373,6 +4607,7 @@ typedef struct XrFrameEndInfoML { +// XR_ML_global_dimmer is a preprocessor guard. Do not pass it to API calls. #define XR_ML_global_dimmer 1 #define XR_ML_global_dimmer_SPEC_VERSION 1 #define XR_ML_GLOBAL_DIMMER_EXTENSION_NAME "XR_ML_global_dimmer" @@ -4391,6 +4626,7 @@ typedef struct XrGlobalDimmerFrameEndInfoML { +// XR_ML_marker_understanding is a preprocessor guard. Do not pass it to API calls. #define XR_ML_marker_understanding 1 XR_DEFINE_ATOM(XrMarkerML) XR_DEFINE_HANDLE(XrMarkerDetectorML) @@ -4619,6 +4855,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrCreateMarkerSpaceML( #endif /* !XR_NO_PROTOTYPES */ +// XR_ML_localization_map is a preprocessor guard. Do not pass it to API calls. #define XR_ML_localization_map 1 XR_DEFINE_HANDLE(XrExportedLocalizationMapML) #define XR_MAX_LOCALIZATION_MAP_NAME_LENGTH_ML 64 @@ -4745,6 +4982,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetExportedLocalizationMapDataML( #endif /* !XR_NO_PROTOTYPES */ +// XR_MSFT_spatial_anchor_persistence is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_spatial_anchor_persistence 1 XR_DEFINE_HANDLE(XrSpatialAnchorStoreConnectionMSFT) #define XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_MSFT 256 @@ -4810,6 +5048,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrClearSpatialAnchorStoreMSFT( #endif /* !XR_NO_PROTOTYPES */ +// XR_MSFT_scene_marker is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_scene_marker 1 #define XR_MSFT_scene_marker_SPEC_VERSION 1 #define XR_MSFT_SCENE_MARKER_EXTENSION_NAME "XR_MSFT_scene_marker" @@ -4882,6 +5121,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSceneMarkerDecodedStringMSFT( #endif /* !XR_NO_PROTOTYPES */ +// XR_ULTRALEAP_hand_tracking_forearm is a preprocessor guard. Do not pass it to API calls. #define XR_ULTRALEAP_hand_tracking_forearm 1 #define XR_HAND_FOREARM_JOINT_COUNT_ULTRALEAP 27 @@ -4921,6 +5161,7 @@ typedef enum XrHandForearmJointULTRALEAP { } XrHandForearmJointULTRALEAP; +// XR_FB_spatial_entity_query is a preprocessor guard. Do not pass it to API calls. #define XR_FB_spatial_entity_query 1 #define XR_FB_spatial_entity_query_SPEC_VERSION 1 #define XR_FB_SPATIAL_ENTITY_QUERY_EXTENSION_NAME "XR_FB_spatial_entity_query" @@ -5020,6 +5261,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrRetrieveSpaceQueryResultsFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_spatial_entity_storage is a preprocessor guard. Do not pass it to API calls. #define XR_FB_spatial_entity_storage 1 #define XR_FB_spatial_entity_storage_SPEC_VERSION 1 #define XR_FB_SPATIAL_ENTITY_STORAGE_EXTENSION_NAME "XR_FB_spatial_entity_storage" @@ -5082,11 +5324,13 @@ XRAPI_ATTR XrResult XRAPI_CALL xrEraseSpaceFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_touch_controller_pro is a preprocessor guard. Do not pass it to API calls. #define XR_FB_touch_controller_pro 1 #define XR_FB_touch_controller_pro_SPEC_VERSION 1 #define XR_FB_TOUCH_CONTROLLER_PRO_EXTENSION_NAME "XR_FB_touch_controller_pro" +// XR_FB_spatial_entity_sharing is a preprocessor guard. Do not pass it to API calls. #define XR_FB_spatial_entity_sharing 1 XR_DEFINE_HANDLE(XrSpaceUserFB) #define XR_FB_spatial_entity_sharing_SPEC_VERSION 1 @@ -5119,6 +5363,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrShareSpacesFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_space_warp is a preprocessor guard. Do not pass it to API calls. #define XR_FB_space_warp 1 #define XR_FB_space_warp_SPEC_VERSION 2 #define XR_FB_SPACE_WARP_EXTENSION_NAME "XR_FB_space_warp" @@ -5151,6 +5396,7 @@ typedef struct XrSystemSpaceWarpPropertiesFB { +// XR_FB_haptic_amplitude_envelope is a preprocessor guard. Do not pass it to API calls. #define XR_FB_haptic_amplitude_envelope 1 #define XR_MAX_HAPTIC_AMPLITUDE_ENVELOPE_SAMPLES_FB 4000u @@ -5167,6 +5413,7 @@ typedef struct XrHapticAmplitudeEnvelopeVibrationFB { +// XR_FB_scene is a preprocessor guard. Do not pass it to API calls. #define XR_FB_scene 1 #define XR_FB_scene_SPEC_VERSION 4 #define XR_FB_SCENE_EXTENSION_NAME "XR_FB_scene" @@ -5177,11 +5424,7 @@ static const XrSemanticLabelsSupportFlagsFB XR_SEMANTIC_LABELS_SUPPORT_MULTIPLE_ static const XrSemanticLabelsSupportFlagsFB XR_SEMANTIC_LABELS_SUPPORT_ACCEPT_DESK_TO_TABLE_MIGRATION_BIT_FB = 0x00000002; static const XrSemanticLabelsSupportFlagsFB XR_SEMANTIC_LABELS_SUPPORT_ACCEPT_INVISIBLE_WALL_FACE_BIT_FB = 0x00000004; -typedef struct XrExtent3DfFB { - float width; - float height; - float depth; -} XrExtent3DfFB; +typedef XrExtent3Df XrExtent3DfFB; typedef struct XrOffset3DfFB { float x; @@ -5263,11 +5506,13 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceRoomLayoutFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_EXT_palm_pose is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_palm_pose 1 -#define XR_EXT_palm_pose_SPEC_VERSION 2 +#define XR_EXT_palm_pose_SPEC_VERSION 3 #define XR_EXT_PALM_POSE_EXTENSION_NAME "XR_EXT_palm_pose" +// XR_ALMALENCE_digital_lens_control is a preprocessor guard. Do not pass it to API calls. #define XR_ALMALENCE_digital_lens_control 1 #define XR_ALMALENCE_digital_lens_control_SPEC_VERSION 1 #define XR_ALMALENCE_DIGITAL_LENS_CONTROL_EXTENSION_NAME "XR_ALMALENCE_digital_lens_control" @@ -5293,6 +5538,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetDigitalLensControlALMALENCE( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_scene_capture is a preprocessor guard. Do not pass it to API calls. #define XR_FB_scene_capture 1 #define XR_FB_scene_capture_SPEC_VERSION 1 #define XR_FB_SCENE_CAPTURE_EXTENSION_NAME "XR_FB_scene_capture" @@ -5322,6 +5568,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrRequestSceneCaptureFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_spatial_entity_container is a preprocessor guard. Do not pass it to API calls. #define XR_FB_spatial_entity_container 1 #define XR_FB_spatial_entity_container_SPEC_VERSION 2 #define XR_FB_SPATIAL_ENTITY_CONTAINER_EXTENSION_NAME "XR_FB_spatial_entity_container" @@ -5345,6 +5592,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceContainerFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_META_foveation_eye_tracked is a preprocessor guard. Do not pass it to API calls. #define XR_META_foveation_eye_tracked 1 #define XR_FOVEATION_CENTER_SIZE_META 2 #define XR_META_foveation_eye_tracked_SPEC_VERSION 1 @@ -5390,6 +5638,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetFoveationEyeTrackedStateMETA( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_face_tracking is a preprocessor guard. Do not pass it to API calls. #define XR_FB_face_tracking 1 #define XR_FACE_EXPRESSSION_SET_DEFAULT_FB XR_FACE_EXPRESSION_SET_DEFAULT_FB @@ -5534,6 +5783,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetFaceExpressionWeightsFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_eye_tracking_social is a preprocessor guard. Do not pass it to API calls. #define XR_FB_eye_tracking_social 1 XR_DEFINE_HANDLE(XrEyeTrackerFB) #define XR_FB_eye_tracking_social_SPEC_VERSION 1 @@ -5599,6 +5849,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetEyeGazesFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_passthrough_keyboard_hands is a preprocessor guard. Do not pass it to API calls. #define XR_FB_passthrough_keyboard_hands 1 #define XR_FB_passthrough_keyboard_hands_SPEC_VERSION 2 #define XR_FB_PASSTHROUGH_KEYBOARD_HANDS_EXTENSION_NAME "XR_FB_passthrough_keyboard_hands" @@ -5620,6 +5871,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrPassthroughLayerSetKeyboardHandsIntensityFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_composition_layer_settings is a preprocessor guard. Do not pass it to API calls. #define XR_FB_composition_layer_settings 1 #define XR_FB_composition_layer_settings_SPEC_VERSION 1 #define XR_FB_COMPOSITION_LAYER_SETTINGS_EXTENSION_NAME "XR_FB_composition_layer_settings" @@ -5641,11 +5893,13 @@ typedef struct XrCompositionLayerSettingsFB { +// XR_FB_touch_controller_proximity is a preprocessor guard. Do not pass it to API calls. #define XR_FB_touch_controller_proximity 1 #define XR_FB_touch_controller_proximity_SPEC_VERSION 1 #define XR_FB_TOUCH_CONTROLLER_PROXIMITY_EXTENSION_NAME "XR_FB_touch_controller_proximity" +// XR_FB_haptic_pcm is a preprocessor guard. Do not pass it to API calls. #define XR_FB_haptic_pcm 1 #define XR_MAX_HAPTIC_PCM_BUFFER_SIZE_FB 4000 @@ -5682,6 +5936,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetDeviceSampleRateFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_composition_layer_depth_test is a preprocessor guard. Do not pass it to API calls. #define XR_FB_composition_layer_depth_test 1 #define XR_FB_composition_layer_depth_test_SPEC_VERSION 1 #define XR_FB_COMPOSITION_LAYER_DEPTH_TEST_EXTENSION_NAME "XR_FB_composition_layer_depth_test" @@ -5707,6 +5962,7 @@ typedef struct XrCompositionLayerDepthTestFB { +// XR_META_local_dimming is a preprocessor guard. Do not pass it to API calls. #define XR_META_local_dimming 1 #define XR_META_local_dimming_SPEC_VERSION 1 #define XR_META_LOCAL_DIMMING_EXTENSION_NAME "XR_META_local_dimming" @@ -5725,6 +5981,7 @@ typedef struct XrLocalDimmingFrameEndInfoMETA { +// XR_META_passthrough_preferences is a preprocessor guard. Do not pass it to API calls. #define XR_META_passthrough_preferences 1 #define XR_META_passthrough_preferences_SPEC_VERSION 1 #define XR_META_PASSTHROUGH_PREFERENCES_EXTENSION_NAME "XR_META_passthrough_preferences" @@ -5750,6 +6007,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetPassthroughPreferencesMETA( #endif /* !XR_NO_PROTOTYPES */ +// XR_META_virtual_keyboard is a preprocessor guard. Do not pass it to API calls. #define XR_META_virtual_keyboard 1 XR_DEFINE_HANDLE(XrVirtualKeyboardMETA) #define XR_MAX_VIRTUAL_KEYBOARD_COMMIT_TEXT_SIZE_META 3992 @@ -5952,6 +6210,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrChangeVirtualKeyboardTextContextMETA( #endif /* !XR_NO_PROTOTYPES */ +// XR_OCULUS_external_camera is a preprocessor guard. Do not pass it to API calls. #define XR_OCULUS_external_camera 1 #define XR_MAX_EXTERNAL_CAMERA_NAME_SIZE_OCULUS 32 #define XR_OCULUS_external_camera_SPEC_VERSION 1 @@ -6009,6 +6268,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateExternalCamerasOCULUS( #endif /* !XR_NO_PROTOTYPES */ +// XR_META_performance_metrics is a preprocessor guard. Do not pass it to API calls. #define XR_META_performance_metrics 1 #define XR_META_performance_metrics_SPEC_VERSION 2 #define XR_META_PERFORMANCE_METRICS_EXTENSION_NAME "XR_META_performance_metrics" @@ -6072,6 +6332,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrQueryPerformanceMetricsCounterMETA( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_spatial_entity_storage_batch is a preprocessor guard. Do not pass it to API calls. #define XR_FB_spatial_entity_storage_batch 1 #define XR_FB_spatial_entity_storage_batch_SPEC_VERSION 1 #define XR_FB_SPATIAL_ENTITY_STORAGE_BATCH_EXTENSION_NAME "XR_FB_spatial_entity_storage_batch" @@ -6102,6 +6363,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSaveSpaceListFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_FB_spatial_entity_user is a preprocessor guard. Do not pass it to API calls. #define XR_FB_spatial_entity_user 1 typedef uint64_t XrSpaceUserIdFB; #define XR_FB_spatial_entity_user_SPEC_VERSION 1 @@ -6133,6 +6395,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpaceUserFB( #endif /* !XR_NO_PROTOTYPES */ +// XR_META_headset_id is a preprocessor guard. Do not pass it to API calls. #define XR_META_headset_id 1 #define XR_META_headset_id_SPEC_VERSION 2 #define XR_META_HEADSET_ID_EXTENSION_NAME "XR_META_headset_id" @@ -6145,6 +6408,7 @@ typedef struct XrSystemHeadsetIdPropertiesMETA { +// XR_META_recommended_layer_resolution is a preprocessor guard. Do not pass it to API calls. #define XR_META_recommended_layer_resolution 1 #define XR_META_recommended_layer_resolution_SPEC_VERSION 1 #define XR_META_RECOMMENDED_LAYER_RESOLUTION_EXTENSION_NAME "XR_META_recommended_layer_resolution" @@ -6174,6 +6438,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetRecommendedLayerResolutionMETA( #endif /* !XR_NO_PROTOTYPES */ +// XR_META_passthrough_color_lut is a preprocessor guard. Do not pass it to API calls. #define XR_META_passthrough_color_lut 1 XR_DEFINE_HANDLE(XrPassthroughColorLutMETA) #define XR_META_passthrough_color_lut_SPEC_VERSION 1 @@ -6248,6 +6513,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrUpdatePassthroughColorLutMETA( #endif /* !XR_NO_PROTOTYPES */ +// XR_META_spatial_entity_mesh is a preprocessor guard. Do not pass it to API calls. #define XR_META_spatial_entity_mesh 1 #define XR_META_spatial_entity_mesh_SPEC_VERSION 1 #define XR_META_SPATIAL_ENTITY_MESH_EXTENSION_NAME "XR_META_spatial_entity_mesh" @@ -6279,16 +6545,19 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceTriangleMeshMETA( #endif /* !XR_NO_PROTOTYPES */ +// XR_META_automatic_layer_filter is a preprocessor guard. Do not pass it to API calls. #define XR_META_automatic_layer_filter 1 #define XR_META_automatic_layer_filter_SPEC_VERSION 1 #define XR_META_AUTOMATIC_LAYER_FILTER_EXTENSION_NAME "XR_META_automatic_layer_filter" +// XR_META_touch_controller_plus is a preprocessor guard. Do not pass it to API calls. #define XR_META_touch_controller_plus 1 #define XR_META_touch_controller_plus_SPEC_VERSION 1 #define XR_META_TOUCH_CONTROLLER_PLUS_EXTENSION_NAME "XR_META_touch_controller_plus" +// XR_FB_face_tracking2 is a preprocessor guard. Do not pass it to API calls. #define XR_FB_face_tracking2 1 XR_DEFINE_HANDLE(XrFaceTracker2FB) #define XR_FB_face_tracking2_SPEC_VERSION 1 @@ -6443,16 +6712,147 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetFaceExpressionWeights2FB( #endif /* !XR_NO_PROTOTYPES */ +// XR_META_environment_depth is a preprocessor guard. Do not pass it to API calls. +#define XR_META_environment_depth 1 +XR_DEFINE_HANDLE(XrEnvironmentDepthProviderMETA) +XR_DEFINE_HANDLE(XrEnvironmentDepthSwapchainMETA) +#define XR_META_environment_depth_SPEC_VERSION 1 +#define XR_META_ENVIRONMENT_DEPTH_EXTENSION_NAME "XR_META_environment_depth" +typedef XrFlags64 XrEnvironmentDepthProviderCreateFlagsMETA; + +// Flag bits for XrEnvironmentDepthProviderCreateFlagsMETA + +typedef XrFlags64 XrEnvironmentDepthSwapchainCreateFlagsMETA; + +// Flag bits for XrEnvironmentDepthSwapchainCreateFlagsMETA + +typedef struct XrEnvironmentDepthProviderCreateInfoMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrEnvironmentDepthProviderCreateFlagsMETA createFlags; +} XrEnvironmentDepthProviderCreateInfoMETA; + +typedef struct XrEnvironmentDepthSwapchainCreateInfoMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrEnvironmentDepthSwapchainCreateFlagsMETA createFlags; +} XrEnvironmentDepthSwapchainCreateInfoMETA; + +typedef struct XrEnvironmentDepthSwapchainStateMETA { + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t width; + uint32_t height; +} XrEnvironmentDepthSwapchainStateMETA; + +typedef struct XrEnvironmentDepthImageAcquireInfoMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpace space; + XrTime displayTime; +} XrEnvironmentDepthImageAcquireInfoMETA; + +typedef struct XrEnvironmentDepthImageViewMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrFovf fov; + XrPosef pose; +} XrEnvironmentDepthImageViewMETA; + +typedef struct XrEnvironmentDepthImageMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t swapchainIndex; + float nearZ; + float farZ; + XrEnvironmentDepthImageViewMETA views[2]; +} XrEnvironmentDepthImageMETA; + +typedef struct XrEnvironmentDepthHandRemovalSetInfoMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrBool32 enabled; +} XrEnvironmentDepthHandRemovalSetInfoMETA; + +// XrSystemEnvironmentDepthPropertiesMETA extends XrSystemProperties +typedef struct XrSystemEnvironmentDepthPropertiesMETA { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsEnvironmentDepth; + XrBool32 supportsHandRemoval; +} XrSystemEnvironmentDepthPropertiesMETA; + +typedef XrResult (XRAPI_PTR *PFN_xrCreateEnvironmentDepthProviderMETA)(XrSession session, const XrEnvironmentDepthProviderCreateInfoMETA* createInfo, XrEnvironmentDepthProviderMETA* environmentDepthProvider); +typedef XrResult (XRAPI_PTR *PFN_xrDestroyEnvironmentDepthProviderMETA)(XrEnvironmentDepthProviderMETA environmentDepthProvider); +typedef XrResult (XRAPI_PTR *PFN_xrStartEnvironmentDepthProviderMETA)(XrEnvironmentDepthProviderMETA environmentDepthProvider); +typedef XrResult (XRAPI_PTR *PFN_xrStopEnvironmentDepthProviderMETA)(XrEnvironmentDepthProviderMETA environmentDepthProvider); +typedef XrResult (XRAPI_PTR *PFN_xrCreateEnvironmentDepthSwapchainMETA)(XrEnvironmentDepthProviderMETA environmentDepthProvider, const XrEnvironmentDepthSwapchainCreateInfoMETA* createInfo, XrEnvironmentDepthSwapchainMETA* swapchain); +typedef XrResult (XRAPI_PTR *PFN_xrDestroyEnvironmentDepthSwapchainMETA)(XrEnvironmentDepthSwapchainMETA swapchain); +typedef XrResult (XRAPI_PTR *PFN_xrEnumerateEnvironmentDepthSwapchainImagesMETA)(XrEnvironmentDepthSwapchainMETA swapchain, uint32_t imageCapacityInput, uint32_t* imageCountOutput, XrSwapchainImageBaseHeader* images); +typedef XrResult (XRAPI_PTR *PFN_xrGetEnvironmentDepthSwapchainStateMETA)(XrEnvironmentDepthSwapchainMETA swapchain, XrEnvironmentDepthSwapchainStateMETA* state); +typedef XrResult (XRAPI_PTR *PFN_xrAcquireEnvironmentDepthImageMETA)(XrEnvironmentDepthProviderMETA environmentDepthProvider, const XrEnvironmentDepthImageAcquireInfoMETA* acquireInfo, XrEnvironmentDepthImageMETA* environmentDepthImage); +typedef XrResult (XRAPI_PTR *PFN_xrSetEnvironmentDepthHandRemovalMETA)(XrEnvironmentDepthProviderMETA environmentDepthProvider, const XrEnvironmentDepthHandRemovalSetInfoMETA* setInfo); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrCreateEnvironmentDepthProviderMETA( + XrSession session, + const XrEnvironmentDepthProviderCreateInfoMETA* createInfo, + XrEnvironmentDepthProviderMETA* environmentDepthProvider); + +XRAPI_ATTR XrResult XRAPI_CALL xrDestroyEnvironmentDepthProviderMETA( + XrEnvironmentDepthProviderMETA environmentDepthProvider); + +XRAPI_ATTR XrResult XRAPI_CALL xrStartEnvironmentDepthProviderMETA( + XrEnvironmentDepthProviderMETA environmentDepthProvider); + +XRAPI_ATTR XrResult XRAPI_CALL xrStopEnvironmentDepthProviderMETA( + XrEnvironmentDepthProviderMETA environmentDepthProvider); + +XRAPI_ATTR XrResult XRAPI_CALL xrCreateEnvironmentDepthSwapchainMETA( + XrEnvironmentDepthProviderMETA environmentDepthProvider, + const XrEnvironmentDepthSwapchainCreateInfoMETA* createInfo, + XrEnvironmentDepthSwapchainMETA* swapchain); + +XRAPI_ATTR XrResult XRAPI_CALL xrDestroyEnvironmentDepthSwapchainMETA( + XrEnvironmentDepthSwapchainMETA swapchain); + +XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateEnvironmentDepthSwapchainImagesMETA( + XrEnvironmentDepthSwapchainMETA swapchain, + uint32_t imageCapacityInput, + uint32_t* imageCountOutput, + XrSwapchainImageBaseHeader* images); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetEnvironmentDepthSwapchainStateMETA( + XrEnvironmentDepthSwapchainMETA swapchain, + XrEnvironmentDepthSwapchainStateMETA* state); + +XRAPI_ATTR XrResult XRAPI_CALL xrAcquireEnvironmentDepthImageMETA( + XrEnvironmentDepthProviderMETA environmentDepthProvider, + const XrEnvironmentDepthImageAcquireInfoMETA* acquireInfo, + XrEnvironmentDepthImageMETA* environmentDepthImage); + +XRAPI_ATTR XrResult XRAPI_CALL xrSetEnvironmentDepthHandRemovalMETA( + XrEnvironmentDepthProviderMETA environmentDepthProvider, + const XrEnvironmentDepthHandRemovalSetInfoMETA* setInfo); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ + + +// XR_EXT_uuid is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_uuid 1 #define XR_EXT_uuid_SPEC_VERSION 1 #define XR_EXT_UUID_EXTENSION_NAME "XR_EXT_uuid" +#define XR_UUID_SIZE_EXT 16 +// XR_EXT_hand_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_hand_interaction 1 #define XR_EXT_hand_interaction_SPEC_VERSION 1 #define XR_EXT_HAND_INTERACTION_EXTENSION_NAME "XR_EXT_hand_interaction" +// XR_QCOM_tracking_optimization_settings is a preprocessor guard. Do not pass it to API calls. #define XR_QCOM_tracking_optimization_settings 1 #define XR_QCOM_tracking_optimization_settings_SPEC_VERSION 1 #define XR_QCOM_TRACKING_OPTIMIZATION_SETTINGS_EXTENSION_NAME "XR_QCOM_tracking_optimization_settings" @@ -6482,6 +6882,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetTrackingOptimizationSettingsHintQCOM( #endif /* !XR_NO_PROTOTYPES */ +// XR_HTC_passthrough is a preprocessor guard. Do not pass it to API calls. #define XR_HTC_passthrough 1 XR_DEFINE_HANDLE(XrPassthroughHTC) #define XR_HTC_passthrough_SPEC_VERSION 1 @@ -6543,6 +6944,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrDestroyPassthroughHTC( #endif /* !XR_NO_PROTOTYPES */ +// XR_HTC_foveation is a preprocessor guard. Do not pass it to API calls. #define XR_HTC_foveation 1 #define XR_HTC_foveation_SPEC_VERSION 1 #define XR_HTC_FOVEATION_EXTENSION_NAME "XR_HTC_foveation" @@ -6609,6 +7011,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrApplyFoveationHTC( #endif /* !XR_NO_PROTOTYPES */ +// XR_HTC_anchor is a preprocessor guard. Do not pass it to API calls. #define XR_HTC_anchor 1 #define XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_HTC 256 #define XR_HTC_anchor_SPEC_VERSION 1 @@ -6649,6 +7052,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialAnchorNameHTC( #endif /* !XR_NO_PROTOTYPES */ +// XR_EXT_active_action_set_priority is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_active_action_set_priority 1 #define XR_EXT_active_action_set_priority_SPEC_VERSION 1 #define XR_EXT_ACTIVE_ACTION_SET_PRIORITY_EXTENSION_NAME "XR_EXT_active_action_set_priority" @@ -6667,6 +7071,7 @@ typedef struct XrActiveActionSetPrioritiesEXT { +// XR_MNDX_force_feedback_curl is a preprocessor guard. Do not pass it to API calls. #define XR_MNDX_force_feedback_curl 1 #define XR_MNDX_force_feedback_curl_SPEC_VERSION 1 #define XR_MNDX_FORCE_FEEDBACK_CURL_EXTENSION_NAME "XR_MNDX_force_feedback_curl" @@ -6709,16 +7114,19 @@ XRAPI_ATTR XrResult XRAPI_CALL xrApplyForceFeedbackCurlMNDX( #endif /* !XR_NO_PROTOTYPES */ +// XR_BD_controller_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_BD_controller_interaction 1 #define XR_BD_controller_interaction_SPEC_VERSION 2 #define XR_BD_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_BD_controller_interaction" +// XR_EXT_local_floor is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_local_floor 1 #define XR_EXT_local_floor_SPEC_VERSION 1 #define XR_EXT_LOCAL_FLOOR_EXTENSION_NAME "XR_EXT_local_floor" +// XR_EXT_hand_tracking_data_source is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_hand_tracking_data_source 1 #define XR_EXT_hand_tracking_data_source_SPEC_VERSION 1 #define XR_EXT_HAND_TRACKING_DATA_SOURCE_EXTENSION_NAME "XR_EXT_hand_tracking_data_source" @@ -6746,9 +7154,10 @@ typedef struct XrHandTrackingDataSourceStateEXT { +// XR_EXT_plane_detection is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_plane_detection 1 XR_DEFINE_HANDLE(XrPlaneDetectorEXT) -#define XR_EXT_plane_detection_SPEC_VERSION 1 +#define XR_EXT_plane_detection_SPEC_VERSION 2 #define XR_EXT_PLANE_DETECTION_EXTENSION_NAME "XR_EXT_plane_detection" typedef enum XrPlaneDetectorOrientationEXT { @@ -6805,11 +7214,7 @@ typedef struct XrPlaneDetectorCreateInfoEXT { XrPlaneDetectorFlagsEXT flags; } XrPlaneDetectorCreateInfoEXT; -typedef struct XrExtent3DfEXT { - float width; - float height; - float depth; -} XrExtent3DfEXT; +typedef XrExtent3Df XrExtent3DfEXT; typedef struct XrPlaneDetectorBeginInfoEXT { XrStructureType type; @@ -6900,11 +7305,72 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetPlanePolygonBufferEXT( #endif /* !XR_NO_PROTOTYPES */ +// XR_OPPO_controller_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_OPPO_controller_interaction 1 #define XR_OPPO_controller_interaction_SPEC_VERSION 1 #define XR_OPPO_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_OPPO_controller_interaction" +// XR_EXT_future is a preprocessor guard. Do not pass it to API calls. +#define XR_EXT_future 1 +XR_DEFINE_OPAQUE_64(XrFutureEXT) +#define XR_EXT_future_SPEC_VERSION 1 +#define XR_EXT_FUTURE_EXTENSION_NAME "XR_EXT_future" +#define XR_NULL_FUTURE_EXT 0 + +typedef enum XrFutureStateEXT { + XR_FUTURE_STATE_PENDING_EXT = 1, + XR_FUTURE_STATE_READY_EXT = 2, + XR_FUTURE_STATE_MAX_ENUM_EXT = 0x7FFFFFFF +} XrFutureStateEXT; +typedef struct XrFutureCancelInfoEXT { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrFutureEXT future; +} XrFutureCancelInfoEXT; + +typedef struct XrFuturePollInfoEXT { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrFutureEXT future; +} XrFuturePollInfoEXT; + +typedef struct XR_MAY_ALIAS XrFutureCompletionBaseHeaderEXT { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrResult futureResult; +} XrFutureCompletionBaseHeaderEXT; + +typedef struct XrFutureCompletionEXT { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrResult futureResult; +} XrFutureCompletionEXT; + +typedef struct XrFuturePollResultEXT { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrFutureStateEXT state; +} XrFuturePollResultEXT; + +typedef XrResult (XRAPI_PTR *PFN_xrPollFutureEXT)(XrInstance instance, const XrFuturePollInfoEXT* pollInfo, XrFuturePollResultEXT* pollResult); +typedef XrResult (XRAPI_PTR *PFN_xrCancelFutureEXT)(XrInstance instance, const XrFutureCancelInfoEXT* cancelInfo); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrPollFutureEXT( + XrInstance instance, + const XrFuturePollInfoEXT* pollInfo, + XrFuturePollResultEXT* pollResult); + +XRAPI_ATTR XrResult XRAPI_CALL xrCancelFutureEXT( + XrInstance instance, + const XrFutureCancelInfoEXT* cancelInfo); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ + + +// XR_EXT_user_presence is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_user_presence 1 #define XR_EXT_user_presence_SPEC_VERSION 1 #define XR_EXT_USER_PRESENCE_EXTENSION_NAME "XR_EXT_user_presence" @@ -6924,6 +7390,7 @@ typedef struct XrSystemUserPresencePropertiesEXT { +// XR_ML_user_calibration is a preprocessor guard. Do not pass it to API calls. #define XR_ML_user_calibration 1 #define XR_ML_user_calibration_SPEC_VERSION 1 #define XR_ML_USER_CALIBRATION_EXTENSION_NAME "XR_ML_user_calibration" @@ -6973,10 +7440,17 @@ XRAPI_ATTR XrResult XRAPI_CALL xrEnableUserCalibrationEventsML( #endif /* !XR_NO_PROTOTYPES */ +// XR_YVR_controller_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_YVR_controller_interaction 1 #define XR_YVR_controller_interaction_SPEC_VERSION 1 #define XR_YVR_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_YVR_controller_interaction" + +// XR_EXT_composition_layer_inverted_alpha is a preprocessor guard. Do not pass it to API calls. +#define XR_EXT_composition_layer_inverted_alpha 1 +#define XR_EXT_composition_layer_inverted_alpha_SPEC_VERSION 1 +#define XR_EXT_COMPOSITION_LAYER_INVERTED_ALPHA_EXTENSION_NAME "XR_EXT_composition_layer_inverted_alpha" + #ifdef __cplusplus } #endif diff --git a/thirdparty/openxr/include/openxr/openxr_loader_negotiation.h b/thirdparty/openxr/include/openxr/openxr_loader_negotiation.h index 619b226760..19c18cc2a3 100644 --- a/thirdparty/openxr/include/openxr/openxr_loader_negotiation.h +++ b/thirdparty/openxr/include/openxr/openxr_loader_negotiation.h @@ -20,6 +20,7 @@ extern "C" { +// XR_LOADER_VERSION_1_0 is a preprocessor guard. Do not pass it to API calls. #define XR_LOADER_VERSION_1_0 1 #define XR_CURRENT_LOADER_API_LAYER_VERSION 1 diff --git a/thirdparty/openxr/include/openxr/openxr_platform.h b/thirdparty/openxr/include/openxr/openxr_platform.h index 1f47428676..cbe1008906 100644 --- a/thirdparty/openxr/include/openxr/openxr_platform.h +++ b/thirdparty/openxr/include/openxr/openxr_platform.h @@ -21,6 +21,7 @@ extern "C" { #ifdef XR_USE_PLATFORM_ANDROID +// XR_KHR_android_thread_settings is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_android_thread_settings 1 #define XR_KHR_android_thread_settings_SPEC_VERSION 6 #define XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME "XR_KHR_android_thread_settings" @@ -46,6 +47,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetAndroidApplicationThreadKHR( #ifdef XR_USE_PLATFORM_ANDROID +// XR_KHR_android_surface_swapchain is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_android_surface_swapchain 1 #define XR_KHR_android_surface_swapchain_SPEC_VERSION 4 #define XR_KHR_ANDROID_SURFACE_SWAPCHAIN_EXTENSION_NAME "XR_KHR_android_surface_swapchain" @@ -64,6 +66,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrCreateSwapchainAndroidSurfaceKHR( #ifdef XR_USE_PLATFORM_ANDROID +// XR_KHR_android_create_instance is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_android_create_instance 1 #define XR_KHR_android_create_instance_SPEC_VERSION 3 #define XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME "XR_KHR_android_create_instance" @@ -79,6 +82,7 @@ typedef struct XrInstanceCreateInfoAndroidKHR { #ifdef XR_USE_GRAPHICS_API_VULKAN +// XR_KHR_vulkan_swapchain_format_list is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_vulkan_swapchain_format_list 1 #define XR_KHR_vulkan_swapchain_format_list_SPEC_VERSION 4 #define XR_KHR_VULKAN_SWAPCHAIN_FORMAT_LIST_EXTENSION_NAME "XR_KHR_vulkan_swapchain_format_list" @@ -93,6 +97,7 @@ typedef struct XrVulkanSwapchainFormatListCreateInfoKHR { #ifdef XR_USE_GRAPHICS_API_OPENGL +// XR_KHR_opengl_enable is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_opengl_enable 1 #define XR_KHR_opengl_enable_SPEC_VERSION 10 #define XR_KHR_OPENGL_ENABLE_EXTENSION_NAME "XR_KHR_opengl_enable" @@ -169,6 +174,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetOpenGLGraphicsRequirementsKHR( #ifdef XR_USE_GRAPHICS_API_OPENGL_ES +// XR_KHR_opengl_es_enable is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_opengl_es_enable 1 #define XR_KHR_opengl_es_enable_SPEC_VERSION 8 #define XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME "XR_KHR_opengl_es_enable" @@ -210,6 +216,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetOpenGLESGraphicsRequirementsKHR( #ifdef XR_USE_GRAPHICS_API_VULKAN +// XR_KHR_vulkan_enable is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_vulkan_enable 1 #define XR_KHR_vulkan_enable_SPEC_VERSION 8 #define XR_KHR_VULKAN_ENABLE_EXTENSION_NAME "XR_KHR_vulkan_enable" @@ -274,6 +281,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsRequirementsKHR( #ifdef XR_USE_GRAPHICS_API_D3D11 +// XR_KHR_D3D11_enable is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_D3D11_enable 1 #define XR_KHR_D3D11_enable_SPEC_VERSION 9 #define XR_KHR_D3D11_ENABLE_EXTENSION_NAME "XR_KHR_D3D11_enable" @@ -311,6 +319,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetD3D11GraphicsRequirementsKHR( #ifdef XR_USE_GRAPHICS_API_D3D12 +// XR_KHR_D3D12_enable is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_D3D12_enable 1 #define XR_KHR_D3D12_enable_SPEC_VERSION 9 #define XR_KHR_D3D12_ENABLE_EXTENSION_NAME "XR_KHR_D3D12_enable" @@ -349,6 +358,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetD3D12GraphicsRequirementsKHR( #ifdef XR_USE_PLATFORM_WIN32 +// XR_KHR_win32_convert_performance_counter_time is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_win32_convert_performance_counter_time 1 #define XR_KHR_win32_convert_performance_counter_time_SPEC_VERSION 1 #define XR_KHR_WIN32_CONVERT_PERFORMANCE_COUNTER_TIME_EXTENSION_NAME "XR_KHR_win32_convert_performance_counter_time" @@ -372,6 +382,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrConvertTimeToWin32PerformanceCounterKHR( #ifdef XR_USE_TIMESPEC +// XR_KHR_convert_timespec_time is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_convert_timespec_time 1 #define XR_KHR_convert_timespec_time_SPEC_VERSION 1 #define XR_KHR_CONVERT_TIMESPEC_TIME_EXTENSION_NAME "XR_KHR_convert_timespec_time" @@ -395,6 +406,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrConvertTimeToTimespecTimeKHR( #ifdef XR_USE_PLATFORM_ANDROID +// XR_KHR_loader_init_android is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_loader_init_android 1 #define XR_KHR_loader_init_android_SPEC_VERSION 1 #define XR_KHR_LOADER_INIT_ANDROID_EXTENSION_NAME "XR_KHR_loader_init_android" @@ -409,6 +421,7 @@ typedef struct XrLoaderInitInfoAndroidKHR { #ifdef XR_USE_GRAPHICS_API_VULKAN +// XR_KHR_vulkan_enable2 is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_vulkan_enable2 1 #define XR_KHR_vulkan_enable2_SPEC_VERSION 2 #define XR_KHR_VULKAN_ENABLE2_EXTENSION_NAME "XR_KHR_vulkan_enable2" @@ -488,6 +501,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsRequirements2KHR( #ifdef XR_USE_PLATFORM_EGL +// XR_MNDX_egl_enable is a preprocessor guard. Do not pass it to API calls. #define XR_MNDX_egl_enable 1 #define XR_MNDX_egl_enable_SPEC_VERSION 2 #define XR_MNDX_EGL_ENABLE_EXTENSION_NAME "XR_MNDX_egl_enable" @@ -506,6 +520,7 @@ typedef struct XrGraphicsBindingEGLMNDX { #ifdef XR_USE_PLATFORM_WIN32 +// XR_MSFT_perception_anchor_interop is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_perception_anchor_interop 1 #define XR_MSFT_perception_anchor_interop_SPEC_VERSION 1 #define XR_MSFT_PERCEPTION_ANCHOR_INTEROP_EXTENSION_NAME "XR_MSFT_perception_anchor_interop" @@ -529,6 +544,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrTryGetPerceptionAnchorFromSpatialAnchorMSFT( #ifdef XR_USE_PLATFORM_WIN32 +// XR_MSFT_holographic_window_attachment is a preprocessor guard. Do not pass it to API calls. #define XR_MSFT_holographic_window_attachment 1 #define XR_MSFT_holographic_window_attachment_SPEC_VERSION 1 #define XR_MSFT_HOLOGRAPHIC_WINDOW_ATTACHMENT_EXTENSION_NAME "XR_MSFT_holographic_window_attachment" @@ -546,6 +562,7 @@ typedef struct XrHolographicWindowAttachmentMSFT { #ifdef XR_USE_PLATFORM_ANDROID +// XR_FB_android_surface_swapchain_create is a preprocessor guard. Do not pass it to API calls. #define XR_FB_android_surface_swapchain_create 1 #define XR_FB_android_surface_swapchain_create_SPEC_VERSION 1 #define XR_FB_ANDROID_SURFACE_SWAPCHAIN_CREATE_EXTENSION_NAME "XR_FB_android_surface_swapchain_create" @@ -568,6 +585,7 @@ typedef struct XrAndroidSurfaceSwapchainCreateInfoFB { #ifdef XR_USE_PLATFORM_ML +// XR_ML_compat is a preprocessor guard. Do not pass it to API calls. #define XR_ML_compat 1 #define XR_ML_compat_SPEC_VERSION 1 #define XR_ML_COMPAT_EXTENSION_NAME "XR_ML_compat" @@ -592,6 +610,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpaceFromCoordinateFrameUIDML( #ifdef XR_USE_PLATFORM_WIN32 +// XR_OCULUS_audio_device_guid is a preprocessor guard. Do not pass it to API calls. #define XR_OCULUS_audio_device_guid 1 #define XR_OCULUS_audio_device_guid_SPEC_VERSION 1 #define XR_OCULUS_AUDIO_DEVICE_GUID_EXTENSION_NAME "XR_OCULUS_audio_device_guid" @@ -614,6 +633,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetAudioInputDeviceGuidOculus( #ifdef XR_USE_GRAPHICS_API_VULKAN +// XR_FB_foveation_vulkan is a preprocessor guard. Do not pass it to API calls. #define XR_FB_foveation_vulkan 1 #define XR_FB_foveation_vulkan_SPEC_VERSION 1 #define XR_FB_FOVEATION_VULKAN_EXTENSION_NAME "XR_FB_foveation_vulkan" @@ -630,6 +650,7 @@ typedef struct XrSwapchainImageFoveationVulkanFB { #ifdef XR_USE_PLATFORM_ANDROID +// XR_FB_swapchain_update_state_android_surface is a preprocessor guard. Do not pass it to API calls. #define XR_FB_swapchain_update_state_android_surface 1 #define XR_FB_swapchain_update_state_android_surface_SPEC_VERSION 1 #define XR_FB_SWAPCHAIN_UPDATE_STATE_ANDROID_SURFACE_EXTENSION_NAME "XR_FB_swapchain_update_state_android_surface" @@ -646,6 +667,7 @@ typedef struct XrSwapchainStateAndroidSurfaceDimensionsFB { #ifdef XR_USE_GRAPHICS_API_OPENGL_ES +// XR_FB_swapchain_update_state_opengl_es is a preprocessor guard. Do not pass it to API calls. #define XR_FB_swapchain_update_state_opengl_es 1 #define XR_FB_swapchain_update_state_opengl_es_SPEC_VERSION 1 #define XR_FB_SWAPCHAIN_UPDATE_STATE_OPENGL_ES_EXTENSION_NAME "XR_FB_swapchain_update_state_opengl_es" @@ -670,6 +692,7 @@ typedef struct XrSwapchainStateSamplerOpenGLESFB { #ifdef XR_USE_GRAPHICS_API_VULKAN +// XR_FB_swapchain_update_state_vulkan is a preprocessor guard. Do not pass it to API calls. #define XR_FB_swapchain_update_state_vulkan 1 #define XR_FB_swapchain_update_state_vulkan_SPEC_VERSION 1 #define XR_FB_SWAPCHAIN_UPDATE_STATE_VULKAN_EXTENSION_NAME "XR_FB_swapchain_update_state_vulkan" @@ -695,6 +718,7 @@ typedef struct XrSwapchainStateSamplerVulkanFB { #ifdef XR_USE_GRAPHICS_API_VULKAN +// XR_META_vulkan_swapchain_create_info is a preprocessor guard. Do not pass it to API calls. #define XR_META_vulkan_swapchain_create_info 1 #define XR_META_vulkan_swapchain_create_info_SPEC_VERSION 1 #define XR_META_VULKAN_SWAPCHAIN_CREATE_INFO_EXTENSION_NAME "XR_META_vulkan_swapchain_create_info" diff --git a/thirdparty/openxr/include/openxr/openxr_reflection.h b/thirdparty/openxr/include/openxr/openxr_reflection.h index b449c7099b..b9d45689a7 100644 --- a/thirdparty/openxr/include/openxr/openxr_reflection.h +++ b/thirdparty/openxr/include/openxr/openxr_reflection.h @@ -86,6 +86,8 @@ XR_ENUM_STR(XrResult); _(XR_ERROR_LOCALIZED_NAME_INVALID, -49) \ _(XR_ERROR_GRAPHICS_REQUIREMENTS_CALL_MISSING, -50) \ _(XR_ERROR_RUNTIME_UNAVAILABLE, -51) \ + _(XR_ERROR_EXTENSION_DEPENDENCY_NOT_ENABLED, -1000710001) \ + _(XR_ERROR_PERMISSION_INSUFFICIENT, -1000710000) \ _(XR_ERROR_ANDROID_THREAD_SETTINGS_ID_INVALID_KHR, -1000003000) \ _(XR_ERROR_ANDROID_THREAD_SETTINGS_FAILURE_KHR, -1000003001) \ _(XR_ERROR_CREATE_SPATIAL_ANCHOR_FAILED_MSFT, -1000039001) \ @@ -135,10 +137,13 @@ XR_ENUM_STR(XrResult); _(XR_ERROR_SPACE_NETWORK_REQUEST_FAILED_FB, -1000169003) \ _(XR_ERROR_SPACE_CLOUD_STORAGE_DISABLED_FB, -1000169004) \ _(XR_ERROR_PASSTHROUGH_COLOR_LUT_BUFFER_SIZE_MISMATCH_META, -1000266000) \ + _(XR_ENVIRONMENT_DEPTH_NOT_AVAILABLE_META, 1000291000) \ _(XR_ERROR_HINT_ALREADY_SET_QCOM, -1000306000) \ _(XR_ERROR_NOT_AN_ANCHOR_HTC, -1000319000) \ _(XR_ERROR_SPACE_NOT_LOCATABLE_EXT, -1000429000) \ _(XR_ERROR_PLANE_DETECTION_PERMISSION_DENIED_EXT, -1000429001) \ + _(XR_ERROR_FUTURE_PENDING_EXT, -1000469001) \ + _(XR_ERROR_FUTURE_INVALID_EXT, -1000469002) \ _(XR_RESULT_MAX_ENUM, 0x7FFFFFFF) #define XR_LIST_ENUM_XrStructureType(_) \ @@ -192,6 +197,9 @@ XR_ENUM_STR(XrResult); _(XR_TYPE_ACTIONS_SYNC_INFO, 61) \ _(XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO, 62) \ _(XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO, 63) \ + _(XR_TYPE_SPACES_LOCATE_INFO, 1000471000) \ + _(XR_TYPE_SPACE_LOCATIONS, 1000471001) \ + _(XR_TYPE_SPACE_VELOCITIES, 1000471002) \ _(XR_TYPE_COMPOSITION_LAYER_CUBE_KHR, 1000006000) \ _(XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR, 1000008000) \ _(XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR, 1000010000) \ @@ -457,6 +465,14 @@ XR_ENUM_STR(XrResult); _(XR_TYPE_FACE_TRACKER_CREATE_INFO2_FB, 1000287014) \ _(XR_TYPE_FACE_EXPRESSION_INFO2_FB, 1000287015) \ _(XR_TYPE_FACE_EXPRESSION_WEIGHTS2_FB, 1000287016) \ + _(XR_TYPE_ENVIRONMENT_DEPTH_PROVIDER_CREATE_INFO_META, 1000291000) \ + _(XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_CREATE_INFO_META, 1000291001) \ + _(XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_STATE_META, 1000291002) \ + _(XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_ACQUIRE_INFO_META, 1000291003) \ + _(XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_VIEW_META, 1000291004) \ + _(XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_META, 1000291005) \ + _(XR_TYPE_ENVIRONMENT_DEPTH_HAND_REMOVAL_SET_INFO_META, 1000291006) \ + _(XR_TYPE_SYSTEM_ENVIRONMENT_DEPTH_PROPERTIES_META, 1000291007) \ _(XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC, 1000317001) \ _(XR_TYPE_PASSTHROUGH_COLOR_HTC, 1000317002) \ _(XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC, 1000317003) \ @@ -478,6 +494,10 @@ XR_ENUM_STR(XrResult); _(XR_TYPE_PLANE_DETECTOR_LOCATION_EXT, 1000429005) \ _(XR_TYPE_PLANE_DETECTOR_POLYGON_BUFFER_EXT, 1000429006) \ _(XR_TYPE_SYSTEM_PLANE_DETECTION_PROPERTIES_EXT, 1000429007) \ + _(XR_TYPE_FUTURE_CANCEL_INFO_EXT, 1000469000) \ + _(XR_TYPE_FUTURE_POLL_INFO_EXT, 1000469001) \ + _(XR_TYPE_FUTURE_COMPLETION_EXT, 1000469002) \ + _(XR_TYPE_FUTURE_POLL_RESULT_EXT, 1000469003) \ _(XR_TYPE_EVENT_DATA_USER_PRESENCE_CHANGED_EXT, 1000470000) \ _(XR_TYPE_SYSTEM_USER_PRESENCE_PROPERTIES_EXT, 1000470001) \ _(XR_STRUCTURE_TYPE_MAX_ENUM, 0x7FFFFFFF) @@ -490,7 +510,7 @@ XR_ENUM_STR(XrResult); #define XR_LIST_ENUM_XrViewConfigurationType(_) \ _(XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO, 1) \ _(XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, 2) \ - _(XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO, 1000037000) \ + _(XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO_WITH_FOVEATED_INSET, 1000037000) \ _(XR_VIEW_CONFIGURATION_TYPE_SECONDARY_MONO_FIRST_PERSON_OBSERVER_MSFT, 1000054000) \ _(XR_VIEW_CONFIGURATION_TYPE_MAX_ENUM, 0x7FFFFFFF) @@ -504,10 +524,10 @@ XR_ENUM_STR(XrResult); _(XR_REFERENCE_SPACE_TYPE_VIEW, 1) \ _(XR_REFERENCE_SPACE_TYPE_LOCAL, 2) \ _(XR_REFERENCE_SPACE_TYPE_STAGE, 3) \ + _(XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR, 1000426000) \ _(XR_REFERENCE_SPACE_TYPE_UNBOUNDED_MSFT, 1000038000) \ _(XR_REFERENCE_SPACE_TYPE_COMBINED_EYE_VARJO, 1000121000) \ _(XR_REFERENCE_SPACE_TYPE_LOCALIZATION_MAP_ML, 1000139000) \ - _(XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT, 1000426000) \ _(XR_REFERENCE_SPACE_TYPE_MAX_ENUM, 0x7FFFFFFF) #define XR_LIST_ENUM_XrActionType(_) \ @@ -566,6 +586,8 @@ XR_ENUM_STR(XrResult); _(XR_OBJECT_TYPE_SPACE_USER_FB, 1000241000) \ _(XR_OBJECT_TYPE_PASSTHROUGH_COLOR_LUT_META, 1000266000) \ _(XR_OBJECT_TYPE_FACE_TRACKER2_FB, 1000287012) \ + _(XR_OBJECT_TYPE_ENVIRONMENT_DEPTH_PROVIDER_META, 1000291000) \ + _(XR_OBJECT_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_META, 1000291001) \ _(XR_OBJECT_TYPE_PASSTHROUGH_HTC, 1000317000) \ _(XR_OBJECT_TYPE_PLANE_DETECTOR_EXT, 1000429000) \ _(XR_OBJECT_TYPE_MAX_ENUM, 0x7FFFFFFF) @@ -1377,6 +1399,11 @@ XR_ENUM_STR(XrResult); _(XR_PLANE_DETECTION_STATE_FATAL_EXT, 4) \ _(XR_PLANE_DETECTION_STATE_MAX_ENUM_EXT, 0x7FFFFFFF) +#define XR_LIST_ENUM_XrFutureStateEXT(_) \ + _(XR_FUTURE_STATE_PENDING_EXT, 1) \ + _(XR_FUTURE_STATE_READY_EXT, 2) \ + _(XR_FUTURE_STATE_MAX_ENUM_EXT, 0x7FFFFFFF) + #define XR_LIST_ENUM_XrHeadsetFitStatusML(_) \ _(XR_HEADSET_FIT_STATUS_UNKNOWN_ML, 0) \ _(XR_HEADSET_FIT_STATUS_NOT_WORN_ML, 1) \ @@ -1424,6 +1451,7 @@ XR_ENUM_STR(XrResult); _(XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT, 0x00000001) \ _(XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT, 0x00000002) \ _(XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT, 0x00000004) \ + _(XR_COMPOSITION_LAYER_INVERTED_ALPHA_BIT_EXT, 0x00000008) \ #define XR_LIST_BITS_XrViewStateFlags(_) \ _(XR_VIEW_STATE_ORIENTATION_VALID_BIT, 0x00000001) \ @@ -1573,6 +1601,10 @@ XR_ENUM_STR(XrResult); _(XR_PERFORMANCE_METRICS_COUNTER_UINT_VALUE_VALID_BIT_META, 0x00000002) \ _(XR_PERFORMANCE_METRICS_COUNTER_FLOAT_VALUE_VALID_BIT_META, 0x00000004) \ +#define XR_LIST_BITS_XrEnvironmentDepthProviderCreateFlagsMETA(_) + +#define XR_LIST_BITS_XrEnvironmentDepthSwapchainCreateFlagsMETA(_) + #define XR_LIST_BITS_XrFoveationDynamicFlagsHTC(_) \ _(XR_FOVEATION_DYNAMIC_LEVEL_ENABLED_BIT_HTC, 0x00000001) \ _(XR_FOVEATION_DYNAMIC_CLEAR_FOV_ENABLED_BIT_HTC, 0x00000002) \ @@ -2152,6 +2184,73 @@ XR_ENUM_STR(XrResult); _(getInstanceProcAddr) \ _(createApiLayerInstance) \ +/// Calls your macro with the name of each member of XrColor3f, in order. +#define XR_LIST_STRUCT_XrColor3f(_) \ + _(r) \ + _(g) \ + _(b) \ + +/// Calls your macro with the name of each member of XrExtent3Df, in order. +#define XR_LIST_STRUCT_XrExtent3Df(_) \ + _(width) \ + _(height) \ + _(depth) \ + +/// Calls your macro with the name of each member of XrSpheref, in order. +#define XR_LIST_STRUCT_XrSpheref(_) \ + _(center) \ + _(radius) \ + +/// Calls your macro with the name of each member of XrBoxf, in order. +#define XR_LIST_STRUCT_XrBoxf(_) \ + _(center) \ + _(extents) \ + +/// Calls your macro with the name of each member of XrFrustumf, in order. +#define XR_LIST_STRUCT_XrFrustumf(_) \ + _(pose) \ + _(fov) \ + _(nearZ) \ + _(farZ) \ + +/// Calls your macro with the name of each member of XrUuid, in order. +#define XR_LIST_STRUCT_XrUuid(_) \ + _(data) \ + +/// Calls your macro with the name of each member of XrSpacesLocateInfo, in order. +#define XR_LIST_STRUCT_XrSpacesLocateInfo(_) \ + _(type) \ + _(next) \ + _(baseSpace) \ + _(time) \ + _(spaceCount) \ + _(spaces) \ + +/// Calls your macro with the name of each member of XrSpaceLocationData, in order. +#define XR_LIST_STRUCT_XrSpaceLocationData(_) \ + _(locationFlags) \ + _(pose) \ + +/// Calls your macro with the name of each member of XrSpaceLocations, in order. +#define XR_LIST_STRUCT_XrSpaceLocations(_) \ + _(type) \ + _(next) \ + _(locationCount) \ + _(locations) \ + +/// Calls your macro with the name of each member of XrSpaceVelocityData, in order. +#define XR_LIST_STRUCT_XrSpaceVelocityData(_) \ + _(velocityFlags) \ + _(linearVelocity) \ + _(angularVelocity) \ + +/// Calls your macro with the name of each member of XrSpaceVelocities, in order. +#define XR_LIST_STRUCT_XrSpaceVelocities(_) \ + _(type) \ + _(next) \ + _(velocityCount) \ + _(velocities) \ + /// Calls your macro with the name of each member of XrCompositionLayerCubeKHR, in order. #define XR_LIST_STRUCT_XrCompositionLayerCubeKHR(_) \ _(type) \ @@ -3231,10 +3330,6 @@ XR_ENUM_STR(XrResult); _(enabled) \ _(changePending) \ -/// Calls your macro with the name of each member of XrUuidEXT, in order. -#define XR_LIST_STRUCT_XrUuidEXT(_) \ - _(data) \ - /// Calls your macro with the name of each member of XrEventDataSpatialAnchorCreateCompleteFB, in order. #define XR_LIST_STRUCT_XrEventDataSpatialAnchorCreateCompleteFB(_) \ _(type) \ @@ -3857,12 +3952,6 @@ XR_ENUM_STR(XrResult); _(amplitudeCount) \ _(amplitudes) \ -/// Calls your macro with the name of each member of XrExtent3DfFB, in order. -#define XR_LIST_STRUCT_XrExtent3DfFB(_) \ - _(width) \ - _(height) \ - _(depth) \ - /// Calls your macro with the name of each member of XrOffset3DfFB, in order. #define XR_LIST_STRUCT_XrOffset3DfFB(_) \ _(x) \ @@ -4348,6 +4437,61 @@ XR_ENUM_STR(XrResult); _(dataSource) \ _(time) \ +/// Calls your macro with the name of each member of XrEnvironmentDepthProviderCreateInfoMETA, in order. +#define XR_LIST_STRUCT_XrEnvironmentDepthProviderCreateInfoMETA(_) \ + _(type) \ + _(next) \ + _(createFlags) \ + +/// Calls your macro with the name of each member of XrEnvironmentDepthSwapchainCreateInfoMETA, in order. +#define XR_LIST_STRUCT_XrEnvironmentDepthSwapchainCreateInfoMETA(_) \ + _(type) \ + _(next) \ + _(createFlags) \ + +/// Calls your macro with the name of each member of XrEnvironmentDepthSwapchainStateMETA, in order. +#define XR_LIST_STRUCT_XrEnvironmentDepthSwapchainStateMETA(_) \ + _(type) \ + _(next) \ + _(width) \ + _(height) \ + +/// Calls your macro with the name of each member of XrEnvironmentDepthImageAcquireInfoMETA, in order. +#define XR_LIST_STRUCT_XrEnvironmentDepthImageAcquireInfoMETA(_) \ + _(type) \ + _(next) \ + _(space) \ + _(displayTime) \ + +/// Calls your macro with the name of each member of XrEnvironmentDepthImageViewMETA, in order. +#define XR_LIST_STRUCT_XrEnvironmentDepthImageViewMETA(_) \ + _(type) \ + _(next) \ + _(fov) \ + _(pose) \ + +/// Calls your macro with the name of each member of XrEnvironmentDepthImageMETA, in order. +#define XR_LIST_STRUCT_XrEnvironmentDepthImageMETA(_) \ + _(type) \ + _(next) \ + _(swapchainIndex) \ + _(nearZ) \ + _(farZ) \ + _(views) \ + +/// Calls your macro with the name of each member of XrEnvironmentDepthHandRemovalSetInfoMETA, in order. +#define XR_LIST_STRUCT_XrEnvironmentDepthHandRemovalSetInfoMETA(_) \ + _(type) \ + _(next) \ + _(enabled) \ + +/// Calls your macro with the name of each member of XrSystemEnvironmentDepthPropertiesMETA, in order. +#define XR_LIST_STRUCT_XrSystemEnvironmentDepthPropertiesMETA(_) \ + _(type) \ + _(next) \ + _(supportsEnvironmentDepth) \ + _(supportsHandRemoval) \ + /// Calls your macro with the name of each member of XrPassthroughCreateInfoHTC, in order. #define XR_LIST_STRUCT_XrPassthroughCreateInfoHTC(_) \ _(type) \ @@ -4483,12 +4627,6 @@ XR_ENUM_STR(XrResult); _(next) \ _(flags) \ -/// Calls your macro with the name of each member of XrExtent3DfEXT, in order. -#define XR_LIST_STRUCT_XrExtent3DfEXT(_) \ - _(width) \ - _(height) \ - _(depth) \ - /// Calls your macro with the name of each member of XrPlaneDetectorBeginInfoEXT, in order. #define XR_LIST_STRUCT_XrPlaneDetectorBeginInfoEXT(_) \ _(type) \ @@ -4539,6 +4677,36 @@ XR_ENUM_STR(XrResult); _(vertexCountOutput) \ _(vertices) \ +/// Calls your macro with the name of each member of XrFutureCancelInfoEXT, in order. +#define XR_LIST_STRUCT_XrFutureCancelInfoEXT(_) \ + _(type) \ + _(next) \ + _(future) \ + +/// Calls your macro with the name of each member of XrFuturePollInfoEXT, in order. +#define XR_LIST_STRUCT_XrFuturePollInfoEXT(_) \ + _(type) \ + _(next) \ + _(future) \ + +/// Calls your macro with the name of each member of XrFutureCompletionBaseHeaderEXT, in order. +#define XR_LIST_STRUCT_XrFutureCompletionBaseHeaderEXT(_) \ + _(type) \ + _(next) \ + _(futureResult) \ + +/// Calls your macro with the name of each member of XrFutureCompletionEXT, in order. +#define XR_LIST_STRUCT_XrFutureCompletionEXT(_) \ + _(type) \ + _(next) \ + _(futureResult) \ + +/// Calls your macro with the name of each member of XrFuturePollResultEXT, in order. +#define XR_LIST_STRUCT_XrFuturePollResultEXT(_) \ + _(type) \ + _(next) \ + _(state) \ + /// Calls your macro with the name of each member of XrEventDataUserPresenceChangedEXT, in order. #define XR_LIST_STRUCT_XrEventDataUserPresenceChangedEXT(_) \ _(type) \ @@ -4644,6 +4812,9 @@ XR_ENUM_STR(XrResult); _(XrEventDataReferenceSpaceChangePending, XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING) \ _(XrEventDataInteractionProfileChanged, XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED) \ _(XrHapticVibration, XR_TYPE_HAPTIC_VIBRATION) \ + _(XrSpacesLocateInfo, XR_TYPE_SPACES_LOCATE_INFO) \ + _(XrSpaceLocations, XR_TYPE_SPACE_LOCATIONS) \ + _(XrSpaceVelocities, XR_TYPE_SPACE_VELOCITIES) \ _(XrCompositionLayerCubeKHR, XR_TYPE_COMPOSITION_LAYER_CUBE_KHR) \ _(XrCompositionLayerDepthInfoKHR, XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR) \ _(XrCompositionLayerCylinderKHR, XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR) \ @@ -4873,6 +5044,14 @@ XR_ENUM_STR(XrResult); _(XrFaceTrackerCreateInfo2FB, XR_TYPE_FACE_TRACKER_CREATE_INFO2_FB) \ _(XrFaceExpressionInfo2FB, XR_TYPE_FACE_EXPRESSION_INFO2_FB) \ _(XrFaceExpressionWeights2FB, XR_TYPE_FACE_EXPRESSION_WEIGHTS2_FB) \ + _(XrEnvironmentDepthProviderCreateInfoMETA, XR_TYPE_ENVIRONMENT_DEPTH_PROVIDER_CREATE_INFO_META) \ + _(XrEnvironmentDepthSwapchainCreateInfoMETA, XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_CREATE_INFO_META) \ + _(XrEnvironmentDepthSwapchainStateMETA, XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_STATE_META) \ + _(XrEnvironmentDepthImageAcquireInfoMETA, XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_ACQUIRE_INFO_META) \ + _(XrEnvironmentDepthImageViewMETA, XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_VIEW_META) \ + _(XrEnvironmentDepthImageMETA, XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_META) \ + _(XrEnvironmentDepthHandRemovalSetInfoMETA, XR_TYPE_ENVIRONMENT_DEPTH_HAND_REMOVAL_SET_INFO_META) \ + _(XrSystemEnvironmentDepthPropertiesMETA, XR_TYPE_SYSTEM_ENVIRONMENT_DEPTH_PROPERTIES_META) \ _(XrPassthroughCreateInfoHTC, XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC) \ _(XrPassthroughColorHTC, XR_TYPE_PASSTHROUGH_COLOR_HTC) \ _(XrPassthroughMeshTransformInfoHTC, XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC) \ @@ -4894,6 +5073,10 @@ XR_ENUM_STR(XrResult); _(XrPlaneDetectorLocationEXT, XR_TYPE_PLANE_DETECTOR_LOCATION_EXT) \ _(XrPlaneDetectorLocationsEXT, XR_TYPE_PLANE_DETECTOR_LOCATIONS_EXT) \ _(XrPlaneDetectorPolygonBufferEXT, XR_TYPE_PLANE_DETECTOR_POLYGON_BUFFER_EXT) \ + _(XrFutureCancelInfoEXT, XR_TYPE_FUTURE_CANCEL_INFO_EXT) \ + _(XrFuturePollInfoEXT, XR_TYPE_FUTURE_POLL_INFO_EXT) \ + _(XrFutureCompletionEXT, XR_TYPE_FUTURE_COMPLETION_EXT) \ + _(XrFuturePollResultEXT, XR_TYPE_FUTURE_POLL_RESULT_EXT) \ _(XrEventDataUserPresenceChangedEXT, XR_TYPE_EVENT_DATA_USER_PRESENCE_CHANGED_EXT) \ _(XrSystemUserPresencePropertiesEXT, XR_TYPE_SYSTEM_USER_PRESENCE_PROPERTIES_EXT) \ _(XrEventDataHeadsetFitChangedML, XR_TYPE_EVENT_DATA_HEADSET_FIT_CHANGED_ML) \ @@ -5200,6 +5383,7 @@ XR_ENUM_STR(XrResult); _(XR_META_automatic_layer_filter, 272) \ _(XR_META_touch_controller_plus, 280) \ _(XR_FB_face_tracking2, 288) \ + _(XR_META_environment_depth, 292) \ _(XR_EXT_uuid, 300) \ _(XR_EXT_hand_interaction, 303) \ _(XR_QCOM_tracking_optimization_settings, 307) \ @@ -5213,9 +5397,13 @@ XR_ENUM_STR(XrResult); _(XR_EXT_hand_tracking_data_source, 429) \ _(XR_EXT_plane_detection, 430) \ _(XR_OPPO_controller_interaction, 454) \ + _(XR_EXT_future, 470) \ _(XR_EXT_user_presence, 471) \ + _(XR_KHR_locate_spaces, 472) \ _(XR_ML_user_calibration, 473) \ _(XR_YVR_controller_interaction, 498) \ + _(XR_EXT_composition_layer_inverted_alpha, 555) \ + _(XR_KHR_maintenance1, 711) \ @@ -5291,6 +5479,14 @@ XR_ENUM_STR(XrResult); _(NegotiateLoaderApiLayerInterface, LOADER_VERSION_1_0) \ +/// For every function defined by XR_VERSION_1_1 in this version of the spec, +/// calls your macro with the function name and extension name. +/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, +/// because it is easy to add back but impossible to remove with the preprocessor. +#define XR_LIST_FUNCTIONS_XR_VERSION_1_1(_) \ + _(LocateSpaces, VERSION_1_1) \ + + /// For every function defined by XR_KHR_android_thread_settings in this version of the spec, /// calls your macro with the function name and extension name. /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, @@ -5973,6 +6169,23 @@ XR_ENUM_STR(XrResult); _(GetFaceExpressionWeights2FB, FB_face_tracking2) \ +/// For every function defined by XR_META_environment_depth in this version of the spec, +/// calls your macro with the function name and extension name. +/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, +/// because it is easy to add back but impossible to remove with the preprocessor. +#define XR_LIST_FUNCTIONS_XR_META_environment_depth(_) \ + _(CreateEnvironmentDepthProviderMETA, META_environment_depth) \ + _(DestroyEnvironmentDepthProviderMETA, META_environment_depth) \ + _(StartEnvironmentDepthProviderMETA, META_environment_depth) \ + _(StopEnvironmentDepthProviderMETA, META_environment_depth) \ + _(CreateEnvironmentDepthSwapchainMETA, META_environment_depth) \ + _(DestroyEnvironmentDepthSwapchainMETA, META_environment_depth) \ + _(EnumerateEnvironmentDepthSwapchainImagesMETA, META_environment_depth) \ + _(GetEnvironmentDepthSwapchainStateMETA, META_environment_depth) \ + _(AcquireEnvironmentDepthImageMETA, META_environment_depth) \ + _(SetEnvironmentDepthHandRemovalMETA, META_environment_depth) \ + + /// For every function defined by XR_QCOM_tracking_optimization_settings in this version of the spec, /// calls your macro with the function name and extension name. /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, @@ -6028,6 +6241,15 @@ XR_ENUM_STR(XrResult); _(GetPlanePolygonBufferEXT, EXT_plane_detection) \ +/// For every function defined by XR_EXT_future in this version of the spec, +/// calls your macro with the function name and extension name. +/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, +/// because it is easy to add back but impossible to remove with the preprocessor. +#define XR_LIST_FUNCTIONS_XR_EXT_future(_) \ + _(PollFutureEXT, EXT_future) \ + _(CancelFutureEXT, EXT_future) \ + + /// For every function defined by XR_ML_user_calibration in this version of the spec, /// calls your macro with the function name and extension name. /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, diff --git a/thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h b/thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h index 4241682e0b..50466abc1b 100644 --- a/thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h +++ b/thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h @@ -32,6 +32,7 @@ This file contains expansion macros (X Macros) for OpenXR structures that have a _avail(XrCompositionLayerCylinderKHR, XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR) \ _avail(XrCompositionLayerEquirectKHR, XR_TYPE_COMPOSITION_LAYER_EQUIRECT_KHR) \ _avail(XrCompositionLayerEquirect2KHR, XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR) \ + _avail(XrCompositionLayerPassthroughFB, XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB) \ _avail(XrCompositionLayerPassthroughHTC, XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_HTC) \ @@ -264,5 +265,18 @@ This file contains expansion macros (X Macros) for OpenXR structures that have a +/// Like XR_LIST_ALL_STRUCTURE_TYPES, but only includes types whose parent struct type is XrFutureCompletionBaseHeaderEXT +#define XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrFutureCompletionBaseHeaderEXT(_avail, _unavail) \ + _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrFutureCompletionBaseHeaderEXT_CORE(_avail, _unavail) \ + + +// Implementation detail of XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrFutureCompletionBaseHeaderEXT() +#define _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrFutureCompletionBaseHeaderEXT_CORE(_avail, _unavail) \ + _avail(XrFutureCompletionEXT, XR_TYPE_FUTURE_COMPLETION_EXT) \ + + + + + #endif diff --git a/thirdparty/openxr/include/openxr/openxr_reflection_structs.h b/thirdparty/openxr/include/openxr/openxr_reflection_structs.h index f973539cbb..e87e55b560 100644 --- a/thirdparty/openxr/include/openxr/openxr_reflection_structs.h +++ b/thirdparty/openxr/include/openxr/openxr_reflection_structs.h @@ -92,6 +92,9 @@ This file contains expansion macros (X Macros) for OpenXR structures. _avail(XrEventDataReferenceSpaceChangePending, XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING) \ _avail(XrEventDataInteractionProfileChanged, XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED) \ _avail(XrHapticVibration, XR_TYPE_HAPTIC_VIBRATION) \ + _avail(XrSpacesLocateInfo, XR_TYPE_SPACES_LOCATE_INFO) \ + _avail(XrSpaceLocations, XR_TYPE_SPACE_LOCATIONS) \ + _avail(XrSpaceVelocities, XR_TYPE_SPACE_VELOCITIES) \ _avail(XrCompositionLayerCubeKHR, XR_TYPE_COMPOSITION_LAYER_CUBE_KHR) \ _avail(XrCompositionLayerDepthInfoKHR, XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR) \ _avail(XrCompositionLayerCylinderKHR, XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR) \ @@ -321,6 +324,14 @@ This file contains expansion macros (X Macros) for OpenXR structures. _avail(XrFaceTrackerCreateInfo2FB, XR_TYPE_FACE_TRACKER_CREATE_INFO2_FB) \ _avail(XrFaceExpressionInfo2FB, XR_TYPE_FACE_EXPRESSION_INFO2_FB) \ _avail(XrFaceExpressionWeights2FB, XR_TYPE_FACE_EXPRESSION_WEIGHTS2_FB) \ + _avail(XrEnvironmentDepthProviderCreateInfoMETA, XR_TYPE_ENVIRONMENT_DEPTH_PROVIDER_CREATE_INFO_META) \ + _avail(XrEnvironmentDepthSwapchainCreateInfoMETA, XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_CREATE_INFO_META) \ + _avail(XrEnvironmentDepthSwapchainStateMETA, XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_STATE_META) \ + _avail(XrEnvironmentDepthImageAcquireInfoMETA, XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_ACQUIRE_INFO_META) \ + _avail(XrEnvironmentDepthImageViewMETA, XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_VIEW_META) \ + _avail(XrEnvironmentDepthImageMETA, XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_META) \ + _avail(XrEnvironmentDepthHandRemovalSetInfoMETA, XR_TYPE_ENVIRONMENT_DEPTH_HAND_REMOVAL_SET_INFO_META) \ + _avail(XrSystemEnvironmentDepthPropertiesMETA, XR_TYPE_SYSTEM_ENVIRONMENT_DEPTH_PROPERTIES_META) \ _avail(XrPassthroughCreateInfoHTC, XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC) \ _avail(XrPassthroughColorHTC, XR_TYPE_PASSTHROUGH_COLOR_HTC) \ _avail(XrPassthroughMeshTransformInfoHTC, XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC) \ @@ -342,6 +353,10 @@ This file contains expansion macros (X Macros) for OpenXR structures. _avail(XrPlaneDetectorLocationEXT, XR_TYPE_PLANE_DETECTOR_LOCATION_EXT) \ _avail(XrPlaneDetectorLocationsEXT, XR_TYPE_PLANE_DETECTOR_LOCATIONS_EXT) \ _avail(XrPlaneDetectorPolygonBufferEXT, XR_TYPE_PLANE_DETECTOR_POLYGON_BUFFER_EXT) \ + _avail(XrFutureCancelInfoEXT, XR_TYPE_FUTURE_CANCEL_INFO_EXT) \ + _avail(XrFuturePollInfoEXT, XR_TYPE_FUTURE_POLL_INFO_EXT) \ + _avail(XrFutureCompletionEXT, XR_TYPE_FUTURE_COMPLETION_EXT) \ + _avail(XrFuturePollResultEXT, XR_TYPE_FUTURE_POLL_RESULT_EXT) \ _avail(XrEventDataUserPresenceChangedEXT, XR_TYPE_EVENT_DATA_USER_PRESENCE_CHANGED_EXT) \ _avail(XrSystemUserPresencePropertiesEXT, XR_TYPE_SYSTEM_USER_PRESENCE_PROPERTIES_EXT) \ _avail(XrEventDataHeadsetFitChangedML, XR_TYPE_EVENT_DATA_HEADSET_FIT_CHANGED_ML) \ diff --git a/thirdparty/openxr/src/common/platform_utils.hpp b/thirdparty/openxr/src/common/platform_utils.hpp index 35369a1477..d047c17d9d 100644 --- a/thirdparty/openxr/src/common/platform_utils.hpp +++ b/thirdparty/openxr/src/common/platform_utils.hpp @@ -47,7 +47,7 @@ #define XR_ARCH_ABI "aarch64" #elif (defined(__ARM_ARCH) && __ARM_ARCH >= 7 && (defined(__ARM_PCS_VFP) || defined(__ANDROID__))) || defined(_M_ARM) #define XR_ARCH_ABI "armv7a-vfp" -#elif defined(__ARM_ARCH_5TE__) +#elif defined(__ARM_ARCH_5TE__) || (defined(__ARM_ARCH) && __ARM_ARCH > 5) #define XR_ARCH_ABI "armv5te" #elif defined(__mips64) #define XR_ARCH_ABI "mips64" @@ -91,8 +91,6 @@ namespace detail { static inline char* ImplGetEnv(const char* name) { return getenv(name); } -static inline int ImplSetEnv(const char* name, const char* value, int overwrite) { return setenv(name, value, overwrite); } - static inline char* ImplGetSecureEnv(const char* name) { #ifdef HAVE_SECURE_GETENV return secure_getenv(name); @@ -162,12 +160,6 @@ static inline std::string PlatformUtilsGetSecureEnv(const char* name) { static inline bool PlatformUtilsGetEnvSet(const char* name) { return detail::ImplGetEnv(name) != nullptr; } -static inline bool PlatformUtilsSetEnv(const char* name, const char* value) { - const int shouldOverwrite = 1; - int result = detail::ImplSetEnv(name, value, shouldOverwrite); - return (result == 0); -} - #elif defined(XR_OS_APPLE) static inline std::string PlatformUtilsGetEnv(const char* name) { @@ -188,12 +180,6 @@ static inline std::string PlatformUtilsGetSecureEnv(const char* name) { static inline bool PlatformUtilsGetEnvSet(const char* name) { return detail::ImplGetEnv(name) != nullptr; } -static inline bool PlatformUtilsSetEnv(const char* name, const char* value) { - const int shouldOverwrite = 1; - int result = detail::ImplSetEnv(name, value, shouldOverwrite); - return (result == 0); -} - static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std::string& file_name) { return detail::ImplTryRuntimeFilename("/usr/local/share/openxr/", major_version, file_name); } @@ -337,19 +323,10 @@ static inline std::string PlatformUtilsGetSecureEnv(const char* name) { return envValue; } -// Sets an environment variable via UTF8 strings. -// The name is case-sensitive. -// Overwrites the variable if it already exists. -// Returns true if it could be set. -static inline bool PlatformUtilsSetEnv(const char* name, const char* value) { - const std::wstring wname = utf8_to_wide(name); - const std::wstring wvalue = utf8_to_wide(value); - BOOL result = ::SetEnvironmentVariableW(wname.c_str(), wvalue.c_str()); - return (result != 0); -} - #elif defined(XR_OS_ANDROID) +#include <sys/system_properties.h> + static inline bool PlatformUtilsGetEnvSet(const char* /* name */) { // Stub func return false; @@ -365,11 +342,6 @@ static inline std::string PlatformUtilsGetSecureEnv(const char* /* name */) { return {}; } -static inline bool PlatformUtilsSetEnv(const char* /* name */, const char* /* value */) { - // Stub func - return false; -} - // Intended to be only used as a fallback on Android, with a more open, "native" technique used in most cases static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std::string& file_name) { // Prefix for the runtime JSON file name @@ -385,6 +357,37 @@ static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std: return false; } + +// Android system properties are sufficiently different from environment variables that we are not re-using +// PlatformUtilsGetEnv for this purpose +static inline std::string PlatformUtilsGetAndroidSystemProperty(const char* name) { + std::string result; + const prop_info* pi = __system_property_find(name); + if (pi == nullptr) { + return {}; + } + +#if __ANDROID_API__ >= 26 + // use callback to retrieve > 92 character sys prop values, if available + __system_property_read_callback( + pi, + [](void* cookie, const char*, const char* value, uint32_t) { + auto property_value = reinterpret_cast<std::string*>(cookie); + *property_value = value; + }, + reinterpret_cast<void*>(&result)); +#endif // __ANDROID_API__ >= 26 + // fallback to __system_property_get if no value retrieved via callback + if (result.empty()) { + char value[PROP_VALUE_MAX] = {}; + if (__system_property_get(name, value) != 0) { + result = value; + } + } + + return result; +} + #else // Not Linux, Apple, nor Windows static inline bool PlatformUtilsGetEnvSet(const char* /* name */) { @@ -402,11 +405,6 @@ static inline std::string PlatformUtilsGetSecureEnv(const char* /* name */) { return {}; } -static inline bool PlatformUtilsSetEnv(const char* /* name */, const char* /* value */) { - // Stub func - return false; -} - static inline bool PlatformGetGlobalRuntimeFileName(uint16_t /* major_version */, std::string const& /* file_name */) { // Stub func return false; diff --git a/thirdparty/spirv-reflect/patches/specialization-constants.patch b/thirdparty/spirv-reflect/patches/1-specialization-constants.patch index ff11841451..ff11841451 100644 --- a/thirdparty/spirv-reflect/patches/specialization-constants.patch +++ b/thirdparty/spirv-reflect/patches/1-specialization-constants.patch diff --git a/thirdparty/spirv-reflect/patches/2-zero-size-for-sc-sized-arrays.patch b/thirdparty/spirv-reflect/patches/2-zero-size-for-sc-sized-arrays.patch new file mode 100644 index 0000000000..dbf6069d07 --- /dev/null +++ b/thirdparty/spirv-reflect/patches/2-zero-size-for-sc-sized-arrays.patch @@ -0,0 +1,18 @@ +diff --git a/thirdparty/spirv-reflect/spirv_reflect.c b/thirdparty/spirv-reflect/spirv_reflect.c +index c96dd85439..2ca9c8580d 100644 +--- a/thirdparty/spirv-reflect/spirv_reflect.c ++++ b/thirdparty/spirv-reflect/spirv_reflect.c +@@ -2692,6 +2692,13 @@ static SpvReflectResult ParseDescriptorBlockVariableSizes(SpvReflectPrvParser* p + // ...then array + uint32_t element_count = (p_member_var->array.dims_count > 0 ? 1 : 0); + for (uint32_t i = 0; i < p_member_var->array.dims_count; ++i) { ++// -- GODOT begin -- ++ if (p_member_var->array.spec_constant_op_ids[i] != (uint32_t)INVALID_VALUE) { ++ // Force size to be reported as 0 to effectively disable buffer size validation, since ++ // the value is unreliable anyway as only valid for the default values of the SCs involved. ++ element_count = 0; ++ } ++// -- GODOT end -- + element_count *= p_member_var->array.dims[i]; + } + p_member_var->size = element_count * p_member_var->array.stride; diff --git a/thirdparty/spirv-reflect/spirv_reflect.c b/thirdparty/spirv-reflect/spirv_reflect.c index c96dd85439..d6c926b40a 100644 --- a/thirdparty/spirv-reflect/spirv_reflect.c +++ b/thirdparty/spirv-reflect/spirv_reflect.c @@ -2692,6 +2692,13 @@ static SpvReflectResult ParseDescriptorBlockVariableSizes(SpvReflectPrvParser* p // ...then array uint32_t element_count = (p_member_var->array.dims_count > 0 ? 1 : 0); for (uint32_t i = 0; i < p_member_var->array.dims_count; ++i) { +// -- GODOT begin -- + if (p_member_var->array.spec_constant_op_ids[i] != (uint32_t)INVALID_VALUE) { + // Force size to be reported as 0 to effectively disable buffer size validation, since + // the value is unreliable anyway as only valid for the default values of the SCs involved. + element_count = 0; + } +// -- GODOT end -- element_count *= p_member_var->array.dims[i]; } p_member_var->size = element_count * p_member_var->array.stride; |