diff --git a/.github/workflows/godot_cpp_test.yml b/.github/workflows/godot_cpp_test.yml index 57114dacfc20..e1c37bbc1e14 100644 --- a/.github/workflows/godot_cpp_test.yml +++ b/.github/workflows/godot_cpp_test.yml @@ -7,7 +7,7 @@ env: # Used for the cache key. Add version suffix to force clean build. GODOT_BASE_BRANCH: master # Used for the godot-cpp checkout. - GODOT_CPP_BRANCH: '4.2' + GODOT_CPP_BRANCH: '4.3' concurrency: group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-cpp-tests diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 37a2608c102c..5b049860204b 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 a1b7b81111df..a15085bcde50 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -1440,6 +1440,14 @@ TypedArray 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 b142a2fbbd9c..69a359e4ed5d 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -447,6 +447,8 @@ class ClassDB : public Object { TypedArray class_get_signal_list(const StringName &p_class, bool p_no_inheritance = false) const; TypedArray 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 8376c0aaf84c..813ee7684ff4 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(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 ab7dbcbd44b7..d31adb72be60 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 // 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 8e2366fc95e1..cb6832ea3984 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 f5158bfabbfb..8e8ec4c7186a 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 91378591b0a0..eba7ded267ba 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 89e48f53d740..95dd623cc032 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -92,9 +92,13 @@ class Input : public Object { RBSet joy_buttons_pressed; RBMap _joy_axis; //RBMap 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 d0598e4dc6c7..b35d405662e6 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 +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(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr); + break; + case FORMAT_RH | (FORMAT_RGBH << 8): + _convert_fast(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr); + break; + case FORMAT_RH | (FORMAT_RGBAH << 8): + _convert_fast(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr); + break; + case FORMAT_RGH | (FORMAT_RH << 8): + _convert_fast(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr); + break; + case FORMAT_RGH | (FORMAT_RGBH << 8): + _convert_fast(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr); + break; + case FORMAT_RGH | (FORMAT_RGBAH << 8): + _convert_fast(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr); + break; + case FORMAT_RGBH | (FORMAT_RH << 8): + _convert_fast(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr); + break; + case FORMAT_RGBH | (FORMAT_RGH << 8): + _convert_fast(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr); + break; + case FORMAT_RGBH | (FORMAT_RGBAH << 8): + _convert_fast(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr); + break; + case FORMAT_RGBAH | (FORMAT_RH << 8): + _convert_fast(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr); + break; + case FORMAT_RGBAH | (FORMAT_RGH << 8): + _convert_fast(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr); + break; + case FORMAT_RGBAH | (FORMAT_RGBH << 8): + _convert_fast(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr); + break; + case FORMAT_RF | (FORMAT_RGF << 8): + _convert_fast(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr); + break; + case FORMAT_RF | (FORMAT_RGBF << 8): + _convert_fast(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr); + break; + case FORMAT_RF | (FORMAT_RGBAF << 8): + _convert_fast(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr); + break; + case FORMAT_RGF | (FORMAT_RF << 8): + _convert_fast(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr); + break; + case FORMAT_RGF | (FORMAT_RGBF << 8): + _convert_fast(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr); + break; + case FORMAT_RGF | (FORMAT_RGBAF << 8): + _convert_fast(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr); + break; + case FORMAT_RGBF | (FORMAT_RF << 8): + _convert_fast(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr); + break; + case FORMAT_RGBF | (FORMAT_RGF << 8): + _convert_fast(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr); + break; + case FORMAT_RGBF | (FORMAT_RGBAF << 8): + _convert_fast(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr); + break; + case FORMAT_RGBAF | (FORMAT_RF << 8): + _convert_fast(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr); + break; + case FORMAT_RGBAF | (FORMAT_RGF << 8): + _convert_fast(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr); + break; + case FORMAT_RGBAF | (FORMAT_RGBF << 8): + _convert_fast(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr); + break; } } diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 928bb95de3a8..4988e736244e 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 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(); + ERR_FAIL_V_MSG(Ref(), "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 e17b8c9fd703..70fad78acbdd 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 3060f31970c3..fd53ed28fd35 100644 --- a/core/math/math_funcs.h +++ b/core/math/math_funcs.h @@ -447,14 +447,22 @@ class Math { 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 6a564b0ca7f6..1cd35b3d1aee 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 a9bce2bd7fea..cc556707e4c6 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 e4d1a8fc9ab1..ac23bf831fdb 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 e48541d07415..9c22cbaf3c2d 100644 --- a/core/os/main_loop.h +++ b/core/os/main_loop.h @@ -64,6 +64,7 @@ class MainLoop : public Object { 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 c0a86e9fb734..c866ff041525 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 resource_saver_binary; static Ref resource_loader_binary; diff --git a/core/string/translation.compat.inc b/core/string/translation.compat.inc index d792d4a6fc75..68bd1831e467 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 432016284a3d..33d4a1bcde25 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::locale_script_info; - -HashMap TranslationServer::language_map; -HashMap TranslationServer::script_map; -HashMap TranslationServer::locale_rename_map; -HashMap TranslationServer::country_name_map; -HashMap TranslationServer::variant_map; -HashMap 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 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 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 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 locale_a_elements = locale_a.split("_"); - Vector 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 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 TranslationServer::get_all_languages() const { - Vector languages; - - for (const KeyValue &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 TranslationServer::get_all_scripts() const { - Vector scripts; - - for (const KeyValue &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 TranslationServer::get_all_countries() const { - Vector countries; - - for (const KeyValue &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 &E : translations) { - const Ref &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 &p_translation) { - translations.insert(p_translation); -} - -void TranslationServer::remove_translation(const Ref &p_translation) { - translations.erase(p_translation); -} - -Ref TranslationServer::get_translation_object(const String &p_locale) { - Ref res; - int best_score = 0; - - for (const Ref &E : translations) { - const Ref &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 &E : translations) { - const Ref &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 &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 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 &p_translation) { - tool_translation = p_translation; -} - -Ref 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 &E : translations) { - const Ref &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 &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 &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 &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 *r_options) const { - const String pf = p_function; - if (p_idx == 0) { - HashMap *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 &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 0a7eacc45f8f..2c5baae8b71e 100644 --- a/core/string/translation.h +++ b/core/string/translation.h @@ -74,132 +74,4 @@ class Translation : public Resource { Translation() {} }; -class TranslationServer : public Object { - GDCLASS(TranslationServer, Object); - - String locale = "en"; - String fallback; - - HashSet> translations; - Ref tool_translation; - Ref property_translation; - Ref doc_translation; - Ref 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 supported_countries; - }; - static Vector locale_script_info; - - static HashMap language_map; - static HashMap script_map; - static HashMap locale_rename_map; - static HashMap country_name_map; - static HashMap country_rename_map; - static HashMap 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 get_translation_object(const String &p_locale); - - Vector get_all_languages() const; - String get_language_name(const String &p_language) const; - - Vector get_all_scripts() const; - String get_script_name(const String &p_script) const; - - Vector 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 &p_translation); - void remove_translation(const Ref &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 &p_translation); - Ref 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 &p_translation); - StringName property_translate(const StringName &p_message, const StringName &p_context = "") const; - void set_doc_translation(const Ref &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 &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 *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 000000000000..9d1ee8b9df23 --- /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 000000000000..6e784881d07d --- /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::locale_script_info; + +HashMap TranslationServer::language_map; +HashMap TranslationServer::script_map; +HashMap TranslationServer::locale_rename_map; +HashMap TranslationServer::country_name_map; +HashMap TranslationServer::variant_map; +HashMap 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 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 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 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 locale_a_elements = locale_a.split("_"); + Vector 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 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 TranslationServer::get_all_languages() const { + Vector languages; + + for (const KeyValue &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 TranslationServer::get_all_scripts() const { + Vector scripts; + + for (const KeyValue &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 TranslationServer::get_all_countries() const { + Vector countries; + + for (const KeyValue &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 &E : translations) { + const Ref &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 &p_translation) { + translations.insert(p_translation); +} + +void TranslationServer::remove_translation(const Ref &p_translation) { + translations.erase(p_translation); +} + +Ref TranslationServer::get_translation_object(const String &p_locale) { + Ref res; + int best_score = 0; + + for (const Ref &E : translations) { + const Ref &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 &E : translations) { + const Ref &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 &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 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 &p_translation) { + tool_translation = p_translation; +} + +Ref 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 &E : translations) { + const Ref &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 &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 &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 &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 *r_options) const { + const String pf = p_function; + if (p_idx == 0) { + HashMap *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 &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 000000000000..ebe81d97124c --- /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> translations; + Ref tool_translation; + Ref property_translation; + Ref doc_translation; + Ref 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 supported_countries; + }; + static Vector locale_script_info; + + static HashMap language_map; + static HashMap script_map; + static HashMap locale_rename_map; + static HashMap country_name_map; + static HashMap country_rename_map; + static HashMap 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 get_translation_object(const String &p_locale); + + Vector get_all_languages() const; + String get_language_name(const String &p_language) const; + + Vector get_all_scripts() const; + String get_script_name(const String &p_script) const; + + Vector 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 &p_translation); + void remove_translation(const Ref &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 &p_translation); + Ref 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 &p_translation); + StringName property_translate(const StringName &p_message, const StringName &p_context = "") const; + void set_doc_translation(const Ref &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 &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 *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 3d37e17ef836..3aaaf46b06f5 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 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 String::split_floats_mk(const Vector &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 String::split_floats_mk(const Vector &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 String::split_ints_mk(const Vector &p_splitters, bool p_allo } String String::join(const Vector &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 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 { @@ -4384,10 +4430,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; @@ -5321,6 +5364,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; @@ -5343,7 +5391,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; } @@ -5393,7 +5441,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 { @@ -5402,7 +5450,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 { @@ -5439,7 +5487,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 { @@ -5448,7 +5496,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 { @@ -5501,7 +5549,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 { @@ -5511,9 +5559,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); } } @@ -5678,7 +5726,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 f22ae1f1d37b..fedcfaec3b9b 100644 --- a/core/templates/cowdata.h +++ b/core/templates/cowdata.h @@ -160,7 +160,7 @@ class CowData { 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 @@ class CowData { } 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 @@ class CowData { }; template -void CowData::_unref(void *p_data) { - if (!p_data) { +void CowData::_unref() { + if (!_ptr) { return; } SafeNumeric *refc = _get_refcount(); - if (refc->decrement() > 0) { return; // still in use } // clean up if constexpr (!std::is_trivially_destructible_v) { - 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 @@ -300,7 +302,7 @@ typename CowData::USize CowData::_copy_on_write() { } } - _unref(_ptr); + _unref(); _ptr = _data_ptr; rc = 1; @@ -321,7 +323,7 @@ Error CowData::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::_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::_ref(const CowData &p_from) { template CowData::~CowData() { - _unref(_ptr); + _unref(); } #if defined(__GNUC__) && !defined(__clang__) diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 2cd3a5172241..339bbb71dd79 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -1248,8 +1248,9 @@ - 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] diff --git a/doc/classes/AnimationNodeBlendSpace2D.xml b/doc/classes/AnimationNodeBlendSpace2D.xml index 596767599c03..da79e15be159 100644 --- a/doc/classes/AnimationNodeBlendSpace2D.xml +++ b/doc/classes/AnimationNodeBlendSpace2D.xml @@ -5,7 +5,7 @@ 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. @@ -93,7 +93,7 @@ - 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. diff --git a/doc/classes/ClassDB.xml b/doc/classes/ClassDB.xml index 59ed0b8fe7f1..66b67d1a59d0 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. + + + + + + Returns the getter method name of [param property] of [param class]. + + @@ -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]. + + + + + + Returns the setter method name of [param property] of [param class]. + + diff --git a/doc/classes/Compositor.xml b/doc/classes/Compositor.xml index 7605083319c5..0732526957b1 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. + $DOCS_URL/tutorials/rendering/compositor.html diff --git a/doc/classes/CompositorEffect.xml b/doc/classes/CompositorEffect.xml index cf3aa9fca47d..76a388791880 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. + $DOCS_URL/tutorials/rendering/compositor.html diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index cd7e7aa75569..675b3308ebe8 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. - 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. - 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. - 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. 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. + + 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. + 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. diff --git a/doc/classes/Geometry2D.xml b/doc/classes/Geometry2D.xml index f21696d02c05..2dd76ad989ba 100644 --- a/doc/classes/Geometry2D.xml +++ b/doc/classes/Geometry2D.xml @@ -126,8 +126,34 @@ - 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] diff --git a/doc/classes/NavigationLink2D.xml b/doc/classes/NavigationLink2D.xml index 0892c9ec4404..2e1c962dd159 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. + + + + Returns the current navigation map [RID] used by this link. + + @@ -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. + + + + + 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. + + diff --git a/doc/classes/NavigationLink3D.xml b/doc/classes/NavigationLink3D.xml index 0fcc106beb7f..174228ea5b27 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. + + + + Returns the current navigation map [RID] used by this link. + + @@ -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. + + + + + 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. + + diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml index ff3e6fd8a64f..42f6235f8b65 100644 --- a/doc/classes/NavigationServer3D.xml +++ b/doc/classes/NavigationServer3D.xml @@ -1167,5 +1167,8 @@ 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 to get the number of active navigation obstacles. + diff --git a/doc/classes/Node3D.xml b/doc/classes/Node3D.xml index 125c7ef3eec8..ae13af4b8268 100644 --- a/doc/classes/Node3D.xml +++ b/doc/classes/Node3D.xml @@ -46,6 +46,14 @@ Returns all the gizmos attached to this [Node3D]. + + + + 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. + + diff --git a/doc/classes/Performance.xml b/doc/classes/Performance.xml index 4d01fe17606a..6bb71932dd22 100644 --- a/doc/classes/Performance.xml +++ b/doc/classes/Performance.xml @@ -221,7 +221,10 @@ 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. - + + Number of active navigation obstacles in the [NavigationServer3D]. + + Represents the size of the [enum Monitor] enum. diff --git a/doc/classes/Polygon2D.xml b/doc/classes/Polygon2D.xml index 90d3522002ed..4e9208d9bb5d 100644 --- a/doc/classes/Polygon2D.xml +++ b/doc/classes/Polygon2D.xml @@ -71,9 +71,6 @@ If [code]true[/code], polygon edges will be anti-aliased. - - Internal list of [Bone2D] nodes used by the assigned [member skeleton]. Edited using the Polygon2D editor ("UV" button on the top toolbar). - 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]. diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index b20b374382e9..1d65f6fb9abe 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -630,6 +630,10 @@ Maximum call stack allowed for debugging GDScript. + + 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). + Maximum number of functions per frame allowed when profiling. @@ -1000,7 +1004,7 @@ prime-run %command% [/codeblock] - + 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. @@ -1424,6 +1428,18 @@ If [code]true[/code], sends touch input events when clicking or dragging the mouse. + + If [code]true[/code], the accelerometer sensor is enabled and [method Input.get_accelerometer] returns valid data. + + + If [code]true[/code], the gravity sensor is enabled and [method Input.get_gravity] returns valid data. + + + If [code]true[/code], the gyroscope sensor is enabled and [method Input.get_gyroscope] returns valid data. + + + If [code]true[/code], the magnetometer sensor is enabled and [method Input.get_magnetometer] returns valid data. + 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. @@ -2322,7 +2338,8 @@ 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. diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 3c9f0fc7aff4..d86b82b72a11 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. + + + + + 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. + + @@ -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. + + + + + + Turns on and off physics interpolation for the instance. + + diff --git a/doc/classes/ResourceImporterDynamicFont.xml b/doc/classes/ResourceImporterDynamicFont.xml index f100670e08b7..b678a04e345a 100644 --- a/doc/classes/ResourceImporterDynamicFont.xml +++ b/doc/classes/ResourceImporterDynamicFont.xml @@ -69,12 +69,13 @@ 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]. - + 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. diff --git a/doc/classes/ResourceImporterImageFont.xml b/doc/classes/ResourceImporterImageFont.xml index 6dbfa1d65142..663c90600192 100644 --- a/doc/classes/ResourceImporterImageFont.xml +++ b/doc/classes/ResourceImporterImageFont.xml @@ -40,7 +40,7 @@ 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. Number of rows in the font image. See also [member columns]. diff --git a/doc/classes/ResourceImporterOBJ.xml b/doc/classes/ResourceImporterOBJ.xml index 55043a311ceb..a63dddb0e839 100644 --- a/doc/classes/ResourceImporterOBJ.xml +++ b/doc/classes/ResourceImporterOBJ.xml @@ -21,9 +21,6 @@ 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. - - Unused parameter. This currently has no effect. - 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. diff --git a/doc/classes/ShapeCast2D.xml b/doc/classes/ShapeCast2D.xml index 576bd62cc33a..385e3a928545 100644 --- a/doc/classes/ShapeCast2D.xml +++ b/doc/classes/ShapeCast2D.xml @@ -139,7 +139,7 @@ The shape's collision mask. Only objects in at least one collision layer enabled in the mask will be detected. - + Returns the complete collision information from the collision sweep. The data returned is the same as in the [method PhysicsDirectSpaceState2D.get_rest_info] method. diff --git a/doc/classes/ShapeCast3D.xml b/doc/classes/ShapeCast3D.xml index 2c6efe2ebe43..f70cf169df82 100644 --- a/doc/classes/ShapeCast3D.xml +++ b/doc/classes/ShapeCast3D.xml @@ -146,7 +146,7 @@ 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. - + Returns the complete collision information from the collision sweep. The data returned is the same as in the [method PhysicsDirectSpaceState3D.get_rest_info] method. diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml index 4fa9700f9cfc..9d476691bfdc 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. + + + + + Returns an array containing all glyph indices in the font. + + diff --git a/doc/classes/TextServerExtension.xml b/doc/classes/TextServerExtension.xml index c148cdad528d..3c27404f8e38 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. + + + + + [b]Required.[/b] + Returns an array containing all glyph indices in the font. + + diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml index d3197efc6b11..0995a5a672fc 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. + + + + + + + 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. + + + + + + + + + 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. + + + + + + + + + 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. + + diff --git a/doc/classes/TileMapLayer.xml b/doc/classes/TileMapLayer.xml index b9acef209529..bead1c32c02d 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. + + + + + Returns [code]true[/code] if the cell at coordinates [param coords] is flipped horizontally. The result is valid only for atlas sources. + + + + + + + Returns [code]true[/code] if the cell at coordinates [param coords] is flipped vertically. The result is valid only for atlas sources. + + + + + + + Returns [code]true[/code] if the cell at coordinates [param coords] is transposed. The result is valid only for atlas sources. + + diff --git a/doc/classes/Timer.xml b/doc/classes/Timer.xml index 9de1e09273ac..f8f9393847c6 100644 --- a/doc/classes/Timer.xml +++ b/doc/classes/Timer.xml @@ -28,7 +28,7 @@ - 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]. diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index b288ee7ff63a..f57185ae872c 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -301,6 +301,7 @@ If [code]true[/code], the viewport will use a unique copy of the [World3D] defined in [member world_3d]. + 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/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp index a33fc977c6ab..38caff648e25 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 37e7256d7642..19ef3d416ce2 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 4a15ed827a06..5a0f394db0be 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 6dd04af6b602..ba70de9e34d5 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -1871,7 +1871,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 aad856a5a2f0..66befbc3b2ca 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 a28b050bf801..2b3c19dbb84e 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 0c9f9bc275ed..ff72fc5b5893 100644 --- a/drivers/gles3/storage/config.h +++ b/drivers/gles3/storage/config.h @@ -96,6 +96,9 @@ class Config { 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 210507c2c636..ea8b42b2e4cd 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 0297aaaf7f35..d36e152ed9e8 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 0e9ad442296c..7ddbae1bbc33 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 &p_event) override; diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp index c83e677b37f5..8ba5811ffa6e 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 d43b12f1d251..0256f39979d6 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 331dacf6adba..bf5b717c19dc 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 33049b6bc237..b415c72c1567 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 bc63bc991629..41f092f30441 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 b62351256e2b..32b2133f234b 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 feca12b40996..02a95fd8364a 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 f8fd05bf1e32..83f1c70c69d8 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 467861c36b9b..bc75e1df595a 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 5cd02cfcd88b..bee9671fb1d1 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 fdb4ec170b8d..19a4165041d2 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 &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 &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 e9e788ab7bd2..2ec78cdb448e 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 @@ class EditorPropertyClassName : public EditorProperty { 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 &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 &p_options); @@ -327,7 +318,6 @@ class EditorPropertyLayers : public EditorProperty { 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 9aa2390230fe..d58d0520cc2b 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 acde7667549e..024c04956f94 100644 --- a/editor/editor_properties_array_dict.h +++ b/editor/editor_properties_array_dict.h @@ -150,7 +150,6 @@ class EditorPropertyArray : public EditorProperty { 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 f23cab676ccc..7b45ba6a9fbf 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 5aecd84fc60a..5b325d09529a 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 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 77154ec3443f..4654d41082c2 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 a6f6c87ee100..0caf0ee0665c 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 a00d874580a9..b1c5855878b8 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 &p_preset); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index d4bd97a3938f..b780d42119c1 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 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 5b303760b02f..bfd7e18de1d7 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 72ff285cf68f..60d6df488b74 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 4e8d6d63bf11..9f9bdb37b36b 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 33a511a22b47..c37d97405803 100644 --- a/editor/icons/Panels1.svg +++ b/editor/icons/Panels1.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/editor/icons/Panels2.svg b/editor/icons/Panels2.svg index 2621c36c57a2..673ae62aecb7 100644 --- a/editor/icons/Panels2.svg +++ b/editor/icons/Panels2.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/editor/icons/Panels2Alt.svg b/editor/icons/Panels2Alt.svg index 0a6c6b6c040e..e2f27e415e70 100644 --- a/editor/icons/Panels2Alt.svg +++ b/editor/icons/Panels2Alt.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/editor/icons/Panels3.svg b/editor/icons/Panels3.svg index 5ee8b7f12044..d5cf987c412d 100644 --- a/editor/icons/Panels3.svg +++ b/editor/icons/Panels3.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/editor/icons/Panels3Alt.svg b/editor/icons/Panels3Alt.svg index dc373c6fc5e1..ea6745df6ff6 100644 --- a/editor/icons/Panels3Alt.svg +++ b/editor/icons/Panels3Alt.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/editor/icons/Panels4.svg b/editor/icons/Panels4.svg index e0ff57de4dd6..407dfba1b64f 100644 --- a/editor/icons/Panels4.svg +++ b/editor/icons/Panels4.svg @@ -1 +1 @@ - \ No newline at end of file + \ 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 2fbf564ae86e..f3770adcb7c2 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> &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 *r_missing_deps) { +static Error _parse_obj(const String &p_path, List> &r_meshes, bool p_single_mesh, bool p_generate_tangents, Vector3 p_scale_mesh, Vector3 p_offset_mesh, bool p_disable_compression, List *r_missing_deps) { Ref 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> &r_meshes, Node *EditorOBJImporter::import_scene(const String &p_path, uint32_t p_flags, const HashMap &p_options, List *r_missing_deps, Error *r_err) { List> 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, Listpush_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 &p_options, List *r_platform_variants, List *r_gen_files, Variant *r_metadata) { List> 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 df4026195c25..8ad8e6201ee5 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 6b741b7dd719..ed3eaa94c1b3 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 anim(res); Ref 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 00ce3d6a7a5d..590e3a9ede05 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 d2705ac98a66..c18101140228 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 c52f53146e03..fa222b279020 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 f1b6d3a6ac7d..f01381904dae 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 11d1b3e0894b..6bc3a27a9584 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 0c79e5d747cc..0da8d8291feb 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 e9dcc41d48cb..adf378bd0665 100644 --- a/editor/node_dock.h +++ b/editor/node_dock.h @@ -55,7 +55,6 @@ class NodeDock : public VBoxContainer { 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 f73c494b2506..60d808952e08 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 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 42170d9ffd1c..66d4e1b7ef04 100644 --- a/editor/plugins/abstract_polygon_2d_editor.h +++ b/editor/plugins/abstract_polygon_2d_editor.h @@ -109,7 +109,6 @@ class AbstractPolygon2DEditor : public HBoxContainer { 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 757d410b788e..7906b50c2c5e 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 8dc820695ae9..a234af68742d 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 c00436b01f83..015dfdbca584 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 f3aa2fc84df3..988ad40ec46a 100644 --- a/editor/plugins/bone_map_editor_plugin.h +++ b/editor/plugins/bone_map_editor_plugin.h @@ -122,8 +122,6 @@ class BonePicker : public AcceptDialog { 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 62b40043c1bf..f4116ed3647c 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 2e4d8a1ee375..1c6838aa024f 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 99b767512a61..5665caefa6c0 100644 --- a/editor/plugins/cpu_particles_2d_editor_plugin.cpp +++ b/editor/plugins/cpu_particles_2d_editor_plugin.cpp @@ -268,9 +268,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 4d59c9981e3e..645e6d834509 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 be2847d1d761..69f287c134f6 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 99178b7fde7c..2a1ea93ac8d7 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 afc60b09d613..5949bc141f52 100644 --- a/editor/plugins/editor_plugin_settings.cpp +++ b/editor/plugins/editor_plugin_settings.cpp @@ -199,9 +199,6 @@ Vector 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 5b470b3e5835..ea7d0ea8b31d 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 e6ce63fe36b8..d712c1486105 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 906040f48d26..57c64720a5a2 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 19dd45a3ea58..21ad8f021ca8 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 81eb8c3c0e36..9865a595055f 100644 --- a/editor/plugins/gpu_particles_2d_editor_plugin.cpp +++ b/editor/plugins/gpu_particles_2d_editor_plugin.cpp @@ -370,9 +370,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 bb0ca5de3a8f..658e4d87e5fc 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 00c5332464ae..4e9be0aa53ec 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 dab2f4290b65..33d24097a76a 100644 --- a/editor/plugins/gpu_particles_3d_editor_plugin.h +++ b/editor/plugins/gpu_particles_3d_editor_plugin.h @@ -62,8 +62,6 @@ class GPUParticles3DEditorBase : public Control { 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 25b076d0e55e..c21a1b5dd667 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 bba8bd258411..3e1dae77ea59 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 5a90d2de61ab..30debfc14f34 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 48c16a480714..276bb74f6be6 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 &p_event); diff --git a/editor/plugins/mesh_library_editor_plugin.cpp b/editor/plugins/mesh_library_editor_plugin.cpp index 0b2549986c9e..58cc67047570 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 94f46beea1df..5a9e32178d3b 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 p_library, bool p_merge, bool p_apply_xforms); static void _import_scene_parse_node(Ref p_library, HashMap &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 3980f23c8a72..76ffdb02e19e 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 5051926c64f8..3fe63bfa9214 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 59a4ac8075f9..22300fe851db 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(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 &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 grid_colors[3]; - Vector grid_points[3]; - Vector grid_normals[3]; + static LocalVector grid_colors[3]; + static LocalVector grid_points[3]; + static LocalVector 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 &ref_grid = grid_points[c]; - Vector &ref_grid_normals = grid_normals[c]; - Vector &ref_grid_colors = grid_colors[c]; + LocalVector &ref_grid = grid_points[c]; + LocalVector &ref_grid_normals = grid_normals[c]; + LocalVector &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)grid_points[c]; + d[RenderingServer::ARRAY_COLOR] = (Vector)grid_colors[c]; + d[RenderingServer::ARRAY_NORMAL] = (Vector)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 e9ddaeb3fe6e..b38965753e8b 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 b7c12ab5c035..d7e97014520d 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 fb6f30cc5779..b057644bb12b 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 e442c37edd51..f0ea3225049f 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(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(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(bone->get_child(j)); - if (!n) { - continue; - } + for (int j = 0; j < bone->get_child_count(); j++) { + Bone2D *n = Object::cast_to(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 8b31aa92b34e..cd422fc2915a 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 5befdb6006e7..a3e90360aef0 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 c51eb44aee47..9da66a086268 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(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(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 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 000000000000..32e690015b9e --- /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 &p_shader) = 0; + virtual void edit_shader_include(const Ref &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 9d1576cdf2be..82c436d3a53e 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(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(p_object); @@ -156,20 +156,18 @@ void ShaderEditorPlugin::edit(Object *p_object) { es.shader = Ref(s); Ref 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(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 &p_for_shader) { +ShaderEditor *ShaderEditorPlugin::get_shader_editor(const Ref &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 &p_for return nullptr; } -VisualShaderEditor *ShaderEditorPlugin::get_visual_shader_editor(const Ref &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 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 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 p_layout) { } shaders.push_back(shader_path); - TextShaderEditor *shader_editor = Object::cast_to(shader_tabs->get_current_tab_control()); - VisualShaderEditor *visual_shader_editor = Object::cast_to(shader_tabs->get_current_tab_control()); + ShaderEditor *shader_editor = Object::cast_to(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(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(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(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 &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(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 386261d84422..f9b9405e45cc 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; Ref shader_inc; - TextShaderEditor *shader_editor = nullptr; - VisualShaderEditor *visual_shader_editor = nullptr; + ShaderEditor *shader_editor = nullptr; String path; String name; }; @@ -121,8 +121,7 @@ class ShaderEditorPlugin : public EditorPlugin { virtual void make_visible(bool p_visible) override; virtual void selected_notify() override; - TextShaderEditor *get_shader_editor(const Ref &p_for_shader); - VisualShaderEditor *get_visual_shader_editor(const Ref &p_for_shader); + ShaderEditor *get_shader_editor(const Ref &p_for_shader); virtual void set_window_layout(Ref p_layout) override; virtual void get_window_layout(Ref p_layout) override; diff --git a/editor/plugins/shader_file_editor_plugin.cpp b/editor/plugins/shader_file_editor_plugin.cpp index 05919fb0f657..d732d51f6929 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 &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 9a915513ef9e..fea770b7e01c 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 8308fe6d6ef4..97c5c0c7dd1a 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 74fd59f1c416..89e2e5d0f25e 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 1753df5cb7bd..9b98b6ffa2ec 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 2ef5467263b7..95ab3696beff 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 276c57533f72..6cd92bdf57f8 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 &p_shader) { +void TextShaderEditor::edit_shader(const Ref &p_shader) { if (p_shader.is_null() || !p_shader->is_text_shader()) { return; } @@ -910,7 +910,7 @@ void TextShaderEditor::edit(const Ref &p_shader) { code_editor->set_edited_shader(shader); } -void TextShaderEditor::edit(const Ref &p_shader_inc) { +void TextShaderEditor::edit_shader_include(const Ref &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 61066ed7c651..55efb4e30ed8 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 @@ class ShaderTextEditor : public CodeTextEditor { ShaderTextEditor(); }; -class TextShaderEditor : public MarginContainer { - GDCLASS(TextShaderEditor, MarginContainer); +class TextShaderEditor : public ShaderEditor { + GDCLASS(TextShaderEditor, ShaderEditor); enum { EDIT_UNDO, @@ -188,19 +189,21 @@ class TextShaderEditor : public MarginContainer { void _bookmark_item_pressed(int p_idx); public: + virtual void edit_shader(const Ref &p_shader) override; + virtual void edit_shader_include(const Ref &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 &p_shader); - void edit(const Ref &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 3942baed8abd..b806d1e0429b 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 8f8e3f8ff7fc..c8b004efd930 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 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 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 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 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(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 5a7955323b2b..663c43582474 100644 --- a/editor/plugins/tiles/tile_map_layer_editor.cpp +++ b/editor/plugins/tiles/tile_map_layer_editor.cpp @@ -2502,7 +2502,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 7e34a36a6e1d..224c4e434f3f 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 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 263e9cfa3b4a..7e5336ce0642 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 22ef779b8d90..305407efdb8b 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 2da9d66d9a43..b2669cf1afd8 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::copy_items_buffer; List VisualShaderEditor::copy_connections_buffer; -void VisualShaderEditor::edit(VisualShader *p_visual_shader) { +void VisualShaderEditor::edit_shader(const Ref &p_shader) { bool changed = false; - if (p_visual_shader) { + VisualShader *visual_shader_ptr = Object::cast_to(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(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(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 a9826fd61780..6ce096f82180 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 @@ class VisualShaderEditedProperty : public RefCounted { 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 @@ class VisualShaderEditor : public VBoxContainer { static void _bind_methods(); public: + virtual void edit_shader(const Ref &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 &p_plugin); void remove_plugin(const Ref &p_plugin); @@ -609,11 +616,8 @@ class VisualShaderEditor : public VBoxContainer { virtual Size2 get_minimum_size() const override; - void edit(VisualShader *p_visual_shader); Ref 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 &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 e7b2435567c1..3e835d5cb69d 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 58ef22ddc6c5..d09822dda615 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 a98d9073b011..4502b42fe622 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 0394c9e24920..d47270841da0 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 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 b162ee6cfdc7..34cade9267d0 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 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 56924fc1fe74..30050901d9b3 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 5028cd5cc9cc..f01e6598cf37 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 fc994a17d182..c50d1237e00f 100644 --- a/editor/themes/editor_fonts.cpp +++ b/editor/themes/editor_fonts.cpp @@ -117,6 +117,7 @@ void editor_register_fonts(const Ref &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 &p_theme) { const float embolden_strength = 0.6; Ref 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 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 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 fallbacks; Ref 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 &p_theme) { default_font_msdf->set_fallbacks(fallbacks); Ref 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 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 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 fallbacks_bold; Ref 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 &p_theme) { Ref default_fc_msdf; default_fc_msdf.instantiate(); if (custom_font_path.length() > 0 && dir->file_exists(custom_font_path)) { - Ref custom_font = load_external_font(custom_font_path, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps); + Ref custom_font = load_external_font(custom_font_path, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, font_allow_msdf); { TypedArray fallback_custom; fallback_custom.push_back(default_font_msdf); @@ -277,7 +278,7 @@ void editor_register_fonts(const Ref &p_theme) { Ref bold_fc_msdf; bold_fc_msdf.instantiate(); if (custom_font_path_bold.length() > 0 && dir->file_exists(custom_font_path_bold)) { - Ref custom_font = load_external_font(custom_font_path_bold, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps); + Ref 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 fallback_custom; fallback_custom.push_back(default_font_bold_msdf); @@ -285,7 +286,7 @@ void editor_register_fonts(const Ref &p_theme) { } bold_fc_msdf->set_base_font(custom_font); } else if (custom_font_path.length() > 0 && dir->file_exists(custom_font_path)) { - Ref custom_font = load_external_font(custom_font_path, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps); + Ref custom_font = load_external_font(custom_font_path, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, font_allow_msdf); { TypedArray 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 9d8cbb053d8f..b271970f2199 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 &p_the // CheckBox. { Ref 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 e42469b51b06..bdae1bb1b078 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 d358d9fa933e..569930d42777 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 8eda697b16dd..0547b3bff07c 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 34162b2da9c4..05d678fe5573 100644 --- a/main/performance.h +++ b/main/performance.h @@ -100,6 +100,7 @@ class Performance : public Object { NAVIGATION_EDGE_MERGE_COUNT, NAVIGATION_EDGE_CONNECTION_COUNT, NAVIGATION_EDGE_FREE_COUNT, + NAVIGATION_OBSTACLE_COUNT, MONITOR_MAX }; diff --git a/misc/dist/macos_tools.app/Contents/Info.plist b/misc/dist/macos_tools.app/Contents/Info.plist index 90888719f85a..c35a209844bf 100644 --- a/misc/dist/macos_tools.app/Contents/Info.plist +++ b/misc/dist/macos_tools.app/Contents/Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 4.3 + 4.4 CFBundleSignature godot CFBundleVersion - 4.3 + 4.4 NSMicrophoneUsageDescription Microphone access is required to capture audio. NSCameraUsageDescription diff --git a/misc/dist/windows/godot.iss b/misc/dist/windows/godot.iss index 6ce51e4babdb..35f318cc12b9 100644 --- a/misc/dist/windows/godot.iss +++ b/misc/dist/windows/godot.iss @@ -1,5 +1,5 @@ #define MyAppName "Godot Engine" -#define MyAppVersion "4.3" +#define MyAppVersion "4.4" #define MyAppPublisher "Godot Engine contributors" #define MyAppURL "https://godotengine.org/" #define MyAppExeName "godot.exe" diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable_4.3-stable.expected similarity index 100% rename from misc/extension_api_validation/4.2-stable.expected rename to misc/extension_api_validation/4.2-stable_4.3-stable.expected diff --git a/misc/extension_api_validation/4.3-stable.expected b/misc/extension_api_validation/4.3-stable.expected new file mode 100644 index 000000000000..24c770209010 --- /dev/null +++ b/misc/extension_api_validation/4.3-stable.expected @@ -0,0 +1,16 @@ +This file contains the expected output of --validate-extension-api when run against the extension_api.json of the +4.3-stable tag (the basename of this file). + +Only lines that start with "Validate extension JSON:" matter, everything else is considered a comment and ignored. They +should instead be used to justify these changes and describe how users should work around these changes. + +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 b2de6b656efa..6ea44c5fc361 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 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 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 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(), "Unrecognized or unsupported color layout in DDS '" + p_path + "'."); @@ -433,10 +493,24 @@ Ref 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); @@ -502,6 +576,44 @@ Ref ResourceFormatDDS::load(const String &p_path, const String &p_orig wb[dst_ofs + 3] = a | (a >> 4); } + } 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. @@ -549,6 +661,8 @@ Ref 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 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 95ed46207005..14887ce469c6 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 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 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(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(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 9d5343740bf9..d50b322fe438 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 b9d9ec7b6cf2..4d3f7554c09d 100644 --- a/modules/fbx/fbx_document.cpp +++ b/modules/fbx/fbx_document.cpp @@ -2114,9 +2114,6 @@ Error FBXDocument::_parse(Ref p_state, String p_path, Ref return OK; } -void FBXDocument::_bind_methods() { -} - Node *FBXDocument::generate_scene(Ref p_state, float p_bake_fps, bool p_trimming, bool p_remove_immutable_tracks) { Ref 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 4a3bb176c2bd..96f1905881b1 100644 --- a/modules/fbx/fbx_document.h +++ b/modules/fbx/fbx_document.h @@ -61,9 +61,6 @@ class FBXDocument : public GLTFDocument { PackedByteArray generate_buffer(Ref p_state) override; Error write_to_filesystem(Ref 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 eaf2565e6920..7bf5e946fbb6 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 4e78fbe30276..6527a0ea4dca 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -459,9 +459,11 @@ class GDScriptLanguage : public ScriptLanguage { friend class GDScriptFunction; SelfList::List function_list; +#ifdef DEBUG_ENABLED bool profiling; bool profile_native_calls; uint64_t script_frame_time; +#endif HashMap orphan_subclasses; diff --git a/modules/gltf/README.md b/modules/gltf/README.md index 5d8966b20106..2d1e92e07075 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 dd059e6b7952..bc142797a395 100644 --- a/modules/gltf/doc_classes/GLTFAccessor.xml +++ b/modules/gltf/doc_classes/GLTFAccessor.xml @@ -1,11 +1,11 @@ - Represents a GLTF accessor. + Represents a glTF accessor. - 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. https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_005_BuffersBufferViewsAccessors.md @@ -13,7 +13,7 @@ - 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". 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. - 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. The number of elements referenced by this accessor. @@ -55,7 +55,7 @@ The offset relative to the start of the bufferView in bytes. - The GLTF accessor type as an enum. Use [member accessor_type] instead. + The glTF accessor type as an enum. Use [member accessor_type] instead. diff --git a/modules/gltf/doc_classes/GLTFAnimation.xml b/modules/gltf/doc_classes/GLTFAnimation.xml index 2c70f4461f83..d269145bbd98 100644 --- a/modules/gltf/doc_classes/GLTFAnimation.xml +++ b/modules/gltf/doc_classes/GLTFAnimation.xml @@ -13,7 +13,7 @@ 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. @@ -22,7 +22,7 @@ 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. diff --git a/modules/gltf/doc_classes/GLTFBufferView.xml b/modules/gltf/doc_classes/GLTFBufferView.xml index e191935fc9c4..b7f499ad72bd 100644 --- a/modules/gltf/doc_classes/GLTFBufferView.xml +++ b/modules/gltf/doc_classes/GLTFBufferView.xml @@ -1,10 +1,10 @@ - Represents a GLTF buffer view. + Represents a glTF buffer view. - 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. diff --git a/modules/gltf/doc_classes/GLTFCamera.xml b/modules/gltf/doc_classes/GLTFCamera.xml index b334bf28676b..9fce21659ca6 100644 --- a/modules/gltf/doc_classes/GLTFCamera.xml +++ b/modules/gltf/doc_classes/GLTFCamera.xml @@ -1,15 +1,15 @@ - Represents a GLTF camera. + Represents a glTF camera. - Represents a camera as defined by the base GLTF spec. + Represents a camera as defined by the base glTF spec. $DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html - https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-camera - https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_015_SimpleCameras.md + https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-camera + https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_015_SimpleCameras.md @@ -41,19 +41,19 @@ - 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. - 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. - 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. - 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. - 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. diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml index 1b51def28e12..ebeed015e9db 100644 --- a/modules/gltf/doc_classes/GLTFDocument.xml +++ b/modules/gltf/doc_classes/GLTFDocument.xml @@ -5,7 +5,7 @@ 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. @@ -21,7 +21,7 @@ - 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. @@ -32,7 +32,7 @@ - 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. @@ -49,7 +49,7 @@ - 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]. @@ -91,7 +91,7 @@ - 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. diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml index 0eabcb5022e8..5c548c472f60 100644 --- a/modules/gltf/doc_classes/GLTFDocumentExtension.xml +++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml @@ -4,7 +4,7 @@ [GLTFDocument] extension class. - 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]. @@ -30,7 +30,7 @@ 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. @@ -38,7 +38,7 @@ 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. @@ -47,7 +47,7 @@ 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. @@ -86,7 +86,7 @@ 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. @@ -123,7 +123,7 @@ 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. @@ -134,7 +134,7 @@ 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. @@ -154,7 +154,7 @@ 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. @@ -166,7 +166,7 @@ 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. @@ -178,7 +178,7 @@ 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. diff --git a/modules/gltf/doc_classes/GLTFLight.xml b/modules/gltf/doc_classes/GLTFLight.xml index 87ea159e7c92..e07d24a14463 100644 --- a/modules/gltf/doc_classes/GLTFLight.xml +++ b/modules/gltf/doc_classes/GLTFLight.xml @@ -1,14 +1,14 @@ - Represents a GLTF light. + Represents a glTF light. - 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. $DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html - https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_lights_punctual + https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_lights_punctual @@ -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. - 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. diff --git a/modules/gltf/doc_classes/GLTFMesh.xml b/modules/gltf/doc_classes/GLTFMesh.xml index b4c3db7618e5..da73c20c1df6 100644 --- a/modules/gltf/doc_classes/GLTFMesh.xml +++ b/modules/gltf/doc_classes/GLTFMesh.xml @@ -1,10 +1,10 @@ - GLTFMesh represents a GLTF mesh. + GLTFMesh represents a glTF mesh. - 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. $DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html @@ -15,7 +15,7 @@ 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. @@ -24,7 +24,7 @@ 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. diff --git a/modules/gltf/doc_classes/GLTFNode.xml b/modules/gltf/doc_classes/GLTFNode.xml index 4a7570e4bc56..2786c25e9adc 100644 --- a/modules/gltf/doc_classes/GLTFNode.xml +++ b/modules/gltf/doc_classes/GLTFNode.xml @@ -1,15 +1,15 @@ - GLTF node class. + glTF node class. - 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]. $DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html - https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_004_ScenesNodes.md" + https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_004_ScenesNodes.md" @@ -17,7 +17,7 @@ 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. @@ -26,25 +26,25 @@ 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. - 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. - 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. 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. - 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. - 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. 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. - The position of the GLTF node relative to its parent. + The position of the glTF node relative to its parent. - The rotation of the GLTF node relative to its parent. + The rotation of the glTF node relative to its parent. - The scale of the GLTF node relative to its parent. + The scale of the glTF node relative to its parent. - 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. - 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. - 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. diff --git a/modules/gltf/doc_classes/GLTFPhysicsBody.xml b/modules/gltf/doc_classes/GLTFPhysicsBody.xml index cd701e2f2f21..1a76b190ba47 100644 --- a/modules/gltf/doc_classes/GLTFPhysicsBody.xml +++ b/modules/gltf/doc_classes/GLTFPhysicsBody.xml @@ -1,21 +1,21 @@ - Represents a GLTF physics body. + Represents a glTF physics body. - 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. $DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html - https://github.com/omigroup/gltf-extensions/tree/main/extensions/2.0/OMI_physics_body + https://github.com/omigroup/gltf-extensions/tree/main/extensions/2.0/OMI_physics_body - 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. @@ -28,7 +28,7 @@ - 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. diff --git a/modules/gltf/doc_classes/GLTFPhysicsShape.xml b/modules/gltf/doc_classes/GLTFPhysicsShape.xml index a4aaf9415cff..53872a942f69 100644 --- a/modules/gltf/doc_classes/GLTFPhysicsShape.xml +++ b/modules/gltf/doc_classes/GLTFPhysicsShape.xml @@ -1,15 +1,15 @@ - Represents a GLTF physics shape. + Represents a glTF physics shape. - 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. $DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html - https://github.com/omigroup/gltf-extensions/tree/main/extensions/2.0/OMI_physics_shape - https://github.com/omigroup/gltf-extensions/tree/main/extensions/2.0/Archived/OMI_collider + https://github.com/omigroup/gltf-extensions/tree/main/extensions/2.0/OMI_physics_shape + https://github.com/omigroup/gltf-extensions/tree/main/extensions/2.0/Archived/OMI_collider @@ -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. - 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). 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 ac03a6ee9eb3..2dd3a3741321 100644 --- a/modules/gltf/doc_classes/GLTFSkeleton.xml +++ b/modules/gltf/doc_classes/GLTFSkeleton.xml @@ -22,7 +22,7 @@ - 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. @@ -39,7 +39,7 @@ - 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. diff --git a/modules/gltf/doc_classes/GLTFSpecGloss.xml b/modules/gltf/doc_classes/GLTFSpecGloss.xml index 722fa5e9ae3b..11151f53d01c 100644 --- a/modules/gltf/doc_classes/GLTFSpecGloss.xml +++ b/modules/gltf/doc_classes/GLTFSpecGloss.xml @@ -1,14 +1,14 @@ - Archived GLTF extension for specular/glossy materials. + Archived glTF extension for specular/glossy materials. - 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. $DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html - https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Archived/KHR_materials_pbrSpecularGlossiness + https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Archived/KHR_materials_pbrSpecularGlossiness diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml index 21a0527813be..c049acf557e9 100644 --- a/modules/gltf/doc_classes/GLTFState.xml +++ b/modules/gltf/doc_classes/GLTFState.xml @@ -1,15 +1,15 @@ - Represents all data of a GLTF file. + Represents all data of a glTF file. - 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. $DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html - https://github.com/KhronosGroup/glTF/blob/main/specification/2.0/schema/asset.schema.json" + https://github.com/KhronosGroup/glTF/blob/main/specification/2.0/schema/asset.schema.json" @@ -17,7 +17,7 @@ - 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. @@ -38,27 +38,27 @@ 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. - 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. - 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. - 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. @@ -69,7 +69,7 @@ - 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. @@ -80,13 +80,13 @@ - 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. - 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. @@ -97,7 +97,7 @@ - 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. @@ -111,7 +111,7 @@ - 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. @@ -125,19 +125,19 @@ - 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. - 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. - 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. @@ -169,7 +169,7 @@ 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. @@ -250,7 +250,7 @@ - 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. @@ -279,17 +279,17 @@ The baking fps of the animation for either import or export. - 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. - 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. - 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. The binary buffer attached to a .glb file. @@ -305,10 +305,10 @@ - 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. - 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. diff --git a/modules/gltf/doc_classes/GLTFTexture.xml b/modules/gltf/doc_classes/GLTFTexture.xml index 9ad7c0f4c66a..2a868a8ba389 100644 --- a/modules/gltf/doc_classes/GLTFTexture.xml +++ b/modules/gltf/doc_classes/GLTFTexture.xml @@ -1,7 +1,7 @@ - GLTFTexture represents a texture in a GLTF file. + GLTFTexture represents a texture in a glTF file. diff --git a/modules/gltf/doc_classes/GLTFTextureSampler.xml b/modules/gltf/doc_classes/GLTFTextureSampler.xml index 2b5bad672467..d00ab463c273 100644 --- a/modules/gltf/doc_classes/GLTFTextureSampler.xml +++ b/modules/gltf/doc_classes/GLTFTextureSampler.xml @@ -1,10 +1,10 @@ - Represents a GLTF texture sampler + Represents a glTF texture sampler - 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. $DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html 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 07faee3dfcd6..676f764c1102 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 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 ca10444eb578..b216a47a7fc5 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 c1d2fea98b3e..f6e91c16353f 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::from_dictionary(const Dictionary p_dictionary) { - ERR_FAIL_COND_V_MSG(!p_dictionary.has("type"), Ref(), "Failed to parse GLTF light, missing required field 'type'."); + ERR_FAIL_COND_V_MSG(!p_dictionary.has("type"), Ref(), "Failed to parse glTF light, missing required field 'type'."); Ref light; light.instantiate(); const String &type = p_dictionary["type"]; @@ -181,7 +181,7 @@ Ref 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::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 7e52cde05947..5c26a1686b2e 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 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 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 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 p_state, Ref return; // The mesh resource is already set up. } TypedArray 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 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 p_state, Refget_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 p_state Ref 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 p_state } } else if (!Object::cast_to(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 p_state, Re if (cast_to(p_scene_node)) { CollisionShape3D *godot_shape = Object::cast_to(p_scene_node); Ref 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 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 7929b46542bd..c11aa5d2ff5d 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::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(p_body_node)) { physics_body->body_type = PhysicsBodyType::VEHICLE; @@ -289,7 +289,7 @@ Ref 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::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::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::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::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::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 6897bdbd3a55..0340eb11b571 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 p_importer_mesh) { Ref _convert_hull_points_to_mesh(const Vector &p_hull_points) { Ref 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 dff1e62e824b..d98b25053824 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -620,7 +620,7 @@ Error GLTFDocument::_parse_nodes(Ref p_state) { for (Ref 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 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 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 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 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 GLTFDocument::_parse_image_bytes_into_image(Ref p_state, c for (Ref 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 p_state) { for (Ref 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 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 p_state) { HashMap 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 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 d56f67a092f1..863e1df967ad 100644 --- a/modules/gltf/structures/gltf_camera.cpp +++ b/modules/gltf/structures/gltf_camera.cpp @@ -62,9 +62,9 @@ Ref 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::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::from_dictionary(const Dictionary p_dictionary) { - ERR_FAIL_COND_V_MSG(!p_dictionary.has("type"), Ref(), "Failed to parse GLTF camera, missing required field 'type'."); + ERR_FAIL_COND_V_MSG(!p_dictionary.has("type"), Ref(), "Failed to parse glTF camera, missing required field 'type'."); Ref camera; camera.instantiate(); const String &type = p_dictionary["type"]; @@ -107,7 +107,7 @@ Ref 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 ef55b06a76ca..1a583c82ccb4 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 f402e2a58328..ea63e0710423 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 c49c62a08b6c..ba59bb25eee8 100644 --- a/modules/hdr/image_loader_hdr.cpp +++ b/modules/hdr/image_loader_hdr.cpp @@ -68,9 +68,11 @@ Error ImageLoaderHDR::load_image(Ref p_image, Ref 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 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 p_image, Ref 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 p_image, Ref 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 9821db059e1e..0a8e91fb9e5f 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 p_image, Ref f, BitField p_flags, float p_scale); virtual void get_recognized_extensions(List *p_extensions) const; + ImageLoaderHDR(); }; diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj index 74623a60ba6e..ee624a443d93 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj @@ -7,7 +7,7 @@ Godot Engine contributors Godot.NET.Sdk - 4.3.0 + 4.4.0 $(PackageVersion_Godot_NET_Sdk) https://github.com/godotengine/godot/tree/master/modules/mono/editor/Godot.NET.Sdk $(RepositoryUrl) 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 217f4676376c..9a8b3ea84630 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 GetGodotPropertyDefaultValues() { - var values = new global::System.Collections.Generic.Dictionary(2); + var values = new global::System.Collections.Generic.Dictionary(10); global::Godot.Node __NodeProperty_default_value = default; values.Add(PropertyName.@NodeProperty, global::Godot.Variant.From(__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 __GodotArrayOfNodesProperty_default_value = default; + values.Add(PropertyName.@GodotArrayOfNodesProperty, global::Godot.Variant.CreateFrom(__GodotArrayOfNodesProperty_default_value)); + global::Godot.Collections.Dictionary __GodotDictionaryWithNodeAsKeyProperty_default_value = default; + values.Add(PropertyName.@GodotDictionaryWithNodeAsKeyProperty, global::Godot.Variant.CreateFrom(__GodotDictionaryWithNodeAsKeyProperty_default_value)); + global::Godot.Collections.Dictionary __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(__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 __GodotArrayOfNodesField_default_value = default; + values.Add(PropertyName.@GodotArrayOfNodesField, global::Godot.Variant.CreateFrom(__GodotArrayOfNodesField_default_value)); + global::Godot.Collections.Dictionary __GodotDictionaryWithNodeAsKeyField_default_value = default; + values.Add(PropertyName.@GodotDictionaryWithNodeAsKeyField, global::Godot.Variant.CreateFrom(__GodotDictionaryWithNodeAsKeyField_default_value)); + global::Godot.Collections.Dictionary __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 067783ea66d1..4613d883c2c9 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,12 +1,37 @@ using Godot; +using Godot.Collections; public partial class ExportDiagnostics_GD0107_OK : Node { [Export] public Node NodeField; + [Export] + public Node[] SystemArrayOfNodesField; + + [Export] + public Array GodotArrayOfNodesField; + + [Export] + public Dictionary GodotDictionaryWithNodeAsKeyField; + + [Export] + public Dictionary GodotDictionaryWithNodeAsValueField; + [Export] public Node NodeProperty { get; set; } + + [Export] + public Node[] SystemArrayOfNodesProperty { get; set; } + + [Export] + public Array GodotArrayOfNodesProperty { get; set; } + + [Export] + public Dictionary GodotDictionaryWithNodeAsKeyProperty { get; set; } + + [Export] + public Dictionary GodotDictionaryWithNodeAsValueProperty { get; set; } } public partial class ExportDiagnostics_GD0107_KO : Resource @@ -14,6 +39,30 @@ public partial class ExportDiagnostics_GD0107_KO : Resource [Export] public Node {|GD0107:NodeField|}; + [Export] + public Node[] {|GD0107:SystemArrayOfNodesField|}; + + [Export] + public Array {|GD0107:GodotArrayOfNodesField|}; + + [Export] + public Dictionary {|GD0107:GodotDictionaryWithNodeAsKeyField|}; + + [Export] + public Dictionary {|GD0107:GodotDictionaryWithNodeAsValueField|}; + [Export] public Node {|GD0107:NodeProperty|} { get; set; } + + [Export] + public Node[] {|GD0107:SystemArrayOfNodesProperty|} { get; set; } + + [Export] + public Array {|GD0107:GodotArrayOfNodesProperty|} { get; set; } + + [Export] + public Dictionary {|GD0107:GodotDictionaryWithNodeAsKeyProperty|} { get; set; } + + [Export] + public Dictionary {|GD0107:GodotDictionaryWithNodeAsValueProperty|} { get; set; } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj index 1aa2979e76a5..8e407da7a6a7 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj @@ -9,7 +9,7 @@ Godot Engine contributors Godot.SourceGenerators - 4.3.0 + 4.4.0 $(PackageVersion_Godot_SourceGenerators) https://github.com/godotengine/godot/tree/master/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators $(RepositoryUrl) 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 efe88d846807..626f51ecae1c 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 @@ void AppendPartialContainingTypeDeclarations(INamedTypeSymbol? containingType) 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 @@ void AppendPartialContainingTypeDeclarations(INamedTypeSymbol? containingType) 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 @@ void AppendPartialContainingTypeDeclarations(INamedTypeSymbol? containingType) 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 cd335934db09..ece1ab44a2bc 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 override string _GetDescription() 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 563a6abe9b16..1fc6e54e097e 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 @@ public static bool IsInstanceIdValid(ulong id) /// /// The instance to check. /// If the instance is a valid object. - 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 27f2713efa79..ef66d5bbe0da 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs @@ -692,10 +692,18 @@ public readonly Vector3 Slerp(Vector3 to, real_t weight) // 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); } /// diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index 6b25087c93ac..b838f8eac786 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -17,7 +17,7 @@ Godot Engine contributors GodotSharp - 4.3.0 + 4.4.0 $(PackageVersion_GodotSharp) https://github.com/godotengine/godot/tree/master/modules/mono/glue/GodotSharp/GodotSharp $(RepositoryUrl) diff --git a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj index 4561fdaf2b6f..65b4824f9424 100644 --- a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj @@ -15,7 +15,7 @@ Godot Engine contributors GodotSharpEditor - 4.3.0 + 4.4.0 $(PackageVersion_GodotSharp) https://github.com/godotengine/godot/tree/master/modules/mono/glue/GodotSharp/GodotSharpEditor $(RepositoryUrl) diff --git a/modules/multiplayer/doc_classes/MultiplayerSpawner.xml b/modules/multiplayer/doc_classes/MultiplayerSpawner.xml index 3da245f806ca..b62029251930 100644 --- a/modules/multiplayer/doc_classes/MultiplayerSpawner.xml +++ b/modules/multiplayer/doc_classes/MultiplayerSpawner.xml @@ -48,11 +48,11 @@ - 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. - 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. diff --git a/modules/navigation/3d/godot_navigation_server_3d.cpp b/modules/navigation/3d/godot_navigation_server_3d.cpp index 430d527844b1..fc96506795f1 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 5ba7ed1088e2..12a1132f07b6 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 d07d3cdff567..f37ed9b16856 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 6114c62ebfd6..f5a471d5318a 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 f917c988eaa9..9796fa350d71 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 d6215ea57f19..3dd6db21dd9b 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 @@ class NavMap : public NavRid { 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 026797c6e012..4d95b079e224 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 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 60b5137f67f4..9a3487439cbd 100644 --- a/modules/openxr/editor/openxr_select_runtime.h +++ b/modules/openxr/editor/openxr_select_runtime.h @@ -40,7 +40,6 @@ class OpenXRSelectRuntime : public OptionButton { 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 d92084a220d6..de4a9e4b8e9a 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 OpenXROpenGLExtension::get_requested_extensions() { HashMap request_extensions; @@ -196,23 +191,6 @@ void OpenXROpenGLExtension::get_usable_depth_formats(Vector &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 a3052d3f5341..8da3ca48f4f6 100644 --- a/modules/openxr/extensions/platform/openxr_opengl_extension.h +++ b/modules/openxr/extensions/platform/openxr_opengl_extension.h @@ -49,9 +49,6 @@ class OpenXROpenGLExtension : public OpenXRGraphicsExtensionWrapper { 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 &p_usable_swap_chains) override; virtual void get_usable_depth_formats(Vector &p_usable_swap_chains) override; virtual String get_swapchain_format_name(int64_t p_swapchain_format) const override; @@ -76,9 +73,6 @@ class OpenXROpenGLExtension : public OpenXRGraphicsExtensionWrapper { Vector 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 98e548415788..c6fd20dac72f 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 68a5d499d4fb..4112b81622f4 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 d0d13fec3fe8..effed1e77246 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 499ddb703b0f..d0c22e9e4da7 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 &gl = at_size->glyph_map; + for (const KeyValue &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 fb95d66229e9..b07ad71cf3cb 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -871,6 +871,7 @@ class TextServerAdvanced : public TextServerExtension { 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 b45c004011a1..a7ddfc719e1c 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 &gl = at_size->glyph_map; + for (const KeyValue &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 6b3b04d13a1a..5e7ba3b2590f 100644 --- a/modules/text_server_fb/text_server_fb.h +++ b/modules/text_server_fb/text_server_fb.h @@ -739,6 +739,7 @@ class TextServerFallback : public TextServerExtension { 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 5169b9417f6a..689360aef6c2 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 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 01d5d9ef9294..05b4f379b329 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 eb9ad9de059e..611a9c4a4022 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 f5555289fd17..771bda694833 100644 --- a/platform/android/java/build.gradle +++ b/platform/android/java/build.gradle @@ -35,115 +35,16 @@ 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'. @@ -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 fa39ccb546f0..111cd484050d 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 = ArrayList() @@ -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 7fbdb34047b5..15a811ce8330 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 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)); @@ -101,11 +101,6 @@ public SurfaceView getView() { return this; } - @Override - public void initInputDevices() { - this.inputHandler.initInputDevices(); - } - @Override public void queueOnRenderThread(Runnable event) { queueEvent(event); @@ -144,11 +139,6 @@ public void onActivityDestroyed() { requestRenderThreadExitAndWait(); } - @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 19ec0fd1a4ae..30821eaa8e10 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 @@ 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 f4411ddf2c35..d5b05913d8d6 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 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)); @@ -80,11 +80,6 @@ public SurfaceView getView() { return this; } - @Override - public void initInputDevices() { - mInputHandler.initInputDevices(); - } - @Override public void queueOnRenderThread(Runnable event) { queueOnVkThread(event); @@ -123,11 +118,6 @@ public void onActivityDestroyed() { requestRenderThreadExitAndWait(); } - @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 273774a33d1e..fb41cd00c025 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 @@ 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.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 @@ /** * 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 mJoysticksDevices = new SparseArray<>(4); private final HashSet 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 boolean onKeyUp(final int keyCode, KeyEvent event) { 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 @@ boolean handleMotionEvent(final MotionEvent event, int eventActionOverride, bool 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 @@ private static float getEventTiltX(MotionEvent event) { 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 @@ boolean handleMouseEvent(int eventAction, boolean sourceMouseRelative) { } 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 @@ boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, flo 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 @@ boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, flo 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 @@ boolean handleTouchEvent(final MotionEvent event, int eventActionOverride) { } 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 @@ boolean handleTouchEvent(final MotionEvent event, int eventActionOverride, boole 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 @@ boolean handleTouchEvent(final MotionEvent event, int eventActionOverride, boole } 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 000000000000..a282791b2e2f --- /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 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 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 f2c0577c21e0..d0b8a8dffae3 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 5410eed72727..146fc04da493 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 711bca02e7d8..8976dd65db63 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 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 GodotPlugin getPlugin(String pluginName) { * Retrieve the full set of loaded plugins. */ public Collection 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 ec42e5bbd8c1..a4a425f685a1 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 70ea4b09c10d..91bf7b48a6f1 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 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 a9bfcbb0b2d5..85dc6bb390f2 100644 --- a/platform/ios/godot_app_delegate.h +++ b/platform/ios/godot_app_delegate.h @@ -32,7 +32,7 @@ typedef NSObject ApplicationDelegateService; -@interface GodotApplicalitionDelegate : NSObject +@interface GodotApplicationDelegate : NSObject @property(class, readonly, strong) NSArray *services; diff --git a/platform/ios/godot_app_delegate.m b/platform/ios/godot_app_delegate.m index 74e8705bc3f6..53e53cd0c658 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 *services = nil; diff --git a/platform/ios/main.m b/platform/ios/main.m index 33b1034d9889..89a00c9ae94a 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 936adddda351..0032b898d2a5 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 &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 &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 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 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 21bd81ed2f97..bbc55b82ce0c 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 &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 *r_options) const override; virtual List get_binary_extensions(const Ref &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 &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override; virtual Error export_project(const Ref &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 2ff02d2e74a1..057fb4ec1653 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 &p_pres List 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 &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 &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 22487d2756a7..5e046d7050db 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 &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 &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 &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 46c5ce4de112..d352fa469225 100644 --- a/platform/web/audio_driver_web.h +++ b/platform/web/audio_driver_web.h @@ -96,6 +96,7 @@ class AudioDriverWeb : public AudioDriver { virtual void stop_sample_playback(const Ref &p_playback) override; virtual void set_sample_playback_pause(const Ref &p_playback, bool p_paused) override; virtual bool is_sample_playback_active(const Ref &p_playback) override; + virtual double get_sample_playback_position(const Ref &p_playback) override; virtual void update_sample_playback_pitch_scale(const Ref &p_playback, float p_pitch_scale = 0.0f) override; virtual void set_sample_playback_bus_volumes_linear(const Ref &p_playback, const HashMap> &p_bus_volumes) override; diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp index 40de4e523b0a..4e55cc137a32 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 2cee3e8110de..8fcabb21c74e 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 d83e465e8e54..d8c1b6033d0a 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 &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 &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 dd5bec00cf9d..4961ebd2bb78 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 8c4e1b1b2480..61b488cf81cb 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 000000000000..bf3ac4ae2d23 --- /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 8b7c5721969d..0ba6eed46407 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); } /** @@ -459,6 +470,14 @@ class SampleNode { return this._playbackRate; } + /** + * Gets the playback position. + * @returns {number} + */ + getPlaybackPosition() { + return this._playbackPosition; + } + /** * Sets the playback rate. * @param {number} val Value to set. @@ -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; } /** @@ -584,18 +607,74 @@ class SampleNode { return this._sampleNodeBuses.get(bus); } + /** + * 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 88ab9a4af087..f0fe56a9c883 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::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 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 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 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 de5b81395337..26328ba8763c 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 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 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 12694b0155dd..b465bd4ecd7d 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 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 &p_p } bool EditorExportPlatformWindows::has_valid_export_configuration(const Ref &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 &p_preset, String &r_error) const { - String err = ""; + String err; bool valid = true; List options; @@ -793,6 +816,43 @@ bool EditorExportPlatformWindows::has_valid_project_configuration(const Ref 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 c644b1f9e1a8..6ccb4a15a747 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 &p_preset, const String &p_path, bool p_console_icon); Error _code_sign(const Ref &p_preset, const String &p_path); + String _get_exe_arch(const String &p_path) const; + public: virtual Error export_project(const Ref &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; virtual Error modify_template(const Ref &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 9025f53f42b2..40b265785f5c 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 checekd_libs; + HashSet checked_libs; HashSet 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 8e91dce425d2..89a0479de3a8 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 1d3f1ceadaff..bfbdb49f2261 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 04ba550888ad..111f5a7b78c2 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 2929691c045c..c724096607d9 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 @@ class NavigationLink2D : public Node2D { 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 @@ class NavigationLink2D : public Node2D { 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 00be84b622c5..b92978bcad6f 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 6b8fd5b798e3..d866dd4edb85 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 @@ class ShapeCast2D : public Node2D { 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 42660604665e..42f7a75c0a7c 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 2643d03d7478..acaf11ee2757 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -536,6 +536,18 @@ NodePath TileMap::get_cell_scene_path(int p_layer, const Vector2i &p_coords) con TILEMAP_CALL_FOR_LAYER_V(p_layer, NodePath(), get_cell_scene_path, p_coords); } +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 TileMap::get_pattern(int p_layer, TypedArray p_coords_array) { TILEMAP_CALL_FOR_LAYER_V(p_layer, Ref(), get_pattern, p_coords_array); } @@ -931,6 +943,10 @@ void TileMap::_bind_methods() { 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("get_cell_scene_path", "layer", "coords"), &TileMap::get_cell_scene_path); + 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 be5f7668d6dc..69ae40b43698 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -168,6 +168,10 @@ class TileMap : public Node2D { TileData *get_cell_tile_data(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; NodePath get_cell_scene_path(int p_layer, const Vector2i &p_coords) 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 get_pattern(int p_layer, TypedArray p_coords_array); Vector2i map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref p_pattern); diff --git a/scene/2d/tile_map_layer.cpp b/scene/2d/tile_map_layer.cpp index ce48ed9bdfcf..afba1c82ee5a 100644 --- a/scene/2d/tile_map_layer.cpp +++ b/scene/2d/tile_map_layer.cpp @@ -1778,6 +1778,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); @@ -2516,6 +2520,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 TileMapLayer::get_pattern(TypedArray 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 4314d553cd37..f5a356d7f8b2 100644 --- a/scene/2d/tile_map_layer.h +++ b/scene/2d/tile_map_layer.h @@ -440,6 +440,10 @@ class TileMapLayer : public Node2D { TypedArray 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 get_pattern(TypedArray p_coords_array); void set_pattern(const Vector2i &p_position, const Ref p_pattern); diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index 6888462876e7..4d3f494ccf3d 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 8515aacba7c1..c70fa3ca2e7a 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 dbf2ffc1dd06..3e9f940ad648 100644 --- a/scene/3d/camera_3d.h +++ b/scene/3d/camera_3d.h @@ -98,7 +98,39 @@ class Camera3D : public Node3D { RID pyramid_shape; Vector 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 3771b385e59c..2cef607d2968 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 038a78609fc6..3f8b0dfb8ee8 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 dc776ebea255..bebba9a6c04d 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 1867082811ff..e894761f400b 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 @@ class NavigationLink3D : public Node3D { 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 @@ class NavigationLink3D : public Node3D { 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 40e04f0fb458..d7397a932d0d 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 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 2e08afb30db1..86ce8a881a2f 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 c1667221df9f..217ee28cf1ab 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -85,7 +85,15 @@ class Node3D : public Node { 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 xform_change; + SelfList _client_physics_interpolation_node_3d_list; // This Data struct is to avoid namespace pollution in derived classes. @@ -101,8 +109,19 @@ class Node3D : public Node { 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 @@ class Node3D : public Node { List children; List::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> 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 @@ class Node3D : public Node { _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 @@ class Node3D : public Node { 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 @@ class Node3D : public Node { 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 150771545b43..6982df12f67d 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 91445710b350..62e9478527c9 100644 --- a/scene/3d/occluder_instance_3d.h +++ b/scene/3d/occluder_instance_3d.h @@ -49,7 +49,6 @@ class Occluder3D : public Resource { 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 ada238c7f27b..8ad651fdf561 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 19b73e3f7222..9fc5e7167000 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 debug_mesh; @@ -123,6 +121,7 @@ class ShapeCast3D : public Node3D { Ref 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 0d6316ee358b..2476e7d5cd40 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(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 9851214194b8..d5c603112ea0 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 f02cd9b700ec..4fe5dd2385f4 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 f14ae3a2856e..79a01450dd18 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 59ede26ac1b1..9b02c928b7ac 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 e4baae1afbc5..cdc85d2b2d2b 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 c7ef7ed62489..2add35d0098b 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 *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 *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 *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 *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 *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 *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 *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 9a9f71548bba..8e9bcd0d5979 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -61,6 +61,11 @@ Ref Tweener::_get_tween() { return Ref(ObjectDB::get_instance(tween_id)); } +void Tweener::_finish() { + finished = true; + emit_signal(SceneStringName(finished)); +} + void Tweener::_bind_methods() { ADD_SIGNAL(MethodInfo("finished")); } @@ -623,9 +628,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; } } @@ -683,9 +687,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; } } @@ -726,9 +729,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; } @@ -812,9 +814,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 0715ed9b3cc2..ae6d3f88c5a0 100644 --- a/scene/animation/tween.h +++ b/scene/animation/tween.h @@ -50,6 +50,7 @@ class Tweener : public RefCounted { static void _bind_methods(); Ref _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 e90c1aa24516..183c4af95076 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 36c14e03d549..206408e3a779 100644 --- a/scene/audio/audio_stream_player_internal.cpp +++ b/scene/audio/audio_stream_player_internal.cpp @@ -132,7 +132,7 @@ Ref 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 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 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 &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 ec4489067e0f..7d8faeba060e 100644 --- a/scene/audio/audio_stream_player_internal.h +++ b/scene/audio/audio_stream_player_internal.h @@ -53,6 +53,7 @@ class AudioStreamPlayerInternal : public Object { 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 @@ class AudioStreamPlayerInternal : public Object { Ref play_basic(); void seek(float p_seconds); - void stop(); + void stop_basic(); bool is_playing() const; float get_playback_position(); @@ -110,7 +111,7 @@ class AudioStreamPlayerInternal : public Object { 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 d169e82e5dba..2d425490d1f2 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 1da3668ebed6..e9fe78e1629d 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 ac6ebd5cc1fb..422783b01bd7 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 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 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 0396f3ab4afa..5c46abc73261 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 &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 ee195ddef965..2f6372dad58c 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -225,6 +225,21 @@ class Node : public Object { // 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 @@ class Node : public Object { 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 @@ class Node : public Object { 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 &p_event); void _call_shortcut_input(const Ref &p_event); diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index b22a76116953..fc6fc87d7301 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 *E = _node_3d_list.first(); E;) { + Node3D *node_3d = E->self(); + + SelfList *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 *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 *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; } @@ -1766,6 +1824,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 6f0a61ec51ed..7e445411050f 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 @@ class SceneTree : public MainLoop { bool changed = false; }; +#ifndef _3D_DISABLED + struct ClientPhysicsInterpolation { + SelfList::List _node_3d_list; + void physics_process(); + } _client_physics_interpolation; +#endif + Window *root = nullptr; double physics_process_time = 0.0; @@ -315,6 +325,7 @@ class SceneTree : public MainLoop { 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 @@ class SceneTree : public MainLoop { 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 *p_elem); + void client_physics_interpolation_remove_node_3d(SelfList *p_elem); +#endif + SceneTree(); ~SceneTree(); }; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 1302e3c53e66..c85fda237128 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 23843938a48d..c2704a384872 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 4a290db86b5e..8218197fd962 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> &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> NavigationPolygon::_get_polygons() const { TypedArray> 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> NavigationPolygon::_get_outlines() const { void NavigationPolygon::add_polygon(const Vector &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 NavigationPolygon::get_polygon(int p_idx) { RWLockRead read_lock(rwlock); ERR_FAIL_INDEX_V(p_idx, polygons.size(), Vector()); - 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 &p_vertices, const Vector> &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 &p_vertices, const Vector> &p_polygons, const Vector> &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 &p_vertices, const Vector void NavigationPolygon::get_data(Vector &r_vertices, Vector> &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 &r_vertices, Vector> &r_polygons, Vector> &r_outlines) { + RWLockRead read_lock(rwlock); + r_vertices = vertices; + r_polygons = polygons; + r_outlines = outlines; } Ref NavigationPolygon::get_navigation_mesh() { @@ -237,6 +248,31 @@ Ref NavigationPolygon::get_navigation_mesh() { return navigation_mesh; } +void NavigationPolygon::set_outlines(const Vector> &p_outlines) { + RWLockWrite write_lock(rwlock); + outlines = p_outlines; + rect_cache_dirty = true; +} + +Vector> NavigationPolygon::get_outlines() const { + RWLockRead read_lock(rwlock); + return outlines; +} + +void NavigationPolygon::set_polygons(const Vector> &p_polygons) { + RWLockWrite write_lock(rwlock); + polygons = p_polygons; + { + MutexLock lock(navigation_mesh_generation); + navigation_mesh.unref(); + } +} + +Vector> NavigationPolygon::get_polygons() const { + RWLockRead read_lock(rwlock); + return polygons; +} + void NavigationPolygon::add_outline(const Vector &p_outline) { RWLockWrite write_lock(rwlock); outlines.push_back(p_outline); @@ -364,7 +400,7 @@ void NavigationPolygon::make_polygons_from_outlines() { for (List::Element *I = out_poly.front(); I; I = I->next()) { TPPLPoly &tp = I->get(); - struct Polygon p; + Vector p; for (int64_t i = 0; i < tp.GetNumPoints(); i++) { HashMap::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 4e99660b0ee5..f43335b397b3 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 vertices; - struct Polygon { - Vector indices; - }; - Vector polygons; + Vector> polygons; Vector> outlines; Vector> baked_outlines; @@ -109,6 +106,8 @@ class NavigationPolygon : public Resource { Vector get_outline(int p_idx) const; void remove_outline(int p_idx); int get_outline_count() const; + void set_outlines(const Vector> &p_outlines); + Vector> get_outlines() const; void clear_outlines(); #ifndef DISABLE_DEPRECATED @@ -116,7 +115,7 @@ class NavigationPolygon : public Resource { #endif // DISABLE_DEPRECATED void set_polygons(const Vector> &p_polygons); - const Vector> &get_polygons() const; + Vector> get_polygons() const; Vector get_polygon(int p_idx); void clear_polygons(); @@ -155,7 +154,9 @@ class NavigationPolygon : public Resource { void clear(); void set_data(const Vector &p_vertices, const Vector> &p_polygons); + void set_data(const Vector &p_vertices, const Vector> &p_polygons, const Vector> &p_outlines); void get_data(Vector &r_vertices, Vector> &r_polygons); + void get_data(Vector &r_vertices, Vector> &r_polygons, Vector> &r_outlines); NavigationPolygon() {} ~NavigationPolygon() {} diff --git a/scene/resources/2d/tile_set.cpp b/scene/resources/2d/tile_set.cpp index d124577d25b6..dd6ae5096a6b 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 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> 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> 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 c800a164635b..0215c0e6f774 100644 --- a/scene/resources/2d/tile_set.h +++ b/scene/resources/2d/tile_set.h @@ -763,6 +763,7 @@ class TileSetAtlasSource : public TileSetSource { 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 f912d2650daf..b0633c06b964 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 &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 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 &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 split_vertex_normals; LocalVector 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 raycaster = StaticRaycaster::create(); + Ref raycaster = p_raycast_normals ? StaticRaycaster::create() : Ref(); 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> 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> 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(); 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 ImporterMesh::create_navigation_mesh() { } HashMap unique_vertices; - LocalVector face_indices; + Vector> face_polygons; + face_polygons.resize(faces.size()); for (int i = 0; i < faces.size(); i++) { + Vector 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 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 vertices; @@ -1086,16 +1136,7 @@ Ref ImporterMesh::create_navigation_mesh() { Ref nm; nm.instantiate(); - nm->set_vertices(vertices); - - Vector 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())); 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 ff8683449b59..5eb4ee884e75 100644 --- a/scene/resources/3d/importer_mesh.h +++ b/scene/resources/3d/importer_mesh.h @@ -86,6 +86,8 @@ class ImporterMesh : public Resource { 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 @@ class ImporterMesh : public Resource { void set_surface_material(int p_surface, const Ref &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 get_shadow_mesh() const; diff --git a/scene/resources/3d/primitive_meshes.cpp b/scene/resources/3d/primitive_meshes.cpp index ee772f960a00..128822d2cb44 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 be96e86879a5..19d3e202040d 100644 --- a/scene/resources/3d/primitive_meshes.h +++ b/scene/resources/3d/primitive_meshes.h @@ -67,6 +67,9 @@ class PrimitiveMesh : public Mesh { // 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 @@ class PrimitiveMesh : public Mesh { 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 104187775d68..6b65ea4cfb97 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 *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(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 *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 2866ae721962..67ed65df0da6 100644 --- a/scene/resources/navigation_mesh.cpp +++ b/scene/resources/navigation_mesh.cpp @@ -61,12 +61,12 @@ void NavigationMesh::create_from_mesh(const Ref &p_mesh) { int rlen = iarr.size(); const int *r = iarr.ptr(); + Vector 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> &p_polygons) { + RWLockWrite write_lock(rwlock); + polygons = p_polygons; + notify_property_list_changed(); +} + +Vector> NavigationMesh::get_polygons() const { + RWLockRead read_lock(rwlock); + return polygons; +} + void NavigationMesh::add_polygon(const Vector &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 NavigationMesh::get_polygon(int p_idx) { RWLockRead read_lock(rwlock); ERR_FAIL_INDEX_V(p_idx, polygons.size(), Vector()); - 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 &p_vertices, const Vector> &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 &r_vertices, Vector> &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 0ec2cc1bb1c6..f695ba3d807c 100644 --- a/scene/resources/navigation_mesh.h +++ b/scene/resources/navigation_mesh.h @@ -39,10 +39,7 @@ class NavigationMesh : public Resource { RWLock rwlock; Vector vertices; - struct Polygon { - Vector indices; - }; - Vector polygons; + Vector> polygons; Ref debug_mesh; protected: @@ -194,6 +191,8 @@ class NavigationMesh : public Resource { int get_polygon_count() const; Vector get_polygon(int p_idx); void clear_polygons(); + void set_polygons(const Vector> &p_polygons); + Vector> get_polygons() const; void clear(); diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 4bedcb18200f..1fa52b9c7335 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 *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 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 7ab150c1413a..e2c86869116f 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 0ca4777d5c40..d02dc6aae7dd 100644 --- a/servers/audio/audio_stream.h +++ b/servers/audio/audio_stream.h @@ -219,9 +219,6 @@ class AudioStreamMicrophone : public AudioStream { HashSet playbacks; -protected: - static void _bind_methods(); - public: virtual Ref instantiate_playback() override; virtual String get_stream_name() const override; @@ -229,8 +226,6 @@ class AudioStreamMicrophone : public AudioStream { 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 54840adcae14..f0f894d03b74 100644 --- a/servers/audio_server.cpp +++ b/servers/audio_server.cpp @@ -1379,6 +1379,12 @@ bool AudioServer::is_playback_active(Ref p_playback) { float AudioServer::get_playback_position(Ref 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 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 &p_pl return AudioDriver::get_singleton()->is_sample_playback_active(p_playback); } +double AudioServer::get_sample_playback_position(const Ref &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 &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 fd6cdb451eee..2d6fc6086073 100644 --- a/servers/audio_server.h +++ b/servers/audio_server.h @@ -141,6 +141,7 @@ class AudioDriver { virtual void stop_sample_playback(const Ref &p_playback) {} virtual void set_sample_playback_pause(const Ref &p_playback, bool p_paused) {} virtual bool is_sample_playback_active(const Ref &p_playback) { return false; } + virtual double get_sample_playback_position(const Ref &p_playback) { return false; } virtual void update_sample_playback_pitch_scale(const Ref &p_playback, float p_pitch_scale = 0.0f) {} virtual void set_sample_playback_bus_volumes_linear(const Ref &p_playback, const HashMap> &p_bus_volumes) {} @@ -484,6 +485,7 @@ class AudioServer : public Object { void stop_sample_playback(const Ref &p_playback); void set_sample_playback_pause(const Ref &p_playback, bool p_paused); bool is_sample_playback_active(const Ref &p_playback); + double get_sample_playback_position(const Ref &p_playback); void update_sample_playback_pitch_scale(const Ref &p_playback, float p_pitch_scale = 0.0f); AudioServer(); diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp index b21c6b60f0c0..398ff1e1f3ec 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 17c0771732fc..cdacf8e7e45f 100644 --- a/servers/navigation_server_3d.h +++ b/servers/navigation_server_3d.h @@ -369,6 +369,7 @@ class NavigationServer3D : public Object { 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 ad19f36257e2..cb655f9b04c5 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 d629f2738d9d..b730b2c819af 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 82bc6868718c..83fa4702b78c 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 @@ -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 3e3cf092bf0d..70e3a27ad6e6 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 @@ -1763,7 +1763,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 99622996d457..c5454e748a84 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 &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 &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 ac4fbba75b0e..ddc4d0927903 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 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 3cd397b8ed6f..6ebb85442b10 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 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 ba8aafda6db5..148a556b4613 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 5579a9779217..b2c93acd44dc 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 @@ class RenderSceneDataRD : public RenderSceneData { 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 b02d3def88f3..06753c3fb703 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 +#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(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(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(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(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(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(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(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 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 0039d1447516..53f1f2d24617 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 @@ class RendererSceneCull : public RenderingMethod { static RendererSceneCull *singleton; + /* EVENT QUEUING */ + + void tick(); + void pre_draw(bool p_will_draw); + /* CAMERA API */ struct Camera { @@ -406,8 +412,16 @@ class RendererSceneCull : public RenderingMethod { 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 @@ class RendererSceneCull : public RenderingMethod { 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 @@ class RendererSceneCull : public RenderingMethod { 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 @@ class RendererSceneCull : public RenderingMethod { 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 @@ class RendererSceneCull : public RenderingMethod { 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 instance_interpolate_update_list; + LocalVector instance_transform_update_lists[2]; + LocalVector *instance_transform_update_list_curr = &instance_transform_update_lists[0]; + LocalVector *instance_transform_update_list_prev = &instance_transform_update_lists[1]; + + bool interpolation_enabled = false; + } _interpolation_data; + RendererSceneCull(); virtual ~RendererSceneCull(); }; diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index 22044a8c2d12..da48b24a1a25 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 38ffd5d23de1..52f086e54f95 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -88,6 +88,9 @@ class RenderingDevice : public RenderingDeviceCommons { 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 aa5e7d83cc84..57fbf97d8cce 100644 --- a/servers/rendering/rendering_method.h +++ b/servers/rendering/rendering_method.h @@ -83,6 +83,8 @@ class RenderingMethod { 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 @@ class RenderingMethod { 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 000000000000..6d27a3a0226c --- /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 51ff009eaf5d..86efccef9a97 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 164ec3cc09fe..6e195a80020f 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -802,6 +802,8 @@ class RenderingServerDefault : public RenderingServer { 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 +1050,6 @@ class RenderingServerDefault : public RenderingServer { /* INTERPOLATION */ - virtual void tick() override; virtual void set_physics_interpolation_enabled(bool p_enabled) override; /* EVENT QUEUING */ @@ -1060,6 +1061,8 @@ class RenderingServerDefault : public RenderingServer { 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 0235d72cfa07..66fcefe2287c 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(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 &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 4a67c8f2d2a6..ad3b78546d1d 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -1037,6 +1037,7 @@ class ShaderLanguage { 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 70b585d68342..9fc67b04b1d7 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 693c82248855..8450cb0198a7 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -1334,6 +1334,8 @@ class RenderingServer : public Object { 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 +1649,6 @@ class RenderingServer : public Object { /* INTERPOLATION */ - virtual void tick() = 0; virtual void set_physics_interpolation_enabled(bool p_enabled) = 0; /* EVENT QUEUING */ @@ -1659,6 +1660,8 @@ class RenderingServer : public Object { 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 a5ab444f554d..1a945ac221f5 100644 --- a/servers/text/text_server_dummy.h +++ b/servers/text/text_server_dummy.h @@ -88,6 +88,7 @@ class TextServerDummy : public TextServerExtension { 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 509d49a1e42a..d387c8ff7ec7 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 16a03b65928b..52654c010ce4 100644 --- a/servers/text/text_server_extension.h +++ b/servers/text/text_server_extension.h @@ -323,8 +323,10 @@ class TextServerExtension : public TextServer { 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 e7a1511064ae..f391c795142b 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 a77953e6f2a5..ba3fdaa35e9b 100644 --- a/servers/text_server.h +++ b/servers/text_server.h @@ -396,6 +396,7 @@ class TextServer : public RefCounted { 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 8fc2489f8b4b..0e1058a62667 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 cf57183a025c..933eeff524d2 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 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 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 keys; - keys.push_back(";"); - keys.push_back(" "); - - Vector 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 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 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 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 keys = { ";", " " }; + const Vector 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 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 keys = { ";", " " }; + const Vector 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 arr = s.split_ints(";"); + CHECK(arr.size() == 2); + for (int i = 0; i < arr.size(); i++) { + CHECK(arr[i] == slices[i]); + } + + const Vector keys = { ";", " " }; + const Vector 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 acdd851b29f9..7c389191e3b2 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 2c20574309e1..ac1599f2e817 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 b2d9f5100e81..69e27fe7a086 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 9a56f6baa41b..bc2964c392f8 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 c0c826b9815a..9ac66d8c690d 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 619b2267606f..19c18cc2a385 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 1f474286760e..cbe100890651 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 b449c7099b96..b9d45689a72f 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 4241682e0b9d..50466abc1bb2 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 f973539cbb87..e87e55b5600e 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 35369a147707..d047c17d9d96 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 + 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(cookie); + *property_value = value; + }, + reinterpret_cast(&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 similarity index 100% rename from thirdparty/spirv-reflect/patches/specialization-constants.patch rename to 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 000000000000..dbf6069d075a --- /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 c96dd854392d..d6c926b40a11 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/version.py b/version.py index f903df3c80da..f6225a2201e5 100644 --- a/version.py +++ b/version.py @@ -1,9 +1,9 @@ short_name = "godot.esuna" name = "Godot Engine [Esuna Edition]" major = 4 -minor = 3 +minor = 4 patch = 0 -status = "stable" +status = "dev" module_config = "" website = "https://godotengine.org" -docs = "4.3" +docs = "latest"